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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 内核PCI驱动总结记录

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

1.??介紹

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

2.??PCI的特點

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

?

獲取、訪問PCI設(shè)備。

?

對比ISA總線三個目標(biāo):

比ISA有更好的性能。

盡可能平臺無關(guān)的。

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

?

支持32位、64位數(shù)據(jù)總線。

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

?

3.??PCI尋址

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

?

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

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


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

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

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

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

?

4.??引導(dǎo)階段

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

系統(tǒng)引導(dǎo)階段,linux內(nèi)核為每個PCI地址區(qū)域申請安全的處理器地址。后續(xù)驅(qū)動可以從/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包含分配給該設(shè)備的內(nèi)存資源。irq包含了該P(yáng)CI設(shè)備的中斷號。


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

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


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

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

  • vendorID

全局性、全球性。16位標(biāo)識。比如intel的0x8086.

  • deviceID

廠商定義的16位標(biāo)識。通常使用vendorID+deviceID 32位標(biāo)識一個設(shè)備。驅(qū)動根據(jù)該32位標(biāo)識,定位到一個設(shè)備。

  • class

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

  • subsystem vendorID
  • subsystem deviceID

subsystem類型的標(biāo)識,用于進(jìn)一步識別設(shè)備。當(dāng)一個芯片是連接到本地板載上的通用芯片時,它可能有多用用途。驅(qū)動使用subsystem標(biāo)識,識別具體連接的設(shè)備。

內(nèi)核標(biāo)識設(shè)備ID的結(jié)構(gòu)是:

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結(jié)構(gòu)導(dǎo)出到用戶空間中,使熱插拔和模塊加載系統(tǒng)知道什么模塊對應(yīng)什么設(shè)備。

例如:

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結(jié)構(gòu)的別名。模塊編譯之后,在對應(yīng)的模塊ELF文件中會有相應(yīng)的__mod_pci_device_table結(jié)構(gòu)符號。在內(nèi)核構(gòu)建時,depmod搜索所有模塊的類似__mod_pci_device_table結(jié)構(gòu)的符號,從中解析出type和name,并取出pci_device_id數(shù)據(jù)導(dǎo)出到/lib/modules/KERNEL_VERSION/modules.pcimap文件中。之后內(nèi)核所有模塊支持的設(shè)備和模塊的名字可在該文件中找到。當(dāng)內(nèi)核告知熱插拔系統(tǒng),發(fā)現(xiàn)一個新的設(shè)備時,熱插拔系統(tǒng)根據(jù)modules.pcimap文件找到對應(yīng)的驅(qū)動。

?

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

?

7.??PCI驅(qū)動注冊

為了正確的注冊到內(nèi)核,PCI驅(qū)動必須創(chuàng)建一個結(jié)構(gòu):

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;

};

包括一些回調(diào)函數(shù)和描述PCI驅(qū)動與PCI核心對應(yīng)的變量。

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

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驅(qū)動結(jié)構(gòu)只需要4個區(qū)域被初始化。

static struct pci_driver pci_driver = {

.name ="pci_skel",

.id_table = ids,

.probe = probe,

.remove =remove,
};

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

static int __init pci_skel_init(void)

{

return pci_register_driver(&pci_driver);

}

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

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

當(dāng)PCI驅(qū)動被卸載時,需要調(diào)用pci_unregister_driver。例如:

static void __exit pci_skel_exit(void)

{

pci_unregister_driver(&pci_driver);

}

8.??使能PCI設(shè)備

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

int pci_enable_device(struct pci_dev *dev);

用來激活設(shè)備。

9.??訪問配置空間

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

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

linux提供了一套訪問配置空間的標(biāo)準(zhǔn)接口。

對驅(qū)動而言,可通過8、16、32位數(shù)據(jù)傳輸訪問配置空間。相關(guān)的函數(shù)定義在<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:訪問設(shè)備的邏輯表示

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

*val:讀取的值

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

?

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:寫入設(shè)備的邏輯表示

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

*val:寫入的值

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

?

在驅(qū)動未獲得pci_dev時,可使用如上函數(shù)讀寫配置空間

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_系列函數(shù),例如:

static unsigned charskel_get_revision(struct pci_dev *dev)
{

u8 revision;

pci_read_config_byte(dev,PCI_REVISION_ID, &revision);

return revision;

}

10.??訪問IO和內(nèi)存空間

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

?

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

?

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

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ū)域的特性。其中幾個重要的標(biāo)志如下:

IORESOURCE_IO

IORESOURCE_MEM

IORESOURCE_PREFETCH

IORESOURCE_READONLY(PCI資源從不設(shè)置該標(biāo)志)

?

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

11.??PCI中斷

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

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

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

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

if (result) {

/* deal witherror */

}

?

12.??總結(jié)歸納

linux中:

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

總結(jié)

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

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。