在编写驱动程序的过程中,我们总免不了要使用ioremap函数来将我们的硬件的地址映射到系统的虚拟地址空间。
ioramp函数原型:
#define ioremap(cookie,size) __ioremap(cookie,size,0,1)
这是一个宏来实现的,当然要进入__ioremap去看看究竟。
void __iomem *
__ioremap(unsigned long phys_addr, size_t size, unsigned long
flags,
unsigned long align)
{
void * addr;
struct vm_struct * area;//管理虚拟页面所使用的结构体
unsigned long offset, last_addr;
last_addr = phys_addr + size - 1;
if (!size || last_addr 《 phys_addr)
return NULL;
offset = phys_addr & ~PAGE_MASK; //取得偏移量
phys_addr &= PAGE_MASK; //得到页面的基地址
size = PAGE_ALIGN(last_addr + 1) - phys_addr;
//取得所需要的尺寸。注意是按照对齐来算的
area = get_vm_area(size, VM_IOREMAP);//通过get_vm_area来获得虚拟内存
if (!area)
return NULL;
addr = area-》addr;
if (remap_area_pages((unsigned long) addr, phys_addr, size, flags))
{
vfree(addr);
return NULL;
}
return (void __iomem *) (offset + (char *)addr);
}
遇到了get_vm_area函数,我们goto Defination去看看。
struct vm_struct *get_vm_area(unsigned long size, unsigned long
flags)
{
return __get_vm_area(size, flags, VMALLOC_START, VMALLOC_END);
}
继续
struct vm_struct *__get_vm_area(unsigned long size, unsigned long
flags,
unsigned long start, unsigned long end)
{
struct vm_struct **p, *tmp, *area;
unsigned long align = 1;
unsigned long addr;
if (flags & VM_IOREMAP) {
int bit = fls(size); //获得最高位为1所在的位置,0x00000600返回10
if (bit 》 IOREMAP_MAX_ORDER) //如果分配超过最大尺寸,按照最大尺寸处理
bit = IOREMAP_MAX_ORDER;
else if (bit 《 PAGE_SHIFT)
bit = PAGE_SHIFT; //以页为单位进行分配,最小单位是页
align = 1ul 《 bit;//以页来对齐
}
addr = ALIGN(start, align);
area = kmalloc(sizeof(*area), GFP_KERNEL);//分配一个空间来保存这些信息的结构体
if (unlikely(!area))
return NULL;
size += PAGE_SIZE;//中间空了一页,目的是安全稳定
if (unlikely(!size)) {
kfree (area);
return NULL;
}
write_lock(&vmlist_lock);
for (p = &vmlist; (tmp = *p) != NULL ;p =
&tmp-》next) {
if ((unsigned long)tmp-》addr 《 addr) {
if((unsigned long)tmp-》addr + tmp-》size 》= addr)
addr = ALIGN(tmp-》size +
(unsigned long)tmp-》addr, align);//找空闲的虚拟地址
continue;
}
if ((size + addr) 《 addr)
goto out;