翻译 DMA-API-HOWTO

=========================
Dynamic DMA mapping Guide 
动态 DMA 映射指南
=========================

:Author: David S. Miller <[email protected]>
:Author: Richard Henderson <[email protected]>
:Author: Jakub Jelinek <[email protected]>

This is a guide to device driver writers on how to use the DMA API
with example pseudo-code.  For a concise description of the API, see
DMA-API.txt.
这是设备驱动程序编写者关于如何使用带有示例伪代码的 DMA API 的指南。 有关 API 的简明描述,请参阅 DMA-API.txt。

CPU and DMA addresses
=====================

There are several kinds of addresses involved in the DMA API, and it's
important to understand the differences.
DMA API 涉及多种地址,了解它们的区别很重要。

The kernel normally uses virtual addresses.  Any address returned by
kmalloc(), vmalloc(), and similar interfaces is a virtual address and can
be stored in a ``void *``.
内核通常使用虚拟地址。 kmalloc()、vmalloc() 和类似接口返回的任何地址都是虚拟地址,可以存储在 ``void *`` 中。

The virtual memory system (TLB, page tables, etc.) translates virtual
addresses to CPU physical addresses, which are stored as "phys_addr_t" or
"resource_size_t".  The kernel manages device resources like registers 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.
虚拟内存系统(TLB、页表等)将虚拟地址转换为 CPU 物理地址,存储为“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 it performs DMA to read or write system
memory, the addresses used by the device are bus addresses.  In some
systems, bus addresses are identical to CPU physical addresses, but in
general they are not.  IOMMUs and host bridges can produce arbitrary
mappings between physical and bus addresses.
I/O 设备使用第三种地址:“总线地址”。 如果设备在 MMIO 地址上有寄存器,
或者如果它执行 DMA 来读取或写入系统内存,则设备使用的地址是总线地址。 
在某些系统中,总线地址与 CPU 物理地址相同,但通常情况并非如此。 
IOMMU 和主机桥可以在物理地址和总线地址之间产生任意映射。

From a device's point of view, DMA uses the bus address space, but it may
be restricted to a subset of that space.  For example, even if a system
supports 64-bit addresses for main memory and PCI BARs, it may use an IOMMU
so devices only need to use 32-bit DMA addresses.
从设备的角度来看,DMA 使用总线地址空间,但它可能仅限于该空间的子集。 
例如,即使系统支持主存和 PCI BAR 的 64 位地址,它也可能使用 IOMMU,因此设备只需要使用 32 位 DMA 地址。

Here's a picture and some examples::

               CPU                  CPU                  Bus
             Virtual              Physical             Address
             Address              Address               Space
              Space                Space

            +-------+             +------+             +------+
            |       |             |MMIO  |   Offset    |      |
            |       |  Virtual    |Space |   applied   |      |
          C +-------+ --------> B +------+ ----------> +------+ A
            |       |  mapping    |      |   by host   |      |
  +-----+   |       |             |      |   bridge    |      |   +--------+
  |     |   |       |             +------+             |      |   |        |
  | CPU |   |       |             | RAM  |             |      |   | Device |
  |     |   |       |             |      |             |      |   |        |
  +-----+   +-------+             +------+             +------+   +--------+
            |       |  Virtual    |Buffer|   Mapping   |      |
          X +-------+ --------> Y +------+ <---------- +------+ Z
            |       |  mapping    | RAM  |   by IOMMU
            |       |             |      |
            |       |             |      |
            +-------+             +------+

During the enumeration process, the kernel learns about I/O devices and
their MMIO space and the host bridges that connect them to the system.  For
example, if a PCI device has a BAR, the kernel reads the bus address (A)
from the BAR and converts it to a CPU physical address (B).  The address B
is stored in a struct resource and usually exposed via /proc/iomem.  When a
driver claims a device, it typically uses ioremap() 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.
在枚举过程中,内核了解 I/O 设备及其 MMIO 空间以及将它们连接到系统的主机桥。 
例如,如果一个 PCI 设备有一个 BAR,内核从 BAR 读取总线地址 (A) 并将其转换为 CPU 物理地址 (B)。 
地址 B 存储在结构资源中,通常通过 /proc/iomem 公开。 当驱动程序声明设备时,
它通常使用 ioremap() 将物理地址 B 映射到虚拟地址 (C)。 然后它可以使用例如 ioread32(C) 来访问总线地址 A 处的设备寄存器。

If the device supports DMA, the driver sets up a buffer using kmalloc() or
a similar interface, which returns a virtual 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 the buffer, but the device itself
cannot because DMA doesn't go through the CPU virtual memory system.
如果设备支持 DMA,则驱动程序使用 kmalloc() 或类似接口设置缓冲区,该接口返回虚拟地址 (X)。 
虚拟内存系统将 X 映射到系统 RAM 中的物理地址 (Y)。 驱动程序可以使用虚拟地址 X 访问缓冲区,
但设备本身不能,因为 DMA 不经过 CPU 虚拟内存系统。

In some simple systems, the device can do DMA directly to physical address
Y.  But in many others, there is IOMMU hardware that translates DMA
addresses to physical addresses, e.g., it translates Z to Y.  This is part
of the reason for the DMA API: the driver can give a virtual address X to
an interface like dma_map_single(), which sets 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 the buffer at address Y in system
RAM.
在一些简单的系统中,设备可以直接对物理地址 Y 进行 DMA。但在许多其他系统中,有 IOMMU 硬件将 DMA 地址转换为物理地址,例如,它将 Z 转换为 Y。
这是 DMA 的部分原因 API:驱动程序可以将虚拟地址 X 提供给 dma_map_single() 之类的接口,该接口设置任何所需的 IOMMU 映射并返回 DMA 地址 Z。
然后驱动程序告诉设备对 Z 进行 DMA,IOMMU 将其映射到 系统 RAM 中地址 Y 处的缓冲区。

So that Linux can use the dynamic DMA mapping, it needs some help from the
drivers, namely it has to take into account that DMA addresses should be
mapped only for the time they are actually used and unmapped after the DMA
transfer.
为了让 Linux 能够使用动态 DMA 映射,它需要一些驱动程序的帮助,
即它必须考虑到 DMA 地址应该只在它们实际使用的时候被映射,而在 DMA 传输之后被取消映射。

The following API will work of course even on platforms where no such hardware exists.
当然,即使在不存在此类硬件的平台上,以下 API 也可以工作。 

Note that the DMA API works with any bus independent of the underlying
microprocessor architecture. You should use the DMA API rather than the
bus-specific DMA API, i.e., use the dma_map_*() interfaces rather than the
pci_map_*() interfaces.
请注意,DMA API 适用于任何独立于底层微处理器架构的总线。 
您应该使用 DMA API 而不是特定于总线的 DMA API,即使用 dma_map_*() 接口而不是 pci_map_*() 接口。

First of all, you should make sure::

	#include <linux/dma-mapping.h>

is in your driver, which provides the definition of dma_addr_t.  This type
can hold any valid DMA address for the platform and should be used
everywhere you hold a DMA address returned from the DMA mapping functions.
首先,驱动程序包含如下头文件 #include<linux/dma-mapping.h>,该头文件提供了 dma_addr_t 的定义。 
这种类型可以保存平台的任何有效 DMA 地址,并且应该在您保存从 DMA 映射函数返回的 DMA 地址的任何地方使用。


What memory is DMA'able?
1 DMA可以操作的地址?
========================

The first piece of information you must know is what kernel memory can
be used with the DMA mapping facilities.  There has been an unwritten
set of rules regarding this, and this text is an attempt to finally
write them down.
首先,你必须知道的是哪些内核空间可以被DMA用来映射。有一系列规则的约束,本段试图将它们解释清楚。

If you acquired your memory via the page allocator
(i.e. __get_free_page*()) or the generic memory allocators
(i.e. kmalloc() or kmem_cache_alloc()) then you may DMA to/from
that memory using the addresses returned from those routines.
如果您通过页面分配器(即 __get_free_page*())或通用内存分配器(即 kmalloc() 或 kmem_cache_alloc())获取内存,
那么您可以使用从这些例程返回的地址对内存进行 DMA 访问。

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 physical addresses, and then
translating each of those pages back to a kernel address using
something like __va().  [ EDIT: Update this when we integrate
Gerd Knorr's generic code which does this. ]
规则一:如果内存块是通过页分配器(如__get_free_page*()接口函数)或普通的内存分配器(如kmalloc或kmem_cache_alloc()),
那么你可以使用DMA进行传输。这就意味着你不能使用vmalloc()返回的地址用来DMA传输[具体原因可以百度vmalloc和kmalloc的区别]。
当然如果一定要使用vmalloc是可以的,但是不能直接使用,需要遍历表获取物理地址[就是寻找到可用且符合大小的连续的物理地址空间],
然后使用__va()将物理地址转换为虚拟地址。

This rule also means that you may use neither kernel image addresses
(items in data/text/bss segments), nor module image addresses, nor
stack addresses for DMA.  These could all be mapped somewhere entirely
different than the rest of physical memory.  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 CPUs with DMA-incoherent caches.
(The CPU could write to one word, DMA would write to a different one
in the same cache line, and one of them could be overwritten.)
这条规定也意味着你不能使用内核镜像地址空间(如内核代码段、数据段和BSS段),也不能使用模块地址,也不能是栈地址空间用于DMA。
即使强制从这些地址空间中找到了物理地址用于DMA,你也应该确保IO缓冲区是行对齐的。否则,你将会遇到缓存一致性问题,
例如CPU也许在某片写了一个字,而DMA写了一个不同的字在该处,从而其中一个会遇到被覆盖的问题。

Also, this means that you cannot take the return of a kmap()
call and DMA to/from that.  This is similar to vmalloc().
同样的,你也不能使用kmap的返回值用于DMA传输,因为它和vmalloc的功能是类似的。

What about block I/O and networking buffers?  The block I/O and
networking subsystems make sure that the buffers 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 addressing limitations?  For example, is
your device only capable of driving the low order 24-bits of address?
If so, you need to inform the kernel of this fact.
待操作的设备是有DMA寻址的限制?例如,如果你的设备只能寻址低24位的地址,那么一定要通知内核。

By default, the kernel assumes that your device can address the full
32-bits.  For a 64-bit capable device, this needs to be increased.
And for a device with limitations, as discussed in the previous
paragraph, it needs to be decreased.
内核默认情况下会认为你的设备可以寻址完整的32bit。如果是64bit的设备,这需要相应的增加。
同样,如果你的设备寻址空间如上所述的受限制只有24比特,也需要相应的减少。

Special note about PCI: PCI-X specification requires PCI-X devices to
support 64-bit addressing (DAC) for all transactions.  And at least
one platform (SGI SN2) requires 64-bit consistent allocations to
operate correctly when the IO bus is in PCI-X mode.
特别注意PCI设备,PCI-X规范标准中要求PCI-X设备支持64比特寻找空间。

For correct operation, you must interrogate the kernel in your device
probe routine to see if the DMA controller on the machine can properly
support the DMA addressing limitation your device has.  It is good
style to do this even if your device holds the default setting,
because this shows that you did think about these issues wrt. your
device.
对于正确的操作,你应该在设备驱动的probe函数中查询内核,确定处理器的DMA控制器是否可以支持你的设备所带来的DMA寻址的限制。
尽管你的设备有一些默认的设置,这是一个非常好的习惯,因为这说明你确实考虑了…

The query is performed via a call to dma_set_mask_and_coherent()::

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

which will query the mask for both streaming and coherent APIs together.
If you have some special requirements, then the 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()::
通过调用dma_set_mask查询流映射的执行;
		int dma_set_mask(struct device *dev, u64 mask);

	The query for consistent allocations is performed via a call
	to dma_set_coherent_mask()::
通过dma_set_coherent_mask();查询一致性的操作。
		int dma_set_coherent_mask(struct device *dev, u64 mask);

Here, dev is a pointer to the device struct of your device, and mask
is a bit mask describing which bits of an address your device
supports.  It returns zero if your card can perform DMA properly on
the machine given the address mask you provided.  In general, the
device struct of your device is embedded in the bus-specific device
struct of your device.  For example, &pdev->dev is a pointer to the
device struct of a PCI device (pdev is a pointer to the PCI device
struct of your device).
在这里,dev 是指向设备的设备结构的指针,而 mask 是一个位掩码,描述了您的设备支持的地址位。 
如果您的卡可以在给定地址掩码的机器上正确执行 DMA,则返回零。通常,描述具体设备的结构体通常属于描述总线通用设备结构体中的成员变量。 
例如,&pdev->dev 是指向 PCI 设备的设备结构的指针(pdev 是指向您设备的 PCI 设备结构的指针)。

If it returns non-zero, your device cannot perform DMA properly on
this platform, and attempting to do so will result 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, you have three options:

1) Use another DMA mask, if possible (see below).
2) Use some non-DMA mode for data transfer, if possible.
3) Ignore this device and do not initialize it.
因此,如果返回错误值,可以有如下三种选择:
1)  使用其它的DMA掩码;
2)  使用非DMA方式传输数据;
3)  忽略这个设备并且不初始化它;[应该就是不使用]


It is recommended that your driver print a kernel KERN_WARNING message
when you end up performing either #2 or #3.  In this manner, if a user
of your driver reports that performance is bad or that the device is not
even detected, you can ask them for the kernel messages to find out
exactly why.
建议如果采用上述2)或者3)的方式,最好打印一条KERN_WARNING级别的信息。如果使用你驱动的开发者认为传输性能差(因为采用了2)),
或者无法发现设备(因为采用了3)),你可以让他们查看内核打印信息寻找具体的原因

The standard 32-bit addressing device would do something like this::
标准的32位寻找范围设备的通常会如下处理:  / GOOD
	if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {
		dev_warn(dev, "mydev: No suitable DMA available\n");
		goto ignore_this_device;
	}

Another common scenario is a 64-bit capable device.  The approach here
is to try for 64-bit addressing, but back down to a 32-bit mask that
should not fail.  The kernel may fail the 64-bit mask not because the
platform is not capable of 64-bit addressing.  Rather, it may fail in
this case simply because 32-bit addressing is done more efficiently
than 64-bit addressing.  For example, Sparc64 PCI SAC addressing is
more efficient than DAC addressing.
另一种常见情况是支持 64 位的设备。 这里的方法是尝试 64 位寻址,如果失败则回退到不应失败的 32 位掩码。 
内核可能无法通过 64 位掩码,而不是因为平台无法进行 64 位寻址。 相反,在这种情况下它可能会失败,
因为 32 位寻址比 64 位寻址更有效。 例如,Sparc64 PCI SAC 寻址比 DAC 寻址更有效。

Here is how you would handle a 64-bit capable 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-bit consistent 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 to set the same or a smaller mask as
the streaming mask. However for the rare case that a device driver only
uses consistent allocations, one would have to check the return value from
dma_set_coherent_mask().
一致性内存映射的掩码通常是相同或者小于流式映射的掩码。然而,对于设备驱动程序仅使用一致分配的罕见情况,必须检查 dma_set_coherent_mask() 的返回值。
因此需要使用 dma_set_coherent_mask() 检查返回值确定使用的一致性内存映射还是流式映射。

Finally, if your device can only drive the low 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() or dma_set_mask_and_coherent() is successful, and
returns zero, the kernel saves away this mask you have provided.  The
kernel will use this information later when you make DMA mappings.
当 dma_set_mask 或者 dma_set_mask_and_coherent 返回成功时(返回值为0),那么内核会保存你提供的掩码。
内核也将在DMA映射时使用这个掩码。

There is a case which we are aware of at this time, which is worth
mentioning in this documentation.  If your device supports multiple
functions (for example a sound card provides playback and record
functions) and the various different functions have _different_
DMA addressing limitations, you may wish to probe each mask and
only provide the functionality which the machine can handle.  It
is important that the last call to dma_set_mask() be for the
most specific mask.
有一个特殊情况需要注意,如果你的设备支持多功能(例如声卡提供回放和录音的功能),
那么在使用不同功能时,可能有不同的DMA寻址限制,你可以尝试每个功能对应的掩码,
根据内核的返回值,确定合适的,但最后一定要记住调用 dma_set_mask 设置。

Here is pseudo-code showing how this might be done::

	#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 DMA limitations\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 was used as an example here because this genre of PCI
devices seems to be littered with ISA chips given a PCI front end,
and thus retaining the 16MB DMA addressing limitations of ISA.
此处使用声卡作为示例,因为这种类型的 PCI 设备似乎到处都是 ISA 芯片给定的 PCI 前端,因此保留了 ISA 的 16MB DMA 寻址限制。


Types of DMA mappings
3 DMA映射类型
=====================

There are two types of DMA mappings:
有两种类型的DMA映射方法:
- Consistent DMA mappings which are usually mapped at driver
  initialization, unmapped at the end and for which the hardware should
  guarantee that the device and the CPU can access the data
  in parallel and will see updates made by each other without any
  explicit software flushing.
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 default is fine for your
  driver.
目前默认情况下,一致性内存返回低32比特的地址空间. 但是,为了将来的兼容性,您应该设置一致性的掩码,即使此默认值适合您的驱动程序。

  Good examples of what to use consistent mappings for are:

	- Network card DMA ring descriptors.
	- SCSI adapter mailbox command data structures.
	- Device firmware microcode executed out of
	  main memory.
下面的场景适合使用一致性内存映射:
 - 网卡DMA描述符;
 - SCSI适配器的命令数据结构体;
 - 超过内存时设备的固件代码;

  The invariant these examples all require is that any CPU store
  to memory is immediately visible to the device, and vice
  versa.  Consistent mappings guarantee this.
上述例子中要求任何CPU存储数据到内存时,设备立即可见,反之亦然。一致性映射可以保证这点。

  .. important::

	     Consistent DMA memory does not preclude the usage of
	     proper memory barriers.  The CPU may reorder stores to
	     consistent memory just as it may normal memory.  Example:
	     if it is important for the device to see the first word
	     of a descriptor updated before the second, you must do
	     something like::
 一致的 DMA 内存并不排除使用适当的内存屏障。 CPU 可以将存储重新排序到一致的内存,就像它可能是正常内存一样。 
 示例:如果设备看到描述符的第一个字在第二个之前更新很重要,则必须执行以下操作:
 
		desc->word0 = address;
		wmb();
		desc->word1 = DESC_VALID;

             in order to get correct behavior on all platforms.
			 这样才可以支持在所有平台上正确执行。

	     Also, on some platforms your driver may need to flush CPU write
	     buffers in much the same way as it needs to flush write buffers
	     found in PCI bridges (such as by reading a register's value
	     after writing it).
		 当然在某些平台上驱动也应该刷新CPU的写缓存,就像需要刷新PCI桥中的写缓存一样。

- Streaming DMA mappings which are usually mapped for one DMA
  transfer, unmapped right after it (unless you use dma_sync_* below)
  and for which hardware can optimize for sequential accesses.
2)流式DMA映射,通常开启传输时映射,传输结束时取消映射(如果使用dma_sync_*就不需要),硬件上可以优化访问顺序。

  Think of "streaming" as "asynchronous" or "outside the coherency
  domain".
可以认为流式映射就像异步的或非一致性的映射。

  Good examples of what to use streaming mappings for are:

	- Networking buffers transmitted/received by a device.
	- Filesystem buffers written/read by a SCSI device.
下面的应用场景适合使用流式映射。
       -设备的网络发送、接收缓冲区;
       -通过SCSI设备读写的文件系统缓冲区;
	   
  The interfaces for using this type of mapping were designed in
  such a way that an implementation can make whatever performance
  optimizations the hardware allows.  To this end, when using
  such mappings you must be explicit about what you want to happen.
  使用这种映射类型是可以实现硬件上允许的最大程度的优化。当使用这种映射时,开发人员也必须十分清楚自己做的操作。

Neither type of DMA mapping has alignment restrictions that come from
the underlying bus, although some devices may have such restrictions.
Also, systems with caches that aren't DMA-coherent will work better
when the underlying buffers don't share cache lines with other data.
上述两种DMA映射都不会因为总线而受到对齐的限制,当然一些设备也许会有一些限制。
当缓存没有和其它数据共享cache时,不加DMA一致性映射的缓存会变得更加高效,[翻译的太别扭了]

两种类型的 DMA 映射都没有来自底层总线的对齐限制,尽管某些设备可能有这样的限制。 
此外,当底层缓冲区不与其他数据共享高速缓存行时,具有非 DMA 一致性高速缓存的系统会更好地工作。


Using Consistent DMA mappings
4 使用DMA一致性映射
=============================

To allocate and map 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 is a ``struct device *``. This may be called in interrupt
context with the GFP_ATOMIC flag.
参数dev是struct device类型的,这可以在中断的上下文中调用,使用GFP_ATOMIC的标志。

Size is the length of the region you want to allocate, in bytes.
参数size是你想申请的缓冲区的大小,按字节单位;

This routine will allocate RAM for that region, so it acts similarly to
__get_free_pages() (but takes size instead of a page order).  If your
driver needs regions sized smaller than a page, you may prefer using
the dma_pool interface, described below.
该例程将为该区域分配 RAM,因此它执行类似于__get_free_pages,
如果你的驱动程序需要小于一页大小的RAM,您可能更喜欢使用 dma_pool 接口,如下所述。

The consistent DMA mapping interfaces, for non-NULL dev, will by
default return a DMA address which is 32-bit addressable.  Even if the
device indicates (via DMA mask) that it may address the upper 32-bits,
consistent allocation will only return > 32-bit addresses for DMA if
the consistent DMA 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 to access it from the CPU and dma_handle which you pass to the
card.
dma_alloc_coherent 返回两个值,处理器能够访问的虚拟地址以及一个设备卡能够使用的dma_handle。

The CPU virtual address and the DMA address are both
guaranteed to be aligned to the smallest PAGE_SIZE order which
is greater than or equal to the requested size.  This invariant
exists (for example) to guarantee that if you allocate a chunk
which is smaller than or equal to 64 kilobytes, the extent of the
buffer you receive will not cross a 64K boundary.
CPU的虚拟地址和DMA地址都保证是向最小页PAGE_SIZE幂次方对齐的,因此返回的缓冲区大小可以超过或者等于申请的大小。
这个可以保证如果你申请小于或者等于64KB时,那么返回的缓冲区大小不会超过64KB。

To unmap and free such a DMA region, you call::
为了释放和取消一块内存的DMA映射,需要调用如下函数,
	dma_free_coherent(dev, size, cpu_addr, dma_handle);

where dev, size are the same as in the above call and cpu_addr and
dma_handle are the values dma_alloc_coherent() returned to you.
This function may not be called in interrupt context.
Dev和size和上述是函数是相同的含意,cpu_addr和dma_handle是dma_alloc_coherent的返回值。这个函数不允许在中断上下文中调用。

If your driver needs lots of smaller memory regions, you can write
custom code to subdivide pages returned by dma_alloc_coherent(),
or you can use the dma_pool API to do that.  A dma_pool is like
a kmem_cache, but it uses dma_alloc_coherent(), not __get_free_pages().
Also, it understands common hardware constraints for alignment,
like queue heads needing to be aligned on N byte boundaries.
如果驱动程序需要很多小块的内存缓冲区,你可以写一些代码将通过 dma_alloc_coherent()返回的地址切分成许多小页,
或者可以使用dma_pool的API完成这些事情。一个dma_pool就像kmem_cache一样,但是它使用的是 dma_alloc_coherent 
而不是__get_free_pages。同时需要知道通用的硬件对齐限制,就像队列头需要N字节对齐。

Create a dma_pool like this::

	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 as above.  The device's hardware alignment requirement for this
type of data is "align" (which is expressed in bytes, and must be a
power of two).  If your device has no boundary crossing restrictions,
pass 0 for boundary; passing 4096 says memory allocated from this pool
must not cross 4KByte boundaries (but at that time it may be better to
use dma_alloc_coherent() directly instead).
参数“name”是标识,dev和size如上所述。Align是设备硬件需要对齐的大小(它以字节表示,并且必须是二的幂),
如果你的设备没有边界限制,则可以设置为0 ,而传入4096则是表示内存池分配的内存不能超过4KB,但建议使用dma_alloc_coherent函数。

Allocate memory from a DMA pool like this::
从DMA池中分配内存,可以如下调用:

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

flags are GFP_KERNEL if blocking is permitted (not in_interrupt nor
holding SMP locks), GFP_ATOMIC otherwise.  Like dma_alloc_coherent(),
this returns two values, cpu_addr and dma_handle.
如果允许阻塞,那么参数flags应该是GFP_KERNEL,相反则是GFP_ATOMIC。
如dma_alloc_coherent一样,该函数返回两个会下cpu_addr和dma_handle。

Free memory that was allocated from a dma_pool like this::
从DMA池中释放内存,则按如下调用

	dma_pool_free(pool, cpu_addr, dma_handle);

where pool is what you passed to dma_pool_alloc(), and cpu_addr and
dma_handle are the values dma_pool_alloc() returned. This function
may be called in interrupt context.
参数pool是调用dma_pool_alloc函数传入的参数,cpu_addr和dma_handle是dma_pool_alloc的返回值,可以在中断上下文中调用。

Destroy a dma_pool by calling::
销毁dma_pool的方法如下:

	dma_pool_destroy(pool);

Make sure you've called dma_pool_free() for all memory allocated
from a pool before you destroy the pool. This function may not
be called in interrupt context.
请保证在调用dma_pool_destroy之前 都已经调用了dma_pool_free释放申请的所有内存资源。这个函数不能在中断上下文中调用。


DMA Direction
5 DMA传输方向
=============

The interfaces described in subsequent portions of this document
take a DMA direction argument, which is an integer and takes on
one of the following values::

 DMA_BIDIRECTIONAL
 DMA_TO_DEVICE
 DMA_FROM_DEVICE
 DMA_NONE

You should provide the exact DMA direction if you know it.
一定要准确提供DMA传输方向;

DMA_TO_DEVICE means "from main memory to the device"
DMA_TO_DEVICE表示从主存到设备;
DMA_FROM_DEVICE means "from the device to main memory"
DMA_FROM_DEVICE表示从设备到主存;
It is the direction in which the data moves during the DMA
transfer.
一定要明确传输的方向;

You are _strongly_ encouraged to specify this as precisely
as you possibly can.

If you absolutely cannot know the direction of the DMA transfer,
specify DMA_BIDIRECTIONAL.  It means that the DMA can go in
either direction.  The platform guarantees that you may legally
specify this, and that it will work, but this may be at the
cost of performance for example.
如果你不能明确DMA传输方向,那么可以指定DMA_BIDIRECTIONAL,这意味着DMA可以任意方向传输,同时平台保证这是合法的,但是会牺牲一定的性能。

The value DMA_NONE is to be used for debugging.  One can
hold this in a data structure before you come to know the
precise direction, and this will help catch cases where your
direction tracking logic has failed to set things up properly.
DMA_NONE是用于调试。在明确方向前,可以保存在一个数据结构中,它会帮助我们发现某方向跟踪逻辑失败信号,
这样再设置合适的方向值。

Another advantage of specifying this value precisely (outside of
potential platform-specific optimizations of such) is for debugging.
Some platforms actually have a write permission boolean which DMA
mappings can be marked with, much like page protections in the user
program address space.  Such platforms can and do report errors in the
kernel logs when the DMA controller hardware detects violation of the
permission setting.
另外一优点就是可以准确的调试。例如有些平台可以设置DMA映射区的写权限,
就像保护用户可编程区域一样。当DMA控制器可以通过这个选项,检测到违反权限,这样平台会在内核日志中汇报错误信息。

Only streaming mappings specify a direction, consistent mappings
implicitly have a direction attribute setting of
DMA_BIDIRECTIONAL.
注意:仅仅流式映射需要指定方向,对于一致性映射可以指定为 DMA_BIDRECIONAL

The SCSI subsystem tells you the direction to use in the
'sc_data_direction' member of the SCSI command your driver is
working on.

For Networking drivers, 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 the DMA_FROM_DEVICE direction specifier.
对于SCSI系统可以通过SCSI命令变量中的 sc_data_direction 成员明确传输方向。
对于网络驱动,就更加简单了。发送时,使用DMA_TO_DEVICE,接收则是DMA_FROM_DEVICE选项。


Using Streaming DMA mappings
6 流映射方法
============================

The streaming DMA mapping routines can be called from interrupt
context.  There are two versions of each map/unmap, one which will
map/unmap a single memory region, and one which will map/unmap a
scatterlist.
流式DAM映射方法可以被中断上下文中调用,在map/unmap时有两种模式,一种是一块内存区域,另外一种是一个集合。

To map a single region, 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 call dma_mapping_error() as dma_map_single() could fail and return
error.  Doing so will ensure that the mapping code will work correctly on all
DMA implementations without any dependency on the specifics of the underlying
implementation. Using the returned address without checking for errors could
result in failures ranging from panics to silent data corruption.  The same
applies to dma_map_page() as well.
如果dma_map_single返回错误,可以通过调用dma_mapping_error返回错误码。
这样做将确保映射代码将在所有 DMA 实现上正常工作,而不依赖于底层实现的细节。 
在不检查错误的情况下使用返回的地址可能会导致从恐慌到静默数据损坏的失败。 这同样适用于 dma_map_page() 。

You should call dma_unmap_single() when the DMA activity is finished, e.g.,
from the interrupt which told you that the DMA transfer is done.
当 DMA 结束时,需要调用 dma_unmap_single(),而DMA结束的信息可以从DMA传输结束时产生的中断得到。

Using CPU pointers like this for single mappings has a disadvantage:
you cannot reference HIGHMEM memory in this way.  Thus, there is a
map/unmap interface pair akin to dma_{map,unmap}_single().  These
interfaces deal with page/offset pairs instead of CPU pointers.
Specifically::
将这样的 CPU 指针用于单个映射有一个缺点:
您不能以这种方式引用 HIGHMEM 内存。 因此,存在类似于 dma_{map,unmap}_single() 的 map/unmap 接口对。 
这些接口处理页/偏移量对而不是 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 call dma_mapping_error() as dma_map_page() could fail and return
error as outlined under the dma_map_single() discussion.
您应该调用 dma_mapping_error() 因为 dma_map_page() 可能会失败并返回错误,如 dma_map_single() 讨论中所述。

You should call dma_unmap_page() when the DMA activity is finished, e.g.,
from the interrupt which told you that the DMA transfer is done.
当DMA结束时,你应该调用dma_unmap_page函数。dma传输完成会产生中断信息

With scatterlists, 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 is the number of entries in the sglist.

The implementation is free to merge several consecutive sglist entries
into one (e.g. if DMA mapping is done with PAGE_SIZE granularity, any
consecutive sglist entries can be merged into one provided the first one
ends and the second one starts on a page boundary - in fact this is a huge
advantage for cards which either cannot do scatter-gather or have very
limited number of scatter-gather entries) and returns the actual number
of sg entries it mapped them to. On failure 0 is returned.
参数nents是sglist中的数量总和,dma_map_sg,这种实现可以很方便地将多个sglist条目合并成一个,
例如DMA映射以PAGE_sIZE为单位,任何连续的sglist条目可以合并成一个。它返回sg条目数量,如果失败则会返回0。

Then you should loop count times (note: this can be less than nents times)
and use sg_dma_address() and sg_dma_len() macros where you previously
accessed sg->address and sg->length as shown above.
然后,根据返回值 count,循环调用count次,在每次循环中调用 sg_dma_address 和 sg_dma_len 宏,从而获得sg->address 和sg->len。

To unmap a scatterlist, just call::
取消scatterlist的映射,按如下方法

	dma_unmap_sg(dev, sglist, nents, direction);

Again, make sure DMA activity has already finished.
请确定DMA传输已经结束后调用。

.. note::

	The 'nents' argument to the dma_unmap_sg call must be
	the _same_ one you passed into the 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;

Every dma_map_{single,sg}() call should have its dma_unmap_{single,sg}()
counterpart, because the DMA address space is a shared resource and
you could render the machine unusable by consuming all DMA addresses.
每一个dma_map_{single,sg}{}的调用都应该有对应的dma_unmap_{single,sg}(),
因为DMA地址空间是一个共享资源,如果你使用了所有DMA地址,那么系统会变得不可用。

If you need to use the same streaming DMA region multiple times and touch
the data in between the DMA transfers, the buffer needs to be synced
properly in order for the CPU and device to see the most up-to-date and
correct copy of the DMA buffer.
如果需要多次使用同一个流式 DMA 区域并在 DMA 传输之间访问数据,那么缓冲区需要按顺序同步。CPU和设备可以始终获得的是最新的DMA缓冲区数据。

So, firstly, just map it with dma_map_{single,sg}(), and after each DMA
transfer call either::
因此,首先通过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 you wish to let the device get at the DMA area again,
finish accessing the data with the CPU, and then before actually
giving the buffer 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.

.. note::

	      The 'nents' argument to dma_sync_sg_for_cpu() and
	      dma_sync_sg_for_device() must be the same passed to
	      dma_map_sg(). It is _NOT_ the count returned by
	      dma_map_sg().
		  请注意:dma_sync_sg_for_cpu 和 dma_sync_sg_for_device 的nents参数必须和 dma_map_sg 中的参数一致,
		  注意这不是 dma_map_sg 的返回值count。

After the last DMA transfer call one of the DMA unmap routines
dma_unmap_{single,sg}(). If you don't touch the data from the first
dma_map_*() call till dma_unmap_*(), then you don't have to call the
dma_sync_*() routines at all.
在最后一次DMA传输结束后调用dma_unmap_{single,sg},当然如果你在map和unmap之间不需要访问数据,你就没有必要调用dma_sync_。

Here is pseudo code which shows a situation in which you would need
to use the dma_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, mapping)) {
			/*
			 * 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);
			}
		}
	}

Drivers converted 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 an equivalent to bus_to_virt() in the
dynamic DMA mapping scheme - you have to always store the DMA addresses
returned by the dma_alloc_coherent(), dma_pool_alloc(), and dma_map_single()
calls (dma_map_sg() stores them in the scatterlist itself if the platform
supports dynamic DMA mapping in hardware) in your driver structures and/or
in the card registers.
驱动程序不应该再使用virt_to_bus或者bus_to_virt函数,如果驱动程序采用的是DMA动态映射的策略,
那么驱动程序不得不做一些改变。因为不存在等价于bus_to_virt的系列函数,
你不得不将dma_alloc_coherent、dma_pool_alloc、dma_map_single函数返回的DMA地址存储在驱动结构体或者设备寄存器中。

All drivers should be using these interfaces with no exceptions.  It
is planned to completely remove virt_to_bus() and bus_to_virt() as
they are entirely deprecated.  Some ports already do not provide these
as it is impossible to correctly support them.
驱动程序不应该再对virt_to_bus类的接口抱有期望,有计划完全移除virt_to_bus或者bus_to_virt,因为已经过时了。
一些接口已经不提供类似的掊,因为它是不可能完整的支持他们的。


Handling Errors
7 错误处理
===============

DMA address space is limited on some architectures and an allocation
failure can be determined by:

- checking if dma_alloc_coherent() returns NULL or dma_map_sg returns 0

- checking the dma_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 pages that are already mapped, when mapping error occurs in the middle
  of a multiple page mapping attempt. These example 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);
	}

Networking drivers must call dev_kfree_skb() to free the socket buffer
and return NETDEV_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 failure case.
如果在传输过程中DMA映射失败,网络设备驱动需要调用dev_kfree_skb来释放套接字缓冲区,并且返回NETDEV_TX_OK。
这就意味着套接字缓冲区处于失败的情况。

SCSI drivers must return SCSI_MLQUEUE_HOST_BUSY if the DMA mapping
fails in the queuecommand hook. This means that the SCSI subsystem
passes the command to the driver again later.
对于SCSI驱动,如果DMA映射失败,则必须返回SCSI_MLQUEUE_HOST_BUSY,这表示SCSI系统需要过会再尝试传输命令。


Optimizing Unmap State Space Consumption
8 优化unmap的操作
========================================

On many platforms, dma_unmap_{single,page}() is simply a nop.
Therefore, keeping track of the mapping address and length is a waste
of space.  Instead of filling your drivers up with ifdefs and the like
to "work around" this (which would defeat the whole purpose of a
portable API) the following facilities are provided.
在许多平台中dma_unmap_{single,page}()仅仅是个符号。因此,跟踪mapping的地址和长度是浪费空间的。取而代之的是填充ifdefs或者诸如”work around”的。提供了如下设备。[没理解,只是照字面意思翻译]

Actually, instead of describing the macros one by one, we'll
transform some example code.
实际上,我们将转换一些示例代码,而不是一一描述宏。

1) Use DEFINE_DMA_UNMAP_{ADDR,LEN} in state saving structures.
在状态保存结构中使用 DEFINE_DMA_UNMAP_{ADDR,LEN}。
   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 these values.
使用 dma_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) Use dma_unmap_{addr,len}() to access these values.
使用 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 an implementation to only
need the address in order to perform the unmap operation.
不言自明的。 我们将 ADDR 和 LEN 分开处理,因为实现可能只需要地址即可执行取消映射操作。


Platform Issues
9 平台相关问题
===============

If you are just writing drivers for Linux and do not maintain
an architecture port for the kernel, you can safely skip down
to "Closing".
如果你只是写一个驱动,而不需要给内核维护一个结构化的驱动,那么可以略过本章。[如果你想建立一个结构化的驱动,那么请遵循下列建议]

1) Struct scatterlist requirements.

   You need to enable CONFIG_NEED_SG_DMA_LENGTH if the architecture
   supports IOMMUs (including software IOMMU).

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. hardware doesn't ensure that data in
   the CPU cache is identical to data in main memory),
   ARCH_DMA_MINALIGN must be set so that the memory allocator
   makes sure that kmalloc'ed buffer doesn't share a cache line with
   the others. See arch/arm/include/asm/cache.h as an example.

   Note that ARCH_DMA_MINALIGN is about DMA memory alignment
   constraints. You don't need to worry about the architecture data
   alignment constraints (e.g. the alignment constraints about 64-bit
   objects).

Closing
=======

This document, and the API itself, would not be in its current
form without the feedback and suggestions from numerous individuals.
We would like to specifically mention, in no particular order, the
following people::

	Russell King <[email protected]>
	Leo Dagum <[email protected]>
	Ralf Baechle <[email protected]>
	Grant Grundler <[email protected]>
	Jay Estabrook <[email protected]>
	Thomas Sailer <[email protected]>
	Andrea Arcangeli <[email protected]>
	Jens Axboe <[email protected]>
	David Mosberger-Tang <[email protected]>

若是有翻译错误的地方,请指出,谢谢。

猜你喜欢

转载自blog.csdn.net/qq_26093511/article/details/132056240
今日推荐