分类: LINUX
2009-11-14 12:36:00
/usr/src/linux-2.6.21.5/kernel/resource.c request_mem_region() -- 将起始地址为[start, start+n-1]的资源插入根资源iomem_resource中。参数start是I/O内存资源的起始物理地址(是CPU的RAM物理地址空间中的物理地址),参数n指定I/O内存资源的大小。 #define request_mem_region(start, n, name) \ __request_region(&iomem_resource, (start), (n), (name)) 注: 调用request_mem_region()不是必须的,但是建议使用。该函数的任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再申请该资源时就会失败。 |
offset: 物理空间(I/O设备上的一块物理内存)的起始地址 size: 物理空间的大小 给一段物理地址(起始地址offset)建立页表(地址映射) -------------------------------------------------- static inline void __iomem * ioremap(unsigned long offset, unsigned long size) { return __ioremap(offset, size, 0); } Remap an arbitrary physical address space into the kernel virtual address space. Needed when the kernel wants to access high addresses directly. -------------------------------------------------- void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags){ void __iomem * addr; struct vm_struct * area; unsigned long offset, last_addr; last_addr = phys_addr + size - 1; if (!size || last_addr < phys_addr) return NULL; if (phys_addr >= ISA_START_ADDRESS && last_addr < ISA_END_ADDRESS) return (void __iomem *) phys_to_virt(phys_addr); 如果需要映射的空间的起始地址phys_addr为于ZONE_NORMAL区,通过virt_to_page()进行映射 |------------------------------------------------------------------------------| | if (phys_addr <= virt_to_phys(high_memory - 1)) | | { | | char *t_addr, *t_end; | | struct page *page; | | t_addr = __va(phys_addr); | | t_end = t_addr + (size - 1); | | for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++) -| | if(!PageReserved(page)) | | return NULL; | | } | |------------------------------------------------------------------------------| offset = phys_addr & ~PAGE_MASK; phys_addr &= PAGE_MASK; size = PAGE_ALIGN(last_addr+1) - phys_addr; 通过“非连续存储器区”进行映射 get_vm_area()创建类型为vm_struct的新描述符 get_vm_area()首先调用kmalloc()为新描述符获得一个存储区;然后扫描类型为struct vm_struct的描述符表,查找一个可用线性地址空间(至少包含size+4096个地址)。 |----------------------------------------------------------| | area = get_vm_area(size, VM_IOREMAP | (flags << 20)); -| | if (!area) | | return NULL; | |----------------------------------------------------------| area->phys_addr = phys_addr; addr = (void __iomem *) area->addr; ioremap_page_range()负责为一段物理地址(起始地址为phys_addr)和一段线性地址(起始地址为addr > VMALLOC_START, 大小为size)建立映射页表(建立的页表由内核页表的最高32项的其中几个目录项进行管理),如果建立成功则返回0 |-----------------------------------------------------------| | if (ioremap_page_range( -(unsigned long) addr, | | (unsigned long) addr + size, | | phys_addr, | | flags)) | | { | | vunmap((void __force *) addr); | | return NULL; | | } | |-----------------------------------------------------------| return (void __iomem *) (offset + (char __iomem *)addr); } |