in sdk/linux_kernel_drivers/xdma/libxdma.c [3060:3261]
ssize_t xdma_xfer_submit(void *dev_hndl, int channel, bool write, u64 ep_addr,
struct sg_table *sgt, bool dma_mapped, int timeout_ms)
{
struct xdma_dev *xdev = (struct xdma_dev *)dev_hndl;
struct xdma_engine *engine;
int rv = 0;
ssize_t done = 0;
struct scatterlist *sg = sgt->sgl;
int nents;
enum dma_data_direction dir = write ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
struct xdma_request_cb *req = NULL;
if (!dev_hndl)
return -EINVAL;
if (debug_check_dev_hndl(__func__, xdev->pdev, dev_hndl) < 0)
return -EINVAL;
if (write == 1) {
if (channel >= xdev->h2c_channel_max) {
pr_warn("H2C channel %d >= %d.\n",
channel, xdev->h2c_channel_max);
return -EINVAL;
}
engine = &xdev->engine_h2c[channel];
} else if (write == 0) {
if (channel >= xdev->c2h_channel_max) {
pr_warn("C2H channel %d >= %d.\n",
channel, xdev->c2h_channel_max);
return -EINVAL;
}
engine = &xdev->engine_c2h[channel];
} else {
pr_warn("write %d, exp. 0|1.\n", write);
return -EINVAL;
}
if (unlikely(!engine || (engine->magic != MAGIC_ENGINE))) {
pr_err("bad engine 0x%p, magic 0x%lx.\n",
engine, engine ? engine->magic : 0UL);
return -EINVAL;
}
xdev = engine->xdev;
if (xdma_device_flag_check(xdev, XDEV_FLAG_OFFLINE)) {
pr_info("xdev 0x%p, offline.\n", xdev);
return -EBUSY;
}
/* check the direction */
if (engine->dir != dir) {
pr_info("0x%p, %s, %d, W %d, 0x%x/0x%x mismatch.\n",
engine, engine->name, channel, write, engine->dir, dir);
return -EINVAL;
}
if (!dma_mapped) {
nents = pci_map_sg(xdev->pdev, sg, sgt->orig_nents, dir);
if (!nents) {
pr_info("map sgl failed, sgt 0x%p.\n", sgt);
return -EIO;
}
sgt->nents = nents;
} else {
if (unlikely(!sgt->nents)) {
pr_err("%s, sgt NOT dma_mapped.\n", engine->name);
return -EINVAL;
}
}
req = xdma_init_request(sgt, ep_addr);
if (!req) {
rv = -ENOMEM;
goto unmap_sgl;
}
dbg_tfr("%s, len %u sg cnt %u.\n",
engine->name, req->total_len, req->sw_desc_cnt);
sg = sgt->sgl;
nents = req->sw_desc_cnt;
mutex_lock(&engine->desc_lock);
while (nents) {
unsigned long flags;
struct xdma_transfer *xfer;
/* build transfer */
rv = transfer_init(engine, req);
if (rv < 0) {
mutex_unlock(&engine->desc_lock);
goto unmap_sgl;
}
xfer = &req->xfer;
if (!dma_mapped)
xfer->flags = XFER_FLAG_NEED_UNMAP;
/* last transfer for the given request? */
nents -= xfer->desc_num;
if (!nents) {
xfer->last_in_request = 1;
xfer->sgt = sgt;
}
dbg_tfr("xfer, %u, ep 0x%llx, done %lu, sg %u/%u.\n",
xfer->len, req->ep_addr, done, req->sw_desc_idx,
req->sw_desc_cnt);
#ifdef __LIBXDMA_DEBUG__
transfer_dump(xfer);
#endif
rv = transfer_queue(engine, xfer);
if (rv < 0) {
mutex_unlock(&engine->desc_lock);
pr_info("unable to submit %s, %d.\n", engine->name, rv);
goto unmap_sgl;
}
/*
* When polling, determine how many descriptors have been queued * on the engine to determine the writeback value expected
*/
if (poll_mode) {
unsigned int desc_count;
spin_lock_irqsave(&engine->lock, flags);
desc_count = xfer->desc_num;
spin_unlock_irqrestore(&engine->lock, flags);
dbg_tfr("%s poll desc_count=%d\n",
engine->name, desc_count);
rv = engine_service_poll(engine, desc_count);
} else {
rv = xdma_wait_event_timeout(xfer->wq,
(xfer->state != TRANSFER_STATE_SUBMITTED),
msecs_to_jiffies(timeout_ms));
}
spin_lock_irqsave(&engine->lock, flags);
switch(xfer->state) {
case TRANSFER_STATE_COMPLETED:
spin_unlock_irqrestore(&engine->lock, flags);
dbg_tfr("transfer %p, %u, ep 0x%llx compl, +%lu.\n",
xfer, xfer->len, req->ep_addr - xfer->len, done);
done += xfer->len;
rv = 0;
break;
case TRANSFER_STATE_FAILED:
pr_info("xfer 0x%p,%u, failed, ep 0x%llx.\n",
xfer, xfer->len, req->ep_addr - xfer->len);
spin_unlock_irqrestore(&engine->lock, flags);
#ifdef __LIBXDMA_DEBUG__
transfer_dump(xfer);
sgt_dump(sgt);
#endif
rv = -EIO;
break;
default:
/* transfer can still be in-flight */
pr_info("xfer 0x%p,%u, s 0x%x timed out, ep 0x%llx.\n",
xfer, xfer->len, xfer->state, req->ep_addr);
engine_status_read(engine, 0, 1);
//engine_status_dump(engine);
transfer_abort(engine, xfer);
xdma_engine_stop(engine);
spin_unlock_irqrestore(&engine->lock, flags);
#ifdef __LIBXDMA_DEBUG__
transfer_dump(xfer);
sgt_dump(sgt);
#endif
rv = -ERESTARTSYS;
break;
}
transfer_destroy(xdev, xfer);
if (rv < 0)
break;
} /* while (sg) */
mutex_unlock(&engine->desc_lock);
unmap_sgl:
if (!dma_mapped && sgt->nents) {
pci_unmap_sg(xdev->pdev, sgt->sgl, sgt->orig_nents, dir);
sgt->nents = 0;
}
if (req)
xdma_request_free(req);
if (rv < 0)
return rv;
return done;
}