linux之DMA使用

前言
对于长期从事嵌入式的开发人员来说,DMA是一个在实际中优化设备而老生常谈的问题,我们可以将它比喻为一个小机器人,长期重复与某一个动作的傻瓜式机器人,当我们设置好了出发添加,这个小机器人就会自动去执行相关的业务而不需要CPU的参与,从而大大的减轻了cpu的负载压力。

一、对于使用DMA控制器有什么内存上限制?

dma的内存要求内存上是连续的一片虚拟内存空间,
1、我们不要使用vmalloc以及kmalloc这些通过get_free_page()函数来触发系统异常来分配在内存上可能是不连续的内存地址,而使用dma_map_接口样式的dma接口函数进行dma的内存分配以及相关的操作。
2、使用全局数据来作为dma的内存地址是完全可行的。

例如、

	dma_addr_t dma_handle;
    cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, gfp);//分配一片内存用于DMA
    dma_free_coherent(dev, size, cpu_addr, dma_handle);//使用申请的dma内存

二、DMA的寻址限制

不同的硬件平台对于dma的寻址是有限制的,比如系统平台是32位,而DMA控制器的智能驱动低24位,那么高于16M的内存空间dma是无法寻址的。
一旦确定了dma的寻址限制后,我们可以使用如下函数对接口进行限制、

int dma_set_mask_and_coherent(struct device *dev, u64 mask);

更具dma的特征,dma buffer可分为两种,一种为临时使用,使用完毕就丢弃,另外一种是长时间占据,临时使用我们需要考虑dma的一致性问题,使用如下函数都可以设置dma掩码来解决的一致性问题。

int dma_set_mask(struct device *dev, u64 mask);//设置临时使用的

int dma_set_coherent_mask(struct device *dev, u64 mask);//对于长期使用

三、DMA mapping

1、consistent DMA mapping(一致性dma映射)
它有如下特点、
1.1、在dma控制器初始化时候已经map,在shutdown发送时候自动unmap
1.2、不存在cache一致性问题,当buffer发生更新时候,dma控制器会自动捕获到。
一般使用场景、
(1)、网卡驱动,SCSI等相关的硬件设计可以通过dma控制器可以通过buffer的某些描述符捕获到buffer发生了更新。
2、streaming dma mapping(流式DMA映射)
所谓流式,就是在使用的时候才进行mapping动作,一旦传输完成,立即unmap
这个就很多了,比如spi,i2c,uart等,但是无论使用哪一个,都需要内存进行对其。

四、使用问题
1、dma_map_single
使用dma_map_single进行内存分配使用,
1、需要调用dma_mapping_error进行错误检查
2、dma_unmap_single进行内存释放

2、多个dma map时候可以使用scatterlist进行分配
scatterlist可以将多个不连续的map buffer在内存空间上进行连续分配。

nt i, count = dma_map_sg(dev, sglist, nents, direction);
struct scatterlist *sg;

for_each_sg(sglist, sg, count, i) {
    hw_address[i] = sg_dma_address(sg);
    hw_len[i] = sg_dma_len(sg);
}

//scatterlist分配的buffer进行释放
dma_unmap_sg(dev, sglist, nents, direction);

3、sync操作

如果你需要多次访问同一个streaming DMA buffer,并且在DMA传输之间读写DMA Buffer上的数据,这时候你需要小心进行DMA buffer的sync操作,以便CPU和设备(DMA controller)可以看到最新的、正确的数据。

my_card_interrupt_handler(int irq, void *devid, struct pt_regs *regs)
{
    struct my_card *cp = devid;

    ...
    if (read_card_status(cp) == RX_BUF_TRANSFERRED) {
        struct my_card_header *hp;

//HW已经完成了传输,在cpu访问buffer之前,cpu需要先sync一下,以便看到最新的数据。
        dma_sync_single_for_cpu(&cp->dev, cp->rx_dma,
                    cp->rx_len,
                    DMA_FROM_DEVICE);

//sync之后就可以安全的读dma buffer了
        hp = (struct my_card_header *) cp->rx_buf;
        if (header_is_ok(hp)) {
            dma_unmap_single(&cp->dev, cp->rx_dma, cp->rx_len,
                     DMA_FROM_DEVICE);
            pass_to_upper_layers(cp->rx_buf);
            make_and_setup_new_rx_buf(cp);
        } else {
            give_rx_buf_to_card(cp);
        }
    }
}

注意、
1、在很多的平台上,dma_unmap_{single,page}()其实什么也没有做,是空函数

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值