登录 免费注册 首页 | 行业黑名单 | 帮助
维库电子市场网
技术交流 | 电路欣赏 | 工控天地 | 数字广电 | 通信技术 | 电源技术 | 测控之家 | EMC技术 | ARM技术 | EDA技术 | PCB技术 | 嵌入式系统
驱动编程 | 集成电路 | 器件替换 | 模拟技术 | 新手园地 | 单 片 机 | DSP技术 | MCU技术 | IC 设计 | IC 产业 | CAN-bus/DeviceNe

灌灌水,转贴<<Linux对I/O资源的管理>>---驱动开发必看

作者:fineamy 栏目:驱动编程
灌灌水,转贴<<Linux对I/O资源的管理>>---驱动开发必看


COPYRIGHT &copy; 2002 by 詹荣开

  E-mail:zhanrk@sohu.com

  Linux-2.4.0

  Version 1.0.0,2002-10-1

  关键词:设备管理、驱动程序、I/O端口、资源
  申明:这份文档是按照自由软件开放源代码的精神发布的,任何人可以免费获得、使用和重新发布,但是你没有限制别人重新发布你发布内容的权利。发布本文的目的是希望它能对读者有用,但没有任何担保,甚至没有适合特定目的的隐含的担保。更详细的情况请参阅GNU通用公共许可证(GPL),以及GNU自由文档协议(GFDL)。



  几乎每一种外设都是通过读写设备上的寄存器来进行的。外设寄存器也称为“I/O端口”,通常包括:控制寄存器、状态寄存器和数据寄存器三大类,而且一个外设的寄存器通常被连续地编址。CPU对外设IO端口物理地址的编址方式有两种:一种是I/O映射方式(I/O-mapped),另一种是内存映射方式(MEMORY-mapped)。而具体采用哪一种则取决于CPU的体系结构。
  有些体系结构的CPU(如,PowerPC、m68k等)通常只实现一个物理地址空间(RAM)。在这种情况下,外设I/O端口的物理地址就被映射到CPU的单一物理地址空间中,而成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。这就是所谓的“内存映射方式”(MEMORY-mapped)。

  而另外一些体系结构的CPU(典型地如X86)则为外设专门实现了一个单独地地址空间,称为“I/O地址空间”或者“I/O端口空间”。这是一个与CPU地RAM物理地址空间不同的地址空间,所有外设的I/O端口均在这一空间中进行编址。CPU通过设立专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元(也即I/O端口)。这就是所谓的“I/O映射方式”(I/O-mapped)。与RAM物理地址空间相比,I/O地址空间通常都比较小,如x86 CPU的I/O空间就只有64KB(0-0xffff)。这是“I/O映射方式”的一个主要缺点。

  Linux将基于I/O映射方式的或内存映射方式的I/O端口通称为“I/O区域”(I/O region)。在讨论对I/O区域的管理之前,我们首先来分析一下Linux是如何实现“I/O资源”这一抽象概念的。

3.1 Linux对I/O资源的描述
  Linux设计了一个通用的数据结构resource来描述各种I/O资源(如:I/O端口、外设内存、DMA和IRQ等)。该结构定义在include/linux/ioport.h头文件中:

  struct resource {
    const CHAR *NAME;
    unsigned LONG start, end;
    unsigned LONG flags;
    struct resource *parent, *sibling, *child;
  };

  各成员的含义如下:
  1.    NAME指针:指向此资源的名称。

  2.    start和end:表示资源的起始物理地址和终止物理地址。它们确定了资源的范围,也即是一个闭区间[start,end]。

  3.    flags:描述此资源属性的标志(见下面)。

  4.    指针parent、sibling和child:分别为指向父亲、兄弟和子资源的指针。

  属性flags是一个unsigned LONG类型的32位标志值,用以描述资源的属性。比如:资源的类型、是否只读、是否可缓存,以及是否已被占用等。下面是一部分常用属性标志位的定义(ioport.h):

/*
* IO resources have these defined flags.
*/
#define IORESOURCE_BITS        0x000000ff    /* Bus-specific bits */

#define IORESOURCE_IO        0x00000100    /* Resource type */
#define IORESOURCE_MEM        0x00000200
#define IORESOURCE_IRQ        0x00000400
#define IORESOURCE_DMA        0x00000800

#define IORESOURCE_PREFETCH    0x00001000    /* No side effects */
#define IORESOURCE_READONLY    0x00002000
#define IORESOURCE_CACHEABLE    0x00004000
#define IORESOURCE_RANGELENGTH    0x00008000
#define IORESOURCE_SHADOWABLE    0x00010000
#define IORESOURCE_BUS_HAS_VGA    0x00080000

#define IORESOURCE_UNSET    0x20000000
#define IORESOURCE_AUTO        0x40000000
#define IORESOURCE_BUSY        0x80000000
    /* Driver has marked this resource busy */


  指针parent、sibling和child的设置是为了以一种树的形式来管理各种I/O资源。

3.2 Linux对I/O资源的管理
  Linux是以一种倒置的树形结构来管理每一类I/O资源(如:I/O端口、外设内存、DMA和IRQ)的。每一类I/O资源都对应有一颗倒置的资源树,树中的每一个节点都是一个resource结构,而树的根结点root则描述了该类资源的整个资源空间。
  基于上述这个思想,Linux在kernel/Resource.c文件中实现了对资源的申请、释放及查找等操作。
  3.2.1 I/O资源的申请
  假设某类资源有如下这样一颗资源树:
  节点root、r1、r2和r3实际上都是一个resource结构类型。子资源r1、r2和r3通过sibling指针链接成一条单向非循环链表,其表头由root节点中的child指针定义,因此也称为父资源的子资源链表。r1、r2和r3的parent指针均指向他们的父资源节点,在这里也就是图中的root节点。
  假设想在root节点中分配一段I/O资源(由图中的阴影区域表示)。函数request_resource()实现这一功能。它有两个参数:①root指针,表示要在哪个资源根节点中进行分配;②new指针,指向描述所要分配的资源(即图中的阴影区域)的resource结构。该函数的源代码如下(kernel/resource.c):

  int request_resource(struct resource *root, struct resource *new)
  {
    struct resource *conflict;

    write_lock(&resource_lock);
    conflict = __request_resource(root, new);
    write_unlock(&resource_lock);
    return conflict ? -EBUSY : 0;
  }

  对上述函数的NOTE如下:
  ①资源锁resource_lock对所有资源树进行读写保护,任何代码段在访问某一颗资源树之前都必须先持有该锁。其定义如下(kernel/Resource.c):

  static rwlock_t resource_lock = RW_LOCK_UNLOCKED;

  ②可以看出,函数实际上是通过调用内部静态函数__request_resource()来完成实际的资源分配工作。如果该函数返回非空指针,则表示有资源冲突;否则,返回NULL就表示分配成功。
  ③最后,如果conflict指针为NULL,则request_resource()函数返回返回值0,表示成功;否则返回-EBUSY表示想要分配的资源已被占用。
  函数__request_resource()完成实际的资源分配工作。如果参数new所描述的资源中的一部分或全部已经被其它节点所占用,则函数返回与new相冲突的resource结构的指针。否则就返回NULL。该函数的源代码如下

(kernel/Resource.c):
/* Return the conflict entry if you can't request it */
static struct resource * __request_resource
  (struct resource *root, struct resource *new)
{
    unsigned LONG start = new->start;
    unsigned LONG end = new->end;
    struct resource *tmp, **p;

    if (end < start)
        return root;
    if (start < root->start)
        return root;
    if (end > root->end)
        return root;
    p = &root->child;
    for (;;) {
        tmp = *p;
        if (!tmp || tmp->start > end) {
            new->sibling = tmp;
            *p = new;
            new->parent = root;
            return NULL;
        }
        p = &tmp->sibling;
        if (tmp->end < start)
            
参与讨论
昵称:
讨论内容:
 
 
相关帖子
有意思,Linux中的一个数学问题!
编译toolchain,在编译glibc时提示参can not find -lgcc_eh?
大家推荐一下是学习linux好还是学习ucos-ii好啊
Cygwin的安装问题
【求助】安装fc5之后鼠标看不到
免费注册为维库电子开发网会员,参与电子工程师社区讨论,点此进入


Copyright © 1998-2006 www.dzsc.com 浙ICP证030469号