PCI设备上有三种地址空间:PCI的I/O空间、PCI的存储空间和PCI的配置空间。CPU可以
访问PCI设备上的所有地址空间,其中I/O空间和存储空间提供给设备驱动程序使用,而
配置空间则由Linux内核中的PCI初始化代码使用。内核在启动时负责对所有PCI设备进行
初始化,配置好所有的PCI设备,包括中断号以及I/O基址,并在文件/proc/pci中列出所
有找到的PCI设备,以及这些设备的参数和属性。

Linux驱动程序通常使用结构(struct)来表示一种设备,而结构体中的变量则代表某一
具体设备,该变量存放了与该设备相关的所有信息。好的驱动程序都应该能驱动多个同
种设备,每个设备之间用次设备号进行区分,如果采用结构数据来代表所有能由该驱动
程序驱动的设备,那么就可以简单地使用数组下标来表示次设备号。

在PCI驱动程序中,下面几个关键数据结构起着非常核心的作用:
1)pci_driver:
1)      这个数据结构在文件include/linux/pci.h里,这是Linux内核版本2.4之后为新型的 PCI
设备驱动程序所添加的,其中最主要的是用于识别设备的id_table结构,以及用于检测

设备的函数probe()和卸载设备的函数remove() 
struct pci_driver {
    struct list_head node;
    char *name;
    const struct pci_device_id *id_table;
    int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);
    void (*remove) (struct pci_dev *dev);
    int  (*save_state) (struct pci_dev *dev, u32 state);
    int  (*suspend)(struct pci_dev *dev, u32 state);
    int  (*resume) (struct pci_dev *dev);
    int  (*enable_wake) (struct pci_dev *dev, u32 state, int enable);
};
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

其中name 是驱动程序名称;id_table指向一个与驱动程序相关的设备ID表的指针。大多数驱动程序应当用MODULE_DEVICE_TABLE(pci,…)将该设备ID表导出。在调用prob( )时设成NULL 以让系统检测到所有的pci设备。

代码中是这样定义的:MODULE_DEVICE_TABLE(pci, sil_pci_tbl);

probe 指向设备检测函数probe( ) 的指针。该函数将在pci设备ID与设备ID表匹配且还没有被其它驱动程序处理时(一般在对已存在的设备执行pci_register_driver或以后又有新设备插入时)被调用。调用时传入一个指向struct pci_driver结构的指针和与设备匹配的设备ID表做参数。若成功(驱动程序检测到pci设备)则返回0,否则返回一个负的错误代码。这个函数总是在上下文之间调用的,因此可以进入睡眠状态的

remove指向一个设备卸载函数remove( )的指针。该函数在pci设备被卸载时(如在注销设备驱动程序或者手动拔出该设备)被调用。同probe一样,该函数也是可以睡眠的。

2)pci_dev:
1)      这个数据结构也在文件include/linux/pci.h里,它详细描述了一个PCI设备几乎所有的
硬件信息,包括厂商ID、设备ID、各种资源等:

struct pci_dev {

       struct list_head global_list;     /* node in list of all PCI devices */

       struct list_head bus_list;   /* node in per-bus list */

       struct pci_bus      *bus;             /* bus this device is on */

       struct pci_bus      *subordinate;      /* bus this device bridges to */

 

       void              *sysdata;      /* hook for sys-specific extension */

       struct proc_dir_entry *procent;     /* device entry in /proc/bus/pci */

 

       unsigned int devfn;           /* encoded device & function index */

       unsigned short    vendor;

       unsigned short    device;

       unsigned short    subsystem_vendor;

       unsigned short    subsystem_device;

       unsigned int class;            /* 3 bytes: (base,sub,prog-if) */

       u8          hdr_type;      /* PCI header type (`multi' flag masked out) */

       u8          rom_base_reg;   /* which config register controls the ROM */

 

       struct pci_driver *driver;  /* which driver has allocated this device */

       u64        dma_mask;  /* Mask of the bits of bus address this

                                      device implements.  Normally this is

                                      0xffffffff.  You only need to change

                                      this if your device has broken DMA

                                      or supports 64-bit transfers.  */

 

       pci_power_t     current_state;  /* Current operating state. In ACPI-speak,

                                      this is D0-D3, D0 being fully functional,

                                      and D3 being off. */

 

       struct     device    dev;              /* Generic device interface */

 

       /* device is compatible with these IDs */

       unsigned short vendor_compatible[DEVICE_COUNT_COMPATIBLE];

       unsigned short device_compatible[DEVICE_COUNT_COMPATIBLE];

 

       int          cfg_size;       /* Size of configuration space */

 

       /*

        * Instead of touching interrupt line and base address registers

        * directly, use the values stored here. They might be different!

        */

       unsigned int irq;

struct resource resource[DEVICE_COUNT_RESOURCE]; /* I/O and memory regions + expansion ROMs */

 

       /* These fields are used by common fixups */

       unsigned int transparent:1;     /* Transparent PCI bridge */

       unsigned int multifunction:1;/* Part of multi-function device */

       /* keep track of device state */

       unsigned int is_enabled:1;       /* pci_enable_device has been called */

       unsigned int is_busmaster:1; /* device is busmaster */

       unsigned int no_msi:1;     /* device may not use msi */

 

       u32        saved_config_space[16]; /* config space saved at suspend time */

       struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */

       int rom_attr_enabled;             /* has display of the rom attribute been enabled? */

       struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */

};

 
             同加载和卸载模块相关的函数或数据结构都要在前面加上__init__exit
标志符,以使同普通函数区分开来。static int __init sil_init(void)

{

       return pci_module_init(&sil_pci_driver);

}

              驱动程式通过pci_module_init向内核注册自己(我们有时会看到pci_register_driver函数,其实他们是同一个,在内核代码中会看到,只是个简单的#define):
 pci_module_init(&sil_pci_driver);

 调用函数后,如果pci_device_id数组中标识的设备存在于系统中,并且该设备恰好还没有驱动程式,则该驱动程式会被安装。

           注册驱动程式成功后,sil_init_one会被调用,在这个函数中,我们能通过插入一些打印输出语句看到PCI的设置地址空间和I/O地址区域的一些情况。
  
            pci_enable_devicepci_disable_device
在一个pci设备可以被使用之前,必须调用pci_enable_device进行激活,该函数会调用底层代码激活PCI设备上的I/O和内存,使之可用。而pci_disable_device所做的事情刚好相反,告诉系统该PCI设备不再使用,
同时,禁用相关的一些资源。