PCI設備驅動


一、PCI簡介
   
PCI是一種外設匯流排規範。我們先來看一下什麼是匯流排:匯流排是一種傳輸信號的路徑或通道。典型情況是,匯流排是連接於一個或多個導體的電氣連線,匯流排上連接的所有設備可在同一時間收到所有的傳輸內容。匯流排由電氣介面和編程介面組成。本文討論Linux 下的設備驅動,所以,重點關注編程介面。
    PCIPeripheral Component Interconnect(週邊設備互聯)的簡稱,是普遍使用在桌面及更大型的電腦上的外設匯流排。PCI架構被設計為ISA標準的替代品,他有三個主要目標:獲得在電腦和外設之間傳輸資料時更好的性能;盡可能的平臺無關;簡化往系統中添加和刪除外設的工作。
二、PCI定址
    從目前開始,我想盡可能通過一些實際的例子來說明問題,而減少理論方面的問題的描述,因為,相關的理論的東西,能在其他地方找到。
   
我們先來看一個例子,我的電腦裝有1GRAM1G以後的實體記憶體位址空間都是外部設備IO在系統記憶體位址空間上的映射。/proc/iomem描述了
系統中所有的設備I/O在記憶體位址空間上的映射。我們來看位址從1G開始的第一個設備在/proc/iomem中是怎麼描述的:
            40000000-400003ff : 0000:00:1f.1
   
這是個PCI設備,40000000-400003ff是他所映射的記憶體位址空間,佔據了記憶體位址空間的1024
bytes的位置,而0000:00:1f.1則是個PCI外設的位址,他以冒號和逗號分隔為4個部分,第一個16位元表示域,第二個8位元表示一個匯流排編號,第三個5位元表示一個設備號,最後是3位,表示功能號。
   
因為PCI規範允許單個系統擁有高達256個匯流排,所以匯流排編號是8位元。但對於大型系統而言,這是不夠的,所以,引入了域的概念,每個PCI域能擁有最多256個匯流排,每個匯流排上可支援32個設備,所以設備號是5位元,而每個設備上最多可有8種功能,所以功能號是3位。由此,我們能得出上述的PCI設備
的位址是0號域0號匯流排上的31號設備上的1號功能。那上述的這個PCI設備到底是什麼呢?下面是我的電腦上的lspci命令的輸出:
    00:00.0 Host bridge: Intel Corporation 82845 845 (Brookdale) Chipset Host Bridge (rev 04)
    00:01.0 PCI bridge: Intel Corporation 82845 845 (Brookdale) Chipset AGP Bridge(rev 04)
    00:1d.0 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #1) (rev 02)
    00:1d.1 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #2) (rev 02)
    00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 42)
    00:1f.0 ISA bridge: Intel Corporation 82801CAM ISA Bridge (LPC) (rev 02)
    00:1f.1 IDE interface: Intel Corporation 82801CAM IDE U100 (rev 02)
    00:1f.3 SMBus: Intel Corporation 82801CA/CAM SMBus Controller (rev 02)
    00:1f.5 Multimedia audio controller:Intel Corporation 82801CA/CAM AC’97 Audio Controller (rev 02)
    00:1f.6 Modem: Intel Corporation 82801CA/CAM AC’97 Modem Controller (rev 02)
    01:00.0 VGA compatible controller: nVidia Corporation NV17 [GeForce4 420 Go](rev a3)
    02:00.0 FireWire (IEEE 1394): VIA Technologies, Inc. IEEE 1394 Host Controller(rev 46)
    02:01.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+(rev 10)
    02:04.0 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01)
    02:04.1 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01)
   
lspci沒有標明域,但對於一台PC而言,一般只有一個域,即0號域。通過這個輸出我們能看到他是個IDE
interface。由上述的輸出能看到,我的電腦上共有3PCI匯流排(0號,1號,2號)。在單個系統上,插入多個匯流排是通過橋(bridge)來完成的,橋是一種用來連接匯流排的特別PCI外設。所以,PCI系統的整體佈局組織為樹型,我們能通過上面的lspci輸出,來畫出我的電腦上的PCI
統的樹型結構:
00:00.0(主橋)--00:01.0(PCI橋)-----01:00:0(nVidia顯卡)
                  |
                 
|---00:1d(USB控制器)--00:1d:0(USB1號控制器)
                 
|                  
|
                 
|                  
|--00:1d:1(USB2號控制器)        
           |
                 
|-00:1e:0(PCI)--02:00.0(IEEE1394)
                 
|              
|
                 
|              
|-02:01.0(8139網卡)
                 
|              
|
                 
|              
|-02:04(CardBus)-02:04.0(1
                 
|                                 
|
                 
|                                 
|--02:04.1(2)
                  |
                 
|-00:1f(多功能板卡)-00:1f:0(ISA橋)
                                      
|
                                      
|--00:1f:1(IDE介面)
                                      
|
                                      
|--00:1f:3(SMBus)
                                      
|
                                      
|--00:1f:5(多媒體聲音控制器)
                                      
|
                                      
|--00:1f:6(數據機)
    由上圖能得出,我的電腦上共有8PCI設備,其中0號匯流排上(主橋)上連有4個,1號匯流排上連有1個,2號匯流排上連有3個。00:1f是個連有5個功能的多功能板卡。
   
每一個PCI設備都有他映射的記憶體位址空間和他的I/O區域,這點是比較容易理解的。除此之外,PCI設備更有他的設置寄存器。有了設置寄存器,PCI的驅動程式就不必探測就能訪問設備。設置寄存器的佈局是標準化的,設置空間的4個位元組含有一個獨一無二的功能ID,因此,驅動程式可通過查詢外設的特定
ID來識別其設備。所以,PCI介面標準在ISA之上的主要創新在於設置位址空間。


前文已講過,PCI驅動程式不必探測就能訪問設備,而這得益於設置位址空間。在系統引導階段,PCI硬體設備保持未啟動狀態,但每個PCI主板均配備有能夠處理PCI的固件,固件通過讀寫PCI控制器中的寄存器,提供了對設備設置位址空間的訪問。
   
設置位址空間的前64位元組是標準化的,他提供了廠商號,設備號,版本號等資訊,唯一標識一個PCI設備。同時,他也提供了最多可多達6個的I/O位址區域,每個區域能是記憶體也能是I/O位址。這幾個I/O位址區域是驅動程式找到設備映射到記憶體和I/O空間的具體位置的唯一途徑。有了這兩點,PCI
動程式就完成了相當於探測的功能。關於這64個位元組的設置空間的周詳情況,可參閱《Linux設備驅動程式第三版》。
   
下面,我們來看一下8139too網卡設備的設置空間的周詳情況。在2.6內核的系統中,能在目錄/sys/bus/pci/drivers/下看到非常多以PCI設備名命名的目錄,但不是說這些設備都存在於你的系統中。我們進入8139too目錄,其中有一個以他的設備位址0000:02:01.0命名
的目錄。在這個目錄下能找到該網卡設備相關的非常多資訊。其中resource記錄了他的6I/O位址區域。內容如下:
        0x0000000000003400 0x00000000000034ff 0x0000000000000101
        0x00000000e0000800 0x00000000e00008ff 0x0000000000000200
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 0x0000000000000000
    由該檔能看出,8139too設備使用了兩個I/O位址區域,第一個是他映射的I/O埠範圍,第二個是他映射的記憶體位址空間。關於這兩個值能在/proc/iomem/proc/ioport中得到驗證。


為了能看到實際的運行效果,我們選擇8139too網卡作為示例,從該網卡的linux驅動程式中裁剪相關代碼。
    一個PCI設備的驅動程式必須要向內核中的PCI核心描述自己。同時,他也必須告訴PCI核心自己能夠驅動哪些設備。下面,就介紹兩個相關的重要資料結構。
    struct pci_device_id {
        __u32 vendor,
device;       /* Vendor and device ID or
PCI_ANY_ID*/
        __u32 subvendor, subdevice; /* Subsystem ID’s or PCI_ANY_ID */
        __u32 class, class_mask;    /* (class,subclass,prog-if) triplet */
        kernel_ulong_t driver_data; /* Data private to the driver */
    };
      
    struct pci_driver {
        struct list_head node;
        char *name;
        struct module *owner;
        const struct pci_device_id *id_table; //驅動所能操縱的設備id列表。
        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_t state);
        int (*resume)(struct pci_dev *dev);
        int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);
        void (*shutdown) (struct pci_dev *dev);
        struct device_driver    driver;
        struct pci_dynids dynids;
    };
   
pci_device_id唯一標識一個PCI設備。他的幾個成員依次分別表示:廠商號,設備號,子廠商號,子設備號,類別,類別遮罩(類可分為基類,子類),私有數據。每一個PCI設備的驅動程式都有一個pci_device_id的陣列,用於告訴PCI核心自己能夠驅動哪些設備。8139too的驅動
程式定義他的pci_device_id陣列如下:
        static struct pci_device_id rtl8139_pci_tbl[];
   
該陣列被初始化為8139系列的一組網卡,當PCI核心得到這個陣列後,會拿陣列中的每一項跟從PCI設置空間中讀取到的資料進行比對,從而為該驅動程式找到正確的設備。而pci_driver代表一個pci驅動程式。成員id_talbe即是指向pci_device_id陣列的指標。name是驅動程式的名字,probe完成探測工作,即拿pci_device_id陣列和內核中的資料進行比對。remove完成驅動程式的移除工作。關鍵的成員就這幾
個。
  






待續...................


http://tw.myblog.yahoo.com/blue-comic/article?mid=913

立你斯 發表在 痞客邦 留言(0) 人氣()