歡迎您光臨本站 註冊首頁

linux下的PCI驅動編程

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0
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設備不再使用,

同時,禁用相關的一些資源.


[火星人 ] linux下的PCI驅動編程已經有668次圍觀

http://coctec.com/docs/linux/show-post-50418.html