本文轉自公眾號,歡迎關注
基于DWC2的USB驅動開發-UVC的單元和終端類請求驅動代碼優化 (qq.com)
前言
前面介紹了UVC的處理單元和相機終端,可以看到各單元和終端的請求形式都是一樣的, 只是支持的CS和操作類型不一樣,數據的大小不一樣。目前的驅動是基于層層switch去解析的,這樣代碼冗余非常大,每一個接口的每一個單元和終端的每一個CS的每一個操作類型都要單獨添加代碼處理,而且這些處理應該位于類相關代碼中,對用戶不可見,用戶只需要關心具體的值的獲取與更新。而現在的處理方式做不到,現在用戶必須去修改這部分代碼才能獲取和更新值,并且添加修改接口,終端和單元就需要修改代碼。所以有必要對驅動進行優化,下面介紹優化細節。
https://mp.weixin.qq.com/s/4CTR1yjUmBsHqZPLEC7BhA
數據抽象
前面我們可以看到,單元和終端請求及其數據都是類似的,變化的是接口號,單元和終端號,CS,操作類型,值的大小,所以對這些進行抽象,抽象為數據結構,對應的變量即數據結構的成員變量。
首先對CS對應的值空間分配進行抽象
規格書P173中類相關的請求,對應如下,即對應不同的數據類型,其中SET只有一個,其他都是GET,暫時不考慮ALL的操作,對于ALL的操作預留回調接口到時單獨實現,目前基本是不會用到ALL相關的操作的。
那么我們需要如下的值空間,共7個值
CUR 當前值
MIN 最小值
MAX 最大值
RES 分辨率
LEN 數據長度
INFO 信息
DEF 默認值
最直接的分配空間的方法是以上7個類型各存一份,可連續放在一起。
但是不是所有的CS都支持這些值,所以為了減少空間占用,我們只分配支持的,不支持的不需要分配空間。并且我們還需要知道每個值的長度,
于是一個CS需要一片空間,這一片空間再分配給具體的某個類型,這樣每個類型還需要一個長度信息,于是抽象出如下結構體
/**
* struct usbd_uvc_utcs_t
* UVC類的終端和單元對應CS的值,所有操作類型的值空間分配
*/
typedef struct
{
uint8_t cs; /**< Control Selector Codes */
uint8_t* buffer; /**< 數據緩沖區 */
uint8_t len[7]; /**< [CUR MIN MAX RES LEN INFO DEF]的順序,索引0對應CUR */
} usbd_uvc_utcs_t;
Cs即為對應的CS
Buffer即分配給該CS下所有類型值的空間
Len[7]對應7個類型,每個類型的長度,長度為0表示該類型不支持。
那么怎么知道該類型在buffer中的偏移呢,可以通過前面所有len累計來計算,
比如MAX在CUR和MIN后面,其偏移就是len[0]+len[1],
并且還可以用GET_XXX的值是連續遞增的特點來計算,比如GET_MAX=GET_CUR+2,
所以前面有2個值,所以計算偏移循環計算前面2個即可。
以上對小的顆粒CS的數據空間進行了抽象,再往上一層,一個單元和終端支持多個CS,但是不一定所有的都會支持,所以需要再將支持的CS進行組合
抽象出如下結構體
typedef void (*uvc_ut_set_pf)(void* data, uint32_t len); /**< SET回調函數 */
typedef void (*uvc_ut_get_pf)(void* data, uint32_t len); /**< GET回調函數 */
/**
* struct usbd_uvc_utcr_t
* UVC類的終端和單元控制請求結構體
* Unit and Terminal Control Requests
*/
typedef struct usbd_uvc_utcr
{
uint8_t itf; /**< 位于的接口 */
uint8_t id; /**< 終端和單元ID */
uint8_t csnum; /**< 終端和單元的CS個數 */
usbd_uvc_utcs_t* cs; /**< 終端和單元CS對應的值空間 */
uvc_ut_set_pf set_cb; /**< SET回調 */
uvc_ut_get_pf get_cb; /**< GET回調 */
struct usbd_uvc_utcr* next; /**< 指向下一個終端或單元 */
} usbd_uvc_utcr_t;
用戶初始化
這樣用戶只需要分配上述空間,并注冊回調函數,實現回調函數即可
usbd_uvc_utcr_t s_uvc_utcr2_t=
{
0, /**< 接口0 */
2, /**< ID2 */
sizeof(s_uvc_utcs2_t)/sizeof(s_uvc_utcs2_t[0]), /**< 總共19個屬性 */
.cs=s_uvc_utcs2_t, /**< 終端和端元CS及其值 */
.set_cb=0, /**< set回調 */
.get_cb=0, /**< get回調 */
.next=0, /**< usbd_uvc_reg_utcr時自動鏈接 */
};
如下注冊即可 usbd_uvc_reg_utcr(&s_uvc_utcr2_t);
對應s_uvc_utcs2_t一行對應一個CS,我們根據手冊的描述來設置
以PU_BACKLIGHT_COMPENSATION_CONTROL為例
該CS支持的類型有CUR,MIN,MAX,RES,INFO,DEF除了LEN都支持
對應長度分別時{2,2,2,2,0,1,2}總長為11,所以需要11字節緩沖區
uint8_t s_backlight_buffer_au8[11];
/** 處理單元
* len[CUR MIN MAX RES LEN INFO DEF]
*/
usbd_uvc_utcs_t s_uvc_utcs2_t[]=
{
{
.cs = PU_BACKLIGHT_COMPENSATION_CONTROL,
.buffer = s_backlight_buffer_au8,
{2,2,2,2,0,1,2},
},
}
以上就完成了初始化,緩沖區可以動態分配也可以靜態分配。
注冊過程
如下將每個終端和的單元的結構體通過單向鏈表鏈接起來,usb_uvc為類結構體全局變量,屬于驅動部分這里不講,后面會講到。
/**
* @fn int usbd_uvc_reg_utcr(usbd_uvc_utcr_t* item)
* 注冊類相關單元和終端控制請求
* @param[in] item ref usbd_uvc_utcr_t
* @retval 0:成功.
* @retval !=0:其他值失敗.
*/
int usbd_uvc_reg_utcr(usbd_uvc_utcr_t* item)
{
if(item == (usbd_uvc_utcr_t*)0)
{
return -1;
}
if(usbd_uvc.utcr_list == (void*)0)
{
/* 當前鏈表為空,直接添加到頭
* 設置Next為空
*/
item- >next = (usbd_uvc_utcr_t*)0;
usbd_uvc.utcr_list = item;
}
else
{
/* 當前鏈表不為空,插入到頭
* 設置Next為之前的頭
*/
item- >next = (usbd_uvc_utcr_t*)(usbd_uvc.utcr_list);
usbd_uvc.utcr_list = item;
}
return 0;
}
處理過程
可以簡單了很多,且支持任意的接口,終端單元,CS和類型的配置。
static void uvc_class_ut_req(dwc_handle *dwc, ureq_t setup)
{
/* 搜尋終端和處理單元號 */
usbd_uvc_utcr_t* p = 0;
usb_class_def* c_p;
int itf;
int id;
int cs;
int req;
int off = 0;
int len = 0;
int getsetlen = 0;
itf = setup- >wIndex & 0xFF;
/* 根據接口號查找接口類 */
c_p = dwc- >pclass_cb;
int getitf=0;
while(c_p != 0)
{
for(int i=0; i< 8; i++)
{
if(c_p- >itfs[i] == 0xFF)
{
break;
}
if(c_p- >itfs[i] == itf)
{
getitf = 1;
break;
}
}
if(getitf != 0)
{
p = c_p- >utcr_list;
break;
}
c_p= c_p- >next;
}
if(p == 0)
{
/* 沒有找到接口對應的類 */
USBD_UVC_WARN(("unknow itf:%drn",itf));
return;
}
id = (setup- >wIndex > > 8) & 0xFF;
cs = (setup- >wValue > > 8) & 0xFF;
req = setup- >bRequest;
#if 0
if((req & 0xF0) == 0x90)
{
/* GET_XXX_ALL*/
usb_ep0_set_stall(dwc); /* 暫時不支持 后面再實現 */
return;
}
else if((req & 0xF0) == 0x10)
{
/* SET_CUR_ALL */
usb_ep0_set_stall(dwc); /* 暫時不支持 后面再實現 */
return;
}
else
{
/* 其他的支持 */
}
#endif
while(p != (usbd_uvc_utcr_t*)0)
{
if((p- >itf == itf) && (p- >id == id))
{
for(int i=0; i< p- >csnum; i++)
{
if(cs == p- >cs[i].cs)
{
/* 如果長度為0說明不支持,直接STALL返回 */
len = p- >cs[i].len[(req&0x0F) - 1];
if(len == 0)
{
usb_ep0_set_stall(dwc);
USBD_UVC_WARN(("NOT SUPPORT ITF:%d ID:%d CS:%d REQ%drn",itf,id,cs,req));
return;
}
getsetlen = setup- >wLength > len ? len : setup- >wLength;
/* 計算偏移值 */
off = 0;
for(int j=0; j< ((req&0x0F) - 1); j++)
{
off += p- >cs[i].len[j];
}
/* 找到CS
* INFO CUR MIN MAX RES DEF LEN
*/
switch(req)
{
case GET_INFO:
case GET_CUR:
case GET_MIN:
case GET_MAX:
case GET_RES:
case GET_DEF:
case GET_LEN:
if(p- >get_cb != 0)
{
p- >get_cb(0,0);
}
usb_ep0_write(dwc, p- >cs[i].buffer + off, getsetlen);
USBD_UVC_LOG(("ITF:%d ID:%d GET CS:%d REQ:%x OF:%d LEN:%drn",itf,id,cs,req,off,getsetlen));
break;
case SET_CUR:
if(p- >set_cb != 0)
{
p- >set_cb(0,0);
}
usb_ep0_read(dwc, p- >cs[i].buffer + off, getsetlen, 0);
USBD_UVC_LOG(("%d %d SET %d %d %d %drn",itf,id,cs,req,off,getsetlen));
break;
default:
usb_ep0_set_stall(dwc);
break;
}
return;
}
}
}
p = p- >next;
}
/* 未找到匹配項則STALL */
usb_ep0_set_stall(dwc);
}
對比之前的設計,
總結
以上實現了終端和單元請求相關驅動的代碼,實現可分層設計,可擴展具備可移植性。后面再考慮實現ALL相關的操作。
審核編輯:湯梓紅
-
usb
+關注
關注
60文章
7936瀏覽量
264474 -
程序
+關注
關注
117文章
3785瀏覽量
81003 -
代碼
+關注
關注
30文章
4779瀏覽量
68521 -
USB驅動
+關注
關注
1文章
136瀏覽量
20191 -
驅動開發
+關注
關注
0文章
130瀏覽量
12072 -
uvc
+關注
關注
1文章
127瀏覽量
14527 -
DWC2
+關注
關注
0文章
35瀏覽量
125
發布評論請先 登錄
相關推薦
評論