溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Linux系統(tǒng)中USB驅動程序的工作流程

發(fā)布時間:2021-09-09 17:07:41 來源:億速云 閱讀:198 作者:chen 欄目:系統(tǒng)運維

本篇內容介紹了“Linux系統(tǒng)中USB驅動程序的工作流程”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

Linux系統(tǒng)中USB驅動程序的工作流程

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

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

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

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

Linux系統(tǒng)中USB驅動程序的工作流程

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

5. 數(shù)據(jù)結構
(1)USB設備:對應數(shù)據(jù)結構struct usb_device
(2)配置:struct usb_host_config (任一時刻,只能有一個配置生效)
(3)USB接口:struct usb_interface (USB 核心將其傳遞給USB設備驅動,并由USB設備驅動負責后續(xù)的控制。一個USB接口代表一個基本功能,每個USB驅動控制一個接口。所以一個物理上的硬件設備可能需要 一個以上的驅動程序。)
(4)端點: struct usb_host_endpoint ,它所包含的真實端點信息在另一個結構中:struct usb_endpoint_descriptor(端點描述符,包含所有的USB特定數(shù)據(jù))。

6. USB端點分類
USB 通訊的最基本形式是通過一個稱為端點的東西。一個USB端點只能向一個方向傳輸數(shù)據(jù)(從主機到設備(稱為輸出端點)或者從設備到主機(稱為輸入端點))。端點可被看作一個單向的管道。
USB 端點有 4 種不同類型, 分別具有不同的數(shù)據(jù)傳送方式:
(1)控制CONTROL
控制端點被用來控制對USB設備的不同部分訪問. 通常用作配置設備、獲取設備信息、發(fā)送命令到設備或獲取設備狀態(tài)報告。這些端點通常較小。每個 USB 設備都有一個控制端點稱為"端點 0", 被 USB 核心用來在插入時配置設備。USB協(xié)議保證總有足夠的帶寬留給控制端點傳送數(shù)據(jù)到設備.
(2)中斷INTERRUPT
每當 USB 主機向設備請求數(shù)據(jù)時,中斷端點以固定的速率傳送小量的數(shù)據(jù)。此為USB 鍵盤和鼠標的主要的數(shù)據(jù)傳送方法。它還用以傳送數(shù)據(jù)到USB設備來控制設備。通常不用來傳送大量數(shù)據(jù)。USB協(xié)議保證總有足夠的帶寬留給中斷端點傳送數(shù)據(jù)到設備.
(3)批量BULK
批量端點用以傳送大量數(shù)據(jù)。這些端點通常比中斷端點大得多. 它們普遍用于不能有任何數(shù)據(jù)丟失的情況。USB 協(xié)議不保證傳輸在特定時間范圍內完成。如果總線上沒有足夠的空間來發(fā)送整個BULK包,它被分為多個包進行傳輸。這些端點普遍用于打印機、USB Mass Storage和USB網(wǎng)絡設備上。
(4)等時ISOCHRONOUS
等時端點也批量傳送大量數(shù)據(jù), 但是這個數(shù)據(jù)不被保證能送達。這些端點用在可以處理數(shù)據(jù)丟失的設備中,并且更多依賴于保持持續(xù)的數(shù)據(jù)流。如音頻和視頻設備等等。
控制和批量端點用于異步數(shù)據(jù)傳送,而中斷和等時端點是周期性的。這意味著這些端點被設置來在固定的時間連續(xù)傳送數(shù)據(jù),USB 核心為它們保留了相應的帶寬。

7. endpoint

C/C++ Code復制內容到剪貼板

  1. struct usb_host_endpoint{   

  2.     struct usb_endpoint_descriptor desc;//端點描述符   

  3.     struct list_head urb_list;//此端點的URB對列,由USB核心維護   

  4.     void *hcpriv;   

  5.     struct ep_device *ep_dev; /* For sysfs info */  

  6.     unsigned char*extra;/* Extra descriptors */  

  7.     int extralen;   

  8.     int enabled;   

  9. };  

當調用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,對應數(shù)據(jù)結構struct usb_hcd )

8. urb
所有USB通訊均為請求-->響應模式,USB設備不會主動向Host發(fā)送數(shù)據(jù)。寫數(shù)據(jù):USB設備驅動發(fā)送urb請求給USB設備,USB設備不需要回數(shù)據(jù)。讀數(shù)據(jù):USB設備驅動發(fā)送urb請求給USB設備,USB設備需要回數(shù)據(jù)。
USB 設備驅動通過urb和所有的 USB 設備通訊。urb用 struct urb 結構描述(include/linux/usb.h )。
urb 以一種異步的方式同一個特定USB設備的特定端點發(fā)送或接受數(shù)據(jù)。一個 USB 設備驅動可根據(jù)驅動的需要,分配多個 urb 給一個端點或重用單個 urb 給多個不同的端點。設備中的每個端點都處理一個 urb 隊列, 所以多個 urb 可在隊列清空之前被發(fā)送到相同的端點。
一個 urb 的典型生命循環(huán)如下:
(1)被創(chuàng)建;
(2)被分配給一個特定 USB 設備的特定端點;
(3)被提交給 USB 核心;
(4)被 USB 核心提交給特定設備的特定 USB 主機控制器驅動;
(5)被 USB 主機控制器驅動處理, 并傳送到設備;
(6)以上操作完成后,USB主機控制器驅動通知 USB 設備驅動。
urb 也可被提交它的驅動在任何時間取消;如果設備被移除,urb 可以被USB核心取消。urb 被動態(tài)創(chuàng)建并包含一個內部引用計數(shù),使它們可以在最后一個用戶釋放它們時被自動釋放。

8.1 提交 urb
一旦 urb 被正確地創(chuàng)建并初始化, 它就可以提交給 USB 核心以發(fā)送出到 USB 設備. 這通過調用函數(shù)sb_submit_urb 實現(xiàn).
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
參數(shù):
struct urb *urb :指向被提交的 urb 的指針
gfp_t mem_flags :使用傳遞給 kmalloc 調用同樣的參數(shù), 用來告訴 USB 核心如何及時分配內存緩沖
因為函數(shù) usb_submit_urb 可被在任何時候被調用(包括從一個中斷上下文), mem_flags 變量必須正確設置. 根據(jù) usb_submit_urb 被調用的時間,只有 3 個有效值可用:
GFP_ATOMIC
只要滿足以下條件,就應當使用此值:
1) 調用者處于一個 urb 結束處理例程,中斷處理例程,底半部,tasklet或者一個定時器回調函數(shù).
2) 調用者持有自旋鎖或者讀寫鎖. 注意如果正持有一個信號量, 這個值不必要.
3) current->state 不是 TASK_RUNNING. 除非驅動已自己改變 current 狀態(tài),否則狀態(tài)應該一直是TASK_RUNNING .
GFP_NOIO
驅動處于塊 I/O 處理過程中. 它還應當用在所有的存儲類型的錯誤處理過程中.
GFP_KERNEL
所有不屬于之前提到的其他情況
在 urb 被成功提交給 USB 核心之后, 直到結束處理例程函數(shù)被調用前,都不能訪問 urb 結構的任何成員

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

9. 探測和斷開
在 struct usb_driver 結構中, 有 2 個 USB 核心在適當?shù)臅r候調用的函數(shù):
(1)當設備插入時, 如果 USB 核心認為這個驅動可以處理(USB核心使用一個列表(是一個包含制造商ID和設備號ID的一個結構體)來判斷對于一個設備該使用哪一個驅動程序),則調用探測(probe)函數(shù),探測函數(shù)檢查傳遞給它的設備信息, 并判斷驅動是否真正合適這個設備.
(2)由于某些原因,設備被移除或驅動不再控制設備時,調用斷開(disconnect)函數(shù),做適當清理.
探測和斷開回調函數(shù)都在USB集線器內核線程上下文中被調用, 因此它們休眠是合法的. 為了縮短 USB 探測時間,大部分工作盡可能在設備打開時完成.這是因為 USB 核心是在一個線程中處理 USB 設備的添加和移除, 因此任何慢設備驅動都可能使 USB 設備探測時間變長。

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


PS:Linux USB驅動相關細節(jié)知識補充
1. 在usb_fill_bulk_urb,usb_fill_int_urb,usb_fill_control_urb都需要指定回調函數(shù),當此URB請求完成時,usb core回調用此函數(shù)。
注意:urb 回調函數(shù)是在中斷上下文運行, 因此它不應做任何內存分配, 持有任何信號量, 或任何可導致進程休眠的事情. 如果從回調中提交 urb 并需要分配新內存塊, 需使用 GFP_ATOMIC 標志來告知 USB 核心不要休眠.

2. urb封裝函數(shù):
(1)int usb_bulk_msg(struct usb_device *usb_dev,unsigned int pipe,void*data, int len, int*actual_length,int timeout)
功能:創(chuàng)建批量 urb 并發(fā)送到指定的設備, 接著在返回之前等待完成.
參數(shù):
  struct usb_device *usb_dev :目標 USB 設備指針
  unsigned int pipe :目標 USB 設備的特定端點. 必須使用特定的宏創(chuàng)建.
  void *data :如果是 OUT 端點, 指向要發(fā)送到設備的數(shù)據(jù)的指針. 如果是 IN 端點, 這是從設備讀取的數(shù)據(jù)的緩沖區(qū)指針.
  int len : data 參數(shù)指向的緩沖的長度
  int *actual_length :指向函數(shù)放置真實字節(jié)數(shù)的指針,根據(jù)端點方向,這些字節(jié)要么是被發(fā)送到設備的,要么是從設備中讀取的.
  int timeout :時鐘嘀噠數(shù), 應等待的時間. 如果為 0, 函數(shù)永遠等待操作完成.
返回值:成功返回0,actual_length 參數(shù)包含被傳送或從設備中讀取的字節(jié)數(shù).否則返回負的錯誤值.

(2)int usb_control_msg(struct usb_device*dev, unsigned int pipe, __u8 request,__u8 requesttype, __u16 value, __u16 index,void *data, __u16 size,int timeout)
功能:創(chuàng)建控制 urb 并發(fā)送到指定的設備, 接著在返回之前等待完成.
參數(shù):
  struct usb_device *usb_dev :目標 USB 設備指針
  unsigned int pipe :目標 USB 設備的特定端點. 必須使用特定的宏創(chuàng)建.
  __u8 request :控制消息的 USB 請求值.
  __u8 requesttype :控制消息的 USB 請求類型.
  __u16 value :控制消息的 USB 消息值.
  __u16 index :控制消息的 USB 消息索引值.
  void *data :如果是 OUT 端點, 指向要發(fā)送到設備的數(shù)據(jù)的指針. 如果是 IN 端點, 這是從設備讀取的數(shù)據(jù)的緩沖區(qū)指針.
  __u16 size : data 參數(shù)指向的緩沖的長度
  int timeout :時鐘嘀噠數(shù), 應等待的時間. 如果為 0, 函數(shù)永遠等待操作完成.
返回值:成功返回被傳送到或從設備讀取的字節(jié)數(shù).否則返回負的錯誤值.

(3)int usb_interrupt_msg(struct usb_device*usb_dev, unsigned int pipe,void *data,int len, int *actual_length,int timeout)
功能:創(chuàng)建中斷 urb 并發(fā)送到指定的設備, 接著在返回之前等待完成.其實就是usb_bulk_msg的包裝,所有參數(shù)和usb_bulk_msg一樣使用

“Linux系統(tǒng)中USB驅動程序的工作流程”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網(wǎng)站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI