in mhi/core/main.c [558:691]
static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
struct mhi_tre *event,
struct mhi_chan *mhi_chan)
{
struct mhi_ring *buf_ring, *tre_ring;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
struct mhi_result result;
unsigned long flags = 0;
u32 ev_code;
ev_code = MHI_TRE_GET_EV_CODE(event);
buf_ring = &mhi_chan->buf_ring;
tre_ring = &mhi_chan->tre_ring;
result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ?
-EOVERFLOW : 0;
/*
* If it's a DB Event then we need to grab the lock
* with preemption disabled and as a write because we
* have to update db register and there are chances that
* another thread could be doing the same.
*/
if (ev_code >= MHI_EV_CC_OOB)
write_lock_irqsave(&mhi_chan->lock, flags);
else
read_lock_bh(&mhi_chan->lock);
if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED)
goto end_process_tx_event;
switch (ev_code) {
case MHI_EV_CC_OVERFLOW:
case MHI_EV_CC_EOB:
case MHI_EV_CC_EOT:
{
dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event);
struct mhi_tre *local_rp, *ev_tre;
void *dev_rp;
struct mhi_buf_info *buf_info;
u16 xfer_len;
if (!is_valid_ring_ptr(tre_ring, ptr)) {
dev_err(&mhi_cntrl->mhi_dev->dev,
"Event element points outside of the tre ring\n");
break;
}
/* Get the TRB this event points to */
ev_tre = mhi_to_virtual(tre_ring, ptr);
dev_rp = ev_tre + 1;
if (dev_rp >= (tre_ring->base + tre_ring->len))
dev_rp = tre_ring->base;
result.dir = mhi_chan->dir;
local_rp = tre_ring->rp;
while (local_rp != dev_rp) {
buf_info = buf_ring->rp;
/* If it's the last TRE, get length from the event */
if (local_rp == ev_tre)
xfer_len = MHI_TRE_GET_EV_LEN(event);
else
xfer_len = buf_info->len;
/* Unmap if it's not pre-mapped by client */
if (likely(!buf_info->pre_mapped))
mhi_cntrl->unmap_single(mhi_cntrl, buf_info);
result.buf_addr = buf_info->cb_buf;
/* truncate to buf len if xfer_len is larger */
result.bytes_xferd =
min_t(u16, xfer_len, buf_info->len);
mhi_del_ring_element(mhi_cntrl, buf_ring);
mhi_del_ring_element(mhi_cntrl, tre_ring);
local_rp = tre_ring->rp;
/* notify client */
mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result);
if (mhi_chan->dir == DMA_TO_DEVICE) {
atomic_dec(&mhi_cntrl->pending_pkts);
/* Release the reference got from mhi_queue() */
mhi_cntrl->runtime_put(mhi_cntrl);
}
/*
* Recycle the buffer if buffer is pre-allocated,
* if there is an error, not much we can do apart
* from dropping the packet
*/
if (mhi_chan->pre_alloc) {
if (mhi_queue_buf(mhi_chan->mhi_dev,
mhi_chan->dir,
buf_info->cb_buf,
buf_info->len, MHI_EOT)) {
dev_err(dev,
"Error recycling buffer for chan:%d\n",
mhi_chan->chan);
kfree(buf_info->cb_buf);
}
}
}
break;
} /* CC_EOT */
case MHI_EV_CC_OOB:
case MHI_EV_CC_DB_MODE:
{
unsigned long pm_lock_flags;
mhi_chan->db_cfg.db_mode = 1;
read_lock_irqsave(&mhi_cntrl->pm_lock, pm_lock_flags);
if (tre_ring->wp != tre_ring->rp &&
MHI_DB_ACCESS_VALID(mhi_cntrl)) {
mhi_ring_chan_db(mhi_cntrl, mhi_chan);
}
read_unlock_irqrestore(&mhi_cntrl->pm_lock, pm_lock_flags);
break;
}
case MHI_EV_CC_BAD_TRE:
default:
dev_err(dev, "Unknown event 0x%x\n", ev_code);
break;
} /* switch(MHI_EV_READ_CODE(EV_TRB_CODE,event)) */
end_process_tx_event:
if (ev_code >= MHI_EV_CC_OOB)
write_unlock_irqrestore(&mhi_chan->lock, flags);
else
read_unlock_bh(&mhi_chan->lock);
return 0;
}