
一、补充点背景知识:
- 根据不同架构处理器,对内存的访问分为两种方式;
a.x86 架构,将外设和普通内存分开,通过专门的 I / O 指令 (IN/OUT) 来访问外设的寄存器,称为“I/ O 地址空间”或“I/ O 端口空间”;
- RISC 的 CPU,比如 ARM/PowerPC 等,采用统一编制,即将所有 I / O 外设的内存空间看作普通内存的一部分;
- 一般外设 I / O 的物理地址是已知的,但是开启 MMU 后,只能通过虚拟地址访问,因此需要将外设地址映射到虚拟地址;
二、ioremap 映射函数
常用映射函数有
1 2 3
| #define ioremap(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRE)) #define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC)) #define ioremap_np(addr, size) __ioremap((addr), (size), __pgprot(PROT_DEVICE_nGnRnE))
|
核心实现如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| static void __iomem *__ioremap_caller(phys_addr_t phys_addr, size_t size, pgprot_t prot, void *caller) { unsigned long last_addr; unsigned long offset = phys_addr & ~PAGE_MASK; int err; unsigned long addr; struct vm_struct *area;
phys_addr &= PAGE_MASK; size = PAGE_ALIGN(size + offset);
last_addr = phys_addr + size - 1; if (!size || last_addr < phys_addr || (last_addr & ~PHYS_MASK)) return NULL;
if (WARN_ON(pfn_valid(__phys_to_pfn(phys_addr)))) return NULL;
area = get_vm_area_caller(size, VM_IOREMAP, caller); if (!area) return NULL; addr = (unsigned long)area->addr; area->phys_addr = phys_addr;
err = ioremap_page_range(addr, addr + size, phys_addr, prot); if (err) {vunmap((void *)addr); return NULL; }
return (void __iomem *)(offset + addr); }
|
三、ioremap_page_range 函数
这部分跟之前普通页面映射差不多,就略过了;
建立好映射之后,应用程序可以通过虚拟地址,访问寄存器地址;


科学边界
人生南北多歧路,君向潇湘我向秦
此文章版权归科学边界所有,如有转载,请注明来自原作者