1.USB主機(jī)
在Linux驅(qū)動(dòng)中,USB驅(qū)動(dòng)處于最底層的是USB主機(jī)控制器硬件,在其之上運(yùn)行的是USB主機(jī)控制器驅(qū)動(dòng),主機(jī)控制器之上為USB核心層,再上層為USB設(shè)備驅(qū)動(dòng)層(插入主機(jī)上的U盤、鼠標(biāo)、USB轉(zhuǎn)串口等設(shè)備驅(qū)動(dòng))。
因此,在主機(jī)側(cè)的層次結(jié)構(gòu)中,要實(shí)現(xiàn)的USB驅(qū)動(dòng)包括兩類:USB主機(jī)控制器驅(qū)動(dòng)和USB設(shè)備驅(qū)動(dòng),前者控制插入其中的USB設(shè)備,后者控制USB設(shè)備如何與主機(jī)通信。Linux內(nèi)核USB核心負(fù)責(zé)USB驅(qū)動(dòng)管理和協(xié)議處理的主要工作。主機(jī)控制器驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng)之間的USB核心非常重要,其功能包括:通過定義一些數(shù)據(jù)結(jié)構(gòu)、宏和功能函數(shù),向上為設(shè)備驅(qū)動(dòng)提供編程接口,向下為USB主機(jī)控制器驅(qū)動(dòng)提供編程接口;通過全局變量維護(hù)整個(gè)系統(tǒng)的USB設(shè)備信息;完成設(shè)備熱插拔控制、總線數(shù)據(jù)傳輸控制等。
2.USB設(shè)備
Linux內(nèi)核中USB設(shè)備側(cè)驅(qū)動(dòng)程序分為3個(gè)層次:UDC驅(qū)動(dòng)程序、Gadget API和Gadget驅(qū)動(dòng)程序。UDC驅(qū)動(dòng)程序直接訪問硬件,控制USB設(shè)備和主機(jī)間的底層通信,向上層提供與硬件相關(guān)操作的回調(diào)函數(shù)。當(dāng)前Gadget API是UDC驅(qū)動(dòng)程序回調(diào)函數(shù)的簡單包裝。Gadget驅(qū)動(dòng)程序具體控制USB設(shè)備功能的實(shí)現(xiàn),使設(shè)備表現(xiàn)出“網(wǎng)絡(luò)連接”、“打印機(jī)”或“USB Mass Storage”等特性,它使用Gadget API控制UDC實(shí)現(xiàn)上述功能。Gadget API把下層的UDC驅(qū)動(dòng)程序和上層的Gadget驅(qū)動(dòng)程序隔離開,使得在Linux系統(tǒng)中編寫USB設(shè)備側(cè)驅(qū)動(dòng)程序時(shí)能夠把功能的實(shí)現(xiàn)和底層通信分離。
3.層次
在USB設(shè)備組織結(jié)構(gòu)中,從上到下分為設(shè)備(device)、配置(config)、接口(interface)和端點(diǎn)(endpoint)四個(gè)層次。USB設(shè)備程序綁定到接口上。
對于這四個(gè)層次的簡單描述如下:
(1)設(shè)備通常具有一個(gè)或多個(gè)的配置
(2)配置經(jīng)常具有一個(gè)或多個(gè)的接口
(3)接口沒有或具有一個(gè)以上的端點(diǎn)
4.端點(diǎn)
USB通信最基本的形式是通過端點(diǎn)(USB端點(diǎn)分中斷(Interrupt)、批量(Bulk)、等時(shí)(ISO)、控制(Control)四種,每種用途不同),USB端點(diǎn)只能往一個(gè)方向傳送數(shù)據(jù),從主機(jī)到設(shè)備或者從設(shè)備到主機(jī),端點(diǎn)可以看作是單向的管道(pipe)。驅(qū)動(dòng)程序把驅(qū)動(dòng)程序?qū)ο笞缘経SB子系統(tǒng)中,稍后再使用制造商和設(shè)備標(biāo)識來判斷是否已經(jīng)安裝了硬件。USB核心使用一個(gè)列表(是一個(gè)包含制造商ID和設(shè)備號ID的一個(gè)結(jié)構(gòu)體)來判斷對于一個(gè)設(shè)備該使用哪一個(gè)驅(qū)動(dòng)程序,熱插撥腳本使用它來確定當(dāng)一個(gè)特定的設(shè)備插入到系統(tǒng)時(shí)該自動(dòng)執(zhí)行哪一個(gè)驅(qū)動(dòng)程序的Probe。
5. 數(shù)據(jù)結(jié)構(gòu)
(1)USB設(shè)備:對應(yīng)數(shù)據(jù)結(jié)構(gòu)struct usb_device
(2)配置:struct usb_host_config (任一時(shí)刻,只能有一個(gè)配置生效)
(3)USB接口:struct usb_interface (USB 核心將其傳遞給USB設(shè)備驅(qū)動(dòng),并由USB設(shè)備驅(qū)動(dòng)負(fù)責(zé)后續(xù)的控制。一個(gè)USB接口代表一個(gè)基本功能,每個(gè)USB驅(qū)動(dòng)控制一個(gè)接口。所以一個(gè)物理上的硬件設(shè)備可能需要 一個(gè)以上的驅(qū)動(dòng)程序。)
(4)端點(diǎn): struct usb_host_endpoint ,它所包含的真實(shí)端點(diǎn)信息在另一個(gè)結(jié)構(gòu)中:struct usb_endpoint_descriptor(端點(diǎn)描述符,包含所有的USB特定數(shù)據(jù))。
6. USB端點(diǎn)分類
USB 通訊的最基本形式是通過一個(gè)稱為端點(diǎn)的東西。一個(gè)USB端點(diǎn)只能向一個(gè)方向傳輸數(shù)據(jù)(從主機(jī)到設(shè)備(稱為輸出端點(diǎn))或者從設(shè)備到主機(jī)(稱為輸入端點(diǎn)))。端點(diǎn)可被看作一個(gè)單向的管道。
USB 端點(diǎn)有 4 種不同類型, 分別具有不同的數(shù)據(jù)傳送方式:
(1)控制CONTROL
控制端點(diǎn)被用來控制對USB設(shè)備的不同部分訪問. 通常用作配置設(shè)備、獲取設(shè)備信息、發(fā)送命令到設(shè)備或獲取設(shè)備狀態(tài)報(bào)告。這些端點(diǎn)通常較小。每個(gè) USB 設(shè)備都有一個(gè)控制端點(diǎn)稱為"端點(diǎn) 0", 被 USB 核心用來在插入時(shí)配置設(shè)備。USB協(xié)議保證總有足夠的帶寬留給控制端點(diǎn)傳送數(shù)據(jù)到設(shè)備.
(2)中斷INTERRUPT
每當(dāng) USB 主機(jī)向設(shè)備請求數(shù)據(jù)時(shí),中斷端點(diǎn)以固定的速率傳送小量的數(shù)據(jù)。此為USB 鍵盤和鼠標(biāo)的主要的數(shù)據(jù)傳送方法。它還用以傳送數(shù)據(jù)到USB設(shè)備來控制設(shè)備。通常不用來傳送大量數(shù)據(jù)。USB協(xié)議保證總有足夠的帶寬留給中斷端點(diǎn)傳送數(shù)據(jù)到設(shè)備.
(3)批量BULK
批量端點(diǎn)用以傳送大量數(shù)據(jù)。這些端點(diǎn)通常比中斷端點(diǎn)大得多. 它們普遍用于不能有任何數(shù)據(jù)丟失的情況。USB 協(xié)議不保證傳輸在特定時(shí)間范圍內(nèi)完成。如果總線上沒有足夠的空間來發(fā)送整個(gè)BULK包,它被分為多個(gè)包進(jìn)行傳輸。這些端點(diǎn)普遍用于打印機(jī)、USB Mass Storage和USB網(wǎng)絡(luò)設(shè)備上。
(4)等時(shí)ISOCHRONOUS
等時(shí)端點(diǎn)也批量傳送大量數(shù)據(jù), 但是這個(gè)數(shù)據(jù)不被保證能送達(dá)。這些端點(diǎn)用在可以處理數(shù)據(jù)丟失的設(shè)備中,并且更多依賴于保持持續(xù)的數(shù)據(jù)流。如音頻和視頻設(shè)備等等。
控制和批量端點(diǎn)用于異步數(shù)據(jù)傳送,而中斷和等時(shí)端點(diǎn)是周期性的。這意味著這些端點(diǎn)被設(shè)置來在固定的時(shí)間連續(xù)傳送數(shù)據(jù),USB 核心為它們保留了相應(yīng)的帶寬。
7. endpoint
C/C++ Code復(fù)制內(nèi)容到剪貼板
- struct usb_host_endpoint{
- struct usb_endpoint_descriptor desc;
- struct list_head urb_list;
- void *hcpriv;
- struct ep_device *ep_dev;
- unsigned char*extra;
- int extralen;
- int enabled;
- };
當(dāng)調(diào)用USB設(shè)備驅(qū)動(dòng)調(diào)用usb_submit_urb提交urb請求時(shí),將調(diào)用int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)把此urb增加到urb_list的尾巴上。(hcd: Host Controller Driver,對應(yīng)數(shù)據(jù)結(jié)構(gòu)struct usb_hcd )
8. urb
所有USB通訊均為請求-->響應(yīng)模式,USB設(shè)備不會(huì)主動(dòng)向Host發(fā)送數(shù)據(jù)。寫數(shù)據(jù):USB設(shè)備驅(qū)動(dòng)發(fā)送urb請求給USB設(shè)備,USB設(shè)備不需要回?cái)?shù)據(jù)。讀數(shù)據(jù):USB設(shè)備驅(qū)動(dòng)發(fā)送urb請求給USB設(shè)備,USB設(shè)備需要回?cái)?shù)據(jù)。
USB 設(shè)備驅(qū)動(dòng)通過urb和所有的 USB 設(shè)備通訊。urb用 struct urb 結(jié)構(gòu)描述(include/linux/usb.h )。
urb 以一種異步的方式同一個(gè)特定USB設(shè)備的特定端點(diǎn)發(fā)送或接受數(shù)據(jù)。一個(gè) USB 設(shè)備驅(qū)動(dòng)可根據(jù)驅(qū)動(dòng)的需要,分配多個(gè) urb 給一個(gè)端點(diǎn)或重用單個(gè) urb 給多個(gè)不同的端點(diǎn)。設(shè)備中的每個(gè)端點(diǎn)都處理一個(gè) urb 隊(duì)列, 所以多個(gè) urb 可在隊(duì)列清空之前被發(fā)送到相同的端點(diǎn)。
一個(gè) urb 的典型生命循環(huán)如下:
(1)被創(chuàng)建;
(2)被分配給一個(gè)特定 USB 設(shè)備的特定端點(diǎn);
(3)被提交給 USB 核心;
(4)被 USB 核心提交給特定設(shè)備的特定 USB 主機(jī)控制器驅(qū)動(dòng);
(5)被 USB 主機(jī)控制器驅(qū)動(dòng)處理, 并傳送到設(shè)備;
(6)以上操作完成后,USB主機(jī)控制器驅(qū)動(dòng)通知 USB 設(shè)備驅(qū)動(dòng)。
urb 也可被提交它的驅(qū)動(dòng)在任何時(shí)間取消;如果設(shè)備被移除,urb 可以被USB核心取消。urb 被動(dòng)態(tài)創(chuàng)建并包含一個(gè)內(nèi)部引用計(jì)數(shù),使它們可以在最后一個(gè)用戶釋放它們時(shí)被自動(dòng)釋放。
8.1 提交 urb
一旦 urb 被正確地創(chuàng)建并初始化, 它就可以提交給 USB 核心以發(fā)送出到 USB 設(shè)備. 這通過調(diào)用函數(shù)sb_submit_urb 實(shí)現(xiàn).
int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
參數(shù):
struct urb *urb :指向被提交的 urb 的指針
gfp_t mem_flags :使用傳遞給 kmalloc 調(diào)用同樣的參數(shù), 用來告訴 USB 核心如何及時(shí)分配內(nèi)存緩沖
因?yàn)楹瘮?shù) usb_submit_urb 可被在任何時(shí)候被調(diào)用(包括從一個(gè)中斷上下文), mem_flags 變量必須正確設(shè)置. 根據(jù) usb_submit_urb 被調(diào)用的時(shí)間,只有 3 個(gè)有效值可用:
GFP_ATOMIC
只要滿足以下條件,就應(yīng)當(dāng)使用此值:
1) 調(diào)用者處于一個(gè) urb 結(jié)束處理例程,中斷處理例程,底半部,tasklet或者一個(gè)定時(shí)器回調(diào)函數(shù).
2) 調(diào)用者持有自旋鎖或者讀寫鎖. 注意如果正持有一個(gè)信號量, 這個(gè)值不必要.
3) current->state 不是 TASK_RUNNING. 除非驅(qū)動(dòng)已自己改變 current 狀態(tài),否則狀態(tài)應(yīng)該一直是TASK_RUNNING .
GFP_NOIO
驅(qū)動(dòng)處于塊 I/O 處理過程中. 它還應(yīng)當(dāng)用在所有的存儲(chǔ)類型的錯(cuò)誤處理過程中.
GFP_KERNEL
所有不屬于之前提到的其他情況
在 urb 被成功提交給 USB 核心之后, 直到結(jié)束處理例程函數(shù)被調(diào)用前,都不能訪問 urb 結(jié)構(gòu)的任何成員
8.2 urb結(jié)束處理例程
如果 usb_submit_urb 被成功調(diào)用, 并把對 urb 的控制權(quán)傳遞給 USB 核心, 函數(shù)返回 0; 否則返回一個(gè)負(fù)的錯(cuò)誤代碼. 如果函數(shù)調(diào)用成功, 當(dāng) urb 被結(jié)束的時(shí)候結(jié)束處理例程會(huì)被調(diào)用一次.當(dāng)這個(gè)函數(shù)被調(diào)用時(shí), USB 核心就完成了這個(gè)urb, 并將它的控制權(quán)返回給設(shè)備驅(qū)動(dòng).
只有3 種結(jié)束urb并調(diào)用結(jié)束處理例程的情況:
(1)urb 被成功發(fā)送給設(shè)備, 且設(shè)備返回正確的確認(rèn).如果這樣, urb 中的status變量被設(shè)置為 0.
(2)發(fā)生錯(cuò)誤, 錯(cuò)誤值記錄在 urb 結(jié)構(gòu)中的 status 變量.
(3)urb 從 USB 核心unlink. 這發(fā)生在要么當(dāng)驅(qū)動(dòng)通過調(diào)用 usb_unlink_urb 或者 usb_kill_urb告知 USB 核心取消一個(gè)已提交的 urb,或者在一個(gè) urb 已經(jīng)被提交給它時(shí)設(shè)備從系統(tǒng)中去除.
9. 探測和斷開
在 struct usb_driver 結(jié)構(gòu)中, 有 2 個(gè) USB 核心在適當(dāng)?shù)臅r(shí)候調(diào)用的函數(shù):
(1)當(dāng)設(shè)備插入時(shí), 如果 USB 核心認(rèn)為這個(gè)驅(qū)動(dòng)可以處理(USB核心使用一個(gè)列表(是一個(gè)包含制造商ID和設(shè)備號ID的一個(gè)結(jié)構(gòu)體)來判斷對于一個(gè)設(shè)備該使用哪一個(gè)驅(qū)動(dòng)程序),則調(diào)用探測(probe)函數(shù),探測函數(shù)檢查傳遞給它的設(shè)備信息, 并判斷驅(qū)動(dòng)是否真正合適這個(gè)設(shè)備.
(2)由于某些原因,設(shè)備被移除或驅(qū)動(dòng)不再控制設(shè)備時(shí),調(diào)用斷開(disconnect)函數(shù),做適當(dāng)清理.
探測和斷開回調(diào)函數(shù)都在USB集線器內(nèi)核線程上下文中被調(diào)用, 因此它們休眠是合法的. 為了縮短 USB 探測時(shí)間,大部分工作盡可能在設(shè)備打開時(shí)完成.這是因?yàn)?USB 核心是在一個(gè)線程中處理 USB 設(shè)備的添加和移除, 因此任何慢設(shè)備驅(qū)動(dòng)都可能使 USB 設(shè)備探測時(shí)間變長。
9.1探測函數(shù)分析
在探測回調(diào)函數(shù)中, USB設(shè)備驅(qū)動(dòng)應(yīng)當(dāng)初始化它可能用來管理 USB 設(shè)備的所有本地結(jié)構(gòu)并保存所有需要的設(shè)備信息到本地結(jié)構(gòu), 因?yàn)樵诖藭r(shí)做這些通常更容易.為了和設(shè)備通訊,USB 驅(qū)動(dòng)通常要探測設(shè)備的端點(diǎn)地址和緩沖大小.
PS:Linux USB驅(qū)動(dòng)相關(guān)細(xì)節(jié)知識補(bǔ)充
1. 在usb_fill_bulk_urb,usb_fill_int_urb,usb_fill_control_urb都需要指定回調(diào)函數(shù),當(dāng)此URB請求完成時(shí),usb core回調(diào)用此函數(shù)。
注意:urb 回調(diào)函數(shù)是在中斷上下文運(yùn)行, 因此它不應(yīng)做任何內(nèi)存分配, 持有任何信號量, 或任何可導(dǎo)致進(jìn)程休眠的事情. 如果從回調(diào)中提交 urb 并需要分配新內(nèi)存塊, 需使用 GFP_ATOMIC 標(biāo)志來告知 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è)備, 接著在返回之前等待完成.
參數(shù):
struct usb_device *usb_dev :目標(biāo) USB 設(shè)備指針
unsigned int pipe :目標(biāo) USB 設(shè)備的特定端點(diǎn). 必須使用特定的宏創(chuàng)建.
void *data :如果是 OUT 端點(diǎn), 指向要發(fā)送到設(shè)備的數(shù)據(jù)的指針. 如果是 IN 端點(diǎn), 這是從設(shè)備讀取的數(shù)據(jù)的緩沖區(qū)指針.
int len : data 參數(shù)指向的緩沖的長度
int *actual_length :指向函數(shù)放置真實(shí)字節(jié)數(shù)的指針,根據(jù)端點(diǎn)方向,這些字節(jié)要么是被發(fā)送到設(shè)備的,要么是從設(shè)備中讀取的.
int timeout :時(shí)鐘嘀噠數(shù), 應(yīng)等待的時(shí)間. 如果為 0, 函數(shù)永遠(yuǎn)等待操作完成.
返回值:成功返回0,actual_length 參數(shù)包含被傳送或從設(shè)備中讀取的字節(jié)數(shù).否則返回負(fù)的錯(cuò)誤值.
(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è)備, 接著在返回之前等待完成.
參數(shù):
struct usb_device *usb_dev :目標(biāo) USB 設(shè)備指針
unsigned int pipe :目標(biāo) USB 設(shè)備的特定端點(diǎn). 必須使用特定的宏創(chuàng)建.
__u8 request :控制消息的 USB 請求值.
__u8 requesttype :控制消息的 USB 請求類型.
__u16 value :控制消息的 USB 消息值.
__u16 index :控制消息的 USB 消息索引值.
void *data :如果是 OUT 端點(diǎn), 指向要發(fā)送到設(shè)備的數(shù)據(jù)的指針. 如果是 IN 端點(diǎn), 這是從設(shè)備讀取的數(shù)據(jù)的緩沖區(qū)指針.
__u16 size : data 參數(shù)指向的緩沖的長度
int timeout :時(shí)鐘嘀噠數(shù), 應(yīng)等待的時(shí)間. 如果為 0, 函數(shù)永遠(yuǎn)等待操作完成.
返回值:成功返回被傳送到或從設(shè)備讀取的字節(jié)數(shù).否則返回負(fù)的錯(cuò)誤值.
(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ā)送到指定的設(shè)備, 接著在返回之前等待完成.其實(shí)就是usb_bulk_msg的包裝,所有參數(shù)和usb_bulk_msg一樣使用