一: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; }