Linux下PCI設(shè)備驅(qū)動開發(fā)詳解(一)

Linux下PCI設(shè)備驅(qū)動開發(fā)詳解(一)

PCI總線是目前應(yīng)用最廣泛的計(jì)算機(jī)總線標(biāo)準(zhǔn),而且是一種兼容性最強(qiáng),功能最全的計(jì)算機(jī)總線。
而linux作為一種開源的操作系統(tǒng),同時(shí)也為PCI總線與各種新型設(shè)備互聯(lián)成為可能。尤其被現(xiàn)在的異構(gòu)計(jì)算GPU/FPGA、軟硬結(jié)合新的方向廣泛運(yùn)用。

一、PCI設(shè)備和驅(qū)動概述

應(yīng)用程序位于用戶空間,驅(qū)動程序位于內(nèi)核空間。linux系統(tǒng)規(guī)定,用戶空間不可以直接調(diào)用內(nèi)核函數(shù),所以必須經(jīng)過系統(tǒng)調(diào)用,應(yīng)用程序才可以調(diào)用驅(qū)動程序的函數(shù)。另外應(yīng)用程序通過系統(tǒng)調(diào)用去調(diào)用驅(qū)動程序的函數(shù),還有一個前提就是驅(qū)動程序必須留有接口,這里的接口就是ops函數(shù)的操作集合。

??????????


1702014908899.png

??驅(qū)動最終通過與文件系統(tǒng)相關(guān)的系統(tǒng)調(diào)用或C庫函數(shù)(本質(zhì)也是基于系統(tǒng)調(diào)用)被訪問,而設(shè)備驅(qū)動的結(jié)構(gòu)也是為了迎合應(yīng)用程序,提供給應(yīng)用程序的SDK API。

sys:linux設(shè)備驅(qū)動模型中的總線、驅(qū)動和設(shè)備都可以在sysfs文件系統(tǒng)中找到相應(yīng)的節(jié)點(diǎn)。當(dāng)內(nèi)核檢測到系統(tǒng)中出現(xiàn)新的設(shè)備后,內(nèi)核會在sysfs文件系統(tǒng)中為設(shè)備生成一項(xiàng)新的記錄。

sysfs是一個虛擬的文件系統(tǒng),它可以產(chǎn)生一個包括<font color="red">所有系統(tǒng)硬件</font>的層級視圖,與<font color=red>提供進(jìn)程和狀態(tài)的proc文件系統(tǒng)</font>十分類似??梢宰層脩艨臻g存取,向<font color=red>用戶空間導(dǎo)出內(nèi)核數(shù)據(jù)結(jié)構(gòu)以及它的屬性</font>。

在linux內(nèi)核中,設(shè)備和驅(qū)動是分開注冊的,注冊1個設(shè)備的時(shí)候,并不需要驅(qū)動已經(jīng)存在,而1個驅(qū)動被注冊的時(shí)候,也不需要對應(yīng)的設(shè)備被注冊。設(shè)備和驅(qū)動各自涌入內(nèi)核,而每個設(shè)備和驅(qū)動涌入內(nèi)核的時(shí)候,都會尋找另外一半_。而正是bus_type的match()成員函數(shù)將兩者綁定在一起。

簡單來說,設(shè)備和驅(qū)動就是紅塵的男女,而bus_type的match()則是牽引紅線的月老,它可以識別什么設(shè)備與什么驅(qū)動,是配對的。一旦成功,xxx_driver的<font color=red>probe</font>就被執(zhí)行。

二、PCI總線描述

??????????


1702019935166.png

PCI是<font color=red>CPU和外圍設(shè)備通信的高速傳輸總線</font>。普通PCI總線帶寬一般為132MB/s或者264MB/s。

PCI總線體系結(jié)構(gòu)是一種層次式的體系結(jié)構(gòu),PCI橋設(shè)備占據(jù)重要的地位,它將父總線與子總線連接在一起,從而使整個系統(tǒng)看起來像一顆倒置的樹形結(jié)構(gòu)。

三、PCI配置空間

PCI有3種地址空間:PCI配置空間、PCI/IO空間、PCI內(nèi)存地址空間。

1. PCI配置空間

??????????


1702020773321.png

deviceID和vendorID寄存器:由pcisig分配,只讀,vendorID代表pci設(shè)備的廠商,deviceID代表廠商的具體設(shè)備;
status:設(shè)備狀態(tài)字;
command:設(shè)備狀態(tài)字;
base address register:決定pci/pcie設(shè)備空間映射到系統(tǒng)具體位置的寄存器,IO和memory映射兩種;

2. PCI/IO空間(PIO)

pio端口的編址是<font color=red>獨(dú)立于系統(tǒng)的地址空間,其實(shí)是一段地址區(qū)域,所有外設(shè)的地址都映射到這段區(qū)域中</font>。不同外設(shè)的IO端口不同,訪問IO端口需要特殊的IO指令,OUT/IN,out用于write操作,in用于read操作;
IO地址空間有限;

3. PCI內(nèi)存地址空間(MMIO)

io內(nèi)存就是把寄存器的地址空間直接映射到系統(tǒng)的地址空間,系統(tǒng)地址空間保留一段內(nèi)存用于MMIO的映射。

上述的方案只適用于外設(shè)和內(nèi)存進(jìn)行小數(shù)據(jù)量的傳輸時(shí),假如進(jìn)行大數(shù)據(jù)量的傳輸,PIO以字節(jié)為單位的傳輸不用說了,MMIO雖然進(jìn)行了內(nèi)存映射,但是范圍相對于大量的數(shù)據(jù),不值得一提,所以即使采用了MMIO仍然滿足不了需要,會讓吧CPU大部分時(shí)間處理繁瑣的映射,極大浪費(fèi)CPU資源。在這種情況下,引入了DMA,由DMA控制器控制,完成后中斷通知CPU,極大解放了CPU。后面的文章會接收DMA。

四、PCI設(shè)備驅(qū)動組成

PCI本質(zhì)上就是一種總線,具體的PCI設(shè)備可以是字符設(shè)備、網(wǎng)絡(luò)設(shè)備、USB等,所以PCI設(shè)備驅(qū)動應(yīng)該包括兩個部分:

  1. PCI通用驅(qū)動
  2. 根據(jù)實(shí)際需要的設(shè)備驅(qū)動
    根據(jù)需求的設(shè)備驅(qū)動是最終目的,PCI驅(qū)動只是手段幫助設(shè)備驅(qū)動達(dá)到最終目的而已。換句話,PCI設(shè)備驅(qū)動不僅實(shí)現(xiàn)PCI驅(qū)動還要包括具體需求的設(shè)備驅(qū)動。

    ??????????
    1702024393312.png

PCI驅(qū)動注冊與注銷:

int pci_register_driver(struct pci_driver *driver);
int pci_unregister_driver(struct pci_driver *driver);

PCI_driver結(jié)構(gòu)體:

struct pci_driver {
    struct list_head node;
    char *name; /* 驅(qū)動程序的名稱 */
    struct module *owner;
    /* 指向設(shè)備驅(qū)動程序感興趣的設(shè)備ID的一個列表,包括:
     * 廠商ID、設(shè)備ID、子廠商ID、子設(shè)備ID、類別、類別掩碼、私有數(shù)據(jù)
     */
    const struct pci_device_id *id_table;
    /* 指向一個函數(shù)對于每一個id_table中的項(xiàng)匹配的且未被其他驅(qū)動程序處理的設(shè)備,
     * 在執(zhí)行pci_register_driver時(shí)候調(diào)用此函數(shù)或者如果是以后插入的一個新設(shè)備的話,只要滿足上述條件也會調(diào)* 用此函數(shù)
     */
    int (*probe) (struct pci_dev *dev, const struct pci_device_id *id);
    /* 指向一個函數(shù)當(dāng)驅(qū)動函數(shù)程序卸載或者被該驅(qū)動程序管理的設(shè)備被卸下的時(shí)候,調(diào)用此函數(shù)
     */
    int (*remove) (struct pci_dev *dev);
    int (*save_state) (struct pci_dev *dev, u32 state); /* 設(shè)備被掛起之前保存的相關(guān)狀態(tài) */
    int (*suspend) (struct pci_dev *dev, u32 state); /* 掛起設(shè)備使之處于節(jié)能狀態(tài) */
    int (*resume) (struct pci_dev *dev); /* 喚醒掛起的設(shè)備 */
    /* 使設(shè)備能夠從掛起態(tài)產(chǎn)生喚醒事件*/
    int   (*enable_wake) (struct pci_dev *dev, u32 state, int enable);
};

五、未完待續(xù)

Linux下PCI設(shè)備驅(qū)動開發(fā)詳解(二),將介紹具體的函數(shù)實(shí)現(xiàn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容