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);
};
其中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_device 和 pci_disable_device在一個pci裝置可以被使用之前,必須調用pci_enable_device進行激活,該函數會調用底層代碼激活PCI裝置上的I/O和記憶體,使之可用。而pci_disable_device所做的事情剛好相反,告訴系統該PCI裝置不再使用,
同時,禁用相關的一些資源。