Migration Guide: QDMA to MDB5 DMA

arrow_back Software Docs

7. Code Migration Reference (continued)

Note

The code snippets shown below highlight the core functionality to illustrate the similarities and differences between QDMA and MDB5 DMA implementations.

7.2 Driver Code

7.2.1 Module Initialization

Both drivers follow standard Linux module initialization patterns. QDMA initializes its custom libqdma framework and netlink interface for queue management, while MDB5 DMA integrates with the DMAEngine framework using dma_request_channel() to discover available DMA channels.

QDMA (driver/src/qdma_mod.c) MDB5 DMA (client-driver/mdb5-dmaclient-mod.c)
static struct pci_driver pci_driver = {
    .name = DRV_MODULE_NAME,
    .id_table = pci_ids,
    .probe = probe_one,
    .remove = remove_one,
    .err_handler = &qdma_err_handler,
};

static int __init qdma_mod_init(void)
{
    int rv;
    pr_info("%s", version);

    rv = libqdma_init(num_threads, NULL);
    if (rv < 0)
        return rv;

    rv = xlnx_nl_init();
    if (rv < 0)
        return rv;

    rv = qdma_cdev_init();
    if (rv < 0)
        return rv;

    return pci_register_driver(&pci_driver);
}
static int __init
amd_mdb5_dma_client_mod_init(void)
{
    amd_mdb5_dma_client_init(&mdb5_dma_client);

    if (amd_mdb5_dma_cdev_init() < 0)
        goto init_err;

    ret = amd_mdb5_dma_probe_channels(
        &mdb5_dma_client, dma_dev_id,
        mdb5_dma_client.rd_max_chan,
        mdb5_dma_client.wr_max_chan);
    if (ret)
        goto init_err;

    ret = amd_mdb5_dma_cdev_ctrl_create(
        &mdb5_dma_client.cdev_ctrl,
        mdb5_dma_client.dev);
    // ...
}

7.2.2 Channel Discovery

QDMA creates queues dynamically via netlink commands. MDB5 discovers channels from the static or loadable module dw-edma controller using DMAEngine API.

QDMA (driver/src/nl.c - xnl_q_add) MDB5 DMA (client-driver/mdb5-dmaclient-mod.c)
static int xnl_q_add(struct sk_buff *skb2,
                     struct genl_info *info)
{
    struct xlnx_pci_dev *xpdev = NULL;
    struct qdma_queue_conf qconf;
    char *buf;
    unsigned int num_q, i;
    unsigned short qidx;

    xpdev = xnl_rcv_check_xpdev(info);
    if (!xpdev)
        return -EINVAL;

    rv = qconf_get(&qconf, info, ...);
    num_q = nla_get_u32(
        info->attrs[XNL_ATTR_NUM_Q]);

    for (i = 0; i < num_q; i++) {
        qconf.qidx = qidx + i;
        rv = xpdev_queue_add(xpdev,
            &qconf, cur, end - cur);
        if (rv < 0)
            goto send_resp;
    }
    // ...
}
int amd_mdb5_dma_probe_channels(
    struct amd_mdb5_dma_client *client,
    u16 dev_id, u16 max_rchan,
    u16 max_wchan)
{
    dma_cap_zero(mask);
    dma_cap_set(DMA_SLAVE, mask);
    dma_cap_set(DMA_CYCLIC, mask);

    for (i = 0; i < (max_rchan + max_wchan); i++) {
        snprintf(filter, sizeof(filter),
            "dma%uchan%u", dev_id, i);

        chan = dma_request_channel(mask,
            amd_mdb5_dma_filter_comp, filter);
        if (!chan)
            break;

        hc = kzalloc(sizeof(*hc), GFP_KERNEL);
        hc->dchan = chan;
        // ...
    }
}

7.2.3 DMA Transfer Preparation

QDMA uses custom scatter-gather and request submission through libqdma. MDB5 uses standard DMAEngine dmaengine_prep_slave_sg().

QDMA (driver/src/cdev.c, driver/libqdma/libqdma_export.c) MDB5 DMA (client-driver/mdb5-dmaclient-cdev.c)
static ssize_t cdev_gen_read_write(
    struct file *file, char __user *buf,
    size_t count, loff_t *pos, bool write)
{
    struct qdma_cdev *xcdev = file->private_data;
    struct qdma_io_cb iocb;
    struct qdma_request *req = &iocb.req;
    unsigned long qhndl;

    qhndl = write ? xcdev->h2c_qhndl
                  : xcdev->c2h_qhndl;

    memset(&iocb, 0, sizeof(iocb));
    iocb.buf = buf;
    iocb.len = count;
    rv = map_user_buf_to_sgl(&iocb, write);

    req->sgcnt = iocb.pages_nr;
    req->sgl = iocb.sgl;
    req->write = write ? 1 : 0;
    req->ep_addr = (u64)*pos;
    req->count = count;
    req->timeout_ms = 10 * 1000;
    req->fp_done = NULL; /* blocking */

    res = xcdev->fp_rw(xcdev->xcb->xpdev
        ->dev_hndl, qhndl, req);
    // ...
}
static int mdb5_dma_io_req_prepare(
    struct amd_mdb5_dma_channel *hchan,
    struct amd_mdb5_dma_io_request *io_req,
    struct io_req_param *param)
{
    struct dma_slave_config sconf;

    // Map user buffer to SG list
    mdb5_dma_sgdma_map_user_buf_to_sgl(
        hchan, io_req);
    mdb5_dma_map_page(hchan, io_req);

    // Configure DMA slave
    memset(&sconf, 0, sizeof(sconf));
    sconf.src_addr = io_req->ep_addr;
    sconf.dst_addr = sg_dma_address(
        io_req->sgt.sgl);
    dmaengine_slave_config(dchan, &sconf);

    // Use standard DMAEngine API
    io_req->txdesc = dmaengine_prep_slave_sg(
        dchan, io_req->sgt.sgl,
        io_req->sgt.nents, dir,
        DMA_PREP_INTERRUPT);
    // ...
}

7.2.4 Request Submission

QDMA submits requests through qdma_request_submit() in libqdma. MDB5 uses standard DMAEngine dmaengine_submit() and dma_async_issue_pending().

QDMA (driver/libqdma/libqdma_export.c, driver/src/cdev.c) MDB5 DMA (client-driver/mdb5-dmaclient-cdev.c)
ssize_t qdma_request_submit(
    unsigned long dev_hndl, unsigned long id,
    struct qdma_request *req)
{
    struct xlnx_dma_dev *xdev = 
        (struct xlnx_dma_dev *)dev_hndl;
    struct qdma_descq *descq;
    enum dma_data_direction dir;
    int wait = req->fp_done ? 0 : 1;
    int rv = 0;

    descq = qdma_device_get_descq_by_id(
        xdev, id, NULL, 0, 1);
    if (!descq)
        return -EINVAL;

    dir = (descq->conf.q_type == Q_C2H) ? 
          DMA_FROM_DEVICE : DMA_TO_DEVICE;

    if (!req->dma_mapped) {
        rv = sgl_map(xdev->conf.pdev, 
            req->sgl, req->sgcnt, dir);
        if (rv < 0)
            return rv;
    }

    // Submit to hardware queue
    rv = qdma_descq_proc_sgt_request(
        descq, req);
    
    return rv;
}
static int mdb5_dma_submit_request(
    struct amd_mdb5_dma_channel *hchan,
    struct amd_mdb5_dma_io_request *io_req)
{
    struct dma_async_tx_descriptor *txdesc;
    dma_cookie_t cookie;
    struct dma_chan *dchan = hchan->dchan;

    // Get the prepared descriptor
    txdesc = io_req->txdesc;
    if (!txdesc)
        return -EINVAL;

    // Set completion callback
    txdesc->callback_result = 
        amd_mdb5_dma_comp_result_cb;
    txdesc->callback_param = io_req->comp;

    // Submit the transaction
    cookie = dmaengine_submit(txdesc);
    if (dma_submit_error(cookie)) {
        dev_err(hchan->dev, 
            "Failed to submit DMA transaction\n");
        return -EIO;
    }

    // Issue pending transactions
    dma_async_issue_pending(dchan);

    // Store cookie for tracking
    io_req->cookie = cookie;
    
    return 0;
}