linux dma 例子,Linux之DMA动态映射指南

DMA映射类型

有两种DMA映射类型。一种为一致性DMA映射,另一种为流式DMA映射。

一致性DMA映射通常在驱动初始化时就完成映射,驱动退出时取消映射。硬件应该保证外设及处理器可以并发访问数据,在没有显性软件刷缓存的情况下,双方都可以看到数据的变换。

可以将“一致性”认为“同步的”。

当前默认返回一致性内存为低32位总线地址,但是考虑到未来的兼容性,你应该设置一致性掩码,尽管默认值对于你的驱动来说是可以正常工作的。

使用一致性映射的好的例子如下:

网卡DMA环描述符;

SCSI适配器邮箱命令数据结构;

当超过主内存时的设备固件代码;

这些例子的共性是当处理器存储数据到内存中,设备立即可见,反之亦然。一致性映射可以保证这点。

重要点:一致性DMA内存并不包括使用适当的内存屏障。处理器可能会重新排序存入一致性内存中的数据。比如,设备需要看到一个描述符的第一个字先得到更新了,然后再看到第二个字得到更新,这个顺序非常重要。你应该像下面这样做来保证在所有平台上都可以正常工作:

desc->word0 = address;

wmb();

desc->word1 = DESC_VALID;

DMA流映射通常映射一次DMA传输,传输完后取消映射(除非使用下面所述的dma_sync_*),硬件可以优化访问顺序。

这里的“流”指的是“异步的”或“在一致性区域外的”。

使用流映射好的例子有:

设备传输/接受网络缓冲区;

SCSI设备的读写文件系统缓冲区;

使用流映射的接口用于实现硬件支持的性能优化。到此为止,当使用这种映射时,你需要明确知道你为什么使用它。

使用DMA一致性映射

申请和映射大型(以页为单位)一致性DMA缓冲区,你应该:

dma_addr_t dma_handle;

cpu_addr =dma_alloc_coherent(dev, size, &dma_handle, gfp);

这里device是struct device *。如果使用GFP_ATOMIC标志,则可以在中断上下文调用。

size是你想申请的缓冲区的大小,单位为bytes。

该函数会在内存中申请一块缓冲区,就像调用__get_free_pages(将size替换成页的阶数)一样。如果你的驱动希望获取小于一个页面的缓冲区,可以调用dma_pool接口,下面有详细描述。

DMA一致性映射接口默认会返回一个32位的DMA地址。即使设备申明(通过DMA掩码)它有超过32位的寻址能力,如果一致性DMA掩码显性的通过调用dma_set_coherent_mask()来设置,那么一致性分配会返回一个大于32位DMA地址。对于dma_pool接口也一样。

dma_alloc_coherent返回两个值:一个处理可以使用的虚拟地址和一个设备卡可以使用的dma_handle。

处理器的返回地址及DMA总线地址是最小的页的幂次方对齐,返回的缓冲区的大小会大于或者等于申请的size。比如你申请的缓存区小于或者等于64KB,那么返回的缓冲区大小不会超过64KB。

如果想取消映射和释放该DMA缓冲区,可以调用下面的函数:

dma_free_coherent(dev, size, cpu_addr, dma_handle);

如果你的驱动需要很多小块内存缓冲区,你可以写一些本地代码将dma_alloc_coherent申请的页分成小块,或者可以调用dma_pool 接口函数。dma_pool有点像kmem_cache,但它使用的是dma_alloc_coherent,而不是__get_free_pages。同样地,它也知道通用硬件的对齐限制,比如队列头需要N字节对齐。

创建一个dma_pool:

struct dma_pool *pool;

pool =dma_pool_create(name, dev, size, align, alloc);

name为内存池的名字(就像struct kmem_cache name一样)。dev及size就如dma_alloc_coherent()参数一样。align为设备硬件需要的对齐大小(单位为字节,必须为2的幂次方)。如果设备没有边界限制,可以设置该参数为0。如果设置为4096,则表示从内存池分配的内存不能超过4K字节的边界(这个时候最好直接使用dma_alloc_coherent)。

从DMA内存池中申请内存:

cpu_addr =dma_pool_alloc(pool, flags, &dma_handle);

使用SLAB_KERNEL表示可以阻塞申请内存(不能在中断上下文及持有SMP锁),SLAB_ATOMIC表示无阻塞申请内存。和dma_alloc_coherent一样,它会返回两个值,cpu_addr 及 dma_handle。

释放内存给dma_pool:

dma_pool_free(pool, cpu_addr, dma_handle);

参数pool为传递给dma_pool_alloc()的pool,参数vaddr及addr为dma_pool_alloc()的返回值。

内存池析构函数用于释放内存池的资源:

dma_pool_destroy(pool);

这个函数在可睡眠上下文调用。请确认在调用此函数时,所有从该内存池申请的内存必须都要归还给内存池。

DMA方向

本文提到的DMA方向为一个整型值,如下所示:

DMA_BIDIRECTIONAL

DMA_TO_DEVICE

DMA_FROM_DEVICE

DMA_NONE

如果你清楚DMA方向的话,应该提供一个准确值。

DMA_TO_DEVICE    数据从内存传输到设备。

DMA_FROM_DEVICE    数据从设备传输到内存。

DMA_BIDIRECTIONAL    不清楚传输方向则可用该类型。

DMA_NONE    仅用于调试目的

只有流映射才需要定义方向。一致性内存映射隐性的设置为DMA_BIDIRECTIONAL。

SCSI子系统通过成员'sc_data_direction'来告诉你使用的方向。

网络驱动就更加简单了。对于传输数据,用方向DMA_TO_DEVICE来map/unmap缓冲区。对于接收数据,用方向DMA_FROM_DEVICE来map/unmap缓冲区。0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值