- 一個簡短的引論
- 它給這一章總線架構的高級概述
- 集中訪問討論Peripheral Component Interconnect(PCI,外圍組件互連)外設內核函數
- PCI公交車是最好的支持的內核總線
- 本章主要介紹PCI驅動程序假設尋找其硬件和得到對它的訪問
- 本章也會介紹ISA總線
- PCI接口
- PCI是一組完整的規範,定義了計算機的各個不一樣部分之間應該怎樣交互
- PCI規範涵蓋了與計算機接口相關的大部分問題
- PCI架構被設計爲ISA標準的替代品,有三個主要目標
- 得到在計算機和外設之間數據傳輸時更好的性能
- 經過使用比ISA更高的時鐘頻率。PCI總線得到了更好的性能。它的時鐘頻率一般是25或者33MHz(實際的頻率是系統時鐘的係數)。最新的實現達到了66MHz甚至133MHz
- 配備了32位的數據總線,而且規範已經包含了64位的擴展
- 儘量的平臺無關性
- 簡化往系統中加入和刪除外設的工做
- PCI設備是無跳線設備,可以引導階段本身主動配置
-
- PCI尋址
- 每個PCI外設由一個總線編號、一個設備編號及一個功能編號來標識
- PCI規範贊成單個系統擁有高達256個總線,但是256個總線對於不少大型系統而言是不夠的,所以,Linux眼下支持PCI域
- 每個PCI域可以擁有最多256個總線
- 每個總線上可支持32個設備,每個設備可以是多功能板,最多可有八種功能
- 每種功能都可以在硬件級由一個16位的地址來標識
- 爲Linux編寫的設備驅動程序可以使用一種特殊的數據結構(pci_dev)來訪問設備
- 當前的工做站通常配置有至少兩個PCI總線。在單個系統中插入多個總線。可經過橋(bridge)來完畢,它是用來鏈接兩個總線的特殊PCI外設
- PCI系統的整體佈局組織爲樹型,當中每個總線鏈接到上一線總線,直到樹根的0號總線
- lspci
- proc/pci
- /proc/bus/pci/
- /sys/bus/pci/devices
- 每個外設板的硬件電路對例如如下三種地址空間的查詢進行應答
- 前兩種地址空間由同一PCI總線上的所有設備共享
- 配置空間利用了地理尋址
- 配置查詢每次僅僅對一個槽尋址
- 每個PCI槽有四個中斷引腳,每個設備功能可以使用當中的一個
- PCI總線中的I/O空間使用32位地址總線。而內存空間可經過32位或64位地址來訪問
- 引導階段
- 當PCI設備上電時,硬件保持未激活狀態
- 不會有內存和I/Oport映射到計算機的地址空間
- 禁止中斷報告
- 每個PCI主板均配備有能夠處理PCI的固件,稱爲BIOS、NVRAM或PROM。固件經過讀寫PCI控制器中的寄存器,提供了對設備配置地址空間的訪問
- 系統引導時,固件在每個PCI外設上運行配置事務。以便爲它提供的每個地址區域分配一個安全的位置
- 配置寄存器和初始化
- 所有的PCI設備都有至少256字節的地址空間
- PCI寄存器始終是小端的
- 驅動程序編寫者在訪問多字節的配置寄存器時。要十分注意字節序,因爲能夠在PC上工做隊的代碼到其它平臺上可能就沒法工做
- vendorID、deviceID和class是常用的三個寄存器
- vendorID
- 16位寄存器,用於標識硬件製造商
- PCI Special Interest Group維護有一個全球的廠商編號註冊表,製造商必須申請一個惟一編號並賦於它們的寄存器
- deviceID
- 16位寄存器,由製造商選擇。該ID一般和廠商ID配對生成生成一個惟一的32位硬件設備標識符
- class
- 每個外部設備屬於某個類
- 16位寄存器,高8位標識了「基類(base class)」或者組
- subsystem vendorID、subsystem deviceID
- struct pci_device_id用於定義驅動程序支持的不一樣類型的PCI設備列表
- __u32 vendor;
- __u32 device;
- 以上兩字段指定了設備的PCI廠商和設備ID,假設驅動程序可以處理不論什麼廠商或者設備ID,這些字段應該使用值PCI_ANY_ID
- __u32 subvendor;
- __u32 subdevice;
- 以上兩字段指定設備的PCI子系統廠商和子系統設備ID,假設驅動程序可以處理不論什麼類型的子系統ID,這些字段應該使用值PCI_ANY_ID
- __u32 class;
- __u32 class_mask;
- 這兩個值使驅動程序可以指定它支持一種PCI類(class)設備,假設驅動程序可以處理不論什麼類型的子系統ID。這些字段應該使用值PCI_ANY_ID
- kernel_ulong_t driver_data
- 初始化
- PCI_DEVICE(vendor, device)
- PCI_DEVICE_CLASS(device_class, device_class_mask)
- MODULE_DEVICE_TABLE
- MODULE_DEVICE_TABLE(pci, i810_ids);
- 建立一個名爲__mod_pci_device_table的局部變量,指向struct pci_device_id數組
- 在內核構建過程當中。depmod程序在所有的模塊中搜索符號__mod_pci_device_table
- 假設找到了該符號,它把數據從該模塊中抽出,加入到文件/lib/modules/KERNEL_VERSION/modules.pcimap中
- 當內核告知熱插拔系統一個新的PCI設備已經被發現時,熱插拔系統使用modules.pcimap文件來尋找要裝載的恰當的驅動程序
- 註冊PCI驅動程序
- 因此的PCI驅動程序都必須建立的主要結構休是struct pci_driver
- const char *name;
- 驅動程序的名字
- 當驅動程序執行在內核中時,它會出現在sysfs的/sys/bus/pci/drivers/如下
- const struct pci_device_id *id_table
- int (*probe) (struct pci_dev *dev, const struct pci_device_id *id);
- 指向PCI驅動程序中的探測函數的指針同。當PCI核心有一個它以爲驅動程序需要控制的struct pci_dev時,就會調用該函數
- void (*remove) (struct pci_dev *dev);
- 指向一個移除函數的指針,當struct pci_dev被從系統中移除。或者PCI驅動程序正在從內核中卸載時。PCI核心調用該函數
- void (*suspend) (struct pci_dev *dev, u32 state);
- 指向一個恢復函數的指針,當struct pci_dev被恢復時PCI核心調用該函數
- int pci_register_driver(struct pci_driver *drv);
- void pci_unregister_driver(struct pci_driver *drv);
- 在支持PCI熱插拔的系統或者CardBus系統上,PCI設備可以在不論什麼時刻出現或者消失
- 2.6內核贊成在驅動程序被裝載以後動態地分配新的PCI ID給它
- 老式PCI探測
- struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from);
- struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device, unsigned int ss_vendor, unsigned int ss_device, struct pci_dev *from);
- struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn);
- 激活PCI設備
- int pci_enable_device(struct pci_dev *dev);
- 訪問配置空間
- 在驅動程序檢測到設備以後,一般需要讀取或寫入三個地址空間
- <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);
- 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);
- int pci_bus_read_config_byte (struct pci_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 (struct pci_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);
- 訪問I/O和內存空間
- 一個PCI設備可實現多達6個I/O地址區域
- 一個接口板經過配置寄存器報告其區域的大小和當前位置,它們的符號名稱爲PCI_BASE_ADDRESS_0到PCI_BASE_ADDRESS_5
- 在內核中,PCI設備的I/O區域已經被集成到通用資源管理,咱們無需訪問配置變量來了解設備被映射到內存或I/O空間的何處
- unsigned long pci_resource_start(struct pci_dev *dev, int bar);
- unsigned long pci_resource_end(struct pci_dev *dev, int bar);
- unsinged long pci_resource_flags(struct pci_dev *dev, int bar);
- <linux/ioport.h>
- IORESOURCE_IO
- IORESOURCE_MEM
- IORESOURCE_PREFETCH
- IORESOURCE_READONLY
- 中斷號保存在配置寄存器60(PCI_INTERRUPT_LINE)中。該寄存器爲一個字節寬
- 假設設備不支持中斷,寄存器61(PCI_INTERRUPT_PIN)是0
- 硬件抽象
- 在PCI管理中。惟一依賴於硬件的操做是讀取和寫入配置寄存器
- 用於配置寄存器訪問的相關結構僅包括2個字段
- struct pci_ops
- int (*read) (struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);
- int (*write) (struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);
- 該結構在<linux/pci.h>中定義,並由drivers/pci/pci.c使用。後者定義了實際的公共函數
- ISA回想
- ISA總線在設計上至關陳舊而且其差勁的性能臭名昭著
- 當要支持老主板而速度又不是很重要時,ISA比起PCI要佔些優點
- 假設你是一位電子愛好者,你可以很easy地設計開發本身的ISA設備
- ISA的最大不足在於它被牢牢綁定在PC架構上
- ISA設計的另一個大問題是缺乏地理尋址
- 硬件資源
- 一個ISA設備可配備有I/Oport、內存區域以及中斷線
- 即插即用規範
- 某些新的ISA設備板遵循特殊的設計原則。需要一個特殊的初始化序列,以便簡化附加接口板的安裝和配置,這些接口板的設計規範稱爲PnP(Plug and Play,即插即用)。
- PnP的目標是得到相似PCI設備那樣的靈活性,而無需改動底層的電氣接口(即ISA總線)。爲此,該規範定義了一組設備無關的配置寄存器。以及地址尋址接口板的方法。
- PC/104和PC/104+
- 這兩個標準都規定了印刷電路板的外形,以及板間互連的電氣/機械規範,這些總線的實際優勢在於,它們可以使用在設備一面的插頭-插座類型的鏈接器把多個電路板垂直堆疊起來
- 這兩個總線的電子和邏輯佈局和ISA(PC/104)及PCI(PC/104+)同樣
- 其它的PC總線
- MCA
- MCA(Micro Channel Architecture,微通道結構)是在PS/2計算機和某些筆記本電腦使用的IBM標準
- 支持多主DMA、32位地址和數據線、共享中斷線和用來訪問板載配置寄存器的地理尋址等
- EISA
- 擴展ISA(EISA)總線是對ISA總線的32擴展,同一時候具備兼容的接口鏈接器
- 爲無跳線設備而設計
- 32位地址和數據線、多主DMA和共享中斷線
- VLB
- VLB(VESA Local Bus,VESA局部總線)接口總線,它經過加入第三個縱向插槽對ISA鏈接器進行了擴展
- SBus
- 存在很是長一段時間了,具備至關高級的設計
- 雖然僅僅有SPARC計算機使用該總線,但它的初衷倒是處理器無關的。並針對I/O外設板進行了優化
- NuBus
- 可以在老式的Mac計算機(使用M68k系列CPU)中找到它
- 所有的總線都是內存映射的。而且設備僅僅能被地理尋址
- 外部總線
- 外部總線包含:USB、FireWire和IEEE1284
- 這些總線既不是功能完整的接口總線(比方PCI),也是啞的通訊通道(比方串口)
- 一般可劃分爲兩個級別
版權聲明:本文博主原創文章,博客,未經贊成不得轉載。linux