《Linux Device Drivers》第十三章 USB驅動程序——note

 

 

1. USB主機
在Linux驅動中,USB驅動處於最底層的是USB主機控制器硬件,在其之上運行的是USB主機控制器驅動,主機控制器之上爲USB核心層,再上層爲USB設備驅動層(插入主機上的U盤、鼠標、USB轉串口等設備驅動)。

因此,在主機側的層次結構中,要實現的USB驅動包括兩類:USB主機控制器驅動和USB設備驅動,前者控制插入其中的USB設備後者控制USB設備如何與主機通信。Linux內核USB核心負責USB驅動管理和協議處理的主要工作。主機控制器驅動和設備驅動之間的USB核心非常重要,其功能包括:通過定義一些數據結構、宏和功能函數,向上爲設備驅動提供編程接口,向下爲USB主機控制器驅動提供編程接口;通過全局變量維護整個系統的USB設備信息;完成設備熱插拔控制、總線數據傳輸控制等。

 

2. USB設備

Linux內核中USB設備側驅動程序分爲3個層次:UDC驅動程序、Gadget API和Gadget驅動程序。UDC驅動程序直接訪問硬件,控制USB設備和主機間的底層通信,向上層提供與硬件相關操作的回調函數。當前Gadget API是UDC驅動程序回調函數的簡單包裝。Gadget驅動程序具體控制USB設備功能的實現,使設備表現出“網絡連接”、“打印機”或“USB Mass Storage”等特性,它使用Gadget API控制UDC實現上述功能。Gadget API把下層的UDC驅動程序和上層的Gadget驅動程序隔離開,使得在Linux系統中編寫USB設備側驅動程序時能夠把功能的實現和底層通信分離。

 

 3. 在USB設備組織結構中,從上到下分爲設備(device)、配置(config)、接口(interface)和端點(endpoint)四個層次。USB設備程序綁定到接口上。
   對於這四個層次的簡單描述如下:
      設備通常具有一個或多個的配置
      配置經常具有一個或多個的接口
      接口沒有或具有一個以上的端點

 

4. USB通信最基本的形式是通過端點(USB端點分中斷(Interrupt)、批量(Bulk)、等時(ISO)、控制(Control)四種,每種用途不同),USB端點只能往一個方向傳送數據,從主機到設備或者從設備到主機,端點可以看作是單向的管道(pipe)。驅動程序把驅動程序對象註冊到USB子系統中,稍後再使用製造商和設備標識來判斷是否已經安裝了硬件。USB核心使用一個列表(是一個包含製造商ID和設備號ID的一個結構體)來判斷對於一個設備該使用哪一個驅動程序,熱插撥腳本使用它來確定當一個特定的設備插入到系統時該自動執行哪一個驅動程序的Probe。

 

5. 數據結構

1) USB設備:對應數據結構struct usb_device

2) 配置:struct usb_host_config (任一時刻,只能有一個配置生效)

3)USB接口:struct usb_interface (USB 核心將其傳遞給USB設備驅動,並由USB設備驅動負責後續的控制。一個USB接口代表一個基本功能,每個USB驅動控制一個接口。所以一個物理上的硬件設備可能需要 一個以上的驅動程序。)

4)端點: struct usb_host_endpoint ,它所包含的真實端點信息在另一個結構中:struct usb_endpoint_descriptor(端點描述符,包含所有的USB特定數據)。

每一個USB設備在主機看來就是端點的集合,主機只能通過端點與設備進行通訊,以使用設備的功能。每個端點實際上就是一個一定大小的數據緩衝區,這些端點在設備出廠時就己定義好。在USB系統中,每一個端點都有唯一的地址,這是由設備地址和端點號給出的。每個端點都有一定的特性,其中包括傳輸方式、總線訪問頻率、帶寬、端點號、數據包的最大容量等等。端點必須在設備配置後才能生效(端點O除外)。端點0通常爲控制端點,用於設備初始化等,端點1、2等一般用作數據端點,存放主機與設備問往來的數據。

 

6. USB端點分類

USB 通訊的最基本形式是通過一個稱爲端點的東西。一個USB端點只能向一個方向傳輸數據(從主機到設備(稱爲輸出端點)或者從設備到主機(稱爲輸入端點))。端點可被看作一個單向的管道。

USB 端點有 4 種不同類型, 分別具有不同的數據傳送方式:

1) 控制CONTROL
控制端點被用來控制對USB設備的不同部分訪問. 通常用作配置設備、獲取設備信息、發送命令到設備或獲取設備狀態報告。這些端點通常較小。每個 USB 設備都有一個控制端點稱爲"端點 0", 被 USB 核心用來在插入時配置設備。USB協議保證總有足夠的帶寬留給控制端點傳送數據到設備.

 

2)中斷INTERRUPT
每當 USB 主機向設備請求數據時,中斷端點以固定的速率傳送小量的數據。此爲USB 鍵盤和鼠標的主要的數據傳送方法。它還用以傳送數據到USB設備來控制設備。通常不用來傳送大量數據。USB協議保證總有足夠的帶寬留給中斷端點傳送數據到設備.

3) 批量BULK
批量端點用以傳送大量數據。這些端點通常比中斷端點大得多. 它們普遍用於不能有任何數據丟失的情況。USB 協議不保證傳輸在特定時間範圍內完成。如果總線上沒有足夠的空間來發送整個BULK包,它被分爲多個包進行傳輸。這些端點普遍用於打印機、USB Mass Storage和USB網絡設備上。

4) 等時ISOCHRONOUS
等時端點也批量傳送大量數據, 但是這個數據被保證能送達。這些端點用在可以處理數據丟失的設備中,並且更多依賴於保持持續的數據流。如音頻和視頻設備等等。

控制和批量端點用於異步數據傳送,而中斷和等時端點是週期性的。這意味着這些端點被設置來在固定的時間連續傳送數據,USB 核心爲它們保留了相應的帶寬。

7. endpoint

struct usb_host_endpoint{
    struct usb_endpoint_descriptor desc;//端點描述符
    struct list_head urb_list;//此端點的URB對列,由USB核心維護
    void *hcpriv;
    struct ep_device *ep_dev; /* For sysfs info */
    unsigned char*extra;/* Extra descriptors */
    int extralen;
    int enabled;
};

當調用USB設備驅動調用usb_submit_urb提交urb請求時,將調用int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)把此urb增加到urb_list的尾巴上。(hcd: Host Controller Driver,對應數據結構struct usb_hcd )

 

 8. urb

      所有USB通訊均爲請求-->響應模式,USB設備不會主動向Host發送數據。寫數據:USB設備驅動發送urb請求給USB設備,USB設備不需要回數據。讀數據:USB設備驅動發送urb請求給USB設備,USB設備需要回數據。

      USB 設備驅動通過urb和所有的 USB 設備通訊。urb用 struct urb 結構描述(include/linux/usb.h )。
 urb 以一種異步的方式同一個特定USB設備的特定端點發送或接受數據。一個 USB 設備驅動可根據驅動的需要,分配多個 urb 給一個端點或重用單個 urb 給多個不同的端點。設備中的每個端點都處理一個 urb 隊列, 所以多個 urb 可在隊列清空之前被髮送到相同的端點。


    一個 urb 的典型生命循環如下:
 (1)被驅動程序創建;
 (2)被分配給一個特定 USB 設備的特定端點;
 (3)被驅動程序提交給 USB 核心;
 (4)被 USB 核心提交給特定設備的特定 USB 主機控制器驅動;
 (5)被 USB 主機控制器驅動處理, 並傳送到設備;
 (6)以上操作完成後,USB主機控制器驅動通知 USB 設備驅動。
 
    urb 也可被提交它的驅動在任何時間取消;如果設備被移除,urb 可以被USB核心取消。urb 被動態創建幷包含一個內部引用計數,使它們可以在最後一個用戶釋放它們時被自動釋放。

 

 

8.1 提交 urb

一旦 urb 被正確地創建並初始化, 它就可以提交給 USB 核心以發送出到 USB 設備. 這通過調用函數sb_submit_urb 實現.

 

int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
參數:
struct urb *urb :指向被提交的 urb 的指針
gfp_t mem_flags :使用傳遞給 kmalloc 調用同樣的參數, 用來告訴 USB 核心如何及時分配內存緩衝

 

因爲函數 usb_submit_urb 可被在任何時候被調用(包括從一箇中斷上下文), mem_flags 變量必須正確設置. 根據 usb_submit_urb 被調用的時間,只有 3 個有效值可用:

GFP_ATOMIC
只要滿足以下條件,就應當使用此值:
1) 調用者處於一個 urb 結束處理例程,中斷處理例程,底半部,tasklet或者一個定時器回調函數.
2) 調用者持有自旋鎖或者讀寫鎖. 注意如果正持有一個信號量, 這個值不必要.
3) current->state 不是 TASK_RUNNING. 除非驅動已自己改變 current 狀態,否則狀態應該一直是TASK_RUNNING .

GFP_NOIO
驅動處於塊 I/O 處理過程中. 它還應當用在所有的存儲類型的錯誤處理過程中.

GFP_KERNEL
所有不屬於之前提到的其他情況

在 urb 被成功提交給 USB 核心之後, 直到結束處理例程函數被調用前,都不能訪問 urb 結構的任何成員

 

8.2 urb結束處理例程

如果 usb_submit_urb 被成功調用, 並把對 urb 的控制權傳遞給 USB 核心, 函數返回 0; 否則返回一個負的錯誤代碼. 如果函數調用成功, 當 urb 被結束的時候結束處理例程會被調用一次.當這個函數被調用時, USB 核心就完成了這個urb, 並將它的控制權返回給設備驅動.
 
只有3 種結束urb並調用結束處理例程的情況:
(1)urb 被成功發送給設備, 且設備返回正確的確認.如果這樣, urb 中的status變量被設置爲 0.
(2)發生錯誤, 錯誤值記錄在 urb 結構中的 status 變量.
(3)urb 從 USB 核心unlink. 這發生在要麼當驅動通過調用 usb_unlink_urb 或者 usb_kill_urb告知 USB 核心取消一個已提交的 urb,或者在一個 urb 已經被提交給它時設備從系統中去除.

 

9. 探測和斷開

在 struct usb_driver 結構中, 有 2 個 USB 核心在適當的時候調用的函數:
(1)探測函數:

當把USB 設備插到 USB 接口上後,USB 主機控制器會檢測到有設備插入 USB 接口了,USB子系統會分配一個 struct usb_device 數據結構來 代表該設備,該數據結構記錄設備的一些屬性及數據。並把該數據結構掛載到一個全局的USB設備鏈表上。在這一期間主機通過0號端點得知了設備的一些信息,並知道了設備 的廠家號和產品號(Vendor和ProdID);然後到一個全局的 USB 驅動鏈上查找哪個驅動程序支持的設備列表中有該設備的廠家號和產品號。當找到後設備就和驅動匹配上了,接着就調用USB驅動的probe函數,而USB網卡設備的初始化和註冊就是在USB設備的probe函數中完成的;當找不到驅動就會driver=(none)。



(2)斷開函數:

由於某些原因,設備被移除或驅動不再控制設備時,調用斷開(disconnect)函數,做適當清理.

探測和斷開回調函數都在USB集線器內核線程上下文中被調用, 因此它們休眠是合法的. 爲了縮短 USB 探測時間,大部分工作儘可能在設備打開時完成.這是因爲 USB 核心是在一個線程中處理 USB 設備的添加和移除, 因此任何慢設備驅動都可能使 USB 設備探測時間變長。

9.1探測函數分析
在探測回調函數中, USB設備驅動應當初始化它可能用來管理 USB 設備的所有本地結構並保存所有需要的設備信息到本地結構, 因爲在此時做這些通常更容易.爲了和設備通訊,USB 驅動通常要探測設備的端點地址和緩衝大小.

發佈了48 篇原創文章 · 獲贊 12 · 訪問量 1141萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章