1. 程式人生 > >Linux USB驅動工作流程

Linux USB驅動工作流程

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

 

 

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特定資料)。

 

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核心使用一個列表(是一個包含製造商ID和裝置號ID的一個結構體)來判斷對於一個裝置該使用哪一個驅動程式),則呼叫探測(probe)函式,探測函式檢查傳遞給它的裝置資訊, 並判斷驅動是否真正合適這個裝置.
(2)由於某些原因,裝置被移除或驅動不再控制裝置時,呼叫斷開(disconnect)函式,做適當清理.

探測和斷開回調函式都在USB集線器核心執行緒上下文中被呼叫, 因此它們休眠是合法的. 為了縮短 USB 探測時間,大部分工作儘可能在裝置開啟時完成.這是因為 USB 核心是在一個執行緒中處理 USB 裝置的新增和移除, 因此任何慢裝置驅動都可能使 USB 裝置探測時間變長。

9.1探測函式分析
在探測回撥函式中, USB裝置驅動應當初始化它可能用來管理 USB 裝置的所有本地結構並儲存所有需要的裝置資訊到本地結構, 因為在此時做這些通常更容易.為了和裝置通訊,USB 驅動通常要探測裝置的端點地址和緩衝大小.

 

 

 

 

 

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述