文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>linux设备驱动之pci设备的I/O和内存

linux设备驱动之pci设备的I/O和内存

时间:2010-05-09  来源:p2pt

  Pci设备的I/O和内存是一个比较复杂的问题.如下的总线结构: 在上图的总线结构中,ethernet设备和pci-pci bridge的同类型资源空间必须要是pci bus0的一个子集 例如,pci bus 0的I/O端口资源是0x00CC~0x01CC. Ethernet设备的I/O范围的是0x00CC~0x0xE0.那么pci-pci bridge的I/O端口范围就必须要在0x0xE0~0x01CC之间. 同样,SCSI和VIDEO同类型资源必须要是pci_bus1的子集.pci bus1上有一个pci桥,对应的资源也就是它所连桥上的资源.即pci_bus->self. 也就是说,下层总线的资源是它上层总线资源的子集。上层总线资源是下层总线资源的父集。 其实,每个PCI设备的资源地始地址都是由操作系统设置的.在x86上,都由bios设置好了.假若没有bios的时候,我们应该怎么去设置设备的资源起始范围呢? 可能在pci枚举完成之后: 1:从根总线开始,设置根总线的资源范围是从0开始,到0xFFFF或者0xFFFFFFFF的最大范围. 2:对其它的设备,可往其资源寄存器全部写入1,就可以求得该资源项的类型和长度. 3:设备从根总线的资源那里分得对应长度的资源. 4:如果设备是pci-pci bridge,则递归配置它.   可能有人会有这样迷惑,对应于上图,如果pci-pci bridge的资源大小是N.而SCSI和video资源范围超过了N怎么办呢? 我们必须要注意一点,总线的区间是可以自已设定的,而设备资源的区间是在设计的时候就已经确定好了.也就是说,我们可以更改pci device区间的起始地址,但我们不能改变它的大小. 因此,出现了上面所说的这种情况.可能是由bios在处理PCI的时候出现了BUG.我们需要调整总线的资源区间.   其实对于pci_bus的资源范围就是它的过滤窗口.对于过滤窗口的作用,我们在枚举的时候分析的很清楚了.   CPU访问PC过程是这样的(只有一个根总线和pci-pci bridge过滤窗口功能打开的情况): 1:cpu向pci发出一个I/O请求.首先经过根总线.它会判断是否在它的资源范围内.如果在它的范围,它就会丢向总线所在的每一个设备.包括pci bridge. 如果没有在根总线的资源范围,则不会处理这个请求. 2:如果pci设备判断该地址属于它的资源范围,则处理后发出应答 4:pci bridge接收到这个请求,它会判断I/O地址是否在它的资源范围内.如果在它的范围,它会把它丢到它的下层子线. 5:下层总线经过经过相同的处理后,就会找到这个PCI设备了   一个PCI设备访问其它PCI设备或者其它外设的过程: 1:首先这个PCI发出一个请求,这个请求会在总线上广播 2:如果要请求的设备是在同级总线,就会产生应答 3:请求的设备不是在同层总线,就会进行pci bridge.pci桥判断该请求不在它的范围内(目的地不是它下层的设备),就会将它丢向上层. 4:这样辗转之后,就能找到对应的设备了   经过这样的分析过来,相信对pci bridge的过滤窗口有更深的理解了.   Linux中使用struct resource的结构来表示I/O端口或者是设备内存。定义如下: struct resource {          resource_size_t start;          resource_size_t end;          const char *name;          unsigned long flags;          struct resource *parent, *sibling, *child; }; Start: 表示它所占资源的起始地址。 End: 表示它所占资源的未尾地址 Name: 所占资源的名字 Flags: 资源的类型。目前有I/O和内存两种 Parent.sibling.child:用来表示资源的所属关系。分别表示它的父结点,兄弟结点和子结点。   从前面的分析可以看到,有一些总线可能bios没有遍历到或许bios的处理有错误,所以需要对整个系统的PCI总线和PCI设备的资源占用情况遍历一次。完整的建立上述的struct resource结构(在之前枚举的时候,只是处理了start和end成员).。这个过程是在pcibios_resource_survey( )完成的。如下所示:   subsys_initcall(pcibios_init); static int __init pcibios_init(void) {          ……          …….          pcibios_resource_survey(); }   pcibios_init这个函数是被fs_initcall()所描述的。在kernel启动的时候,会调用宏所描述的函数。在pcibios_init ()又会调用pcibios_assign_resources(),它的代码如下所示: void __init pcibios_resource_survey(void) {          DBG("PCI: Allocating resources\n");          pcibios_allocate_bus_resources(&pci_root_buses);          pcibios_allocate_resources(0);          pcibios_allocate_resources(1); } 它先对总线的资源进行处理。然后再对PCI设备的资源进行处理。我们先看pcibios_allocate_bus_resources() static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) {          struct pci_bus *bus;          struct pci_dev *dev;          int idx;          struct resource *r, *pr;            /* Depth-First Search on bus tree */          list_for_each_entry(bus, bus_list, node) {                      //pci-bridge                    if ((dev = bus->self)) {                             for (idx = PCI_BRIDGE_RESOURCES;                                 idx < PCI_NUM_RESOURCES; idx++) {                                      r = &dev->resource[idx];                                      if (!r->flags)                                                continue;                                      pr = pci_find_parent_resource(dev, r);                                      if (!r->start || !pr ||                                          request_resource(pr, r) < 0) {                                                printk(KERN_ERR "PCI: Cannot allocate "                                                         "resource region %d "                                                         "of bridge %s\n",                                                         idx, pci_name(dev));                                                /*                                                 * Something is wrong with the region.                                                 * Invalidate the resource to prevent                                                 * child resource allocations in this                                                 * range.                                                 */                                                r->flags = 0;                                      }                             }                    }                    pcibios_allocate_bus_resources(&bus->children);          } } 这个是一个深度优先搜索算法。类似的算法在pci结构中用得很多。 它遍历pci_root_buses中的每一个根总线下面的所有总线。如果该总线有对应的pci-pci bridge,就先处理这个pci桥的资源. PCI桥的资源范围是PCI_BRIDGE_RESOURCES~ PCI_NUM_RESOURCES.对于它的每个资源区间。都要从它的上层总线中获得.代码中遍历PCI桥的每一个资源区间,然后找到它在上层总线的对应区间。然后为它建立结构关系。 pci_find_parent_resource()就是为PCI的资源区间找到一个合适的父结点。代码如下: struct resource * pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) {          const struct pci_bus *bus = dev->bus;          int i;          struct resource *best = NULL;            for(i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {                    struct resource *r = bus->resource[i];                    if (!r)                             continue;                    if (res->start && !(res->start >= r->start && res->end <= r->end))                             continue;  /* Not contained */                    if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM))                             continue;  /* Wrong type */                    if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH))                             return r;     /* Exact match */                    if ((res->flags & IORESOURCE_PREFETCH) && !(r->flags & IORESOURCE_PREFETCH))                             best = r;    /* Approximating prefetchable by non-prefetchable */          }          return best; } 首先从pci_dev ->bus就找到了它的上层总线,每条总线拥有PCI_BUS_NUM_RESOURCES个资源区间. 所要寻找的父结点必须要满足以后几个条件: 1:子结点的范围必须要落在父结点的区间范围内 2:父子区间的基本类型应该一致。(基本类型即IO或者内存) 3:如果父子窗口都是可预读的,就完全匹配了 4:如果子结点可预读,而父结点不可预读。也将就着可以了. 注意:父结点可预读而子结点不可预读是不允许的。   找到它所属的父结点之后,会调用request_resource()从父结点中请求资源。代码如下: 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; } __request_resource()代码如下: static struct resource * __request_resource(struct resource *root, struct resource *new) {          resource_size_t start = new->start;          resource_size_t 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)                             continue;                    return tmp;          } } 这个函数的逻辑比较简单。即在它父节点的子节点中找个合适的位置插下去。父结点的子结点都是按照起始资源地址从小到大的顺序排列的。   返回到pcibios_allocate_bus_resources()中,如果它的资源分配过程失败,它会怎么处理呢?看下面的代码片段: static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) {          ……          …… if (!r->start || !pr || request_resource(pr, r) < 0) {                                                printk(KERN_ERR "PCI: Cannot allocate "                                                         "resource region %d "                                                         "of bridge %s\n",                                                         idx, pci_name(dev));                                                r->flags = 0;          }          ……          …… } 也就是说,如果分配失败了,它会将资源的flags标志置为0   回到pcibios_resource_survey()中,接着往下来,会发现它以不同的参数调用了pcibios_allocate_resources()两次。跟进这个函数的代码进行分析: static void __init pcibios_allocate_resources(int pass) {          struct pci_dev *dev = NULL;          int idx, disabled;          u16 command;          struct resource *r, *pr;            for_each_pci_dev(dev) {                    pci_read_config_word(dev, PCI_COMMAND, &command);                    for (idx = 0; idx < PCI_ROM_RESOURCE; idx++) {                             r = &dev->resource[idx];                             if (r->parent)               /* Already allocated */                                      continue;                             if (!r->start)                  /* Address not assigned at all */                                      continue;                             if (r->flags & IORESOURCE_IO)                                      disabled = !(command & PCI_COMMAND_IO);                             else                                      disabled = !(command & PCI_COMMAND_MEMORY);                             //对于已经启用的,在第一次扫描的时候就将其配制                             //否则。要等到第二次                             if (pass == disabled) {                                      DBG("PCI: Resource %08lx-%08lx "                                          "(f=%lx, d=%d, p=%d)\n",                                          r->start, r->end, r->flags, disabled, pass);                                      pr = pci_find_parent_resource(dev, r);                                      if (!pr || request_resource(pr, r) < 0) {                                                printk(KERN_ERR "PCI: Cannot allocate "                                                         "resource region %d "                                                         "of device %s\n",                                                         idx, pci_name(dev));                                                /* We'll assign a new address later */                                                r->end -= r->start;                                                r->start = 0;                                      }                             }                    }                    if (!pass) {                               //对于ROM。在第一次扫描时就将它关闭                             r = &dev->resource[PCI_ROM_RESOURCE];                             if (r->flags & IORESOURCE_ROM_ENABLE) {                                      /* Turn the ROM off, leave the resource region,                                       * but keep it unregistered. */                                      u32 reg;                                      DBG("PCI: Switching off ROM of %s\n",                                                pci_name(dev));                                      r->flags &= ~IORESOURCE_ROM_ENABLE;                                      pci_read_config_dword(dev,                                                         dev->rom_base_reg, &reg);                                      pci_write_config_dword(dev, dev->rom_base_reg,                                                         reg & ~PCI_ROM_ADDRESS_ENABLE);                             }                    }          } } 该函数遍历整个pci设备。如果该设备的相应空间已经启用了(I/O或者内存)。那在以0为参数调用的时候就让它分配好资源。对于没有被启用的资源。要等到第二次以1为参数调用参数时才会处理。 另外:在第一次处理中就把设备的ROM区间关闭。要等到使用设备的时候再把区间打开。这个打开的过程一般在设备驱动程序里完成。 到这里,我们终于知道为什么要用不同的参数调用函数二次。这样做是为了优先让已经被启用的资源从父节点中分得资源。 如果资源分配失败了。就会将相应资源的start设为0。End成员则设为这个区间的长度。   有必要提一下linux2.4.12中这部份的处理,它是这样子的: 对于pci bus的资源分配,如果分配失败.不做任何事情,只是打印出一条警告语句. 而在linux2.6.25中.如果pci bus资源分配失败,会将资源的flag置为0. 为什么2.6要这样做呢? 其实2.4.12中的处理是一个BUG.如果pci bus资源分配失败没有任何的处理的话.那接下来为该总线下面的pci device分配资源的时候,是从该总线的资源中请求的.而事实上,该总线的资源又是有冲突的.这样也造成了该总线下层设备的资源无效. 而在linux 2.6中.将分配失败总线的flag置为了0.下层设备在请求资源的分配的时候,就会类型匹配失败,而避免了上面说的这个部问题. 这个BUG不知道在后面的版本中有没有被修正 :-)   pcibios_resource_survey()执先完了之后。Pci的所有总线和设备的资源都被验证分配了一次。对于不能正确分配资源的设备。也做好了标记。接下来,我们来看一下怎么处理资源分配失败的设备。   看下面的这段代码: fs_initcall(pcibios_assign_resources); fs_initcall()的优先级比subsys_initcall的优先级小,它在pcibios_init()之后才会得到运行。看一下它的代码: static int __init pcibios_assign_resources(void) {          struct pci_dev *dev = NULL;          struct resource *r, *pr;            if (!(pci_probe & PCI_ASSIGN_ROMS)) {                    /*                     * Try to use BIOS settings for ROMs, otherwise let                     * pci_assign_unassigned_resources() allocate the new                     * addresses.                     */                    for_each_pci_dev(dev) {                             r = &dev->resource[PCI_ROM_RESOURCE];                             if (!r->flags || !r->start)                                      continue;                             pr = pci_find_parent_resource(dev, r);                             if (!pr || request_resource(pr, r) < 0) {                                      r->end -= r->start;                                      r->start = 0;                             }                    }          }            pci_assign_unassigned_resources();            return 0; } 之前在pcibios_resource_survey()中没有处理ROM的区间。在这里,先遍历每个设备,处理一下它的ROM空间的资源分配。照以前的方式一样,如果资源分配失败,就让它的start置为0。End置为区间的长度。 处理完之后,进入到pci_assign_unassigned_resources()。我们希望每一个PCI设备,bios都为我们处理好了。可是总是事与愿违。进行的这里,不得不处理一下之前资源分配失败的设备了。代码如下: void __init pci_assign_unassigned_resources(void) {          struct pci_bus *bus;            /* Depth first, calculate sizes and alignments of all             subordinate buses. */          list_for_each_entry(bus, &pci_root_buses, node) {                    pci_bus_size_bridges(bus);          }          /* Depth last, allocate resources and update the hardware. */          list_for_each_entry(bus, &pci_root_buses, node) {                    pci_bus_assign_resources(bus);                    pci_enable_bridges(bus);          } } 首先我们要处理资源分配失败的pci_bus。在上面的分析中,如果pci bus资源分配失败。就会将其所属资源的flags置为0.对于这些总线,是在第一个循环里处理的. 第一个循环,遍历挂在pci_root_buses上的所有根结点。然后调用pci_bus_size_bridges()。代码如下: void __ref pci_bus_size_bridges(struct pci_bus *bus) {          struct pci_dev *dev;          unsigned long mask, prefmask;            list_for_each_entry(dev, &bus->devices, bus_list) {                    struct pci_bus *b = dev->subordinate;                    if (!b)                             continue;                      switch (dev->class >> 8) {                    case PCI_CLASS_BRIDGE_CARDBUS:                             pci_bus_size_cardbus(b);                             break;                      case PCI_CLASS_BRIDGE_PCI:                    default:                             pci_bus_size_bridges(b);                             break;                    }          }            /* The root bus? */          if (!bus->self)                    return;            switch (bus->self->class >> 8) {          case PCI_CLASS_BRIDGE_CARDBUS:                    /* don't size cardbuses yet. */                    break;            case PCI_CLASS_BRIDGE_PCI:                    pci_bridge_check_ranges(bus);          default:                    pbus_size_io(bus);                    /* If the bridge supports prefetchable range, size it                       separately. If it doesn't, or its prefetchable window                       has already been allocated by arch code, try                       non-prefetchable range for both types of PCI memory                       resources. */                    mask = IORESOURCE_MEM;                    prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;                    if (pbus_size_mem(bus, prefmask, prefmask))                             mask = prefmask; /* Success, size non-prefetch only. */                    pbus_size_mem(bus, mask, IORESOURCE_MEM);                    break;          } } 这是一个深度优先搜索算法。首先遍历总线上的所有设备,如果是pci bridge,递归调用pci_bus_size_bridges()处理下层pci bus.对于每一条不是根总线的pci bus都会经过大循环后面的处理,即对应于case PCI_CLASS_BRIDGE_PCI后面的处理.它要经过的第一个函数是pci_bridge_check_ranges().代码如下: static void pci_bridge_check_ranges(struct pci_bus *bus) {          u16 io;          u32 pmem;          struct pci_dev *bridge = bus->self;          struct resource *b_res;            b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];          b_res[1].flags |= IORESOURCE_MEM;            pci_read_config_word(bridge, PCI_IO_BASE, &io);          if (!io) {                    pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0);                    pci_read_config_word(bridge, PCI_IO_BASE, &io);                   pci_write_config_word(bridge, PCI_IO_BASE, 0x0);         }         if (io)                    b_res[0].flags |= IORESOURCE_IO;          /*  DECchip 21050 pass 2 errata: the bridge may miss an address              disconnect boundary by one PCI data phase.              Workaround: do not use prefetching on this device. */          if (bridge->vendor == PCI_VENDOR_ID_DEC && bridge->device == 0x0001)                    return;          pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);          if (!pmem) {                    pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE,                                                       0xfff0fff0);                    pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem);                    pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0);          }          if (pmem)                    b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; } Pci bridge设备的7,8,9项是属于过滤窗口,也就是对应于总线的资源。 在这里,检查pci bus是否支持I/O和prefetch memory类型的窗口.如果不支持,则相应寄存器是只读的,而且值为0. 顺便说一句,所有的pci bus都是支持memory窗口的.如果相应类型的资源有效则给相应资源的flags置相应的标志。最后还要记得将相关寄存器清空。 我们在前面分析过. 经过这里的处理,只要判断区间存在,就会置相应的标志。那么,对于我们在上面分配资源失败的pci bus.如果区间确实存在的话。就会重新设置这个标志。 重新设置标志的操作是很重要的一个步骤.它能将前面处理时,因资源冲突的pci bus修正过来.运行到这里.怎么区分哪些pci bus是有待修正的,而哪些pci bus是正常的呢? 可能根据 resource->parent来判断.如果是正常的pci bus.该成员会指向它的父结点.如果是有待修正的pci bus.它的这个域是空的.也就是说,还没有链接到父结点.   回到pci_bus_size_bridges()中,经过pci_bridge_check_ranges()的处理过后,流程会转向pbus_size_io(). 从字面意思看这个函数是用来计算i/o的大小.主要是用来处理有待修正的pci bus.我们来思考一下,为什么pci bus的资源会有冲突呢?可能有两个原因: 1:pci bus的起始地址冲突.在上层pci bus中,该区间已经被其它设备占用了.这种情况只需要在上层设备中偏移到特定位置就可以了. 2:pci bus的长度过长.这可能是bios在处理PCI的时候出现了错误,我们需要重新计算pci bus的长度. 而pbus_size_io()就是用来处理第二种情况的,处理资源的类型为I/O.代码如下: static void pbus_size_io(struct pci_bus *bus) {          struct pci_dev *dev;          struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);          unsigned long size = 0, size1 = 0;            if (!b_res)                   return;            list_for_each_entry(dev, &bus->devices, bus_list) {                   int i;                      for (i = 0; i < PCI_NUM_RESOURCES; i++) {                             struct resource *r = &dev->resource[i];                             unsigned long r_size;                               if (r->parent || !(r->flags & IORESOURCE_IO))                                      continue;                             r_size = r->end - r->start + 1;                               if (r_size < 0x400)                                      /* Might be re-aligned for ISA */                                      size += r_size;                             else                                      size1 += r_size;                    }          } /* To be fixed in 2.5: we should have sort of HAVE_ISA    flag in the struct pci_bus. */ #if defined(CONFIG_ISA) || defined(CONFIG_EISA)          size = (size & 0xff) + ((size & ~0xffUL) << 2); #endif          size = ALIGN(size + size1, 4096);          if (!size) {                    b_res->flags = 0;                    return;          }          /* Alignment of the IO window is always 4K */          b_res->start = 4096;          b_res->end = b_res->start + size - 1; } 首先,它查看对应类型(I/O)的总线资源是否有冲突的情况.如果有冲突,则计算下层需要该资源的总数.冲突资源的长度就是下层设备所需该资源的总数.只不过,对于pci bus 的I/O资源是4K对齐的.因此总长度也是4K对齐.   find_free_bus_resource()就是用来检查冲突资源项的.代码如下: static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned long type) {          int i;          struct resource *r;          unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |                                        IORESOURCE_PREFETCH;            for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {                    r = bus->resource[i];                    if (r == &ioport_resource || r == &iomem_resource)                             continue;                    if (r && (r->flags & type_mask) == type && !r->parent)                             return r;          }          return NULL; } 对于资源是ioport_resource, iomem_resource的情况.表示该总线是根总线,不需要处理.注意这一句: if (r && (r->flags & type_mask) == type && !r->parent) 不仅要求类型匹配,而且要父节点为空.父节点为空.说明之前在分配资源的时候失败,也即资源冲突.   因为pci_bus_size_bridges()使用的是深度优先搜索算法,它是先处理最底层的总线,然后再逐层上处理.而下层总线又是上层总线的一个设备(通过pci-pci bridge相连)所以,下层所需的资源长度就可以从下往上反应到上层总线中.最后,到最上层资源冲突的pci bus就可以知道下层需要多少长度的资源了.   pbus_size_mem()类似于pbus_size_io(),只是它是用来查找memory类型资源的长度.在代码中,以不同的参数两次调用了这个函数,分别是用来查找带预读的存储区间和一般的存储区间. pbus_size_mem()和pbus_size_io()相比只是对齐因子不一样,其它大部份的处理都是一样,所以在这里不分析这个函数的代码了.请自行查阅.   回到pci_assign_unassigned_resources()函数中.剩余的代码如下所示: void __init pci_assign_unassigned_resources(void) {          ……          …… list_for_each_entry(bus, &pci_root_buses, node) {                    pci_bus_assign_resources(bus);                    //启用pci-pci briage的窗口机制                    pci_enable_bridges(bus);          } } 遍历pci_root_buses中存放的根总线,对每条根总线调用pci_bus_assign_resources().代码如下: void __ref pci_bus_assign_resources(struct pci_bus *bus) {          struct pci_bus *b;          struct pci_dev *dev;            pbus_assign_resources_sorted(bus);            list_for_each_entry(dev, &bus->devices, bus_list) {                    b = dev->subordinate;                    if (!b)                             continue;                      pci_bus_assign_resources(b);                      switch (dev->class >> 8) {                    case PCI_CLASS_BRIDGE_PCI:                             pci_setup_bridge(b);                             break;                      case PCI_CLASS_BRIDGE_CARDBUS:                             pci_setup_cardbus(b);                             break;                      default:                             printk(KERN_INFO "PCI: not setting up bridge %s "                                    "for bus %d\n", pci_name(dev), b->number);                             break;                    }          } } 这也是一个使用深度优先搜索算法的函数. 现在是时候处理资源冲突的设备了.这是在pbus_assign_resources_sorted()中完成的,代码如下: static void pbus_assign_resources_sorted(struct pci_bus *bus) {          struct pci_dev *dev;          struct resource *res;          struct resource_list head, *list, *tmp;          int idx;            head.next = NULL;          //遍历该总线下的所有设备          list_for_each_entry(dev, &bus->devices, bus_list) {                    u16 class = dev->class >> 8;                      /* Don't touch classless devices or host bridges or ioapics.  */                    if (class == PCI_CLASS_NOT_DEFINED ||                        class == PCI_CLASS_BRIDGE_HOST)                             continue;                      /* Don't touch ioapic devices already enabled by firmware */                    if (class == PCI_CLASS_SYSTEM_PIC) {                             u16 command;                             pci_read_config_word(dev, PCI_COMMAND, &command);                             if (command & (PCI_COMMAND_IO | PCI_COMMAND_MEMORY))                                      continue;                    }                      //所有没有成功分配资源的resource会放到head链表中                    pdev_sort_resources(dev, &head);          }            for (list = head.next; list;) {                    res = list->res;                    idx = res - &list->dev->resource[0];                    if (pci_assign_resource(list->dev, idx)) {                             res->start = 0;                             res->end = 0;                             res->flags = 0;                    }                    tmp = list;                    list = list->next;                    kfree(tmp);          } } 首先,它将有总线下有资源冲突的设备加到链表中,然后遍历这个链表,处理每个资源冲突的设备. pdev_sort_resources()代码很简单,就是计算对齐因子,然后按照对齐因子大小添加到链表.在这里要特别注意.对于pci bus中的资源是要按起始地址对齐的(还记得我们在为冲突的pci bus计算I/O资源长度的时候,它的start成员就是设为4K.表示要4K对齐).这个函数代码比较简单.不再详细分析,请自己阅读. 具体的资源冲突处理过程是在pci_assign_resource()中完成的,代码如下: int pci_assign_resource(struct pci_dev *dev, int resno) {          struct pci_bus *bus = dev->bus;          struct resource *res = dev->resource + resno;          resource_size_t size, min, align;          int ret;            //空间大小          size = res->end - res->start + 1;          //起点最小值          min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;          /* The bridge resources are special, as their             size != alignment. Sizing routines return             required alignment in the "start" field. */                         //对于pci-pci bridge.有特殊的对齐方式          align = (resno < PCI_BRIDGE_RESOURCES) ? size : res->start;            /* First, try exact prefetching match.. */          //首先,带IORESOURCE_PREFETCH 标志处理                   ret = pci_bus_alloc_resource(bus, res, size, align, min,                                           IORESOURCE_PREFETCH,                                           pcibios_align_resource, dev);            if (ret < 0 && (res->flags & IORESOURCE_PREFETCH)) {                    /*                     * That failed.                     *                     * But a prefetching area can handle a non-prefetching                     * window (it will just not perform as well).                     */                    //分配失败,不带IORESOURCE_PREFETCH标志处理                    ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,                                                     pcibios_align_resource, dev);          }            if (ret) {                    printk(KERN_ERR "PCI: Failed to allocate %s resource "                             "#%d:%llx@%llx for %s\n",                             res->flags & IORESOURCE_IO ? "I/O" : "mem",                             resno, (unsigned long long)size,                             (unsigned long long)res->start, pci_name(dev));          } else if (resno < PCI_BRIDGE_RESOURCES) {                    //如果冲突修正成功                    pci_update_resource(dev, res, resno);          }            return ret; } 首先计算该资源的长度和起始地址的最小值以及对齐因子.然后向父结点去申请资源.如果分配失败,那就放松条件,不带预读标志再去分配一次.如果成功,则更新设备的寄存器.如果还是失败,那就没什么办法了. pci_bus_alloc_resource()代码如下: int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,                    resource_size_t size, resource_size_t align,                    resource_size_t min, unsigned int type_mask,                    void (*alignf)(void *, struct resource *, resource_size_t,                                      resource_size_t),                    void *alignf_data) {          int i, ret = -ENOMEM;            type_mask |= IORESOURCE_IO | IORESOURCE_MEM;            for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {                    struct resource *r = bus->resource[i];                    if (!r)                             continue;                      /* type_mask must match */                    if ((res->flags ^ r->flags) & type_mask)                             continue;                      /* We cannot allocate a non-prefetching resource                       from a pre-fetching area */                    if ((r->flags & IORESOURCE_PREFETCH) &&                        !(res->flags & IORESOURCE_PREFETCH))                             continue;                      /* Ok, try it out.. */                    ret = allocate_resource(r, res, size,                                                r->start ? : min,                                                -1, align,                                                alignf, alignf_data);                    if (ret == 0)                             break;          }          return ret; } 遍历上层总线类型相同的资源区,从中分配资源.如果分配成功退出. allocate_resource()如下所示: int allocate_resource(struct resource *root, struct resource *new,                          resource_size_t size, resource_size_t min,                          resource_size_t max, resource_size_t align,                          void (*alignf)(void *, struct resource *,                                           resource_size_t, resource_size_t),                          void *alignf_data) {          int err;            write_lock(&resource_lock);          err = find_resource(root, new, size, min, max, align, alignf, alignf_data);          if (err >= 0 && __request_resource(root, new))                    err = -EBUSY;          write_unlock(&resource_lock);          return err; } 先到父节点中寻找是否有符合的资源区间.如果有,则向父节点请求这个区间. __request_resource()的代码之前我们研究了,这里不再赘述. find_resource()代码如下: static int find_resource(struct resource *root, struct resource *new,                              resource_size_t size, resource_size_t min,                              resource_size_t max, resource_size_t align,                              void (*alignf)(void *, struct resource *,                                                resource_size_t, resource_size_t),                              void *alignf_data) {          struct resource *this = root->child;            new->start = root->start;          /*           * Skip past an allocated resource that starts at 0, since the assignment           * of this->start - 1 to new->end below would cause an underflow.           */          if (this && this->start == 0) {                    new->start = this->end + 1;                    this = this->sibling;          }          for(;;) {                    if (this)                             new->end = this->start - 1;                    else                             new->end = root->end;                    if (new->start < min)                             new->start = min;                    if (new->end > max)                             new->end = max;                   new->start = ALIGN(new->start, align);                    if (alignf)                             alignf(alignf_data, new, size, align);                    if (new->start < new->end && new->end - new->start >= size - 1) {                             new->end = new->start + size - 1;                             return 0;                    }                    if (!this)                             break;                    new->start = this->end + 1;                    this = this->sibling;          }          return -EBUSY; } 这个函数比较简单,就是判断父节点是否有足够长的空间区间. 返回到pci_assign_resource()中,如果修正成功,就会调用pci_update_resource().因为pci设备的资源区间的起始地址改了,要将它更新到寄存器.这正是pci_update_resource()要处理的事情.函数比较简单.就不详细分析了,但这个函数中有个特别的处理,需要指出来.代码片段如下: void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno) {          ……          ……          if (resno < 6) {                    reg = PCI_BASE_ADDRESS_0 + 4 * resno;          } else if (resno == PCI_ROM_RESOURCE) {                    if (!(res->flags & IORESOURCE_ROM_ENABLE))                             return;                    new |= PCI_ROM_ADDRESS_ENABLE;                    reg = dev->rom_base_reg;          } else {                    /* Hmm, non-standard resource. */                             return;                /* kill uninitialised var warning */          } …… …… } 从上面的处理可以看到,它只会处理前7个资源区间,对于8.9.10区间,是pci-pci bridge的中存放的关于pci bus中的资源信息. 也就是说,这个函数只会处理pci device中的寄存器,不会处理pci bus.   返回到pci_bus_assign_resources()中.剩余的代码片段如下: void __ref pci_bus_assign_resources(struct pci_bus *bus) {          ……          ……          list_for_each_entry(dev, &bus->devices, bus_list) {                    b = dev->subordinate;                    if (!b)                             continue;                      pci_bus_assign_resources(b);                      switch (dev->class >> 8) {                    case PCI_CLASS_BRIDGE_PCI:                             pci_setup_bridge(b);                             break;                      case PCI_CLASS_BRIDGE_CARDBUS:                             pci_setup_cardbus(b);                             break;                      default:                             printk(KERN_INFO "PCI: not setting up bridge %s "                                    "for bus %d\n", pci_name(dev), b->number);                             break;                    }          } } 处理完pbus_assign_resources_sorted()之后,遍历总线下面的pci-pci bridge(dev->subordinate不为空).对每一条下层总线,都递归调用pci_bus_assign_resources(). Switch后面的部份,是处理完下层总线,回到本层的处理了.忽略对cardbus的处理.看到了pci_setup_bridge(). 我们在前面已经分析到.对于修正过后的处理,只会更新pci device的寄存器信息,而不会更新pci bus中的寄存器. pci_setup_bridge()就是更新pci bus寄存器信息的.只是不管它之前是不是资源冲突的.所有的pci bus全部都会更新一下.该函数代码比较简单.就不做分析了.   回到pci_assign_unassigned_resources()中.对每条根总线,我们还要经过pci_enable_bridges()的处理,这个函数是这一节分析的最后一个函数了. 代码如下: void pci_enable_bridges(struct pci_bus *bus) {          struct pci_dev *dev;          int retval;            list_for_each_entry(dev, &bus->devices, bus_list) {                    if (dev->subordinate) {                             retval = pci_enable_device(dev);                             pci_set_master(dev);                             pci_enable_bridges(dev->subordinate);                    }          } } 运行到这里,pci总线和设备都准备好了,现在就可以启用pci-pci bridge了.pci_enable_bridge()做的工作如下: 1:启用所有pci-pci bridge的所有I/O和内存. 2:启用pci-pci bridge的中断 3:设置pci-pci bridge 的电源管理状态 4:设置pci-pci bridge对总线的控制能力 在这里要注意了,这个函数只是启用了pci-pci bridge的相关功能.对于一般pci_dev.需要驱动程序在使用的时候启用它们. 函数比较简单,内部就不加详细分析了.
相关阅读 更多 +
排行榜 更多 +
马里奥赛车世界游戏手机版下载

马里奥赛车世界游戏手机版下载

赛车竞速 下载
无畏契约皮肤开箱器手游下载

无畏契约皮肤开箱器手游下载

休闲益智 下载
旭日之城官方正版下载

旭日之城官方正版下载

策略塔防 下载