日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 内核PCI驱动总结记录

發(fā)布時間:2023/12/18 linux 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 内核PCI驱动总结记录 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

1.??介紹

Peripheral ComponentInterconnect (PCI,外圍設備互聯(lián))。總線由電氣接口、編程接口組成。主要討論編程接口。最常用的總線,內核支持最好的總線。ISA裸金屬總線,電子愛好者偏愛。

2.??PCI的特點

是一種完整的規(guī)范,定義計算機計算機不同部分之間的通信。

?

獲取、訪問PCI設備。

?

對比ISA總線三個目標:

比ISA有更好的性能。

盡可能平臺無關的。

簡化系統(tǒng)添加、刪除外設。

?

支持32位、64位數據總線。

對驅動編寫者最相關的是接口板的自動發(fā)現(xiàn)。PCI設備是無跳線設備,在系統(tǒng)引導階段自動配置。包括設備的配置信息等一些工作都是自動完成的,不需要任何的探測。之后,驅動編寫者就能夠訪問設備的配置信息,以便初始化設備。

?

3.??PCI尋址

每個PCI外設由bus:device.function一個16位地址標識。bus(8位)、device(5位)、function(3位)。單個總共256個總線、每個總線最多32個設備、每個設備最多8個功能(比如聲音功能)。linux為了擴展總線數量,提供domain(16位)。系統(tǒng)把PCI設備抽象為pci_dev結構,因此不需要訪問這些二進制地址。

?

當前的工作站一般都配有2個以上的pci總線。不同PCI總線之間通過PCI橋連接(一個特殊的PCI設備)。PCI系統(tǒng)的整體布局是一個樹狀的結構。每個總線都連接上一級總線,一直到根總線0.

可以使用lspci命令查看當前系統(tǒng)的pci。或者在文件系統(tǒng)/proc/pci和/proc/bus/pci中。


外設板電路響應三種地址空間:內存、IO、配置空間。前兩種地址空間在同一個PCI總線上是共享的。配置空間是物理尋址的,每次只對一個槽尋址。

內存和IO空間通常通過inb、readb等方式訪問。配置空間需要通過特殊的內核函數訪問配置寄存器。每個PCI槽有4個中斷引腳,每個設備功能使用其中的一個。

???????? 1個PCI總線使用32位的地址總線用于IO尋址(4G),32位的地址總線(現(xiàn)在設備有的支持64位)用于內存尋址。在系統(tǒng)啟動階段,固件初始化PCI硬件的時候,把每個區(qū)域映射到不同的地址。驅動程序不需要探測,而從配置空間讀取映射的地址。

???????? 對于每個設備功能,PCI配置空間由256字節(jié)組成(PCIE的是64KB),并且配置空間的布局是標準的。配置空間的4個字節(jié)(哪4個字節(jié)?)標識唯一的功能ID。

?

4.??引導階段

主板上的固件(比如BIOS),讀寫PCI設備中的寄存器,訪問配置空間。

系統(tǒng)引導階段,linux內核為每個PCI地址區(qū)域申請安全的處理器地址。后續(xù)驅動可以從/sys/bus/pci/devices/*目錄中讀取映射的地址。

$ tree /sys/bus/pci/devices/0000:00:10.0
/sys/bus/pci/devices/0000:00:10.0
|-- class
|-- config
|-- detach_state
|-- device
|-- irq
|-- power
| ?`-- state
|-- resource
|-- subsystem_device
|-- subsystem_vendor
`-- vendor

其中,config包含配置信息,resource包含分配給該設備的內存資源。irq包含了該PCI設備的中斷號。


5.??配置寄存器和初始化

所有的PCI設備至少包含256字節(jié)的配置地址空間(PCIE是64KB)。其中前64字節(jié)是標志的。


PCI配置寄存器包括可選和必需兩部分,必需的部分聲明功能和其他字段是否可用。

PCI寄存器是小端字節(jié)序。

  • vendorID

全局性、全球性。16位標識。比如intel的0x8086.

  • deviceID

廠商定義的16位標識。通常使用vendorID+deviceID 32位標識一個設備。驅動根據該32位標識,定位到一個設備。

  • class

16位的標識,高8位標識基本類(group)。比如,以太網、令牌環(huán)網屬于網絡group,串行、并行屬于通信group。一些驅動支持多種相同類型的設備,驅動可以根據類型區(qū)分支持的設備。

  • subsystem vendorID
  • subsystem deviceID

subsystem類型的標識,用于進一步識別設備。當一個芯片是連接到本地板載上的通用芯片時,它可能有多用用途。驅動使用subsystem標識,識別具體連接的設備。

內核標識設備ID的結構是:

struct pci_device_id {

???????? __u32vendor, device;?????????????? /* Vendorand device ID or PCI_ANY_ID*/

???????? __u32subvendor, subdevice;? /* Subsystem ID'sor PCI_ANY_ID */

???????? __u32class, class_mask; /*(class,subclass,prog-if) triplet */

???????? kernel_ulong_tdriver_data;?? /* Data private to thedriver */

};

6.??MODULE_DEVICE_TABLE

通過把pci_device_id結構導出到用戶空間中,使熱插拔和模塊加載系統(tǒng)知道什么模塊對應什么設備。

例如:

MODULE_DEVICE_TABLE(pci, i810_ids);

具體的實現(xiàn)是:

extern const typeof(name)__mod_##type##__##name##_device_table????????????? \

?__attribute__ ((unused, alias(__stringify(name))))

?

其中pci是模塊名,i810_ids是pci_device_id變量名。MODULE_DEVICE_TABLE宏把例如i810_ids的變量名,起一個__mod_pci_device_table結構的別名。模塊編譯之后,在對應的模塊ELF文件中會有相應的__mod_pci_device_table結構符號。在內核構建時,depmod搜索所有模塊的類似__mod_pci_device_table結構的符號,從中解析出type和name,并取出pci_device_id數據導出到/lib/modules/KERNEL_VERSION/modules.pcimap文件中。之后內核所有模塊支持的設備和模塊的名字可在該文件中找到。當內核告知熱插拔系統(tǒng),發(fā)現(xiàn)一個新的設備時,熱插拔系統(tǒng)根據modules.pcimap文件找到對應的驅動。

?

注:模塊的概念。PCI是一個模塊。

?

7.??PCI驅動注冊

為了正確的注冊到內核,PCI驅動必須創(chuàng)建一個結構:

struct pci_driver {

??? struct list_head node;

??? const char *name;

??? const struct pci_device_id *id_table;??/* must be non-NULL for probe to be called*/

??? int? (*probe)?(struct pci_dev*dev,const struct pci_device_id*id);??/* New device inserted */

??? void (*remove)(struct pci_dev*dev);??/* Device removed (NULL if not a hot-plugcapable driver) */

??? int? (*suspend)(struct pci_dev*dev, pm_message_tstate);?/* Device suspended */

??? int? (*suspend_late)(struct pci_dev*dev, pm_message_tstate);

??? int? (*resume_early)(struct pci_dev*dev);

??? int? (*resume)(struct pci_dev*dev);??????????????????/* Device woken up */

??? void (*shutdown)(struct pci_dev*dev);

??? int (*sriov_configure)(struct pci_dev*dev,int num_vfs);/* PF pdev */

??? const struct pci_error_handlers *err_handler;

??? struct device_driver??? driver;

??? struct pci_dynids dynids;

};

包括一些回調函數和描述PCI驅動與PCI核心對應的變量。

其中一些區(qū)域需要PCI驅動注意。

const 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? (*suspend)(struct pci_dev*dev, pm_message_tstate);

int? (*resume)(struct pci_dev*dev);

總的來說,一個PCI驅動結構只需要4個區(qū)域被初始化。

static struct pci_driver pci_driver = {

.name ="pci_skel",

.id_table = ids,

.probe = probe,

.remove =remove,
};

通常在模塊初始化代碼中,注冊pci驅動。比如:

static int __init pci_skel_init(void)

{

return pci_register_driver(&pci_driver);

}

2.6更新后,在支持PCI熱插拔、或CardBus系統(tǒng)上,PCI設備可以出現(xiàn)在任何時刻。

在系統(tǒng)運行時刻,通過寫值到驅動的new_id中,指定驅動支持的新設備(原內核未認知的設備)。

當PCI驅動被卸載時,需要調用pci_unregister_driver。例如:

static void __exit pci_skel_exit(void)

{

pci_unregister_driver(&pci_driver);

}

8.??使能PCI設備

在PCI的探測函數中,在驅動訪問PCI設備的任何資源之前(IO區(qū)域或資源),驅動程序必須調用函數:

int pci_enable_device(struct pci_dev *dev);

用來激活設備。

9.??訪問配置空間

在驅動監(jiān)測到設備之后,通常需要訪問三個區(qū)域:內存、IO區(qū)域、配置空間。

訪問配置空間尤其重要,因為需要通過配置空間找到內存區(qū)域映射和IO區(qū)域映射。

linux提供了一套訪問配置空間的標準接口。

對驅動而言,可通過8、16、32位數據傳輸訪問配置空間。相關的函數定義在<linux/
pci.h>:中:

int pci_read_config_byte(struct pci_dev*dev, int where, u8 *val);
int pci_read_config_word(struct pci_dev *dev, int where, u16 *val);
int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);

dev:訪問設備的邏輯表示

where:要讀取位置在配置空間中的位移

*val:讀取的值

不需要考慮字節(jié)序,會自動轉換。

?

int pci_write_config_byte(struct pci_dev*dev, int where, u8 val);
int pci_write_config_word(struct pci_dev *dev, int where, u16 val);
int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);

dev:寫入設備的邏輯表示

where:要寫入位置在配置空間中的位移

*val:寫入的值

不需要考慮字節(jié)序,會自動轉換。

?

在驅動未獲得pci_dev時,可使用如上函數讀寫配置空間

int pci_bus_read_config_byte (structpci_bus *bus, unsigned int devfn, int
where, u8 *val);
int pci_bus_read_config_word (struct pci_bus *bus, unsigned int devfn, int
where, u16 *val);
int pci_bus_read_config_dword (struct pci_bus *bus, unsigned int devfn, int
where, u32 *val);

?

int pci_bus_write_config_byte (structpci_bus *bus, unsigned int devfn, int
where, u8 val);
int pci_bus_write_config_word (struct pci_bus *bus, unsigned int devfn, int
where, u16 val);
int pci_bus_write_config_dword (struct pci_bus *bus, unsigned int devfn, int
where, u32 val);

?

訪問配置空間的最好方式是通過pci_read_系列函數,例如:

static unsigned charskel_get_revision(struct pci_dev *dev)
{

u8 revision;

pci_read_config_byte(dev,PCI_REVISION_ID, &revision);

return revision;

}

10.??訪問IO和內存空間

一個PCI設備最多可實現(xiàn)6個IO地址區(qū)域。每個區(qū)域可以是內存或者IO地址。大多數設備在內存區(qū)域實現(xiàn)IO寄存器,這也是一個明智的方法。需要注意的是,和常規(guī)內存不同,IO寄存器不應該由CPU緩存,因為每次訪問都可能邊緣效應。為了取消這個默認設置,內存區(qū)域實現(xiàn)的IO寄存器,可以通過在其配置寄存器中設置“memory-is-prefetchable”。若是可預取的,CPU可緩存其內容并進行各種優(yōu)化。若不是可預取的,則不能優(yōu)化,因為每次訪問都有邊際效應,就行IO端口一樣。

?

接口板(PCI設備)通過6個32位的寄存器(PCI_BASE_ADDRESS_0到PCI_BASE_ADDRESS_5)聲明區(qū)域的大小和位置。所以最多實現(xiàn)6IO地址區(qū)域。因為PCI的IO地址空間是32位的,所以不管是內存或IO區(qū)域使用相同的配置接口是有道理的。如果設備的數據總線是64位的,那么每個區(qū)域使用兩個連續(xù)的32位寄存器實現(xiàn)。一個PCI設備既提供32位區(qū)域又提供64位區(qū)域是有可能的。

?

內核已經把PCI設備的IO區(qū)域信息映射進了通用資源管理中。所以,不需要通過訪問配置寄存器來獲取IO區(qū)域信息。可以通過訪問/sys/bus/pci/devices/*/resource的內容獲取。但首選的方法是通過下列函數獲取。

unsigned long pci_resource_start(structpci_dev *dev, int bar);

unsigned long pci_resource_end(structpci_dev *dev, int bar);

bar:指定要獲取的區(qū)域(0到5)

?

unsigned long pci_resource_flags(structpci_dev *dev, int bar);

資源flag用來定義某個區(qū)域的特性。其中幾個重要的標志如下:

IORESOURCE_IO

IORESOURCE_MEM

IORESOURCE_PREFETCH

IORESOURCE_READONLY(PCI資源從不設置該標志)

?

驅動程序不需要訪問配置寄存器去獲得這些資源信息,因為系統(tǒng)已經構建了這些資源信息,驅動直接使用pci_resource_系列函數獲取即可。

11.??PCI中斷

在linux系統(tǒng)啟動時,已經為PCI設備分配了一個唯一的中斷號,位于配置空間的第60寄存器(PCI_INTERRUPT_LINE),一個字節(jié)長度,最多256個中斷號。第61個寄存器(PCI_INTERRUPT_PIN)說明PCI設備是否支持中斷,如果不支持,則為0,如果支持,非0.

如果是非0的,PCI_INTERRUPT_PIN的值是中斷引腳的編號()。

驅動通過下面代碼讀取中斷號,以便使用:

result = pci_read_config_byte(dev,PCI_INTERRUPT_LINE, &myirq);

if (result) {

/* deal witherror */

}

?

12.??總結歸納

linux中:

先module初始化,內含pci初始化(對于pci設備而言)

總結

以上是生活随笔為你收集整理的linux 内核PCI驱动总结记录的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。