linux 裝置驅動編程

來源:互聯網
上載者:User
文章目錄
  • 目 錄

 

驅動

 

目 錄

  1. 驅動
    1. I/O連接埠
    2. from smth
      1. 基本結構
      2. 驅動程式
      3. 具體實現
    3. PCI
    4. loopback
    5. Sis 900
    6. ISA匯流排DMA的實現

驅動

    Linux系統支援三種類型的硬體裝置:字元裝置、塊設

備和網路裝置。字元裝置是直接讀取的,不必使用緩衝區。例如,系統的串列口/dev/cua0和/dev/cua1。塊裝置每次只能讀取一定大小的塊的倍數,通常一塊是512或者1024位元組。塊裝置通過緩衝區讀寫,並且可以隨機地讀寫。塊裝置可以通過它們的裝置檔案存取,但通常是通過檔案系統存取。只有塊裝置支援掛接的檔案系統。網路裝置是通過BSD通訊端介面存取的。

    Linux系統支援多種裝置,這些裝置的驅動程式之間有一些共同的特點:
    * 核心代碼:裝置驅動程式是系統核心的一部分,所以如果驅動程式出現錯誤的話,將可能嚴重地破壞整個系統。
    * 核心介面:裝置驅動程式必須為系統核心或者它們的子系統提供一個標準的介面。例如,一個終端驅動程式必須為Linux核心提供一個檔案I/O介面;一個SCSI裝置驅動程式應該為SCSI子系統提供一個SCSI裝置介面,同時SCSI子系統也應為系統核心提供檔案I/O和緩衝區。
    * 核心機制和服務:裝置驅動程式利用一些標準的核心服務,例如記憶體配置等。
    * 可裝入:大多數的Linux裝置驅動程式都可以在需要時裝入核心,在不需要時卸載。
    * 可設定:Linux系統裝置驅動程式可以整合為系統核心的一部分,至於哪一部分需要整合到核心中,可以在系統編譯時間設定。

 

[目錄]

I/O連接埠

  關鍵詞:裝置管理、驅動程式、I/O連接埠、資源

  申明:這份文檔是按照自由軟體開放原始碼的精神發布的,任何人可以免費獲得、使用和重新發布,但是你沒有限制別人重新發布你發布內容的權利。發布本文的目的是希望它能對讀者有用,但沒有任何擔保,甚至沒有適合特定目的的隱含的擔保。更詳細的情況請參閱GNU通用公用許可證(GPL),以及GNU自由文檔協議(GFDL)。

  幾乎每一種外設都是通過讀寫裝置上的寄存器來進行的。外設寄存器也稱為“I/O連接埠”,通常包括:控制寄存器、狀態寄存器和資料寄存器三大類,而且一個外設的寄存器通常被連續地編址。CPU對外設IO連接埠物理地址的編址方式有兩種:一種是I/O映射方式(I/O-mapped),另一種是記憶體映射方式(Memory-mapped)。而具體採用哪一種則取決於CPU的體繫結構。

  有些體繫結構的CPU(如,PowerPC、m68k等)通常只實現一個物理地址空間(RAM)。在這種情況下,外設I/O連接埠的物理地址就被映射到CPU的單一物理地址空間中,而成為記憶體的一部分。此時,CPU可以象訪問一個記憶體單元那樣訪問外設I/O連接埠,而不需要設立專門的外設I/O指令。這就是所謂的“記憶體映射方式”(Memory-mapped)。

  而另外一些體繫結構的CPU(典型地如X86)則為外設專門實現了一個單獨地地址空間,稱為“I/O地址空間”或者“I/O連接埠空間”。這是一個與CPU地RAM物理地址空間不同的地址空間,所有外設的I/O連接埠均在這一空間中進行編址。CPU通過設立專門的I/O指令(如X86的IN和OUT指令)來訪問這一空間中的地址單元(也即I/O連接埠)。這就是所謂的“I/O映射方式”(I/O-mapped)。與RAM物理地址空間相比,I/O地址空間通常都比較小,如x86 CPU的I/O空間就只有64KB(0-0xffff)。這是“I/O映射方式”的一個主要缺點。

  Linux將基於I/O映射方式的或記憶體映射方式的I/O連接埠通稱為“I/O地區”(I/O region)。在討論對I/O地區的管理之前,我們首先來分析一下Linux是如何?“I/O資源”這一抽象概念的。

3.1 Linux對I/O資源的描述

  Linux設計了一個通用的資料結構resource來描述各種I/O資源(如:I/O連接埠、外設記憶體、DMA和IRQ等)。該結構定義在include/linux/ioport.h標頭檔中:

  struct resource {
        const char *name;
        unsigned long start, end;
        unsigned long flags;
        struct resource *parent, *sibling, *child;
  };

  各成員的含義如下:

  1. name指標:指向此資源的名稱。
  2. start和end:表示資源的起始物理地址和終止物理地址。它們確定了資源的範圍,也即是一個閉區間[start,end]。
  3. flags:描述此資源屬性的標誌(見下面)。
  4. 指標parent、sibling和child:分別為指向父親、兄弟和子資源的指標。

  屬性flags是一個unsigned long類型的32位標誌值,用以描述資源的屬性。比如:資源的類型、是否唯讀、是否可緩衝,以及是否已被佔用等。下面是一部分常用屬性標誌位的定義(ioport.h):

/*
* IO resources have these defined flags.
*/
#define IORESOURCE_BITS                0x000000ff        /* Bus-specific bits */

#define IORESOURCE_IO                0x00000100        /* Resource type */
#define IORESOURCE_MEM                0x00000200
#define IORESOURCE_IRQ                0x00000400
#define IORESOURCE_DMA                0x00000800

#define IORESOURCE_PREFETCH        0x00001000        /* No side effects */
#define IORESOURCE_READONLY        0x00002000
#define IORESOURCE_CACHEABLE        0x00004000
#define IORESOURCE_RANGELENGTH        0x00008000
#define IORESOURCE_SHADOWABLE        0x00010000
#define IORESOURCE_BUS_HAS_VGA        0x00080000

#define IORESOURCE_UNSET        0x20000000
#define IORESOURCE_AUTO                0x40000000
#define IORESOURCE_BUSY                0x80000000
        /* Driver has marked this resource busy */

 

  指標parent、sibling和child的設定是為了以一種樹的形式來管理各種I/O資源。

3.2 Linux對I/O資源的管理

  Linux是以一種倒置的樹形結構來管理每一類I/O資源(如:I/O連接埠、外設記憶體、DMA和IRQ)的。每一類I/O資源都對應有一顆倒置的資源樹,樹中的每一個節點都是一個resource結構,而樹的根結點root則描述了該類資源的整個資源空間。

  基於上述這個思想,Linux在kernel/Resource.c檔案中實現了對資源的申請、釋放及尋找等操作。

  3.2.1 I/O資源的申請

  假設某類資源有如下這樣一顆資源樹:

  節點root、r1、r2和r3實際上都是一個resource結構類型。子資源r1、r2和r3通過sibling指標連結成一條單向非迴圈鏈表,其表頭由root節點中的child指標定義,因此也稱為父資源的子資源鏈表。r1、r2和r3的parent指標均指向他們的父資源節點,在這裡也就是圖中的root節點。

  假設想在root節點中分配一段I/O資源(由圖中的陰影地區表示)。函數request_resource()實現這一功能。它有兩個參數:①root指標,表示要在哪個資源根節點中進行分配;②new指標,指向描述所要分配的資源(即圖中的陰影地區)的resource結構。該函數的原始碼如下(kernel/resource.c):

  int request_resource(struct resource *root, struct resource *new)
  {
        struct resource *conflict;

        write_lock(&resource_lock);
        conflict = __request_resource(root, new);
        write_unlock(&resource_lock);
        return conflict ? -EBUSY : 0;
  }

 

  對上述函數的NOTE如下:

  ①資源鎖resource_lock對所有資源樹進行讀防寫保護,任何程式碼片段在訪問某一顆資源樹之前都必須先持有該鎖。其定義如下(kernel/Resource.c):

  static rwlock_t resource_lock = RW_LOCK_UNLOCKED;

  ②可以看出,函數實際上是通過調用內部靜態函數__request_resource()來完成實際的資源分派工作。如果該函數返回非null 指標,則表示有資源衝突;否則,返回NULL就表示分配成功。

  ③最後,如果conflict指標為NULL,則request_resource()函數返回傳回值0,表示成功;否則返回-EBUSY表示想要分配的資源已被佔用。

  函數__request_resource()完成實際的資源分派工作。如果參數new所描述的資源中的一部分或全部已經被其它節點所佔用,則函數返回與new相衝突的resource結構的指標。否則就返回NULL。該函數的原始碼如下

(kernel/Resource.c):
/* Return the conflict entry if you can't request it */
static struct resource * __request_resource
  (struct resource *root, struct resource *new)
{
        unsigned long start = new->start;
        unsigned long end = new->end;
        struct resource *tmp, **p;

        if (end < start)
                return root;
        if (start < root->start)
                return root;
        if (end > root->end)
                return root;
        p = &root->child;
        for (;;) {
                tmp = *p;
                if (!tmp || tmp->start > end) {
                        new->sibling = tmp;
                        *p = new;
                        new->parent = root;
                        return NULL;
                }
                p = &tmp->sibling;
                if (tmp->end < start)
                        continue;
                return tmp;
        }
}

 

  對函數的NOTE:

  ①前三個if語句判斷new所描述的資源範圍是否被包含在root內,以及是否是一段有效資源(因為end必須大於start)。否則就返回root指標,表示與根結點相衝突。

  ②接下來用一個for迴圈遍曆根節點root的child鏈表,以便檢查是否有資源衝突,並將new插入到child鏈表中的合適位置(child鏈表是以I/O資源物理地址從低到高的順序排列的)。為此,它用tmp指標指向當前正被掃描的resource結構,用指標p指向前一個resource結構的sibling指標成員變數,p的初始值為指向root->sibling。For迴圈體的執行步驟如下:

  l 讓tmp指向當前正被掃描的resource結構(tmp=*p)。

  l 判斷tmp指標是否為空白(tmp指標為空白說明已經遍曆完整個child鏈表),或者當前被掃描節點的起始位置start是否比new的結束位置end還要大。只要這兩個條件之一成立的話,就說明沒有資源衝突,於是就可以把new鏈入child鏈表中:①設定new的sibling指標指向當前正被掃描的節點tmp(new->sibling=tmp);②當前節點tmp的前一個兄弟節點的sibling指標被修改為指向new這個節點(*p=new);③將new的parent指標設定為指向root。然後函數就可以返回了(傳回值NULL表示沒有資源衝突)。

  l 如果上述兩個條件都不成立,這說明當前被掃描節點的資源網域有可能與new相衝突(實際上就是兩個閉區間有交集),因此需要進一步判斷。為此它首先修改指標p,讓它指向tmp->sibling,以便於繼續掃描child鏈表。然後,判斷tmp->end是否小於new->start,如果小於,則說明當前節點tmp和new沒有資源衝突,因此執行continue語句,繼續向下掃描child鏈表。否則,如果tmp->end大於或等於new->start,則說明tmp->[start,end]和new->[start,end]之間有交集。所以返回當前節點的指標tmp,表示發生資源衝突。

  3.2.2 資源的釋放

  函數release_resource()用於實現I/O資源的釋放。該函數只有一個參數——即指標old,它指向所要釋放的資源。起原始碼如下:

int release_resource(struct resource *old)
{
        int retval;

        write_lock(&resource_lock);
        retval = __release_resource(old);
        write_unlock(&resource_lock);
        return retval;
}

 

  可以看出,它實際上通過調用__release_resource()這個內部靜態函數來完成實際的資源釋放工作。函數__release_resource()的主要任務就是將資來源區域old(如果已經存在的話)從其父資源的child鏈表重摘除,它的原始碼如下:

static int __release_resource(struct resource *old)
{
        struct resource *tmp, **p;

        p = &old->parent->child;
        for (;;) {
                tmp = *p;
                if (!tmp)
                        break;
                if (tmp == old) {
                        *p = tmp->sibling;
                        old->parent = NULL;
                        return 0;
     &nbsp

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.