☰
Linux DMA驱动详解(二)-----DMA的使用者
2026/10/10 2:36:57 网站建设 项目流程

文章目录

    • 使用者视角的标准四步
    • 选择使用DMA的方法

使用者视角的标准四步

任何外设驱动使用 DMA 都是固定套路,后面读驱动就知道每个回调是被谁、在什么时机调进来的:

/* ① 申请通道(通常开机时做一次) */chan=dma_request_chan(dev,"tx");/* 根据设备树 dmas/dma-names 找到通道 *//* ② 配置通道(告诉 DMA 外设 FIFO 地址、位宽等) */structdma_slave_configcfg={.direction=DMA_MEM_TO_DEV,.dst_addr=i2s_fifo_phys_addr,/* 外设 FIFO 物理地址 */.dst_addr_width=DMA_SLAVE_BUSWIDTH_4_BYTES,};dmaengine_slave_config(chan,&cfg);/* ③ 准备描述符(描述"搬什么")并提交 */desc=dmaengine_prep_slave_sg(chan,sgl,sg_len,DMA_MEM_TO_DEV,flags);desc->callback=my_done_callback;/* 搬完后通知我 */cookie=dmaengine_submit(desc);/* 进入"已提交"队列,还没开始搬! *//* ④ 发令枪:真正启动 */dma_async_issue_pending(chan);

举个例子

staticintguc_adc_dma_request(structdevice*dev,structiio_dev*indio_dev){structguc_adc*info=iio_priv(indio_dev);structdma_slave_configconfig;intret;info->dma_chan=dma_request_chan(dev,"rx");if(IS_ERR(info->dma_chan)){ret=PTR_ERR(info->dma_chan);if(ret!=-ENODEV)returndev_err_probe(dev,ret,"DMA channel request failed with\n");/* DMA is optional: fall back to IRQ mode */info->dma_chan=NULL;return0;}info->rx_buf=dma_alloc_coherent(info->dma_chan->device->dev,GUC_DMA_BUFFER_SIZE,&info->rx_dma_buf,GFP_KERNEL);if(!info->rx_buf){ret=-ENOMEM;gotoerr_release;}/* Configure DMA channel to read data register */memset(&config,0,sizeof(config));config.src_addr=(dma_addr_t)info->phys_addr;config.src_addr+=GUC_CTRL_NOR_STS1;config.src_addr_width=DMA_SLAVE_BUSWIDTH_2_BYTES;config.dst_addr_width=DMA_SLAVE_BUSWIDTH_2_BYTES;config.src_msize=64;config.dst_msize=64;ret=dmaengine_slave_config(info->dma_chan,&config);if(ret)gotoerr_free;return0;err_free:dma_free_coherent(info->dma_chan->device->dev,GUC_DMA_BUFFER_SIZE,info->rx_buf,info->rx_dma_buf);err_release:dma_release_channel(info->dma_chan);returnret;}

选择使用DMA的方法

① 申请通道dma_request_chan(dev,"tx")← 走 DTS dmas/dma-namesdma_request_chan_by_mask(&mask)← 按能力找空闲通道(memcpy 用) │ ② 配置dmaengine_slave_config(chan,&cfg)← 外设 FIFO 地址/位宽/msize │ ③ 描述dmaengine_prep_slave_single()┐dmaengine_prep_slave_sg()├─ 四选一 ★dmaengine_prep_dma_cyclic()│dmaengine_prep_dma_memcpy()┘ desc->callback=fn;← 可选,完成通知 │ ④ 提交启动 cookie=dmaengine_submit(desc)← 仅入队,没开始搬dma_async_issue_pending(chan)← 发令枪 │ ⑤ 收尾dmaengine_tx_status()/回调/轮询 ← 等完成dmaengine_pause()/_resume()dmaengine_terminate_sync()← 中止dma_release_channel(chan)← 释放

mem_to_mem 和 外设的使用是不一样的

/* 路径 1:外设专用通道,走设备树 */chan=dma_request_chan(dev,"rx");↑ 内核查 DTS 的 dmas=<&axi_dmac25>;dma-names="rx";再调驱动的 of_xlate(:1541chan->hw_handshake_num=dma_spec->args[0]) → 拿到的通道被绑定了特定握手号/* 路径 2:任意空闲通道,按能力匹配(memcpy 专用) */dma_cap_zero(mask);dma_cap_set(DMA_MEMCPY,mask);chan=dma_request_chan_by_mask(&mask);↑ 不需要 DTS,因为 M2M 不用握手线 (dma_request_slave_channel()已废弃,新代码别用。)
  • 四种 prep 的差异:
客户端侧 4 个入口 驱动侧只实现 3 个回调 ───────────────── ──────────────────── prep_slave_single ─┐ ├──────> device_prep_slave_sg :892 prep_slave_sg ─────┘ prep_dma_cyclic ─────────> device_prep_dma_cyclic :811 prep_dma_memcpy ─────────> device_prep_dma_memcpy :976
  • 完成通知的三种方式
/* A. 回调(最常用)——在 tasklet 上下文,不能睡眠 */desc->callback=my_done;desc->callback_param=ctx;/* B. 轮询 cookie —— 不要中断时用 */status=dmaengine_tx_status(chan,cookie,&state);/* DMA_COMPLETE / DMA_IN_PROGRESS / DMA_PAUSED;state.residue = 剩余字节 *//* C. completion 同步等待 —— 进程上下文 */desc->callback=complete_fn;/* 回调里 complete(&done) */dma_async_issue_pending(chan);wait_for_completion_timeout(&done,HZ);

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询