999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

嵌入式Linux的USB讀卡器驅動深入研究*

2018-09-07 02:31:50
單片機與嵌入式系統應用 2018年9期
關鍵詞:設備

(江西財經大學 現代經濟管理學院,南昌330013)

引 言

Linux內核支持兩種主要類型的USB驅動程序:宿主(host)系統上的驅動程序和設備(device)上的驅動程序。從宿主的觀點來看(一個普通的USB宿主是一個桌面計算機),宿主系統的USB驅動層次控制插入其中的USB設備,而USB設備的驅動程序控制該設備如何作為一個USB設備和主機通信。設備驅動程序一般存放在內核的drivers/usb/gadget目錄中,針對某個具體設備的USB驅動程序一般都是指宿主設備驅動,本文的USB讀卡器驅動就是這一類。在Linux內核中,USB設備驅動是分很多層的,驅動層只是整個框架中的一小部分,要所有層的配合才能將設備驅動起來。USB驅動程序存在于不同的內核子系統(塊設備、網絡設備、字符設備等)和USB硬件控制器中。

USB核心為USB驅動程序提供了一個用于訪問和控制USB硬件的接口,而不必考慮系統當前存在的各種不同類型的USB硬件控制器,在USB 核心中對USB需要使用的各種資源進行分配及初始化,如注冊USB總線usb_bus_type、初始化USB主控制器、初始化USB HUB等;同時還提供了一些接口給其它層使用,如usb_alloc_dev函數(當有USB設備插入時,用于分配并初始化usb_device)。USB控制器主要負責處理各個USB設備的通信,解析驅動發送給每個設備的請求,并按照請求通知USB設備進行相應的處理,同時發送處理結果給驅動程序,并調用驅動傳入的complete函數。

1 USB驅動層

1.1 USB設備基礎

USB設備是一個非常復雜的東西。如圖1所示,USB設備由配置、接口、端點組成,而USB驅動是綁定到接口上的,每個接口對應于一個設備驅動,對于某些多接口的設備(如帶音頻接口的USB鍵盤),此設備就同時需要USB鍵盤和USB音頻這2個驅動。

圖1 USB設備框架

如圖1所示,一個USB設備通常包含一個或多個配置,一個配置通常包含一個或多個接口,一個接口通常包含0個或多個端點。層次結構如圖2所示。

圖2 USB設備層次結構

(1)端點

USB通信的最基本形式是通過端點來實現的,驅動和接口的通信都是通過端點,一個接口可以有0個或多個端點,端點根據傳輸方式的不同分為以下4種:控制端點、中斷端點、等時端點和批量端點,分別對應于4種不同傳輸方式。

控制端點主要通過向USB設備發送請求來設置或獲取USB設備的配置、狀態等信息,每個USB設備都有一個名為“端點0”的控制端點,USB核心使用該端點在插入時進行設備的配置;中斷端點主要實現以一個固定的速率來傳輸少量的數據,中斷端點是USB鍵盤、鼠標等設備所使用的主要傳輸方式;批量端點用于傳輸大量數據,這些端點通常比中斷端點大得多,常用于需要確保沒有數據丟失的設備(如打印機、存儲設備、網絡設備);等時端點同樣可以傳輸大批量數據,當數據是否到達沒有保證,等時端點用于可以應付數據丟失的設備,這類設備更注重于保持一個恒定的數據流(如USB音頻、視頻)。

(2)接口

USB端點被捆綁為接口,USB接口對應于一個設備驅動,對應于一種功能的設備,對于多接口的復合設備就需要多個驅動。

(3)配置

USB接口被捆綁為配置,一個USB設備可以有多個配置,而且可以在配置之間切換已改變設備的狀態。例如一些允許下載固件到其上的設備包含多個配置以完成這個工作,而某一個時刻只能激活一個配置。

1.2 USB urb

Linux內核中的USB代碼通過一個稱為urb(USB請求塊)的東西和所有的USB設備通信。這個請求塊使用struct urb結構體來描述,可以從include/linux/usb.h文件中找到。urb被用來以一種異步的方式向/從特定的USB 設備上的特定USB端點發送/接收數據。USB設備驅動程序可能會為單個端點分配許多urb,也可能對許多不同的端點重用單個的urb,這取決于驅動程序的需要。設備中的每個端點都可以處理一個urb隊列,所以多個urb可以在隊列為空之前發送到同一個端點。一個urb的典型生命周期如下:

①由USB設備驅動程序創建;②分配給一個特定USB 設備的特定端點;③由USB設備驅動程序遞交到USB 核心;④由USB核心遞交到特定設備的特定USB 主控制器驅動程序;⑤由USB主控制器驅動程序處理,從設備進行USB 傳送;⑥當urb結束之后,USB主控制器驅動程序通知USB設備驅動程序。

urb可以在任何時刻被遞交該urb的驅動程序取消掉,或者被USB核心取消,如果該設備已從系統中移除。urb被動態地創建,它包含一個內部引用計數,使得它們可以在最后一個使用者釋放它們時自動地銷毀。

struct urb結構體不能在驅動程序中或者另一個結構體中靜態地創建,因為這樣會破壞USB核心對urb所使用的引用計數機制。它必須使用usb_alloc_urb函數來創建。該函數原型如下:

struct urb *usb_alloc_urb(int iso_packets, int mem_flags);

第一個參數iso_packets是該urb應該包含的等時數據包的數量。如果不打算創建等時urb,該值應該設置為0。第二個參數mem_flags和傳遞給用于從內核分配內存的kmalloc函數的標志有相同的類型。如果該函數成功地為urb分配了足夠的內存空間,指向該urb的指針將被返回給調用函數。如果返回值為NULL,說明USB核心內發生了錯誤,驅動程序需要進行適當的清理。

當一個urb被創建之后,在它可以被USB核心使用之前必須被正確地初始化。

驅動程序必須調用usb_free_urb函數來告訴USB核心驅動程序已經使用完urb。該函數只有一個參數:

void usb_free_urb(struct urb *urb);

這個參數指向所需釋放的struct urb的指針。在該函數被調用之后,urb結構體就消失了,驅動程序不能再訪問它。

一旦urb被USB驅動程序正確地創建和初始化之后,就可以提交到USB核心以發送到USB設備了。這是通過調用usb_submit_urb函數來完成的:

int usb_submit_urb(struct urb *urb, int mem_flags);

urb參數是指向即將被發送到設備的urb的指針。mem_flags參數等同于傳遞給kmalloc調用的同一個參數,用于通知USB核心如何在此時及時地分配內存緩沖區。

當一個urb被成功地提交到USB核心之后,在接收函數被調用之前不能訪問該urb結構體中的任何字段。因為usb_submit_urb函數可以在任何時刻調用(包括從一個中斷上下文中),mem_flags變量的內容必須是正確的。其實只有三個有效的值可以被使用,取決于usb_submit_urb何時被調用:

(1)GFP_ATOMIC

只要下列條件成立就應該使用該值:

①調用者是在一個urb結束處理例程、中斷處理例程、底半部、tasklet或者定時器回調函數中。

②調用者正持有一個自旋鎖或讀寫鎖,注意如果持有了信號量,該值就不需要了。

③current->state不是TASK_RUNNING,該狀態永遠是TASK_RUNNING,除非驅動程序自己改變了當前的狀態。

(2)GFP_NOIO

如果驅動程序處于塊I/O路徑中應該使用該值,在所有存儲類型的設備的錯誤處理路徑中也應該使用它。

(3)GFP_KERNEL

該值應該在前述類別之外的所有情況中使用。

1.3 USB驅動寫法

在寫USB驅動前,首先需要確定當前設備的一些基本信息,例如當前設備屬于哪類設備,有哪些接口,每個接口有哪些端點,設備、接口、端點、配置描述符的信息是什么,這些信息都可以通過軟件來獲取,常用軟件有windriver、USBtrace、bushound等,其中windriver不但可以看到設備的相關信息,還可以向設備發送標準請求,也可以直接向端點傳輸數據,通過此工具也可以驗證寫好的驅動通信是否正確。

U盤一般提供4個端點:一個控制端點、一個中斷端點、兩個批量端點,進入每個端點后就可以對各個端點進行操作了,對于控制端點可以對其發送各種USB標準請求,這里就可以發送獲取描述符的請求了,對于兩個bulk端點可以直接對其進行讀寫。

2 USB讀卡器驅動的設計與實現

2.1 獲取讀卡器信息

剛剛拿到讀卡器,首先當然是要獲取USB讀卡器的相關信息,通過工具USBtrace獲取各描述符信息如表1所列。

表1 USBtrace獲取各描述符信息

續表1

從這張表中可以獲取到一些有用的信息,USB設備的venderID為0x3EB,deviceID為0x6124,設備有兩個接口,第一個接口的類型為0x2,即Communications and CDC Control,這是一個通信類的接口,子類型為0x2 (Abstract Control Model),這個接口提供了1個端點,端點類型為中斷端點,方向為in。第二個接口類型為0xa(CDC DATA),這個接口一般用于傳輸數據,所以這個結構提供2個bulk端點,一個作為輸入,一個作為輸出。當然每個設備都會有控制端點,控制端點既可作為輸入又可作為輸出。每個端點都是最大允許傳輸數據大小,2個bulk端點最大可傳輸64 B,而中斷端點最大8 B。至此信息基本獲取完畢,開始寫驅動。

2.2 讀卡器驅動設計與實現

首先注冊usb_driver到設備模型,前面講了是通過函數usb_register實現的,下面就是實現usb_driver了,在這里只實現了4個成員:id_table、probe、disconnect、name。id_table就是利用前面獲取的venderID、deviceID來判斷設備,probe和disconnect就是相應的初始化和卸載函數。

2.2.1 初始化、卸載設備

初始化工作在probe函數中實現,主要就是針對前面獲取的端點信息,對每個端點進行初始化。首先要獲取設備的端點,從設備描述符中看到設備有2個bulk端點和1個interrupt端點。Probe函數傳入的是當前接口,即一個usb_interface結構體,如何從usb_interface中獲取端點信息呢?在usb_interface結構中有個成員cur_altsetting,表示當前設置,這是一個usb_host_interface結構體,其中又有一個成員endpoint,這是一個usb_host_endpoint結構體的數組,每一個元素代表一個端點,但這里不保存控制端點,因為控制端點是被單獨保存在設備結構體usb_device中的。

找到每個端點后開始為其創建相應管道。USB通信中有4種端點:控制、等時、中斷、批量,同時對應4種傳輸方式,也對應了4種管道,其中4種管道中又分in或out管道,內核中提供了8個宏來創建管道,定義如下:

#defineusb_sndctrlpipe(dev,endpoint)/*創建out控制管道*/

((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint))

#defineusb_rcvctrlpipe(dev,endpoint)/*創建in控制管道*/

((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)

#defineusb_sndisocpipe(dev,endpoint)/*創建out等時管道*/

((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint))

#defineusb_rcvisocpipe(dev,endpoint)/*創建in等時管道*/

((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) |USB_DIR_IN)

#defineusb_sndbulkpipe(dev,endpoint)/*創建out批量管道*/

((PIPE_BULK << 30) | __create_pipe(dev,endpoint))

#defineusb_rcvbulkpipe(dev,endpoint)/*創建in批量管道*/

((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)

#defineusb_sndintpipe(dev,endpoint)/*創建out中斷管道*/

((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint))

#defineusb_rcvintpipe(dev,endpoint)/*創建in中斷管道*/

((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN)

其中__create_pipe也是一個宏,定義如下:

static inline unsigned int __create_pipe(struct usb_device *dev,

unsigned int endpoint){

return (dev->devnum << 8) | (endpoint << 15);

}

從以上幾個宏可以知道管道的組成,其實管道就是提供了一個通信地址,讓HC知道這個urb包應該發到哪個設備、哪個端點、是什么類型的包。在這里看到了4個宏,其中PIPE_ISOCHRONOUS就是標志等時通道、PIPE_INTERRUPT就是中斷通道、PIPE_CONTROL就是控制通道、PIPE_BULK 就是BULK通道。

在內核里使用一個unsigned int類型的變量來表征一個pipe,其中8~14位是設備號,即devnum,15~18位是端點號,即endpoint。宏USB_DIR_IN用來在pipe里面標志數據傳輸方向,一個管道要么只能輸入,要么只能輸出。在pipe里面,第7位(bit 7)是表征方向的。所以這里0x80也就是說讓bit 7 為1,這就表示傳輸方向是由設備向主機,也就是所謂的in,而如果這一位是0,就表示傳輸方向是由主機向設備的,也就是所謂的out。正是因為USB_DIR_OUT是0,而USB_DIR_IN是1,所以定義管道的時候只用到了USB_DIR_IN,而沒有用到USB_DIR_OUT,因為它是0,任何數和0相或都沒有意義。

圖3 循環緩沖區

接下來為每一個端點分配一個緩沖區,這個緩沖區就是urb結構體中的transfer_buffer,這個緩沖區就是傳輸過程中用來保存數據的,這時使用的分配函數是usb_buffer_alloc,這個函數的作用就是先分配一段內存空間,然后對其進行dma映射,就是從dma緩沖池分配一段內存,即usb_hcd->pool[i]。

最后將接口注冊到字符設備層,通過函數usb_register_dev實現,由于客戶需要定位每個USB口,這里筆者做了一個封裝,做了一點點簡單的改動,下面會講到。

2.2.2 讀、寫數據

所謂讀、寫設備就是實現file_operations中的read、write函數。

這里的read、write就是實現對usb的讀、寫數據和USB通信,這里在一個接口函數基礎上筆者做了一個簡單的封裝,根據實際需求改進了一點小地方,函數聲明如下:

int usb_reader_bulk_msg(struct urb *urb, struct usb_reader_data *usb_reader, unsigned int pipe,void *data, int len,int *actual_length, int timeout);

函數傳入7個參數,第一個參數為需要發送的urb包,這個urb在probe函數中已分配好內存,usb_reader就是讀卡器相關信息結構體,這是為該驅動專門定義的結構體,data是傳輸數據的緩沖區,len為希望傳輸多少數據,actual_length為實際傳輸的數據大小,timeout為傳輸的超時值,若在timeout內未完成傳輸則返回傳輸失敗。

這個函數主要實現填充傳入的urb,主要填充管道、緩沖區、傳輸數據長度、dma緩沖區、complete函數等成員,最后通過usb_submit_urb將urb提交到HC層做處理,usb_submit_urb在講HC層時已經分析過了,這里只要提交過去就行了。

在實現讀寫函數時,使用了一個4 096 B大小的循環緩沖區,每次應用程序調用write時,先將應用程序傳入的數據寫到USB讀卡器,然后馬上從讀卡器上讀取返回數據,并將返回數據寫到緩沖區中。當應用程序調用read時,將循環緩沖區的數據返回給用戶,而不是直接從設備讀取數據再返回。這樣處理的好處就是允許用戶進行多次連續寫入操作,最后只要通過一個讀取操作就能把前幾次寫入操作的結果全部讀取出去。

循環緩沖區寫入時將數據存入數組的尾部,讀取時從數組的另一端開始,當寫入數據到達數組的尾部時,回到數組頭部繼續寫。因此,一個循環緩沖區需要一個數組以及兩個索引值:一個用于下一個要寫入的數據的位置,另一個用于指定下一個要從緩沖區中移走的位置。

如圖3所示,這個緩存被定義成一個空情況,由讀寫指針相同來指示, 而滿情況發生在寫指針緊跟在讀指針后面的時候(小心解決繞回!)。

2.2.3 ioctl函數

這里還提供了一個ioctl接口,主要用于實現USB的標準請求,USB標準請求都是通過控制端點來實現的,控制端點的管道創建比較特殊,最后一個參數為0,創建函數如下:

usb_rcvctrlpipe(usb_reader->dev, 0);

usb_sndctrlpipe(usb_reader->dev, 0);

標準請求和請求參數通過setup包傳送給USB設備,一個setup包由8個字節組成,在內核中用usb_ctrlrequest結構體表示,結構體定義如下:

struct usb_ctrlrequest{

__u8bRequestType;

__u8bRequest;

__le16wValue;

__le16wIndex;

__le16wLength;

} __attribute__((packed));

usb_ctrlrequest被保存在urb->setup_packet里,共8個字節,意義如下:

byte0:bmRequestType,注意在剛才代碼中數據結構struct ctrlrequest 里邊寫的是bRequestType,但是它們對應的是相同的內容。而之所以USB協議里寫成bmRequestType,是因為它實際上又是一個位圖(m表示map),也就是說,盡管它只有1個字節,但是仍然被當作8位來用。

USB協議定義了11個標準請求,各請求對應setup包的各成員的值。

每個標準請求對應的代碼見表2,這個表對應的代碼和內核中為每個請求定義的宏是一一對應的。

對于DESCRIPTOR設置或獲取請求,需要填充一個描述符種類或索引,USB設備描述符對應的索引見表3。

表中列出了USB設備的5大描述符,USB設備的所有信息都保存在這些描述符中,在內核中為這5大描述符分別定義了相應的結構體,分別是usb_device_descriptor、usb_config_descriptor、usb_string_descriptor、usb_interface_descriptor、usb_endpoint_descriptor,上面提到的一些標準請求就是圍繞這些結構體來展開的。

表2 USB標準請求對應代碼

表3 設備描述符代碼

結 語

猜你喜歡
設備
諧響應分析在設備減振中的應用
調試新設備
當代工人(2020年13期)2020-09-27 23:04:20
基于VB6.0+Access2010開發的設備管理信息系統
基于MPU6050簡單控制設備
電子制作(2018年11期)2018-08-04 03:26:08
廣播發射設備中平衡輸入與不平衡輸入的轉換
電子制作(2018年10期)2018-08-04 03:24:48
食之無味,棄之可惜 那些槽點滿滿的可穿戴智能設備
500kV輸變電設備運行維護探討
工業設計(2016年12期)2016-04-16 02:52:00
HTC斥資千萬美元入股虛擬現實設備商WEVR
IT時代周刊(2015年8期)2015-11-11 05:50:37
Automechanika Shanghai 2014 之“看” 汽保設備篇
如何在設備采購中節省成本
主站蜘蛛池模板: 免费激情网址| 黄色福利在线| 国产精品太粉嫩高中在线观看 | 一区二区三区四区日韩| 视频在线观看一区二区| 亚洲天堂在线免费| 无码AV日韩一二三区| 国产91特黄特色A级毛片| 一级成人a毛片免费播放| 99视频免费观看| www.亚洲色图.com| 中文字幕人成乱码熟女免费| 欧美在线三级| 亚洲天堂777| 色综合狠狠操| 97影院午夜在线观看视频| 91精品国产综合久久不国产大片| 色哟哟国产精品| 欧美区一区| 亚洲精品欧美重口| 热99精品视频| 免费人成网站在线观看欧美| 中文字幕66页| 国产AV毛片| 婷婷六月色| 国产精品美女自慰喷水| 日日噜噜夜夜狠狠视频| 中文字幕一区二区人妻电影| 午夜小视频在线| 亚洲中文精品人人永久免费| 精品人妻系列无码专区久久| 国产精品99r8在线观看| 久久久久国产精品免费免费不卡| 色天天综合| 毛片免费在线视频| 91福利国产成人精品导航| 亚洲资源站av无码网址| 午夜毛片免费看| 久久99蜜桃精品久久久久小说| 亚洲天堂视频在线观看| 久久99国产综合精品女同| 国产手机在线ΑⅤ片无码观看| 99精品国产电影| 丰满人妻久久中文字幕| 日韩免费中文字幕| 欧美精品在线免费| 91麻豆国产视频| 亚洲最新在线| 香蕉久久国产超碰青草| 91在线激情在线观看| 欧美精品一区在线看| 国产在线精品网址你懂的| 国产欧美视频在线| 亚洲男人天堂2020| 国产后式a一视频| 91精品国产一区自在线拍| 欧美三级视频网站| 国产高潮流白浆视频| 97se亚洲综合| 制服丝袜无码每日更新| 日本一区二区不卡视频| 亚洲精品色AV无码看| 日韩欧美亚洲国产成人综合| 欧美人与牲动交a欧美精品| 天天综合色网| 国产成人1024精品| 国产香蕉一区二区在线网站| 日韩专区欧美| 欧美精品亚洲精品日韩专区va| 台湾AV国片精品女同性| 2021国产在线视频| 精品视频一区在线观看| 激情影院内射美女| 亚洲九九视频| 精品国产自在在线在线观看| 午夜老司机永久免费看片| 欧美日韩另类在线| 女人18毛片久久| 国产啪在线91| 亚洲人成网址| 国产成人91精品免费网址在线 | 久久无码免费束人妻|