Linux核心驅動是Linux系統中最重要的組成部分之一。它們負責與硬體設備進行通信,使得作業系統能夠正確地識別和使用硬體。然而,開發Linux核心驅動並不是一件容易的事。在本文中,我們將深入探討Linux核心驅動的實作方法,為讀者提供全面的了解與指導。
#在Linux驅動中,USB驅動處於最底層的是USB主機控制器硬件,在其之上運行的是USB主機控制器驅動,主機控制器之上為USB核心層,再上層為USB設備驅動層(插入主機上的USB隨身碟、滑鼠、USB轉串口等裝置驅動程式)。
因此,在主機側的層次結構中,要實現的USB驅動包括兩類:USB主機控制器驅動和USB設備驅動,前者控制插入其中的USB設備,後者控制USB設備如何與主機通訊。 Linux核心USB核心負責USB驅動管理和協定處理的主要工作。主機控制器驅動和設備驅動之間的USB核心非常重要,其功能包括:透過定義一些資料結構、巨集和功能函數,向上為設備驅動提供編程接口,向下為USB主機控制器驅動提供編程接口;透過全域變數維護整個系統的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設備側驅動程式時能夠把功能的實作和底層通訊分開。
對於這四個層次的簡單描述如下:
設備通常具有一個或多個的配置
配置經常具有一個或多個的介面
介面沒有或具有一個以上的端點
\4. USB通訊最基本的形式是透過端點(USB端點分**中斷(Interrupt)、批次(Bulk)、等時(ISO)、控制(Control)**四種,每種用途不同),USB端點只能往一個方向傳送數據,從主機到設備或從設備到主機,端點可以看作是單向的管道(pipe)。驅動程式把驅動程式物件註冊到USB子系統中,稍後再使用製造商和裝置識別來判斷是否已經安裝了硬體。 USB核心使用一個清單(是一個包含製造商ID和設備號ID的一個結構體)來判斷對於一個設備該使用哪一個驅動程序,熱插撥腳本使用它來確定當一個特定的設備插入到系統時該自動執行哪一個驅動程式的Probe。
\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端點只能向一個方向傳輸資料(從主機到設備(稱為輸出端點)或從設備到主機(稱為輸入端點))。端點可被看作單向的管道。
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 核心為它們保留了相應的頻寬。
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 )
所有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 已經被提交給它時設備從系統中去除.
在 struct usb_driver 結構中, 有 2 個 USB 核心在適當的時候調用的函數:
(1)當設備插入時, 如果USB 核心認為這個驅動可以處理(USB核心使用一個列表(是一個包含製造商ID和設備號ID的一個結構體)來判斷對於一個設備該使用哪一個驅動程序) ,則呼叫探測(probe)函數,探測函數檢查傳遞給它的設備資訊, 並判斷驅動是否真正合適這個設備.
(2)由於某些原因,設備被移除或驅動不再控制設備時,調用斷開(disconnect)函數,做適當清理.
探測和斷開回調函數都在USB集線器內核線程上下文中被調用, 因此它們休眠是合法的. 為了縮短USB 探測時間,大部分工作盡可能在設備打開時完成。這是因為USB 核心是在一個執行緒中處理USB 設備的添加和移除, 因此任何慢設備驅動都可能使USB 設備探測時間變長。
9.1探測函數分析
在探測回調函數中, USB設備驅動應當初始化它可能用來管理USB 設備的所有本地結構並保存所有需要的設備信息到本地結構, 因為在此時做這些通常更容易.為了和設備通訊,USB 驅動通常要探測設備的端點位址和緩衝大小.
在本文中,我們詳細介紹了Linux核心驅動的實作方法,包括核心驅動的框架、核心模組的編寫、裝置驅動的註冊與註銷等方面。我們相信,透過本文的學習,讀者可以更深入地了解Linux核心驅動的實作原理,為自己的開發工作提供更多的參考和幫助。
以上是詳解Linux USB驅動程式工作流程的詳細內容。更多資訊請關注PHP中文網其他相關文章!