PCI驱动框架简单分析

转载 2018年04月16日 09:41:27

一、PCI 概念介绍

    PCI是CPU和外围设备通信的高速传输总线。PCI规范能够实现32位并行数据传输,工作频率为 33MHz 或 66MHz ,最大吞吐率高达266MB/s,PCI的衍生物包括 CardBus、mini-PCI、PCI-Express、cPCI等。

    PCI总线体系结构是一种层次式的体系结构。在这种层次体系结构中,PCI桥设备占据着重要的地位,它将父总线与子总线连接在一起,从而使整个系统看起来像一个倒置的树状结构,树的顶端是CPU,它通过一个较为特殊的CPI桥设备-Host/PCI桥设备与根PCI总线连接起来。

    作为特殊的PCI设备,PCI桥包括以下几种:

    HOST/PCI桥,用于连接CPU与PCI根总线,第一个根总线的编号为0。在PC中,内存控制器也通常被集成到Host/PCI桥设备芯片中,因此,Host/PCI桥也通常被称为“北桥”芯片组。

    PCI/ISA桥,用作连接旧的ISA总线,通常,PCI中的类似的i8359A中断控制器这样的设备也会被集成到PCI/ISA桥设备中,因此,PCI/ISA桥通常也被称作“南桥”芯片组。

    PCI-to-PCI桥,用于连接PCI主总线与次总线,PCI桥所处的总线被称作“主总线”(父总线),PCI桥设备所连接的总线为“次总线”(子总线)。

    

二、PCI设备与配置空间

    在i386系统结构中,对内存的访问和对输入/输出寄存器的访问通过两套不同的指令完成,所有的存储器和IO两个不同的地址空间。一般而言,内存的物理地址以及输入/输出寄存器的地址是由硬件决定的,不过对于内存的物理地址还可以通过地址映射机制来一次转换(I/O也可以映射)。可是,怎样处理外设的存储空间呢?理想的办法是系统软件自动设置,思路是:

    1、外设通过某种途径告诉系统,它有几个存储区间以及I/O地址空间,每个区间是多大,以及各自在本地的地址,显然这些地址都是局部的内部的,都从0开始算起。

    2、系统软件在知道了一共有多少外设,各自又有什么样的存储区间以后,就可以为这些区间分配“物理地址”,并且建立起这些区间与总线之间的连接,以后就可以通过这些地址来访问。显然,这里所谓的“物理地址”与真正的物理地址还是有些区别的,它实际上也是一种逻辑地址,所以常成为“总线地址”,因为这是CPU在总线上所看到的地址。可想而知,外设上一定有着某种地址映射机制。所谓的“为外设分配地址”,就是为其分配总线地址,并建立起映射。

    PCI设备上存在许多完成上述工作的寄存器(配置空间),那么系统初始化的时候如何访问这些寄存器该何如?对于i386结构的处理器,PCI总线的设计者在I/O地址空间保留了8个字节用于这个目的,那就是0xCF8~0xCFF,这8个字节的地址空间构成了两个32位的寄存器,第一个是“地址寄存器”0xCF8,第二个是“数据寄存器”0xCFC,要访问配置空间的寄存器时,CPU先向地址寄存器写入目标地址,然后通过数据寄存器进行读写数据不过,写入地址寄存器的目标地址是一种包括总线号、设备号、功能号以及配置寄存器地址的综合地址。每个PCI设备最多有8个功能,所以设备号和功能号组合在一起又被称作“逻辑设备”号。


    如上图所示,PCI标准规定每个设备的配置寄存器组最多可以有256字节的连续空间,其中开头的64字节的用途和格式是标准的,成为配置寄存器组的“头部”,这样的头部又有两种,“0型”头部用于一般的PCI设备,“1型”头部用于PCI桥无论是“0型”还是“1型”,其开头的16个字节的用途和格式是共同的


三、PCI驱动框架分析

    在内核中与PCI相关的结构体大概有pci_driver 、pci_bus_type 、pci_dev 、pci_bus ,我们前边所说的所有的PCI总线都是指的 pci_bus 。

  3.1 pci_bus

  1. struct pci_bus {  
  2.     struct list_head node;      /* node in list of buses */  
  3.     struct pci_bus  *parent;    /* parent bus this bridge is on */  
  4.     struct list_head children;  /* list of child buses */  
  5.     struct list_head devices;   /* list of devices on this bus */  
  6.     struct pci_dev  *self;      /* bridge device as seen by parent */  
  7.     struct list_head slots;     /* list of slots on this bus */  
  8.     struct resource *resource[PCI_BUS_NUM_RESOURCES];  
  9.                     /* address space routed to this bus */  
  10.   
  11.     struct pci_ops  *ops;       /* configuration access functions */  
  12.     void        *sysdata;   /* hook for sys-specific extension */  
  13.     struct proc_dir_entry *procdir; /* directory entry in /proc/bus/pci */  
  14.   
  15.     unsigned char   number;     /* bus number */  
  16.     unsigned char   primary;    /* number of primary bridge */  
  17.     unsigned char   secondary;  /* number of secondary bridge */  
  18.     unsigned char   subordinate;    /* max number of subordinate buses */  
  19.   
  20.     char        name[48];  
  21.   
  22.     unsigned short  bridge_ctl; /* manage NO_ISA/FBB/et al behaviors */  
  23.     pci_bus_flags_t bus_flags;  /* Inherited by child busses */  
  24.     struct device       *bridge;  
  25.     struct device       dev;  
  26.     struct bin_attribute    *legacy_io; /* legacy I/O for this bus */  
  27.     struct bin_attribute    *legacy_mem; /* legacy mem */  
  28.     unsigned int        is_added:1;  
  29. };  
    几个重要的成员:

    children:  PCI桥可以使当前总线得到扩展,当前总线上有几个PCI桥,那么当前总线就会拥有几个子总线,子总线会连接到父总线的children链表中。

    device: 连接在这条总线上的设备链表。

    ops: 当前总线访问总线上设备配置空间的 read、write 方法。

    在内核启动的过程中,首先会创建0级总线,然后枚举探测0级总线上的设备,如果是PCI桥,那么还要进入下一级子总线,最终所有的连接的PCI设备都将被探测到,详细的探测过程,我们在后边分析。


  3.2 pci_bus_type

    看到 bus_type 显然这是个设备总线驱动模型里的“总线”,与前边提到的 pci_bus ,完全是两码事,那么pci_driver 和 pci_dev 就是注册到 pci_bus_type 的驱动和设备。分析总线设备驱动模型的时候,总要分析一下它的 match 函数(匹配规则)。

  1. static int pci_bus_match(struct device *dev, struct device_driver *drv)  
  2. {  
  3.     struct pci_dev *pci_dev = to_pci_dev(dev);  
  4.     struct pci_driver *pci_drv = to_pci_driver(drv);  
  5.     const struct pci_device_id *found_id;  
  6.   
  7.     found_id = pci_match_device(pci_drv, pci_dev);  
  8.     if (found_id)  
  9.         return 1;  
  10.   
  11.     return 0;  
  12. }  
  1. static const struct pci_device_id *pci_match_device(struct pci_driver *drv,  
  2.                             struct pci_dev *dev)  
  3. {  
  4.     struct pci_dynid *dynid;  
  5.   
  6.     /* Look at the dynamic ids first, before the static ones */  
  7.     spin_lock(&drv->dynids.lock);  
  8.     list_for_each_entry(dynid, &drv->dynids.list, node) {  
  9.         if (pci_match_one_device(&dynid->id, dev)) {  
  10.             spin_unlock(&drv->dynids.lock);  
  11.             return &dynid->id;  
  12.         }  
  13.     }  
  14.     spin_unlock(&drv->dynids.lock);  
  15.   
  16.     return pci_match_id(drv->id_table, dev);  
  17. }  
  1. static inline const struct pci_device_id *  
  2. pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)  
  3. {  
  4.     if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) &&  
  5.         (id->device == PCI_ANY_ID || id->device == dev->device) &&  
  6.         (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) &&  
  7.         (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) &&  
  8.         !((id->class ^ dev->class) & id->class_mask))  
  9.         return id;  
  10.     return NULL;  
  11. }  
  1. const struct pci_device_id *pci_match_id(const struct pci_device_id *ids,  
  2.                      struct pci_dev *dev)  
  3. {  
  4.     if (ids) {  
  5.         while (ids->vendor || ids->subvendor || ids->class_mask) {  
  6.             if (pci_match_one_device(ids, dev))  
  7.                 return ids;  
  8.             ids++;  
  9.         }  
  10.     }  
  11.     return NULL;  
  12. }  
    通过分析代码,PCI设备与驱动的匹配方式有两种,一种是通过 pci_driver->dynids ,另一种是通过 pci_driver->idtable 。使用idtable 是总线设备驱动模型中常用的匹配方法,一般都是通过设备名来匹配,但是PCI比较特殊,它是通过设备的 vendor 、subvendor 、device 、subdevice 来匹配(这些都是在配置空间里可以读取到的)。

    至于 pci_driver->dynids ,它是通过用户空间给驱动增加匹配条件的一种方法(还记得I2C可以在用户空间创建设备吗,一样的)。

  1. error = pci_create_newid_file(drv);  
  2. static int  
  3. pci_create_newid_file(struct pci_driver *drv)  
  4. {  
  5.     int error = 0;  
  6.     if (drv->probe != NULL)  
  7.         error = driver_create_file(&drv->driver, &driver_attr_new_id);  
  8.     return error;  
  9. }  
    在 pci_register_driver 函数中会调用到一个 pci_create_newid_file 函数,它在 sysfs 文件系统中会创建一个 new_id 的属性文件,通过这个属性文件,我们就可以来为该驱动增加匹配条件。

    内核帮助文档有说明:

    New PCI IDs may be added to a device driver pci_ids table at runtime as shown below:
    echo "vendor device subvendor subdevice class class_mask driver_data" > \
/sys/bus/pci/drivers/{driver}/new_id

    对于这种方法不在详细分析。

    分析完设备总线驱动模型,我想整个PCI驱动的框架就非常清楚了,内核启动时,通过pci_bus之间的关系枚举出所有的 PCI 设备,并为每一个 PCI 设备创建一个 pci_dev ,根据配置空间的信息填充 pci_dev 之后,注册到pci_bus_type 。而,我们写的 pci_driver 在 idtable 里指定它所支持的设备信息,同样也注册到 pci_bus_type中去,信息一致匹配成功则调用 driver->probe 函数,然后你可以注册字符设备、块设备等等。


四、PCI设备的枚举探测过程

    在内核启动过程中,PCI设备的探测过程是完全自动的,内核已经集成好了方法,我们无需更改,在这里还是分析一边代码作为了解。

    分析之前,先看一下全部的函数调用关系,大致了解一下

  1. <span style="font-size:10px;">pci_arch_init /* 判断host/pci桥的类型 */  
  2.     pci_direct_probe  
  3.         pci_check_type1  
  4.             pci_sanity_check  
  5.       
  6.     pci_direct_init  
  7.         raw_pci_ops = &pci_direct_conf1;  
  8.         raw_pci_ext_ops = &pci_direct_conf1;    
  9.   
  10. /* 第二个过程,枚举各级总线上的设备 */  
  11. pci_subsys_init  
  12.     pci_legacy_init  
  13.         pcibios_scan_root  
  14.             pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd);    
  15.                 pci_create_bus(parent, bus, ops, sysdata);  // 创建 0 级总线  
  16.                 pci_scan_child_bus(b); // 探测当前总线设备以及子总线、子总线设备  
  17.                     pci_scan_slot(bus, devfn);  // 探测当前总线的设备  
  18.                         pci_scan_single_device(bus, devfn); // 探测单功能设备  
  19.                         pci_scan_single_device(bus, devfn + fn); //探测多功能设备  
  20.                             pci_scan_device(bus, devfn);    //通过配置空间 枚举设备  
  21.                                 pci_setup_device    //根据配置空间信息,设置pci_dev  
  22.                             pci_device_add(dev, bus);                         
  23.                                 list_add_tail(&dev->bus_list, &bus->devices); // 将探测到的设备加入到当前总线的设备链表   
  24.                     pci_scan_bridge  //此时已经完成当前总线设备的探测,如果这些设备里有PCI桥,那么进入下一级,探测桥下的设备  
  25.                         child = pci_add_new_bus(bus, dev, busnr);  
  26.                         pci_scan_child_bus(child);  // 进入下一级探测  
  27.         pci_bus_add_devices // 全部设备探测完毕,注册设备。  
  28.             pci_bus_add_device(dev);   
  29.                 device_add  // 将设备注册到 pci_bus_type  
  30.             pci_bus_add_devices(child); //它最终也会调用到 device_add 将各个子总线上的设备注册到 pci_bus_type  
    下面来看具体的探测过程。

  1. static __init int pci_arch_init(void)  
  2. {  
  3. #ifdef CONFIG_PCI_DIRECT  
  4.     int type = 0;  
  5.     type = pci_direct_probe();  
  6. #endif  
  7.   
  8. #ifdef CONFIG_PCI_BIOS  
  9.     pci_pcbios_init();  
  10. #endif  
  11.   
  12. #ifdef CONFIG_PCI_DIRECT  
  13.     pci_direct_init(type);  
  14. #endif  
  15.   
  16.     dmi_check_pciprobe();  
  17.   
  18.     dmi_check_skip_isa_align();  
  19.   
  20.     return 0;  
  21. }  
  22. arch_initcall(pci_arch_init);  
    这个函数是放在 init 段中,内核启动时会调用。
  1. int __init pci_direct_probe(void)  
  2. {  
  3.     struct resource *region, *region2;  
  4.     /* 申请IO资源 */  
  5.     region = request_region(0xCF8, 8, "PCI conf1");  
  6.       
  7.     /* 探测那种类型 ,0型(PCI设备)和1型(PCI桥) */  
  8.     if (pci_check_type1()) {  
  9.         raw_pci_ops = &pci_direct_conf1;  
  10.         port_cf9_safe = true;  
  11.         return 1;  
  12.     }  
  13.     release_resource(region);  
  14.   
  15.     return 0;  
  16. }  
    这里,我们以“1型”也就是PCI桥为例,看看是如何判断类型的。

  1. static int __init pci_check_type1(void)  
  2. {  
  3.     unsigned long flags;  
  4.     unsigned int tmp;  
  5.     int works = 0;  
  6.   
  7.     local_irq_save(flags);  
  8.       
  9.     /* i386 pci地址寄存器 0xcfb 写 0x01 */  
  10.     outb(0x01, 0xCFB);  
  11.     tmp = inl(0xCF8);  
  12.     outl(0x80000000, 0xCF8);  
  13.     /* 判断设备类型 */  
  14.     if (inl(0xCF8) == 0x80000000 && pci_sanity_check(&pci_direct_conf1)) {  
  15.         works = 1;  
  16.     }  
  17.     outl(tmp, 0xCF8);  
  18.     local_irq_restore(flags);  
  19.   
  20.     return works;  
  21. }  
  1. static int __init pci_sanity_check(struct pci_raw_ops *o)  
  2. {  
  3.     u32 x = 0;  
  4.     int year, devfn;  
  5.   
  6.     /* Assume Type 1 works for newer systems. 
  7.        This handles machines that don't have anything on PCI Bus 0. */  
  8.     dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL);  
  9.     if (year >= 2001)  
  10.         return 1;  
  11.   
  12.     for (devfn = 0; devfn < 0x100; devfn++) {  
  13.         /* 读  CLASS_DEVICE ,PCI_CLASS_DEVICE 是片内偏移地址 */  
  14.         if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x))  
  15.             continue;  
  16.         /* 如果 CLASS_DEVICE 为 HOST-PCI桥(北桥),PCI-PCI桥,PCI-ISA桥(南桥)正确返回 */  
  17.         if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)  
  18.             return 1;  
  19.         /* 读  VENDOR_ID 制造商ID */  
  20.         if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x))  
  21.             continue;  
  22.         /* 如果 VENDOR_ID 为  INTEL 或 COMPAQ 正常返回 */  
  23.         if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)  
  24.             return 1;  
  25.     }  
  26.   
  27.     DBG(KERN_WARNING "PCI: Sanity check failed\n");  
  28.     return 0;  
  29. }  
    检测完是“0型”还是“1型”设备之后,在 raw_pci_ops 中指定对应的读写配置空间的方法。

  1. /* 地址是由 总线编号、设备号、片内地址 组成 */  
  2. #define PCI_CONF1_ADDRESS(bus, devfn, reg) \  
  3.     (0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \  
  4.     | (devfn << 8) | (reg & 0xFC))  
  5.   
  6. static int pci_conf1_read(unsigned int seg, unsigned int bus,  
  7.               unsigned int devfn, int reg, int len, u32 *value)  
  8. {  
  9.     unsigned long flags;  
  10.     /* 最多256个总线 ,256个设备 片内寄存器范围 0~4095 */  
  11.     if ((bus > 255) || (devfn > 255) || (reg > 4095)) {  
  12.         *value = -1;  
  13.         return -EINVAL;  
  14.     }  
  15.   
  16.     spin_lock_irqsave(&pci_config_lock, flags);  
  17.       
  18.     /* 向地址寄存器 写要读取的地址 */  
  19.     outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);  
  20.       
  21.     /* 从数据寄存器读取数据 */  
  22.     switch (len) {  
  23.     case 1:  
  24.         *value = inb(0xCFC + (reg & 3));  
  25.         break;  
  26.     case 2:  
  27.         *value = inw(0xCFC + (reg & 2));  
  28.         break;  
  29.     case 4:  
  30.         *value = inl(0xCFC);  
  31.         break;  
  32.     }  
  33.   
  34.     spin_unlock_irqrestore(&pci_config_lock, flags);  
  35.   
  36.     return 0;  
  37. }  
  38.   
  39. struct pci_raw_ops {  
  40.     int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,  
  41.                         int reg, int len, u32 *val);  
  42.     int (*write)(unsigned int domain, unsigned int bus, unsigned int devfn,  
  43.                         int reg, int len, u32 val);  
  44. };  
  45. struct pci_raw_ops *raw_pci_ops;  
  1. /* 设置全局的 配置空间读写函数 */  
  2. void __init pci_direct_init(int type)  
  3. {  
  4.     if (type == 1) {  
  5.         raw_pci_ops = &pci_direct_conf1;  
  6.   
  7.         raw_pci_ext_ops = &pci_direct_conf1;  
  8.         return;  
  9.     }  
  10. }  
    在内核启动过程中,还有一个PCI相关的函数会被调用
  1. int __init pci_subsys_init(void)  
  2. {  
  3. #ifdef CONFIG_X86_NUMAQ  
  4.     pci_numaq_init();  
  5. #endif  
  6. #ifdef CONFIG_ACPI  
  7.     pci_acpi_init();  
  8. #endif  
  9. #ifdef CONFIG_X86_VISWS  
  10.     pci_visws_init();  
  11. #endif  
  12.     pci_legacy_init();  
  13.     pcibios_fixup_peer_bridges();  
  14.     pcibios_irq_init();  
  15.     pcibios_init();  
  16.   
  17.     return 0;  
  18. }  
  19. subsys_initcall(pci_subsys_init);  
  1. struct pci_bus *pci_root_bus;  
  2. static int __init pci_legacy_init(void)  
  3. {  
  4.     pci_root_bus = pcibios_scan_root(0);//创建0级总线  
  5.     if (pci_root_bus)  
  6.         pci_bus_add_devices(pci_root_bus);  
  7.   
  8.     return 0;  
  9. }  
  1. extern struct list_head pci_root_buses; /* list of all known PCI buses */  
  2. struct pci_bus * __devinit pcibios_scan_root(int busnum)  
  3. {  
  4.     struct pci_bus *bus = NULL;  
  5.     struct pci_sysdata *sd;  
  6.     /* 在全局 pci_root_buses 链表寻找 总线编号为 busnum 的总线 */  
  7.     while ((bus = pci_find_next_bus(bus)) != NULL) {  
  8.         if (bus->number == busnum) {  
  9.             /* 如果已经存在,返回它 */  
  10.             return bus;  
  11.         }  
  12.     }  
  13.   
  14.     /* 如果这个总线编号不存在, 那么创建这个Bus */  
  15.     sd = kzalloc(sizeof(*sd), GFP_KERNEL);  
  16.     sd->node = get_mp_bus_to_node(busnum);  
  17.   
  18.     bus = pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd);  
  19.   
  20.     return bus;  
  21. }  
  1. struct pci_bus * __devinit pci_scan_bus_parented(struct device *parent,  
  2.         int bus, struct pci_ops *ops, void *sysdata)  
  3. {  
  4.     struct pci_bus *b;  
  5.     /* 创建 Bus */  
  6.     b = pci_create_bus(parent, bus, ops, sysdata);  
  7.     if (b)  
  8.         b->subordinate = pci_scan_child_bus(b);  
  9.     return b;  
  10. }  
  1. unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)  
  2. {  
  3.     unsigned int devfn, pass, max = bus->secondary;  
  4.     struct pci_dev *dev;  
  5.   
  6.     /* 探测总线上的设备 */  
  7.     for (devfn = 0; devfn < 0x100; devfn += 8)  
  8.         pci_scan_slot(bus, devfn);  
  9.   
  10.     /* Reserve buses for SR-IOV capability. */  
  11.     max += pci_iov_bus_range(bus);  
  12.   
  13.     /* 
  14.      * After performing arch-dependent fixup of the bus, look behind 
  15.      * all PCI-to-PCI bridges on this bus. 
  16.      */  
  17.     if (!bus->is_added) {  
  18.         pr_debug("PCI: Fixups for bus %04x:%02x\n",  
  19.              pci_domain_nr(bus), bus->number);  
  20.         pcibios_fixup_bus(bus);  
  21.         if (pci_is_root_bus(bus))  
  22.             bus->is_added = 1;  
  23.     }  
  24.     /* 探测 pci 桥上的设备,创建子Bus,挂到父 bus->child */  
  25.     for (pass=0; pass < 2; pass++)  
  26.         list_for_each_entry(dev, &bus->devices, bus_list) {  
  27.             if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||  
  28.                 dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)  
  29.                 max = pci_scan_bridge(bus, dev, max, pass);  
  30.         }  
  31.   
  32.     /* 
  33.      * We've scanned the bus and so we know all about what's on 
  34.      * the other side of any bridges that may be on this bus plus 
  35.      * any devices. 
  36.      * 
  37.      * Return how far we've got finding sub-buses. 
  38.      */  
  39.     pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",  
  40.         pci_domain_nr(bus), bus->number, max);  
  41.     return max;  
  42. }  
  1. int pci_scan_slot(struct pci_bus *bus, int devfn)  
  2. {  
  3.     int fn, nr = 0;  
  4.     struct pci_dev *dev;  
  5.       
  6.     dev = pci_scan_single_device(bus, devfn);  
  7.       
  8.     /* 如果是多功能设备 */  
  9.     if (dev && dev->multifunction) {  
  10.         for (fn = 1; fn < 8; fn++) {  
  11.             dev = pci_scan_single_device(bus, devfn + fn);  
  12.             if (dev) {  
  13.                 if (!dev->is_added)  
  14.                     nr++;  
  15.                 dev->multifunction = 1;  
  16.             }  
  17.         }  
  18.     }  
  19.   
  20.     return nr;  
  21. }  
  1. struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn)  
  2. {  
  3.     struct pci_dev *dev;  
  4.     /* 遍历 bus->devices 设备链表,查找是否有 devfn 号设备存在 */  
  5.     dev = pci_get_slot(bus, devfn);  
  6.     /* 如果已经存在,返回它 */  
  7.     if (dev) {  
  8.         pci_dev_put(dev);  
  9.         return dev;  
  10.     }  
  11.     /* 通过访问配置空间,探测设备 */  
  12.     dev = pci_scan_device(bus, devfn);  
  13.     /* 探测失败 返回Null */  
  14.     if (!dev)  
  15.         return NULL;  
  16.     /* 探测成功 */  
  17.     pci_device_add(dev, bus);  
  18.   
  19.     return dev;  
  20. }  
  1. static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)  
  2. {  
  3.     struct pci_dev *dev;  
  4.     u32 l;  
  5.     int delay = 1;  
  6.       
  7.     /* 读  PCI_VENDOR_ID 制造商ID */  
  8.     if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))  
  9.         return NULL;  
  10.       
  11.     /* id 等于这些值,认为探测失败 ,返回 */  
  12.     if (l == 0xffffffff || l == 0x00000000 ||  
  13.         l == 0x0000ffff || l == 0xffff0000)  
  14.         return NULL;  
  15.     ....  
  16.       
  17.     /* 探测成功,分配一个 pci_dev 结构 */  
  18.     dev = alloc_pci_dev();  
  19.   
  20.     dev->bus = bus;  
  21.     dev->devfn = devfn;  
  22.     dev->vendor = l & 0xffff;  
  23.     dev->device = (l >> 16) & 0xffff;  
  24.     /* 读取配置空间,更详细的设置,指定 dev->bus 等 */  
  25.     if (pci_setup_device(dev)) {  
  26.         kfree(dev);  
  27.         return NULL;  
  28.     }  
  29.   
  30.     return dev;  
  31. }  
  1. int pci_setup_device(struct pci_dev *dev)  
  2. {  
  3.     u32 class;  
  4.     u8 hdr_type;  
  5.     struct pci_slot *slot;  
  6.   
  7.   
  8.     dev->sysdata = dev->bus->sysdata;  
  9.     dev->dev.parent = dev->bus->bridge;  
  10.       
  11.     /* 设置 dev 所属的总线 */  
  12.     dev->dev.bus = &pci_bus_type;  
  13.     dev->hdr_type = hdr_type & 0x7f;  
  14.     dev->multifunction = !!(hdr_type & 0x80);  
  15.     dev->error_state = pci_channel_io_normal;  
  16.     set_pcie_port_type(dev);  
  17.   
  18.   
  19.     list_for_each_entry(slot, &dev->bus->slots, list)  
  20.         if (PCI_SLOT(dev->devfn) == slot->number)  
  21.             dev->slot = slot;  
  22.   
  23.   
  24.     dev->dma_mask = 0xffffffff;  
  25.     /* 设备名 */  
  26.     dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(dev->bus),  
  27.              dev->bus->number, PCI_SLOT(dev->devfn),  
  28.              PCI_FUNC(dev->devfn));  
  29.     /* 设备类型 */  
  30.     pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);  
  31.     dev->revision = class & 0xff;  
  32.     class >>= 8;                  /* upper 3 bytes */  
  33.     dev->class = class;  
  34.     class >>= 8;  
  35.   
  36.   
  37.     /* need to have dev->class ready */  
  38.     dev->cfg_size = pci_cfg_space_size(dev);  
  39.   
  40.   
  41.     /* "Unknown power state" */  
  42.     dev->current_state = PCI_UNKNOWN;  
  43.   
  44.   
  45.     /* Early fixups, before probing the BARs */  
  46.     pci_fixup_device(pci_fixup_early, dev);  
  47.     /* device class may be changed after fixup */  
  48.     class = dev->class >> 8;  
  49.   
  50.   
  51.     switch (dev->hdr_type) {         /* header type */  
  52.     case PCI_HEADER_TYPE_NORMAL:            /* standard header */  
  53.         ...  
  54.     case PCI_HEADER_TYPE_BRIDGE:            /* bridge header */  
  55.         /* 设置 dev->irq  */  
  56.         pci_read_irq(dev);  
  57.         dev->transparent = ((dev->class & 0xff) == 1);  
  58.         /* 设置 dev->rom_base_reg */  
  59.         pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);  
  60.         set_pcie_hotplug_bridge(dev);  
  61.         break;  
  62.   
  63.   
  64.     case PCI_HEADER_TYPE_CARDBUS:           /* CardBus bridge header */  
  65.         ...  
  66.         break;  
  67.           
  68.     return 0;  
  69. }  
  1. void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)  
  2. {  
  3.     device_initialize(&dev->dev);  
  4.     dev->dev.release = pci_release_dev;  
  5.     pci_dev_get(dev);  
  6.   
  7.     dev->dev.dma_mask = &dev->dma_mask;  
  8.     dev->dev.dma_parms = &dev->dma_parms;  
  9.     dev->dev.coherent_dma_mask = 0xffffffffull;  
  10.   
  11.     pci_set_dma_max_seg_size(dev, 65536);  
  12.     pci_set_dma_seg_boundary(dev, 0xffffffff);  
  13.   
  14.     /* Fix up broken headers */  
  15.     pci_fixup_device(pci_fixup_header, dev);  
  16.   
  17.     /* Clear the state_saved flag. */  
  18.     dev->state_saved = false;  
  19.   
  20.     /* Initialize various capabilities */  
  21.     pci_init_capabilities(dev);  
  22.   
  23.     /* 将设备挂入 bus->devices链表 */  
  24.     down_write(&pci_bus_sem);  
  25.     list_add_tail(&dev->bus_list, &bus->devices);  
  26.     up_write(&pci_bus_sem);  
  27. }  
  1. void pci_bus_add_devices(const struct pci_bus *bus)  
  2. {  
  3.     struct pci_dev *dev;  
  4.     struct pci_bus *child;  
  5.     int retval;  
  6.     /* 遍历当前总线的 dev ,注册设备 */  
  7.     list_for_each_entry(dev, &bus->devices, bus_list) {  
  8.         /* Skip already-added devices */  
  9.         if (dev->is_added)  
  10.             continue;  
  11.         retval = pci_bus_add_device(dev);  
  12.         if (retval)  
  13.             dev_err(&dev->dev, "Error adding device, continuing\n");  
  14.     }  
  15.     /* 遍历子总线的dev,注册设备 */  
  16.     list_for_each_entry(dev, &bus->devices, bus_list) {  
  17.         BUG_ON(!dev->is_added);  
  18.   
  19.         child = dev->subordinate;  
  20.         /* 
  21.          * If there is an unattached subordinate bus, attach 
  22.          * it and then scan for unattached PCI devices. 
  23.          */  
  24.         if (!child)  
  25.             continue;  
  26.         if (list_empty(&child->node)) {  
  27.             down_write(&pci_bus_sem);  
  28.             list_add_tail(&child->node, &dev->bus->children);  
  29.             up_write(&pci_bus_sem);  
  30.         }  
  31.         pci_bus_add_devices(child);  
  32.   
  33.         /* 
  34.          * register the bus with sysfs as the parent is now 
  35.          * properly registered. 
  36.          */  
  37.         if (child->is_added)  
  38.             continue;  
  39.         retval = pci_bus_add_child(child);  
  40.         if (retval)  
  41.             dev_err(&dev->dev, "Error adding bus, continuing\n");  
  42.     }  
  43. }  
  1. int pci_bus_add_device(struct pci_dev *dev)  
  2. {  
  3.     int retval;  
  4.       
  5.     /* 将设备注册到 pci_bus_type */  
  6.     retval = device_add(&dev->dev);  
  7.     if (retval)  
  8.         return retval;  
  9.   
  10.     dev->is_added = 1;  
  11.     pci_proc_attach_device(dev);  
  12.     pci_create_sysfs_dev_files(dev);  
  13.     return 0;  

  1. }  

转载:http://blog.csdn.net/lizuobin2/ 

Linux下PCI设备驱动程序开发 --- linux 驱动框架(二)

  • fengyv
  • fengyv
  • 2006-06-19 17:36:00
  • 7592

再识PCI:一个PCI驱动实例

之前写了第一篇关于PCI的文章,当时只是作为入门的接触笔记,后来对PCI又研究了一下,主要包括PCI设备的扫描过程及PCI驱动注册过程。 本文主要给出一个PCI实例,并在内核中做很多的打印以便跟踪其...
  • subfate
  • subfate
  • 2015-05-06 13:24:47
  • 1712

PCI简易通讯控制器驱动的安装办法

使用Intel芯片组的主板,大部分主板在使用X动精灵或X动大师安装后,会发现在设备管理器中有一个“PCI 建议通讯控制器”为黄色未安装驱动的状态,笔者就目前使用量较大的B250系列主板就此驱动列出以下...
  • ryu2003
  • ryu2003
  • 2017-03-30 10:25:47
  • 9820

linux中PCI总线驱动

欢迎转载! 一.理论知识 1.      PCI总线的特点: (1)速度快,时钟频率提高到33M,而且还为进一步把时钟频率提高到66MHZ、总线带宽提高到64位留下了余地。(2)对于地址的分配和...
  • u014379540
  • u014379540
  • 2016-09-07 15:35:50
  • 1249

设备驱动中的pci(kernel-4.7)

PCI 总线架构主要被分成三部分: 1.PCI 设备。 符合 PCI 总线标准的设备就被称为 PCI 设备,PCI 总线架构中可以包含多个 PCI 设备。Audio 、LAN 都是一个 PC...
  • viewsky11
  • viewsky11
  • 2017-01-19 23:45:35
  • 534

PCI简易通讯控制器,到底用什么驱动呢?实践证明

Intel英特尔Management Engine Interface(Intel ME)
  • supersyd
  • supersyd
  • 2014-03-31 21:53:02
  • 1336

基于WDF的PCI/PCIe接口卡Windows驱动程序(2)-开发者需要了解的WDF中的一些重要的概念

原文出处:http://www.cnblogs.com/jacklu/p/4646601.html 1、WinDBG是唯一的内核驱动调试利器,但是开发PCIe的WDF驱动可以采用...
  • wolfman125
  • wolfman125
  • 2016-07-11 14:32:02
  • 902

我的内核学习笔记6:PCI驱动probe的一点认知

对于PCI的学习,在文章《初识PCI》和《再识PCI:一个PCI驱动实例》中有介绍,文中使用大量代码进行演示。但总觉得有些认知不到位。于是就再写一文。...
  • subfate
  • subfate
  • 2016-12-03 13:14:50
  • 1849

PCI设备的DMA映射操作详解

上周认真学习了LDD3第15章直接内存访问部分,这周调试PCI的网卡和视频采集卡,结合代码对DMA映射有了进一步的理解,这里按照LDD315章的顺序总结一下,记一下笔记,以后忘了再来翻,本人刚毕业1年...
  • skyflying2012
  • skyflying2012
  • 2013-04-14 17:27:29
  • 4539

PCI/PCIe接口卡Windows驱动程序(4)- 驱动程序代码(源文件)

PCI/PCIe接口卡Windows驱动程序(4)- 驱动程序代码(源文件) http://www.cnblogs.com/jacklu/p/4687325.html 本篇文章将对...
  • zdy0_2004
  • zdy0_2004
  • 2015-07-29 21:38:47
  • 5533
收藏助手
不良信息举报
您举报文章:PCI驱动框架简单分析
举报原因:
原因补充:

(最多只允许输入30个字)