usb-skeleton 代碼分析
文章目錄
- usb-skeleton 代碼分析
-
- 驅動注冊
- 主端探測函數 probe
- 裝置操作集
-
- 打開裝置
- 關閉裝置
- 寫操作
- 讀操作
- flush
usb-skeleton.c
是USB Host端代碼的一個骨架,如果想要編寫自己的Host端
bulk
傳輸的代碼,可以參考這個部分的代碼進行編寫,至于其他
isoc
的傳輸方式,可能還需要參考其他的驅動代碼進行編寫。
目前核心版本
linux-4.9.37
。
驅動注冊
使用
module_usb_driver
注冊HOST端驅動,聲明比對的gadget驅動清單(主要依賴VID與PID),完善相關的探測、斷開連接配接等函數:
主端探測函數 probe
看一下
skel_probe
的第一部分裡面做了什麼:周遊接口的目前配置的所有端點,找到
bulk in
端點位址并申請
urb
傳輸描述符。
一開始用到的
usb_skel
結構體如下,成員變量大部分都有注釋,主要的也增加了中文的注釋。
再看一下第二部分的
skel_probe
,主要是執行一個
USB
裝置注冊,傳入一個
struct usb_class_driver
類型的
skel_class
。
看一下
skel_class
的内容,主要關注是裝置的操作集合
skel_fops
。
裝置操作集
看一下
skel_fops
的概況,主要關注打開、關閉、讀寫和
flush
函數。
打開裝置
skel_open()
根據傳入的
inode
節點,找到次裝置号後,使用次裝置号找到對應的裝置接口,再從裝置接口中找到私有的
usb_skel
結構體指針,并儲存在
file
結構體中,友善後續
read/write
等函數使用。
關閉裝置
skel_release()
主要作用是釋放資源。
看一下
skel_delete()
,就是各種資源的釋放。
寫操作
函數分為三個部分截圖,看第一部分,這裡主要就是一些參數的檢查,以及如果上一次傳輸錯誤的話直接報告出錯。
看第二部分的工作就是申請
urb
,申請核心态的記憶體,并将使用者态的資料拷貝過來,填充
urb
的回調函數等資訊後送出給硬體控制器。
copy_form_user/copy_to_user
是比較耗時的工作,如果想要提高傳輸的效率,可以考慮将這個拷貝部分去掉,比如使用
mmap
方式;有些特殊情況可以更加友善,比如
MMZ
的記憶體可以直接将碼流的實體位址交給
USB
驅動,然後也不需要進行
DMA
映射,填充
urb
直接交給
USB
控制器,就可以進行傳輸,省略拷貝的過程速度提高是很可觀的。
看第三部分主要是
urb
的釋放以及出錯處理。
看一下填充
urb
時的回調函數
skel_write_bulk_callback()
,内容也比較簡單,如果有錯誤,儲存錯誤碼;釋放申請的一緻性記憶體。
讀操作
讀操作和寫操作有些不一樣,我們一般的
read
操作是阻塞的,而
write
操作直接送出給USB控制器之後就可以了,而阻塞的
read
需要等待資料的回來并報告應用層。
這裡也分為兩部分,看第一部分,進行檢查相關的參數後,如果發現正在等待資料回來的過程,不是阻塞IO的則傳回
-EAGAIN
,阻塞IO的話則等待讀等待隊列的喚醒。
第二部分就是根據使用者空間要讀取的資料和已經讀取到資料的數量,進行發起實際的IO操作。
看一下實際發起IO操作的函數
skel_do_read_io()
,也是填充
urb
并送出給控制器,以及私有的資料初始化。
以及讀回調函數
skel_read_bulk_callback()
,與寫回掉函數的差異多了實際接收長度的記錄以及喚醒等待隊列。
flush
至于這個
skel_flush()
函數,主要就是調用
skel_draw_down()
進行停止相關的傳輸,如果有錯誤的話将錯誤傳回。
skel_draw_down()
函數等待以及送出的寫
urb
傳回,如果逾時則停止;同時停止讀的
urb
。
至此整個代碼就基本分析完成了,關于控制器驅動部分,比較複雜,有機會再寫些文檔分析一下,其實這個代碼我進行修改來适配從端的驅動,目前從端的驅動也有修改,主要作用是将
MMZ
碼流從一端傳輸到另一端,中間的優化過程就是上面的省略拷貝的過程。