PCI驱动开发学习笔记(一)
- PCIE簡介
???????? PCIe作為串行總線的一種,它的發展必然和另一種總線架構密不可分:并行總線。
??????? 像PCIe接口的顯卡、聲卡、網卡,都屬于功能設備,在PCIe規范中,我們統稱為Endpoint(簡稱EP)。還有其他兩類設備,? 一個是Root Complex,簡稱RC,另一個是switch(或者PCI中稱為bridge),RC是根設備,通過switch和下游設備(可以是EP也可以是switch)進行橋接,從而各個PCIe設備組成了一個PCIe設備網絡,信息就以數據包(TLP、DLLP)的形式在網絡中傳遞。
一、PCI設備編號
???????? PCI設備的ID號由總線號(BUS NUMBER)、設備號(DEVICE NUMBER)和功能號(FUNCTION NUMBER)組成。
???????? 一條PCI總線的設備號由PCI設備的IDSEL信號與PCI總線地址線的連接關系確定,即每一個PCI插槽的總線號和設備號都是固定的,這是硬件工程師決定的。
???????? PCI功能號與PCI設備的具體設計相關。在一個PCI設備中最多有8個功能設備,而且每一個功能設備都有各自的PCI配置空間,而在絕大數PCI設備中只有一個功能設備。
lspci? --- 枚舉PCI設備
???????? 01:00.0 VGA compatible controller: NVIDIA Corporation GM107 [GeForce GTX 745] (rev a2)
??????? (bus number 0~0xff[2^8]):(device number 0~0x1f[2^5]).(function number 0~0x07[2^3])? Class:Vendor Device (revision)
二、PCI配置空間
每個PCI邏輯設備都有自己的配置空間,里面存儲了一些基本信息,生產商,IRQ中斷號,還有就是定義了mem空間和io空間的起始地址和大小。
HOST主橋使用寄存器號,訪問PCI設備配置空間的某個寄存器。
三、PCI驅動函數接口
???????? 1.? pci驅動注冊
????????? pci_register_driver(struct pci_driver *drv)
????????? pci_unregister_driver(struct pci_driver *drv)
static struct pci_device_id vt8623_devices[] = {{PCI_DEVICE(PCI_VENDOR_ID_VIA, 0x3122)},{0, 0, 0, 0, 0, 0, 0} }; static struct pci_driver vt8623fb_pci_driver = {.name = "vt8623fb",.id_table = vt8623_devices,.probe = vt8623_pci_probe,.remove = vt8623_pci_remove,.suspend = vt8623_pci_suspend,.resume = vt8623_pci_resume, };pci_register_driver(&vt8623fb_pci_driver);????????? 2. 激活PCI設備
??????????????? 在驅動程序可以訪問PCI設備的任何設備資源之前(I/O區域或者中斷),驅動程序必須調用該函數:
int pci_enable_device(struct pci_dev *dev); /*driver/pci/pci.c*/這個函數主要就是把PCI配置空間的Command域的0位和1?位置成了1,從而達到了開啟設備的目的。????????? 3.? 訪問PCI配置空間
int pci_read_config_byte(conststruct pci_dev *dev,int where, u8 *val);/*8位,讀入一個字節*/int pci_read_config_word(conststruct pci_dev *dev,int where, u16 *val);/*16位,讀入兩個字節*/int pci_read_config_dword(conststruct pci_dev *dev,int where, u32 *val);/*32位,讀入四個字節*/ int pci_write_config_byte(conststruct pci_dev *dev,int where, u8 *val);/*8位,寫入一個字節*/int pci_write_config_word(conststruct pci_dev *dev,int where, u16 *val);/*16位,寫入兩個字節*/int pci_write_config_dword(conststruct pci_dev *dev,int where, u32 *val);/*32位,寫入四個字節*/????????? 4. 申請IO端口和內存資源
?????????????? 通知內核該設備對應的IO端口和內存資源已經使用,其他的PCI設備不要再使用這個區域
???????????? int? pci_request_regions(struct pci_dev *dev, const char *res_name)
???????????? int? pci_release_regions(struct pci_dev *pdev)
???????????? int? pci_request_mem_regions(struct pci_dev *pdev, const char *name)
???????????? int? pci_release_mem_regions(struct pci_dev *pdev)
???????????? int? pci_request_io_regions(struct pci_dev *pdev, const char *name)
???????????? int? pci_release_io_regions(struct pci_dev *pdev)
?
?????????? 5. 訪問PCI的I/O和內存空間
???????????? int pci_resource_[start|end|flags|len](struct pci_dev *dev,? int bar)? //Bar值的范圍為0-5
?在硬件加電初始化時,BIOS固件統一檢查了所有的PCI設備,并統一為他們分配了一個和其他互不沖突的地址,讓他們的驅動程序可以向這些地址映射他們的寄存器,這些地址被BIOS寫進了各個設備的配置空間,因為這個活動是一個PCI的標準的活動,所以自然寫到各個設備的配置空間里而不是他們風格各異的控制寄存器空間里。當操作系統初始化時,他為每個PCI設備分配了pci_dev結構,并且把BIOS獲得的并寫到了配置空間中的地址讀出來寫到了pci_dev中的resource字段中。這樣以后我們在讀這些地址就不需要在訪問配置空間了,直接跟pci_dev要就可以了,我們這里的四個函數就是直接從pci_dev讀出了相關數據。
Linux認為這個地址是IO地址,如果要訪問的話可以通過ioremap映射到內核空間,然后通過readl/writel等IO接口進行操作。
?????????? 6.? 私有數據
???????????? pci_set_drvdata??? 設置驅動私有數據
???????????? pci_get_drvdata??? 獲取驅動私有數據
?????????? 7. 主設備模式
????????????? pci_set_master??? 設定設備工作在總線主設備模式
?
?
?
?
?
總結
以上是生活随笔為你收集整理的PCI驱动开发学习笔记(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: h5在线聊天室(附源码)
- 下一篇: rails中关于carrierwave、