《Linux Device Drivers》第十二章 PCI司機——note

  • 一個簡短的引論
    • 它給這一章總線架構的高級概述
    • 集中訪問討論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/
        • 查看PCI設備清單和設備的配置寄存器
      • /sys/bus/pci/devices
      • 每個外設板的硬件電路對例如如下三種地址空間的查詢進行應答
        • 內存位置
        • I/Oport
        • 配置寄存器
      • 前兩種地址空間由同一PCI總線上的所有設備共享
      • 配置空間利用了地理尋址
      • 配置查詢每次僅僅對一個槽尋址
      • 每個PCI槽有四個中斷引腳,每個設備功能可以使用當中的一個
      • PCI總線中的I/O空間使用32位地址總線。而內存空間可經過32位或64位地址來訪問
    • 引導階段
      • 當PCI設備上電時,硬件保持未激活狀態
        • 不會有內存和I/Oport映射到計算機的地址空間
        • 禁止中斷報告
      • 每個PCI主板均配備有能夠處理PCI的固件,稱爲BIOS、NVRAM或PROM。固件經過讀寫PCI控制器中的寄存器,提供了對設備配置地址空間的訪問
      • 系統引導時,固件在每個PCI外設上運行配置事務。以便爲它提供的每個地址區域分配一個安全的位置
    • 配置寄存器和初始化
      • 所有的PCI設備都有至少256字節的地址空間
        • 前64字節是標準化的。而其他的是設備相關的
      • 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驅動程序用於區分不一樣設備的信息
      • 初始化
        • 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);
        • 註冊成功返回0。不然,返回一個負的錯誤編號
      • 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);
    • 訪問配置空間
      • 在驅動程序檢測到設備以後,一般需要讀取或寫入三個地址空間
        • 內存
        • port
        • 配置
      • <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

相關文章
相關標籤/搜索