一:dma_mask与coherent_dma_mask的定义
在linux内核中,引入了platform_device与platform_driver,这样就很方便了平台的设备与驱动。在include\linux\platform_device.h下:
struct platform_device {
const char * name;
int id;
struct
device dev;
u32 num_resources;
struct resource * resource;
const struct
platform_device_id *id_entry;
struct
pdev_archdata archdata;
};
而struct device dev,在include\linux\device.h中:
struct device {
struct
device *parent;
struct device_private *p;
struct kobject kobj;
const
char *init_name;
struct device_type *type;
struct
mutex mutex;
struct
bus_type *bus;
struct device_driver
*driver;
void *platform_data;
struct dev_pm_info power;
#ifdef CONFIG_NUMA
int numa_node;
#endif
u64 *dma_mask;
u64 coherent_dma_mask;
struct device_dma_parameters *dma_parms;
struct
list_head dma_pools;
struct
dma_coherent_mem *dma_mem;
struct
dev_archdata archdata;
#ifdef CONFIG_OF
struct
device_node *of_node;
#endif
dev_t devt;
spinlock_t devres_lock;
struct
list_head devres_head;
struct
klist_node knode_class;
struct
class *class;
const struct attribute_group
**groups;
void (*release)(struct device
*dev);
};
dma_mask与 coherent_dma_mask这两个参数表示它能寻址的物理地址的范围,内核通过这两个参数分配合适的物理内存给
device。其中dma_coherent_mask则作用于申请一致性DMA缓冲区。因为不是所有的硬件都能够支持64bit的地址宽度。如果
addr_phy 是一个物理地址,且 (u64)addr_phy <= *dev->dma_mask,那么 该
device 就可以寻址该物理地址。如果 device 只能寻址32位地址,那么 mask 应为
0xffffffff。依此类推。
例如,在linux2.6.38中:
static u64
nuc900_device_usb_ehci_dmamask = 0xffffffffUL;
static struct platform_device nuc900_device_ehci = {
.name =
"nuc900-ehci",
.id =
-1,
.num_resources =
ARRAY_SIZE(nuc900_ehci_resource),
.resource =
nuc900_ehci_resource,
.dev
= {
.dma_mask = &nuc900_device_usb_ehci_dmamask,
.coherent_dma_mask =
0xffffffffUL
}
};
二:sample
code
这段代码摘录自 arch\arm\mm\dma-mapping.c
u64
limit;
size =
PAGE_ALIGN(size);
//这个 limit
就是通过 mask 计算得到的设备最大寻址范围。
limit =
(mask + 1) & ~mask;
//当 size 超出
limit 时,说明分配的地址超出了设备的最大寻址能力,这时返回错误。
if ((limit
&& size >= limit) ||
size >= (CONSISTENT_END - CONSISTENT_BASE)) {
printk(KERN_WARNING "coherent allocation too big "
"(requested %#x mask %#llx)\n", size, mask);
goto no_page;
}