译:DMA-API-HOWTO.txt(待完善)

            Dynamic DMA mapping Guide

            =========================

 

        David S. Miller

        Richard Henderson

         Jakub Jelinek

 

This is a guide to device driver writers onhow to use the DMA API

with example pseudo-code.  For a concise description of the API, see

DMA-API.txt.

本文档是用于介绍如何使用DMA API,以及一些伪代码,关于具体的可以参考DMA-API.txt

                       CPU and DMA addresses

0 CPUDMA 地址

There are several kinds of addressesinvolved in the DMA API, and it's

important to understand the differences.

DMA API中涉及了多个地址的概念,有必要介绍它们之间的区别。

The kernel normally uses virtualaddresses.  Any address returned by

kmalloc(), vmalloc(), and similarinterfaces is a virtual address and can

be stored in a "void *".

1)虚拟地址,内核通常使用的都是虚拟地址,例如kmallocvmalloc或者类似的接口返回的都是虚拟地址,并且存储为void*

The virtual memory system (TLB, pagetables, etc.) translates virtual

addresses to CPU physical addresses, whichare stored as "phys_addr_t" or

"resource_size_t".  The kernel manages device resources likeregisters as

physical addresses.  These are the addresses in /proc/iomem.  The physical

address is not directly useful to a driver;it must use ioremap() to map

the space and produce a virtual address.

2)物理地址:虚拟存储系统中例如TLB、页表等将虚拟地址转换成物理地址,物理地址存储类型为phys_addr_t或者resource_size_t。当然,内核管理具体的硬件资源如寄存器时,操作的是物理地址。这些地址可以通过/proc/iomem文件查看到,需要特别说明的是,物理地址对驱动程序并不直接可用,必须使用ioremap映射出一个虚拟地址,进行操作。

I/O devices use a third kind of address: a"bus address".  If a device has

registers at an MMIO address, or if itperforms DMA to read or write system

memory, the addresses used by the deviceare bus addresses.  In some

systems, bus addresses are identical to CPUphysical addresses, but in

general they are not.  IOMMUs and host bridges can produce arbitrary

mappings between physical and busaddresses.

3)总线地址:IO设备使用第三种地址,总线地址。[关于总线地址,详见另一篇博客]??这段最后翻译????????????????????????

From a device's point of view, DMA uses thebus address space, but it may

be restricted to a subset of thatspace.  For example, even if a system

supports 64-bit addresses for main memoryand PCI BARs, it may use an IOMMU

so devices only need to use 32-bit DMAaddresses.

从设备角度分析,DMA使用的是总线地址空间,但是它也许只是其中的一个子空间,例如尽管计算机系统支持64位地址空间,但对于支持IOMMU的设备而言,只需要使用32位的DMA地址。

Here's a picture and some examples:

译:DMA-API-HOWTO.txt(待完善)_第1张图片


During the enumeration process, the kernellearns about I/O devices and

their MMIO space and the host bridges thatconnect them to the system.  For

example, if a PCI device has a BAR, thekernel reads the bus address (A)

from the BAR and converts it to a CPUphysical address (B).  The address B

is stored in a struct resource and usuallyexposed via /proc/iomem.  When a

driver claims a device, it typically usesioremap() to map physical address

B at a virtual address (C).  It can then use, e.g., ioread32(C), to access

the device registers at bus address A.

内核是能。例如,一个拥有BARPCI设备,内核可以从BAR寄存器中读取总线地址A,并且将其转换为CPU的物理地址B。而这个地址B是被存储在一个结构体资源中,通过/proc/iomem文件可以查看到地址B。当对应设备的驱动程序加载时,它可以获取地址B,然后通常通过ioremap函数将物理地址B映射到虚拟地址C。然后通过操作虚拟地址C,从而操作设备总线地址A[要理解这段话,需要掌握PCI的相关知识]

If the device supports DMA, the driver setsup a buffer using kmalloc() or

a similar interface, which returns avirtual address (X).  The virtual

memory system maps X to a physical address(Y) in system RAM.  The driver

can use virtual address X to access thebuffer, but the device itself

cannot because DMA doesn't go through theCPU virtual memory system.

如果设备支持DMA,那么驱动应该通过kmalloc建立一个缓冲区或者类似的接口(kmalloc返回的是虚拟地址X),同时,虚拟存储系统会映射X到系统RAM上的物理地址Y。驱动使用虚拟地址去访问该缓冲区,但是设备自身不能够访问缓冲区,因为DMA不会通过CPU的虚拟地址空间。

In some simple systems, the device can doDMA directly to physical address

Y. But in many others, there is IOMMU hardware that translates DMA

addresses to physical addresses, e.g., ittranslates Z to Y.  This is part

of the reason for the DMA API: the drivercan give a virtual address X to

an interface like dma_map_single(), whichsets up any required IOMMU                                                       

mapping and returns the DMA address Z.  The driver then tells the device to                                               

do DMA to Z, and the IOMMU maps it to thebuffer at address Y in system                                                   

RAM.

在一些简单的系统中,设备可以通过DMA直接访问物理地址Y。但其它系统需要通过IOMMU硬件将DMA地址(总线地址)转换为物理地址。因此,DMA的许多APIdma_map_single()提供了将虚拟地址X转换为DMA地址Z的接口[DMA所操作的地址只能是DMA总线地址]。然后驱动程序通过DMA操作地址Z,然后设备的IOMMU硬件将Z转换为X然后又映射成了系统RAM的物理地址Y

So that Linux can use the dynamic DMAmapping, it needs some help from the                                                

drivers, namely it has to take into accountthat DMA addresses should be                                                  

mapped only for the time they are actuallyused and unmapped after the DMA                                                

transfer.

为了支持Linux使用动态DMA映射,它需要驱动提供支持,即驱动程序编写时需要考虑只能映射它真正使用的那一块,并且在DMA传输结束后立即unmap

The following API will work of course evenon platforms where no such                                                     

hardware exists.

Note that the DMA API works with any busindependent of the underlying                                                    

microprocessor architecture. You should usethe DMA API rather than the                                                   

bus-specific DMA API, i.e., use thedma_map_*() interfaces rather than the                                                

pci_map_*() interfaces.

注意DMAAPI工作是有特定的总线的DMA总线,并不依赖于具体的处理器架构,因此你应该使用DMA API而不是特定的总线的DMA API。例如,使用dma_map_*()接口而不是pci_map_*()接口。

First of all, you should make sure

#include

is in your driver, which provides thedefinition of dma_addr_t.  This type

can hold any valid DMA address for theplatform and should be used

everywhere you hold a DMA address returnedfrom the DMA mapping functions.

首先,驱动程序包含如下头文件

#include,该头文件提供了dma_addr_t的定义,dma_addr_t可以存储任意有意义的DMA地址,并且当使用DMA映射函数返回的DMA地址也需要声明为该类型。

            What memory is DMA'able?

1 DMA可以操作的地址?

The first piece of information you mustknow is what kernel memory can

be used with the DMA mappingfacilities.  There has been an unwritten

set of rules regarding this, and this textis an attempt to finally

write them down.

首先,你必须知道的是哪些内核空间可以被DMA用来映射。有一系列规则的约束,本段试图将它们解释清楚。

If you acquired your memory via the pageallocator

(i.e. __get_free_page*()) or the genericmemory allocators

(i.e. kmalloc() or kmem_cache_alloc()) thenyou may DMA to/from

that memory using the addresses returnedfrom those routines.

This means specifically that you may _not_use the memory/addresses

returned from vmalloc() for DMA.  It is possible to DMA to the

_underlying_ memory mapped into a vmalloc()area, but this requires

walking page tables to get the physicaladdresses, and then

translating each of those pages back to akernel address using

something like __va().  [ EDIT: Update this when we integrate

Gerd Knorr's generic code which does this.]

规则一:如果内存块是通过页分配器(如__get_free_page*()接口函数)或普通的内存分配器(如kmallockmem_cache_alloc()),那么你可以使用DMA进行传输。这就意味着你不能使用vmalloc()返回的地址用来DMA传输[具体原因可以百度vmallockmalloc的区别]。当然如果一定要使用vmalloc是可以的,但是不能直接使用,需要遍历表获取物理地址[就是寻找到可用且符合大小的连续的物理地址空间],然后使用__va()将物理地址转换为虚拟地址。

This rule also means that you may useneither kernel image addresses

(items in data/text/bss segments), normodule image addresses, nor

stack addresses for DMA.  These could all be mapped somewhere entirely

different than the rest of physicalmemory.  Even if those classes of

memory could physically work with DMA,you'd need to ensure the I/O

buffers were cacheline-aligned.  Without that, you'd see cacheline

sharing problems (data corruption) on CPUswith DMA-incoherent caches.

(The CPU could write to one word, DMA wouldwrite to a different one

in the same cache line, and one of themcould be overwritten.)

这条规定也意味着你不能使用内核镜像地址空间(如内核代码段、数据段和BSS段),也不能使用模块地址,也不能是栈地址空间用于DMA。即使强制从这些地址空间中找到了物理地址用于DMA,你也应该确保IO缓冲区是行对齐的。否则,你将会遇到缓存一致性问题,例如CPU也许在某片写了一个字,而DMA写了一个不同的字在该处,从而其中一个会遇到被覆盖的问题。

Also, this means that you cannot take thereturn of a kmap()

call and DMA to/from that.  This is similar to vmalloc().

同样的,你也不能使用kmap的返回值用于DMA传输,因为它和vmalloc的功能是类似的。

What about block I/O and networkingbuffers?  The block I/O and

networking subsystems make sure that thebuffers they use are valid

for you to DMA from/to.

那么阻塞型IO或者网络缓冲区使用DMA传输时呢,这些是分别由自身的系统负责,如阻塞IO系统或者网络系统,保证各自可以用于DMA传输。

           DMA addressing limitations

2 DMA寻址限制

Does your device have any DMA addressinglimitations?  For example, is

your device only capable of driving the loworder 24-bits of address?

If so, you need to inform the kernel ofthis fact.

待操作的设备是有DMA寻址的限制?例如,如果你的设备只能寻址低24位的地址,那么一定要通知内核。

By default, the kernel assumes that yourdevice can address the full

32-bits. For a 64-bit capable device, this needs to be increased.

And for a device with limitations, asdiscussed in the previous

paragraph, it needs to be decreased.

内核默认情况下会认为你的设备可以寻址完整的32比特。如果是64比特的设备,这需要相应的增加。同样,如果你的设备寻址空间如上所述的受限制只有24比特,也需要相应的减少。

Special note about PCI: PCI-X specificationrequires PCI-X devices to

support 64-bit addressing (DAC) for alltransactions.  And at least

one platform (SGI SN2) requires 64-bitconsistent allocations to

operate correctly when the IO bus is inPCI-X mode.

特别注意PCI设备,PCI-X规范标准中要求PCI-X设备支持64比特寻找空间。

For correct operation, you must interrogatethe kernel in your device

probe routine to see if the DMA controlleron the machine can properly

support the DMA addressing limitation yourdevice has.  It is good 

style to do this even if your device holdsthe default setting,

because this shows that you did think aboutthese issues wrt. your

device.

对于正确的操作,你应该在设备驱动的probe函数中查询内核,确定处理器的DMA控制器是否可以支持你的设备所带来的DMA寻址的限制。尽管你的设备有一些默认的设置,这是一个非常好的习惯,因为这说明你确实考虑了

The query is performed via a call todma_set_mask_and_coherent():

intdma_set_mask_and_coherent(struct device *dev, u64 mask);

which will query the mask for bothstreaming and coherent APIs together.

If you have some special requirements, thenthe following two separate

queries can be used instead: 

上述查询内核的过程可以通过调用dma_set_mask_and_coherent函数实现。这个函数同时可以查询流操作和内存一致性API的掩码。[简单说就是可以得到一些设置信息]。如果你有特殊的需求,可以使用以下两个分别查询。

   The query for streaming mappings is performed via a call to

dma_set_mask():

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

The query for consistentallocations is performed via a call

   to dma_set_coherent_mask():

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

通过调用dma_set_mask查询流映射的执行;

通过dma_set_coherent_mask();查询一致性的操作。

Here, dev is a pointer to the device structof your device, and mask  

is a bit mask describing which bits of anaddress your device         

supports. It returns zero if your card can perform DMA properly on   

the machine given the address mask youprovided.  In general, the     

device struct of your device is embedded inthe bus-specific device   

struct of your device.  For example, &pdev->dev is a pointerto the   

device struct of a PCI device (pdev is apointer to the PCI device    

struct of your device).

这里,参数dev是一个指向struct device的指针,而参数mask是一个描述设备支持寻址空间范围的掩码。如果函数返回0,就代表处理器能够支持设备的DMA寻址请求。通常而言,描述具体设备的结构体通常属于描述总线通用设备结构体中的成员变量,例如描述PCI总线设备结构体pdev,而pdev->dev是一个指向设备具体设备的结构体。[没有理解,待研读完代码后再补充pdev->devdev的区别]

If it returns non-zero, your device cannotperform DMA properly on    

this platform, and attempting to do so willresult in undefined       

behavior. You must either use a different mask, or not use DMA.      

如果函数返回非0值,则表示设备不可以使用DMA在该平台上,即处理器不支持设备请求的DMA寻址空间,如果强制使用会造成一些不确定的后果。此时,要么不使用DMA,要么使用其它的请求掩码。

This means that in the failure case, youhave three options:

1) Use another DMA mask, if possible (seebelow).

2) Use some non-DMA mode for data transfer,if possible.

3) Ignore this device and do not initializeit.

因此,如果返回错误值,可以有如下三种选择:

1)  使用其它的DMA掩码;

2)  使用非DMA方式传输数据;

3)  忽略这个设备并且不初始化它;[应该就是不使用]

It is recommended that your driver print akernel KERN_WARNING message

when you end up performing either #2 or#3.  In this manner, if a user

of your driver reports that performance isbad or that the device is not

even detected, you can ask them for thekernel messages to find out

exactly why.

建议如果采用上述2)或者3)的方式,最好打印一条KERN_WARNING级别的信息。如果使用你驱动的开发者认为传输性能差(因为采用了2)),或者无法发现设备(因为采用了3)),你可以让他们查看内核打印信息寻找具体的原因

The standard 32-bit addressing device woulddo something like this:

   if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {

       dev_warn(dev, "mydev: No suitable DMA available\n");

       goto ignore_this_device;

}

标准的32位寻找范围设备的通常会如下处理:代码见上

Another common scenario is a 64-bit capabledevice.  The approach here

is to try for 64-bit addressing, but backdown to a 32-bit mask that

should not fail.  The kernel may fail the 64-bit mask notbecause the

platform is not capable of 64-bitaddressing.  Rather, it may fail in

this case simply because 32-bit addressingis done more efficiently

than 64-bit addressing.  For example, Sparc64 PCI SAC addressing is

more efficient than DAC addressing.

另外一种情况,如果是一个64比特寻址空间的设备,也会使用该方法,但是会使用32位掩码的配置,这样就不会失败。而相反,如果使用64比特的掩码配置,内核也许会返回失败,当请求64比特寻址空间时,但这并不是因为处理器不和我聊天64比特寻址能力,而是因为32位寻址空间比64位寻址空间更加高效。例如,Sparc64 PCI SAC 寻址比DAC寻址更加高效。[ SAC寻址是什么,DAC又是什么]

Here is how you would handle a 64-bitcapable device which can drive

all 64-bits when accessing streaming DMA:

下面就是64比特设备如何操作,如果需要使用DMA流式映射。

   int using_dac;

 

   if (!dma_set_mask(dev, DMA_BIT_MASK(64))) {

       using_dac = 1;

    }else if (!dma_set_mask(dev, DMA_BIT_MASK(32))) {

       using_dac = 0;

    }else {

       dev_warn(dev, "mydev: No suitable DMA available\n");

       goto ignore_this_device;

    }

If a card is capable of using 64-bitconsistent allocations as well,

the case would look like this:

下面就是64比特设备如何使用DMA一致性内存映射。

   int using_dac, consistent_using_dac;

 

   if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {

       using_dac = 1;

       consistent_using_dac = 1;

    }else if (!dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {

       using_dac = 0;

       consistent_using_dac = 0;

    }else {

       dev_warn(dev, "mydev: No suitable DMA available\n");

       goto ignore_this_device;

    }

The coherent mask will always be able toset the same or a smaller mask as

the streaming mask. However for the rarecase that a device driver only

uses consistent allocations, one would haveto check the return value from

dma_set_coherent_mask().

一致性内存映射的掩码通常是相同或者小于流式映射的掩码。但是极少数情况下设备驱动仅使用的相同的,因此需要使用dma_set_coherent_mask()检查返回值确定使用的一致性内存映射还是流式映射。

Finally, if your device can only drive thelow 24-bits of

address you might do something like:

最后如果设备只有低24比特的寻址空间,那建议如下操作。

   if (dma_set_mask(dev, DMA_BIT_MASK(24))) {

       dev_warn(dev, "mydev: 24-bit DMA addressing not available\n");

       goto ignore_this_device;

    }

When dma_set_mask() ordma_set_mask_and_coherent() is successful, and

returns zero, the kernel saves away thismask you have provided.  The

kernel will use this information later whenyou make DMA mappings.

当dma_set_mask或者dma_set_mask_and_coherent返回成功时(返回值为0),那么内核会保存你提供的掩码。内核也将在DMA映射时使用这个掩码。

There is a case which we are aware of atthis time, which is worth

mentioning in this documentation.  If your device supports multiple

functions (for example a sound cardprovides playback and record

functions) and the various different functionshave _different_

DMA addressing limitations, you may wish toprobe each mask and

only provide the functionality which themachine can handle.  It

is important that the last call todma_set_mask() be for the

most specific mask.

Here is pseudo-code showing how this mightbe done:

有一个特殊情况需要注意,如果你的设备支持多功能(例如声卡提供回放和纪录的功能),那么在使用不同功能时,可能有不同的DMA寻址限制,你可以尝试每个功能对应的掩码,根据内核的返回值,确定合适的,但最后一定要记住调用dma_set_mask设置。

下面给出伪代码示范

   #define PLAYBACK_ADDRESS_BITS  DMA_BIT_MASK(32)

   #define RECORD_ADDRESS_BITS DMA_BIT_MASK(24)

 

   struct my_sound_card *card;

   struct device *dev;

 

   ...

   if (!dma_set_mask(dev, PLAYBACK_ADDRESS_BITS)) {

       card->playback_enabled = 1;

    }else {

       card->playback_enabled = 0;

       dev_warn(dev, "%s: Playback disabled due to DMAlimitations\n",

               card->name);

    }

   if (!dma_set_mask(dev, RECORD_ADDRESS_BITS)) {

       card->record_enabled = 1;

    }else {

       card->record_enabled = 0;

       dev_warn(dev, "%s: Record disabled due to DMA limitations\n",

               card->name);

}

A sound card wasused as an example here because this genre of PCI

devices seems tobe littered with ISA chips given a PCI front end,

and thusretaining the 16MB DMA addressing limitations of ISA.

之所以在这里使用声卡作为例子,是因为ISA芯片前端到处都是这类型的PCI设备,并且这样被ISA芯片限制了16MBDMA寻址空间

            Types of DMA mappings

3 DMA映射类型

There are twotypes of DMA mappings:

- Consistent DMAmappings which are usually mapped at driver

  initialization, unmapped at the end and forwhich the hardware should

  guarantee that the device and the CPU canaccess the data

  in parallel and will see updates made by eachother without any

  explicit software flushing.

有两种类型的DMA映射方法:

1)一致性DAM映射,通常在驱动初始化的时候映射,驱动结束时取消,同时保证设备和CPU都能够并行访问数据,并且不用显示软件刷新就可以观察到数据的交互。

Think of "consistent" as "synchronous" or"coherent".

可以将一致性认为是同步的

The current default is to return consistent memory in the low 32

  bits of the DMA space.  However, for future compatibility you should

  set the consistent mask even if this defaultis fine for your

  driver.

目前默认情况下,一致性内存返回低32比特的地址空间,然而出于程序的兼容性考虑,你仍然要使用一致性掩码。

  Good examples of what to use consistentmappings for are:  

     

- Network card DMA ring descriptors.

    - SCSI adapter mailbox command datastructures.

    - Device firmware microcode executed out of

      main memory.

下面的场景适合使用一致性内存映射:

网卡DMA描述符;

SCSI适配器的命令数据结构体;

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

  The invariant these examples all require isthat any CPU store

  to memory is immediately visible to thedevice, and vice

  versa. Consistent mappings guarantee this.

上述例子中要求任何CPU存储数据到内存时,设备立即可见,反之亦然。一致性映射可以保证这点。

  IMPORTANT: Consistent DMA memory does notpreclude the usage of

             proper memory barriers.  The CPU may reorder stores to

         consistent memory just as it maynormal memory.  Example:

         if it is important for the device tosee the first word

         of a descriptor updated before thesecond, you must do

         something like:

       注意:一致性DMA映射并不会阻止合适。[不理解]CPU可能重新排列这一连续的内存。例如,设备如果需要先察觉到描述符的第一个字节更新,然后才是第二个,那么驱动程序应该如下做:

        desc->word0 = address;

        wmb();

        desc->word1 = DESC_VALID;

             in order to get correct behavioron all platforms.

         Also, on some platforms your drivermay need to flush CPU write

         buffers in much the same way as itneeds to flush write buffers

         found in PCI bridges (such as byreading a register's value

         after writing it).

这样才可以支持在所有平台上正确执行。当然在某些平台上驱动也应该刷新CPU的写缓存,就像需要刷新PCI桥中的写缓存一样。

- Streaming DMAmappings which are usually mapped for one DMA

  transfer, unmapped right after it (unless youuse dma_sync_* below)

  and for which hardware can optimize forsequential accesses.

2)流式DMA映射,通常开启传输时映射,传输结束时取消映射(如果使用dma_sync_*就不需要),硬件上可以优化访问顺序。

  Think of "streaming" as"asynchronous" or "outside the coherency

  domain".

  Good examples of what to use streamingmappings for are:

    - Networking buffers transmitted/receivedby a device.

    - Filesystem buffers written/read by a SCSIdevice.

可以认为流式映射就像异步的或非一致性的映射。

下面的应用场景适合使用流式映射。

       -设备的网络发送、接收缓冲区;

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

  The interfaces for using this type of mappingwere designed in

  such a way that an implementation can makewhatever performance

  optimizations the hardware allows.  To this end, when using

  such mappings you must be explicit about whatyou want to happen.

使用这种映射类型是可以实现硬件上允许的最大程度的优化。当使用这种映射时,开发人员也必须十分清楚自己做的操作。

Neither type ofDMA mapping has alignment restrictions that come from

the underlyingbus, although some devices may have such restrictions.

Also, systemswith caches that aren't DMA-coherent will work better

when theunderlying buffers don't share cache lines with other data.

上述两种DMA映射都不会因为总线而受到对齐的限制,当然一些设备也许会有一些限制。当缓存没有和其它数据共享cache时,不加DMA一致性映射的缓存会变得更加高效,[翻译的太别扭了]

 

         Using Consistent DMA mappings.

4 使用DMA一致性映射

To allocate andmap large (PAGE_SIZE or so) consistent DMA regions,

you should do:

为了分配和映射大的连续的DMA区域,应该按如下方式使用。

    dma_addr_t dma_handle;

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

where device isa struct device *. This may be called in interrupt

context with theGFP_ATOMIC flag.

参数devstruct device类型的,这可以在中断的上下文中调用,使用GFP_ATOMIC的标志。

Size is thelength of the region you want to allocate, in bytes.

参数size是你想申请的缓冲区的大小,按字节单位;

This routinewill allocate RAM for that region, so it acts similarly to

__get_free_pages()(but takes size instead of a page order). If your

driver needsregions sized smaller than a page, you may prefer using

the dma_poolinterface, described below.

这个方法将分配足够的RAM,因此它执行类似于__get_free_pages,如果你的驱动程序需要小于一页大小的RAM,那么你最后如下使用dma_pool大小。

The consistentDMA mapping interfaces, for non-NULL dev, will by

default return aDMA address which is 32-bit addressable. Even if the

device indicates(via DMA mask) that it may address the upper 32-bits,

consistentallocation will only return > 32-bit addresses for DMA if

the consistentDMA mask has been explicitly changed via

dma_set_coherent_mask().  This is true of the dma_pool interface as

well.

上述DMA映射接口函数dma_alloc_coherent,如果参数dev是非NULL值,那么将会返回32位大小的DMA地址。虽然在此之前也许通过其它接口函数申请了高于32比特的地址空间(如通过DMA掩码),但是只有当一致性掩码通过dma_set_coherent_mask显示的修改,才会返回大于32位的地址。这样同样适用于dma_pool接口函数。

dma_alloc_coherent()returns two values: the virtual address which you

can use toaccess it from the CPU and dma_handle which you pass to the

card.

Dma_alloc_coherent返回两个值,处理器能够访问的虚拟地址以及一个设备卡能够使用的dma_handle

The CPU virtualaddress and the DMA address are both                  

guaranteed to bealigned to the smallest PAGE_SIZE order which        

is greater thanor equal to the requested size.  Thisinvariant       

exists (forexample) to guarantee that if you allocate a chunk        

which is smallerthan or equal to 64 kilobytes, the extent of the     

buffer youreceive will not cross a 64K boundary. 

CPU的虚拟地址和DMA地址都保证是向最小页PAGE_SIZE幂次方对齐的,因此返回的缓冲区大小可以超过或者等于申请的大小。这个可以保证如果你申请小于或者等于64KB时,那么返回的缓冲区大小不会超过64KB

To unmap andfree such a DMA region, you call:       

dma_free_coherent(dev, size, cpu_addr, dma_handle);

where dev, sizeare the same as in the above call and cpu_addr and

dma_handle arethe values dma_alloc_coherent() returned to you.

This functionmay not be called in interrupt context.   

为了释放和取消一块内存的DMA映射,需要调用如下函数,

Devsize和上述是函数是相同的含意,cpu_addrdma_handledma_alloc_coherent的返回值。这个函数不允许在中断上下文中调用。

If your driverneeds lots of smaller memory regions, you can write

custom code tosubdivide pages returned by dma_alloc_coherent(),

or you can usethe dma_pool API to do that.  A dma_poolis like

a kmem_cache,but it uses dma_alloc_coherent(), not __get_free_pages().

Also, itunderstands common hardware constraints for alignment,

like queue headsneeding to be aligned on N byte boundaries.

Create adma_pool like this:

如果驱动程序需要很多小块的内存缓冲区,你可以写一些代码将通过dma_alloc_coherent()返回的地址切分成许多小页,或者可以使用dma_poolAPI完成这些事情。一个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, boundary);

The"name" is for diagnostics (like a kmem_cache name); dev and size

are asabove.  The device's hardware alignmentrequirement for this

type of data is"align" (which is expressed in bytes, and must be a

power oftwo).  If your device has no boundarycrossing restrictions,

pass 0 forboundary; passing 4096 says memory allocated from this pool

must not cross4KByte boundaries (but at that time it may be better to

usedma_alloc_coherent() directly instead).

参数“name”是标识,devsize如上所述。Align是设备硬件需要对齐的大小,如果你的设备没有边界限制,则可以设置为0 ,而传入4096则是表示内存池分配的内存不能超过4KB,但建议使用dma_alloc_coherent函数。

Allocate memoryfrom a DMA pool like this:

DMA池中分配内存,可以如下调用:

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

flags areGFP_KERNEL if blocking is permitted (not in_interrupt nor

holding SMPlocks), GFP_ATOMIC otherwise.  Likedma_alloc_coherent(),

this returns twovalues, cpu_addr and dma_handle.

如果允许阻塞,那么参数flags应该是GFP_KERNEL,相反则是GFP_ATOMIC。如dma_alloc_coherent一样,该函数返回两个会下cpu_addrdma_handle

Free memory thatwas allocated from a dma_pool like this:

DMA池中释放内存,则按如下调用

    dma_pool_free(pool, cpu_addr, dma_handle);

where pool iswhat you passed to dma_pool_alloc(), and cpu_addr and

dma_handle arethe values dma_pool_alloc() returned. This function

may be called ininterrupt context.

参数pool是调用dma_pool_alloc函数传入的参数,cpu_addrdma_handledma_pool_alloc的返回值,可以在中断上下文中调用。

Destroy adma_pool by calling:

销毁dma_pool的方法如下:

    dma_pool_destroy(pool);

Make sure you'vecalled dma_pool_free() for all memory allocated

from a poolbefore you destroy the pool. This function may not

be called ininterrupt context.

请保证在调用之前都已经调用了dma_pool_free释放申请的所有内存资源。这个函数不能在中断上下文中调用。

            DMA Direction

5 DMA传输方向

The interfacesdescribed in subsequent portions of this document

take a DMAdirection argument, which is an integer and takes on

one of thefollowing values:

接下来讨论DMA传输方向参数的讨论,整数类型,如下值参考。

 DMA_BIDIRECTIONAL

 DMA_TO_DEVICE

 DMA_FROM_DEVICE

 DMA_NONE

You shouldprovide the exact DMA direction if you know it.

一定要准确提供DMA传输方向;

DMA_TO_DEVICEmeans "from main memory to the device"

DMA_FROM_DEVICEmeans "from the device to main memory"

It is thedirection in which the data moves during the DMA

transfer.

DMA_TO_DEVICE表示从主存到设备;

DMA_FROM_DEVICE表示从设备到主存;

You are _strongly_encouraged to specify this as precisely

as you possiblycan.

一定要明确传输的方向;

If youabsolutely cannot know the direction of the DMA transfer,

specifyDMA_BIDIRECTIONAL.  It means that the DMAcan go in

eitherdirection.  The platform guarantees thatyou may legally

specify this,and that it will work, but this may be at the

cost ofperformance for example.

如果你不能明确DMA传输方向,那么可以指定DMA_BIDIRECTIONAL,这意味着DMA可以任意方向传输,同时平台保证这是合法的,但是会牺牲一定的性能。

The valueDMA_NONE is to be used for debugging. One can

hold this in adata structure before you come to know the

precisedirection, and this will help catch cases where your

directiontracking logic has failed to set things up properly.

Anotheradvantage of specifying this value precisely (outside of

potentialplatform-specific optimizations of such) is for debugging.

Some platformsactually have a write permission boolean which DMA

mappings can bemarked with, much like page protections in the user

program addressspace.  Such platforms can and do reporterrors in the

kernel logs whenthe DMA controller hardware detects violation of the

permissionsetting.

DMA_NONE是用于调试。在明确方向前,可以保存在一个数据结构中,它会帮助我们发现某方向跟踪逻辑失败信号,这样再设置合适的方向值。另外一优点就是可以准确的调试。例如有些平台可以设置DMA映射区的写权限,就像保护用户可编程区域一样。当DMA控制器可以通过这个选项,检测到违反权限,这样平台会在内核日志中汇报错误信息。

Only streaming mappingsspecify a direction, consistent mappings

implicitly havea direction attribute setting of

DMA_BIDIRECTIONAL.

注意:仅仅流式映射需要指定方向,对于一致性映射可以指定为DMA_BIDRECIONAL

The SCSIsubsystem tells you the direction to use in the

'sc_data_direction'member of the SCSI command your driver is

working on.

For Networkingdrivers, it's a rather simple affair. For transmit

packets,map/unmap them with the DMA_TO_DEVICE direction

specifier.  For receive packets, just the opposite,map/unmap them

with theDMA_FROM_DEVICE direction specifier.

对于SCSI系统可以通过SCSI命令变量中的sc_data_direction成员明确传输方向。

对于网络驱动,就更加简单了。发送时,使用DMA_TO_DEVICE,接收则是DMA_FROM_DEVICE选项。

          Using Streaming DMA mappings

6 流映射方法

The streamingDMA mapping routines can be called from interrupt

context.  There are two versions of each map/unmap, onewhich will

map/unmap asingle memory region, and one which will map/unmap a

scatterlist.

流式DAM映射方法可以被中断上下文中调用,在map/unmap时有两种模式,一种是一块内存区域,另外一种是一个集合。

To map a singleregion, you do:

对于映射一块内存区域你可以如下做。

    struct device *dev = &my_dev->dev;

    dma_addr_t dma_handle;

    void *addr = buffer->ptr;

    size_t size = buffer->len;

 

    dma_handle = dma_map_single(dev, addr,size, direction);

    if (dma_mapping_error(dev, dma_handle)) {

        /*

         * reduce current DMA mapping usage,

         * delay and try again later or

         * reset driver.

         */

        goto map_error_handling;

    }

 

and to unmap it:

 

    dma_unmap_single(dev, dma_handle, size,direction);

 

You should calldma_mapping_error() as dma_map_single() could fail and return

error. Not allDMA implementations support the dma_mapping_error() interface.

However, it is agood practice to call dma_mapping_error() interface, which

will invoke thegeneric mapping error check interface. Doing so will ensure

that the mappingcode will work correctly on all DMA implementations without

any dependencyon the specifics of the underlying implementation. Using the

returned addresswithout checking for errors could result in failures ranging

from panics tosilent data corruption. A couple of examples of incorrect ways

to check forerrors that make assumptions about the underlying DMA

implementationare as follows and these are applicable to dma_map_page() as

well.

如果dma_map_single返回错误,可以通过调用dma_mapping_error返回错误码。不是所有的DMA实现都支持dma_mapping_error接口。然后,调用它是一个好的习惯,可以检查映射的是否出错,如此确保映射代码正常工作,无论DMA实现有否。如果返回的地址不加检查直接使用会导致严重错误数据崩溃。如下两个示例就是错误的使用,这些错误同样在dma_map_page中也会出现。

Incorrectexample 1:

错误示例1

    dma_addr_t dma_handle;

 

    dma_handle = dma_map_single(dev, addr,size, direction);

    if ((dma_handle & 0xffff != 0) ||(dma_handle >= 0x1000000)) {

        goto map_error;

    }

 

Incorrectexample 2:

    dma_addr_t dma_handle;

 

    dma_handle = dma_map_single(dev, addr,size, direction);

    if (dma_handle == DMA_ERROR_CODE) {

        goto map_error;

    }

You should calldma_unmap_single() when the DMA activity is finished, e.g.,

from theinterrupt which told you that the DMA transfer is done.

DMA结束时,需要调用dma_unmap_single。而DMA结束的信息可以从DMA传输结束时产生的中断得到。

Using CPUpointers like this for single mappings has a disadvantage:

you cannotreference HIGHMEM memory in this way. Thus, there is a

map/unmapinterface pair akin to dma_{map,unmap}_single().  These

interfaces dealwith page/offset pairs instead of CPU pointers.

Specifically:

CPU指针按哪下single_map时有一些缺点,你不可以指向高端内存。因此,提供了mapunmap接口如dma_{map,unmap}_single一样,这些接口可以处理页的偏移量而不是使用CPU指针;如下

    struct device *dev = &my_dev->dev;

    dma_addr_t dma_handle;

    struct page *page = buffer->page;

    unsigned long offset = buffer->offset;

    size_t size = buffer->len;

 

    dma_handle = dma_map_page(dev, page,offset, size, direction);

    if (dma_mapping_error(dev, dma_handle)) {

        /*

         * reduce current DMA mapping usage,

         * delay and try again later or

         * reset driver.

         */

        goto map_error_handling;

    }

 

    ...

 

    dma_unmap_page(dev, dma_handle, size,direction);

 

Here,"offset" means byte offset within the given page.

参数offset是指在提供的页中偏移多少个字节;

You should calldma_mapping_error() as dma_map_page() could fail and return

error asoutlined under the dma_map_single() discussion.

你也应该使用dma_mapping_error因为dma_map_page可能失败,返回错误,这如上所论述一样,在dma_map_singel

You should calldma_unmap_page() when the DMA activity is finished, e.g.,

from theinterrupt which told you that the DMA transfer is done.

DMA活动结束时,你应该调用dma_unmpa_page函数,

Withscatterlists, you map a region gathered from several regions by:

如果是使用scatterlist而不是一块内存时,需要将scatterlist中若干个区域合并成一个,如下所示。

    int 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);

    }

 

where nents isthe number of entries in the sglist.

Theimplementation is free to merge several consecutive sglist entries

into one (e.g.if DMA mapping is done with PAGE_SIZE granularity, any

consecutivesglist entries can be merged into one provided the first one

ends and thesecond one starts on a page boundary - in fact this is a huge

advantage for cardswhich either cannot do scatter-gather or have very

limited numberof scatter-gather entries) and returns the actual number

of sg entries itmapped them to. On failure 0 is returned.

参数nentssglist中的数量总和,dma_map_sg,这种实现可以很方便地将多个sglist条目合并成一个,例如DMA映射以PAGE_sIZE为单位,任何连续的sglist条目可以合并成一个。它返回sg条目数量,如果失败则会返回0

Then you shouldloop count times (note: this can be less than nents times)

and usesg_dma_address() and sg_dma_len() macros where you previously

accessedsg->address and sg->length as shown above.

然后,根据返回值count,循环调用count次,在每次循环中调用sg_dma_addresssg_dma_len宏,从而获得sg->addresssg->len

To unmap ascatterlist, just call:

取消scatterlist的映射,按如下方法

    dma_unmap_sg(dev, sglist, nents,direction);

Again, make sureDMA activity has already finished.

请确定DMA传输已经结束后调用。

PLEASENOTE:  The 'nents' argument to thedma_unmap_sg call must be

              the _same_ one you passed intothe dma_map_sg call,

          it should _NOT_ be the 'count' value_returned_ from the

              dma_map_sg call.

请注意:dma_unmap_sg中的nents参数必须是与dma_map_sg_call中传入的是一样的,它并不是dma_map_sg函数调用的返回值count

Everydma_map_{single,sg}() call should have its dma_unmap_{single,sg}()

counterpart,because the DMA address space is a shared resource and

you could renderthe machine unusable by consuming all DMA addresses.

每一个dma_map_{single,sg}{}的调用都应该有对应的dma_unmap_{single,sg}(),因为DMA地址空间是一个共享资源,如果你使用了所有DMA地址,那么系统会变得不可用。

If you need touse the same streaming DMA region multiple times and touch

the data inbetween the DMA transfers, the buffer needs to be synced

properly inorder for the CPU and device to see the most up-to-date and

correct copy ofthe DMA buffer.

如果需要多次使用同一块DMA区,同时在DMA传输之间访问数据,那么缓冲区需要按顺序同步。CPU和设备可以始终获得的是最新的DMA缓冲区数据。

So, firstly,just map it with dma_map_{single,sg}(), and after each DMA

transfer calleither:

因此

首先通过dma_map_{single,sg}()映射,然后在DMA传输结束后,调用如下任意一个接口函数。

    dma_sync_single_for_cpu(dev, dma_handle,size, direction);

or:

    dma_sync_sg_for_cpu(dev, sglist, nents,direction);

as appropriate.

Then, if youwish to let the device get at the DMA area again,

finish accessingthe data with the CPU, and then before actually

giving thebuffer to the hardware call either:

然后,如果希望缓冲区在传给硬件前,让设备再次访问缓冲区,完成数据的访问,可以调用如下函数中的任意一个。[理解就是起到同步的作用,需要调用]

    dma_sync_single_for_device(dev, dma_handle,size, direction);

or:

     dma_sync_sg_for_device(dev, sglist, nents,direction);

as appropriate.

PLEASENOTE:  The 'nents' argument todma_sync_sg_for_cpu() and

          dma_sync_sg_for_device() must be thesame passed to

          dma_map_sg(). It is _NOT_ the countreturned by

          dma_map_sg().

请注意:dma_sync_sg_for_cpudma_sync_sg_for_devicenents参数必须和dma_map_sg中的参数一致,注意这不是dma_map_sg的返回值count

After the lastDMA transfer call one of the DMA unmap routines

dma_unmap_{single,sg}().If you don't touch the data from the first

dma_map_*() calltill dma_unmap_*(), then you don't have to call the

dma_sync_*()routines at all.

在最后一次DMA传输结束后调用dma_unmap_{single,sg},当然如果你在mapunmap之间不需要访问数据,你就没有必要调用dma_sync_

Here is pseudocode which shows a situation in which you would need

to use thedma_sync_*() interfaces.

如下演示了一个需要使用dma_sync_系列接口函数的情境的伪代码。

    my_card_setup_receive_buffer(struct my_card *cp, char *buffer, int len)

    {

        dma_addr_t mapping;

 

        mapping = dma_map_single(cp->dev, buffer, len, DMA_FROM_DEVICE);

        if (dma_mapping_error(cp->dev, dma_handle)) {

            /*

             * reduce current DMA mapping usage,

             * delay and try again later or

             * reset driver.

             */

            goto map_error_handling;

        }

        cp->rx_buf = buffer;

        cp->rx_len = len;

        cp->rx_dma = mapping;

 

        give_rx_buf_to_card(cp);

    }

 

    ...

 

    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;

 

            /* Examine the header to see if we wish

             * to accept the data.  But synchronize

             * the DMA transfer with the CPU first

             * so that we see updated contents.

             */

            dma_sync_single_for_cpu(&cp->dev, cp->rx_dma,

                        cp->rx_len,

                        DMA_FROM_DEVICE);

 

            /* Now it is safe to examine the 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 {

                /* CPU should not write to

                 * DMA_FROM_DEVICE-mapped area,

                 * so dma_sync_single_for_device() is

                 * not needed here. It would be required

                 * for DMA_BIDIRECTIONAL mapping if

                 * the memory was modified.

                 */

                give_rx_buf_to_card(cp);

            }

        }

    }

 

       Driversconverted fully to this interface should not use virt_to_bus() any

 longer, nor should they use bus_to_virt().Some drivers have to be changed a

 little bit, because there is no longer anequivalent to bus_to_virt() in the

 dynamic DMA mapping scheme - you have toalways store the DMA addresses

 returned by the dma_alloc_coherent(),dma_pool_alloc(), and dma_map_single()

 calls (dma_map_sg() stores them in thescatterlist itself if the platform

 supports dynamic DMA mapping in hardware) inyour driver structures and/or

 inthe card registers.

      驱动程序不应该再使用virt_to_bus或者bus_to_virt函数,如果驱动程序采用的是DMA动态映射的策略,那么驱动程序不得不做一些改变。因为不存在等价于bus_to_virt的系列函数,你不得不将dma_alloc_coherentdma_pool_allocdma_map_single函数返回的DMA地址存储在驱动结构体或者设备寄存器中。

 Alldrivers should be using these interfaces with no exceptions.  It

 isplanned to completely remove virt_to_bus() and bus_to_virt() as

 theyare entirely deprecated.  Some portsalready do not provide these

 asit is impossible to correctly support them.

       驱动程序不应该再对virt_to_bus类的接口抱有期望,有计划完全移除virt_to_bus或者bus_to_virt,因为已经过时了。一些接口已经不提供类似的掊,因为它是不可能完整的支持他们的。

            Handling Errors

7 错误处理

DMA addressspace is limited on some architectures and an allocation

failure can bedetermined by:

- checking ifdma_alloc_coherent() returns NULL or dma_map_sg returns 0

- checking thedma_addr_t returned from dma_map_single() and dma_map_page()

  by using dma_mapping_error():

DMA地址空间是受体系结构的限制的,一个分配失败可能会因为:

-检查dma_alloc_coherent返回NULL或者0

-检查dma_addr_t,通过使用dma_mapping_error检查dma_map_single还有dma_map_page的返回值;

    dma_addr_t dma_handle;

 

    dma_handle = dma_map_single(dev, addr, size, direction);

    if (dma_mapping_error(dev, dma_handle)) {

        /*

         * reduce current DMA mapping usage,

         * delay and try again later or

         * reset driver.

         */

        goto map_error_handling;

    }

- unmap pagesthat are already mapped, when mapping error occurs in the middle

  of a multiple page mapping attempt. Theseexample are applicable to

  dma_map_page() as well.

-当试图多页映射发生错误时,需要unmap已经map的页。如下例子也可用于dma_map_page

Example 1:

    dma_addr_t dma_handle1;

    dma_addr_t dma_handle2;

 

    dma_handle1 = dma_map_single(dev, addr, size, direction);

    if (dma_mapping_error(dev, dma_handle1)) {

        /*

         * reduce current DMA mapping usage,

         * delay and try again later or

         * reset driver.

         */

        goto map_error_handling1;

    }

    dma_handle2 = dma_map_single(dev, addr, size, direction);

    if (dma_mapping_error(dev, dma_handle2)) {

        /*

         * reduce current DMA mapping usage,

         * delay and try again later or

         * reset driver.

         */

        goto map_error_handling2;

    }

 

    ...

 

    map_error_handling2:

        dma_unmap_single(dma_handle1);

    map_error_handling1:

 

Example 2: (if buffers are allocated in a loop, unmap all mapped buffers when

        mapping error is detected in the middle)

 

    dma_addr_t dma_addr;

    dma_addr_t array[DMA_BUFFERS];

    int save_index = 0;

 

    for (i = 0; i < DMA_BUFFERS; i++) {

 

        ...

 

        dma_addr = dma_map_single(dev, addr, size, direction);

        if (dma_mapping_error(dev, dma_addr)) {

            /*

             * reduce current DMA mapping usage,

             * delay and try again later or

             * reset driver.

             */

            goto map_error_handling;

        }

        array[i].dma_addr = dma_addr;

        save_index++;

    }

 

    ...

 

    map_error_handling:

 

    for (i = 0; i < save_index; i++) {

 

        ...

 

        dma_unmap_single(array[i].dma_addr);

    }

 

Networkingdrivers must call dev_kfree_skb() to free the socket buffer

and returnNETDEV_TX_OK if the DMA mapping fails on the transmit hook

(ndo_start_xmit).This means that the socket buffer is just dropped in

the failurecase.

SCSI driversmust return SCSI_MLQUEUE_HOST_BUSY if the DMA mapping

fails in thequeuecommand hook. This means that the SCSI subsystem

passes thecommand to the driver again later.

如果在传输过程中DMA映射失败,网络设备驱动需要调用dev_kfree_skb来释放套接字缓冲区,并且返回NETDEV_TX_OK。这就意味着套接字缓冲区处于失败的情况。

对于SCSI驱动,如果DMA映射失败,则必须返回SCSI_MLQUEUE_HOST_BUSY,这表示SCSI系统需要过会再尝试传输命令。

         Optimizing Unmap State SpaceConsumption

8 优化unmap的操作

 On many platforms, dma_unmap_{single,page}()is simply a nop.

 Therefore, keeping track of the mappingaddress and length is a waste

 of space. Instead of filling your drivers up with ifdefs and the like

 to "work around" this (which woulddefeat the whole purpose of a

 portable API) the following facilities areprovided.

在许多平台中dma_unmap_{single,page}()仅仅是个符号。因此,跟踪mapping的地址和长度是浪费空间的。取而代之的是填充ifdefs或者诸如”work around”的。提供了如下设备。[没理解,只是照字面意思翻译]

 Actually, instead of describing the macros oneby one, we'll

 transform some example code.

 可以使用如下所示的一些宏定义。

 1) Use DEFINE_DMA_UNMAP_{ADDR,LEN} in statesaving structures.

1)使用DEFINE_DMA_UNMAP的宏存储结构

    Example, before:

 

     struct ring_state {

         struct sk_buff *skb;

         dma_addr_t mapping;

         __u32 len;

     };

 

    after:

 

     struct ring_state {

         struct sk_buff *skb;

         DEFINE_DMA_UNMAP_ADDR(mapping);

         DEFINE_DMA_UNMAP_LEN(len);

     };

 2) Use dma_unmap_{addr,len}_set() to set thesevalues.

2)使用dma_set_unmap_{addr,len}_set设置这些宏

    Example, before:

 

     ringp->mapping = FOO;

     ringp->len = BAR;

 

    after:

 

     dma_unmap_addr_set(ringp, mapping, FOO);

     dma_unmap_len_set(ringp, len, BAR);

  3) Usedma_unmap_{addr,len}() to access these values.

 3)使用dma_unmap_{addr,len}()去访问这些宏

   Example, before:

 

     dma_unmap_single(dev, ringp->mapping,ringp->len,

              DMA_FROM_DEVICE);

 

    after:

 

     dma_unmap_single(dev,

              dma_unmap_addr(ringp, mapping),

              dma_unmap_len(ringp, len),

              DMA_FROM_DEVICE);

 

 It really should be self-explanatory.  We treat the ADDR and LEN

 separately, because it is possible for animplementation to only

 need the address in order to perform the unmapoperation.

显然,需要分别对待ADDRLEN,因为部分unmap的实现只需要地址参数。

             Platform Issues

9 平台相关问题

 If you are just writing drivers for Linux anddo not maintain

 an architecture port for the kernel, you cansafely skip down

 to "Closing".

 如果你只是写一个驱动,而不需要给内核维护一个结构化的驱动,那么可以略过本章。[如果你想建立一个结构化的驱动,那么请遵循下列建议]

 1) Struct scatterlist requirements.

    Don't invent the architecture specificstruct scatterlist; just use

    . You needto enable

    CONFIG_NEED_SG_DMA_LENGTH if thearchitecture supports IOMMUs

    (including software IOMMU).

1)  scatterlist的需求结构

不需要为scatterlist创建一个新的结构,使用就可以,但是如果支持IOMMU,那么需要使能CONFIG_NEED_SG_DMA_LENGTH配置选项。

 2) ARCH_DMA_MINALIGN

    Architectures must ensure that kmalloc'ed buffer is

    DMA-safe. Drivers and subsystems depend on it.If an architecture

    isn't fully DMA-coherent (i.e. hardwaredoesn't ensure that data in

    the CPU cache is identical to data in mainmemory),

    ARCH_DMA_MINALIGN must be set so that thememory allocator

    makes sure that kmalloc'ed buffer doesn'tshare a cache line with

    the others. Seearch/arm/include/asm/cache.h as an example.

     Notethat ARCH_DMA_MINALIGN is about DMA memory alignment

    constraints. You don't need to worry aboutthe architecture data

    alignment constraints (e.g. the alignmentconstraints about 64-bit

    objects).

2)  ARCH_DMA_MINALIGN

驱动的结构必须保证kmalloc的缓冲是DMA安全的,驱动和子系统都依赖于此。如果驱动结构不是完全DMA一致性(例如硬件不能保证在处理器缓存中的数据和主内存中的数据时刻保持一致),那么ARCH_DMA_MINALIGN需要选上,以便存储分配器可以确保kmalloc的缓冲区不会和其它程序共享一个cache line。可以参考arch/arm/include/asm/cache.h作为参考。请注意ARCH_DMA_MINALIGN是和DMA存储对齐有关的。

 3) Supporting multiple types of IOMMUs

    If your architecture needs to supportmultiple types of IOMMUs, you

    can useinclude/linux/asm-generic/dma-mapping-common.h. It's a

    library to support the DMA API withmultiple types of IOMMUs. Lots

    of architectures (x86, powerpc, sh, alpha,ia64, microblaze and

    sparc) use it. Choose one to see how it canbe used. If you need to

    support multiple types of IOMMUs in asingle system, the example of

    x86 or powerpc helps.

3)支持多种类型的IOMMU

 如果架构需要支持多种类型的IOMMU,你可以使用include/linux/asm-generic/dma-mapping-common.h。它是支持多种IOMMUDMA API库,包括x86powerpcshalphaalphax64microblazesparc都在用。可以选择一个学习如何使用,比较好的是x86或者powerpc

                Closing

 

 This document, and the API itself, would notbe in its current

 form without the feedback and suggestions fromnumerous individuals.

 We would like to specifically mention, in noparticular order, the

 following people:

 

     Russell King

     Leo Dagum

     Ralf Baechle

     Grant Grundler

     Jay Estabrook

     Thomas Sailer

     Andrea Arcangeli

     Jens Axboe

     David Mosberger-Tang

 

你可能感兴趣的:(Linux驱动)