Chinaunix首页 | 论坛 | 博客
  • 博客访问: 42603
  • 博文数量: 8
  • 博客积分: 165
  • 博客等级: 入伍新兵
  • 技术积分: 96
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-21 16:54
文章分类

全部博文(8)

文章存档

2014年(1)

2013年(1)

2012年(1)

2011年(5)

分类: LINUX

2012-03-06 23:45:55

在编写驱动程序的过程中,我们总免不了要使用ioremap函数来将我们的硬件的地址映射到系统的虚拟地址空间.
ioramp函数原型:
  1. #define ioremap(cookie,size)        __ioremap(cookie,size,0,1)

这是一个宏来实现的,当然要进入__ioremap去看看究竟。

  1. void __iomem *
  2. __ioremap(unsigned long phys_addr, size_t size, unsigned long flags,
  3.      unsigned long align)
  4. {
  5.     void * addr;
  6.     struct vm_struct * area;//管理虚拟页面所使用的结构体
  7.     unsigned long offset, last_addr;

  8.     /* Don't allow wraparound or zero size */
  9.     last_addr = phys_addr + size - 1;
  10.     if (!size || last_addr < phys_addr)
  11.         return NULL;

  12.     /*
  13.      * Mappings have to be page-aligned
  14.      */
  15.     offset = phys_addr & ~PAGE_MASK; //取得偏移量
  16.     phys_addr &= PAGE_MASK; //得到页面的基地址
  17.     size = PAGE_ALIGN(last_addr + 1) - phys_addr; //取得所需要的尺寸。注意是按照对齐来算的

  18.     /*
  19.      * Ok, go for it..
  20.      */
  21.     area = get_vm_area(size, VM_IOREMAP);//通过get_vm_area来获得虚拟内存
  22.     if (!area)
  23.         return NULL;
  24.     addr = area->addr;
  25.     if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) {
  26.         vfree(addr);
  27.         return NULL;
  28.     }
  29.     return (void __iomem *) (offset + (char *)addr);
  30. }

遇到了get_vm_area函数,我们goto Defination去看看。

 

  1. struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
  2. {
  3.     return __get_vm_area(size, flags, VMALLOC_START, VMALLOC_END);
  4. }

继续

 

  1. struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
  2.                 unsigned long start, unsigned long end)
  3. {
  4.     struct vm_struct **p, *tmp, *area;
  5.     unsigned long align = 1;
  6.     unsigned long addr;

  7.     if (flags & VM_IOREMAP) {
  8.         int bit = fls(size); //获得最高位为1所在的位置,0x00000600返回10

  9.         if (bit > IOREMAP_MAX_ORDER) //如果分配超过最大尺寸,按照最大尺寸处理
  10.             bit = IOREMAP_MAX_ORDER;
  11.         else if (bit < PAGE_SHIFT)
  12.             bit = PAGE_SHIFT; //以页为单位进行分配,最小单位是页

  13.         align = 1ul << bit;//以页来对齐
  14.     }
  15.     addr = ALIGN(start, align);

  16.     area = kmalloc(sizeof(*area), GFP_KERNEL);//分配一个空间来保存这些信息的结构体
  17.     if (unlikely(!area))
  18.         return NULL;

  19.     /*
  20.      * We always allocate a guard page.
  21.      */
  22.     size += PAGE_SIZE;//中间空了一页,目的是安全稳定
  23.     if (unlikely(!size)) {
  24.         kfree (area);
  25.         return NULL;
  26.     }

  27.     write_lock(&vmlist_lock);
  28.     for (p = &vmlist; (tmp = *p) != NULL ;p = &tmp->next) {
  29.         if ((unsigned long)tmp->addr < addr) {
  30.             if((unsigned long)tmp->addr + tmp->size >= addr)
  31.                 addr = ALIGN(tmp->size +
  32.                      (unsigned long)tmp->addr, align);//找空闲的虚拟地址
  33.             continue;
  34.         }
  35.         if ((size + addr) < addr)
  36.             goto out;
  37.         if (size + addr <= (unsigned long)tmp->addr)//找到合适的空闲地址,上一个已经用了的虚拟地址和下一个已经用了的虚拟地址之间还剩下的虚拟地址能够满足分配条件。
  38.             goto found;
  39.         addr = ALIGN(tmp->size + (unsigned long)tmp->addr, align);
  40.         if (addr > end - size)
  41.             goto out;
  42.     }

  43. found:
  44.     area->next = *p;//将分配到的虚拟空间的管理结构体插入到链表中。
  45.     *p = area;
  46. //进行适当的初始化
  47.     area->flags = flags;
  48.     area->addr = (void *)addr;
  49.     area->size = size;
  50.     area->pages = NULL;
  51.     area->nr_pages = 0;
  52.     area->phys_addr = 0;
  53.     write_unlock(&vmlist_lock);

  54.     return area;

  55. out:
  56.     write_unlock(&vmlist_lock);
  57.     kfree(area);
  58.     if (printk_ratelimit())
  59.         printk(KERN_WARNING "allocation failed: out of vmalloc space - use vmalloc= to increase size.\n");
  60.     return NULL;
  61. }
阅读(3309) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~