Chinaunix首页 | 论坛 | 博客
  • 博客访问: 544424
  • 博文数量: 99
  • 博客积分: 4010
  • 博客等级: 上校
  • 技术积分: 1117
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-23 15:17
文章分类

全部博文(99)

文章存档

2011年(4)

2010年(13)

2009年(82)

我的朋友

分类: LINUX

2009-11-14 12:36:00

一、request_mem_region() -- I/O内存申请效验
/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()不是必须的,但是建议使用。该函数的任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再申请该资源时就会失败。
二、ioremap()建立虚实映射的函数
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);
}

阅读(2087) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~