SylixOS USB Gadget層介紹

1. Gadget層介紹

由於SylixOS中沒有Platform的概念,所以在筆者眼中Gadget層兼顧了Platform的功能,實現了UDC設備層和驅動層的連接。

Gadget層結構以及函數調用過於複雜,無法對每個函數做一一介紹,在此會抽調一些重要的函數以及結構體簡單介紹。具體綁定傳輸流程還需讀者花時間精力慢慢理順。

1.1      上層函數操作集

如圖 1‑1所示,在上層函數操作集中包含了4個重要的結構,這些結構體中包含大量的回調函數來獲取底層信息,建立連接(此層的主要代碼在composite.c文件中)。這些函數中以下幾個尤其重要:

1.      composite_bind函數

Bind函數主要完成UDC驅動層Gadget設備和設備層composite設備關係的建立,分配控制端點0的請求數據結構,設置設備描述符,並調用功能層的bind函數分配功能層所需要的資源等工作。

2.      composite_unbind函數

Unbind函數完成bind函數中分配的資源,並遍歷所有配置和各個配置下的功能,調用其對應的unbind函數來釋放資源。

3.      usb_add_function函數

add_function在配置中增加一個功能,每個配置初始化後都必須至少有一個功能。這個函數會在多個地方調用,使用需特別注意。

4.      composite_setup函數

setup函數完成了ep0所需要處理的而底層不能處理的功能,由於UDC層實現了這個函數,所以其他層就不需要關注於USB協議的這些事務性處理,而重點放在需要實現的功能上。

圖 1‑1 Gadget層需獲取的函數集合

1.2      底層函數操作集

如圖 1‑1所示,在底層操作函數操作集中大部分都是提供給上層的回調函數接口。其中有兩個結構體,幾乎貫穿整個USB虛擬網卡,詳細介紹如下:

1.      usb_request結構體

struct usb_request {

void *buf;                                  //數據緩存區

unsigned length;                          //數據長度

dma_addr_t dma;                        //與buf關聯的DMA地址,DMA傳輸時使用

unsigned no_interrupt:1;              //當爲true時,表示沒有完成函數,則通過中斷通知傳輸完成,這個由DMA控制器直接控制

unsigned zero:1;                          //當輸出的最後的數據包不夠長度是是否填充0

unsigned short_not_ok:1;             //當接收的數據不夠指定長度時,是否報錯

void (*complete)(struct usb_ep *ep, struct usb_request *req);//請求完成函數

void *context;                             //被completion回調函數使用

struct list_head list;                      //被Gadget Driver使用,插入隊列

int status;                                    //返回完成結果,0表示成功

unsigned actual;                          //實際傳輸的數據長度

};

 

在USB傳輸的過程中所有的消息最終都是填充在一個buf中來進行傳輸的,USB爲了更好的管理傳輸的消息定義了一個usb_request結構體來描述一幀消息的信息。

2.      usb_ep_ops

struct usb_ep_ops {

int (*enable) (struct usb_ep *ep,  const struct usb_endpoint_descriptor *desc);

int (*disable) (struct usb_ep *ep);

struct usb_request *(*alloc_request) (struct usb_ep *ep, gfp_t gfp_flags);

void (*free_request) (struct usb_ep *ep, struct usb_request *req);

int (*queue) (struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags);

int (*dequeue) (struct usb_ep *ep, struct usb_request *req);

int (*set_halt) (struct usb_ep *ep, int value);

int (*set_wedge) (struct usb_ep *ep);

int (*fifo_status) (struct usb_ep *ep);

void (*fifo_flush) (struct usb_ep *ep);

};

 

結構體中有兩個重要的回調函數,一個是alloc_request函數,另一個是queue函數。

在usb虛擬網卡中所有的消息都以隊列的形式來進行傳輸的。調用alloc_request函數申請一個傳輸請求大小的空間,這個大小的值可根據實際傳輸時所需的最大空間進行配置。在每次消息傳出之前都要提前調用alloc_request函數來申請內存。在消息接收完畢或者填充完畢之後都需要調用queue函數把需要傳輸的消息加入到消息隊列中,進行傳輸。

1.3      注意事項

USB虛擬網卡移植的過程中,主要參考的是Linux代碼,SylixOS和Linux有許多區別,比如SylixOS中沒有dev的概念,也沒有platform的概念。需要改許多的代碼框架。在SylixOS中使用的是LWIP網絡協議棧,而Linux中使用的是TCP/IP協議棧。在數據傳輸的過程中LWIP使用的是pbuf來封裝數據,而Linux使用的是skbbuf,兩者的使用機制完全不一樣。類似的結構體不同還有很多,在這不一一說明。

移植USB驅動時需特別注意虛擬地址和物理地址相互轉換以及cache同步問題。USB傳輸的數據量會很大,因此會使用DMA。DMA使用的是物理地址,而CPU卻只能識別虛擬地址。在物理地址和虛擬地址轉換過程中很有可能會出現物理地址未映射成虛擬地址的情況。就算有做物理地址和虛擬地址的轉換也需要注意cache刷新時刷新的字節大小問題。如果需要刷新內存的大小和cache刷新大小字節不對齊,會出現cache刷新失敗問題。這個問題十分嚴重,會直接導致讀出的數據是髒數據,系統直接跑飛,需特別注意。按照常理來說使用cache的數據處理速度應該遠大於未使用cache,但是在SylixOS的實測中,有無cache操作速度差距不大,甚至不使用cache速度會更快。目前的USB虛擬網卡使用的是無cache版本的操作。

實際使用過程中,需要根據使用USB協議的版本號和實際速度,來配置初始化接收緩衝區個數。如果速度快而緩衝區個數過少,會出現嚴重的丟包現象,影響USB速率。

由於USB驅動中代碼回調過於複雜,本文也只是簡單提煉了一下框架和重點,如果想要完全弄清還需耐下心花時間從頭到尾按部就班追一下數據傳輸流程的代碼才行。由於作者水平有限可能會出現理解錯誤或者描述不到位的情況,請大家諒解,也請大家及時指正,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章