1.描述符布局

如圖為 bulk 傳輸描述符布局,相對于同步傳輸,批量傳輸只有一個(gè)可選擇的配置,沒有備用配置。
VideoControl :無變化
VideoStream:只有一個(gè) bAlternateSetting(刪除alt=1描述符)。同時(shí)支持bulk in 端點(diǎn)。
需要修改的地方:
staticstructusb_interface_descriptoruvc_streaming_intf_alt0={ .bLength=USB_DT_INTERFACE_SIZE, .bDescriptorType=USB_DT_INTERFACE, .bInterfaceNumber=UVC_INTF_VIDEO_STREAMING, .bAlternateSetting=0, .bNumEndpoints=1,/*alt0掛一個(gè)bulk端點(diǎn)*/ .bInterfaceClass=USB_CLASS_VIDEO, .bInterfaceSubClass=UVC_SC_VIDEOSTREAMING, .bInterfaceProtocol=0x00, .iInterface=0, };
端點(diǎn)描述符:
staticstructusb_endpoint_descriptoruvc_hs_streaming_ep={
.bLength=USB_DT_ENDPOINT_SIZE,
.bDescriptorType=USB_DT_ENDPOINT,
.bEndpointAddress=USB_DIR_IN,
.bmAttributes=USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize=512,
.bInterval=0,
};
2. 控制流程
根據(jù)USB規(guī)范可知,同步傳輸方式是只要帶中帶有同步端點(diǎn)的接口,系統(tǒng)會定時(shí)從設(shè)備中讀取數(shù)據(jù),無論設(shè)備中是否有數(shù)據(jù)。而如要停止數(shù)據(jù)的傳輸,只需要選中不帶有同步端點(diǎn)的接口即可。
USB同步傳輸這種靈活的數(shù)據(jù)傳輸方式是依靠視頻流接口的轉(zhuǎn)換接口即我們常說的備份接口實(shí)現(xiàn)的。在默認(rèn)情況下數(shù)據(jù)不傳輸時(shí),視頻數(shù)據(jù)流接口和備份接口ID為0,其它的備份接口是可根據(jù)視頻數(shù)據(jù)傳輸?shù)拇笮】砂葱柽x擇。
我們知道,批量傳輸只有一個(gè)可選擇的altsetting ,那么如何知道設(shè)備控制設(shè)備的開流和關(guān)流動(dòng)作呢?
2.1 stream on
使用視頻流接口的VS_COMMIT_CONTROL 提交給設(shè)備,讓其以指定的數(shù)據(jù)格式進(jìn)行數(shù)據(jù)采樣。

2.2 stream off
關(guān)流操作,通過抓包可以看到,通過發(fā)送一個(gè)clear_halt 請求,來中斷流的操作。

2.3 代碼分析
基于 linux 4.14.281 內(nèi)核版本:分析host 端uvc 開關(guān)流流程
drivers/media/usb/uvc/uvc_queue.c
開流操作:uvc_start_streaming
staticintuvc_start_streaming(structvb2_queue*vq,unsignedintcount)
{
structuvc_video_queue*queue=vb2_get_drv_priv(vq);
structuvc_streaming*stream=uvc_queue_to_stream(queue);
unsignedlongflags;
intret;
queue->buf_used=0;
ret=uvc_video_enable(stream,1);
if(ret==0)
return0;
spin_lock_irqsave(&queue->irqlock,flags);
uvc_queue_return_buffers(queue,UVC_BUF_STATE_QUEUED);
spin_unlock_irqrestore(&queue->irqlock,flags);
returnret;
}
關(guān)流操作:uvc_stop_streaming
staticvoiduvc_stop_streaming(structvb2_queue*vq)
{
structuvc_video_queue*queue=vb2_get_drv_priv(vq);
structuvc_streaming*stream=uvc_queue_to_stream(queue);
unsignedlongflags;
uvc_video_enable(stream,0);
spin_lock_irqsave(&queue->irqlock,flags);
uvc_queue_return_buffers(queue,UVC_BUF_STATE_ERROR);
spin_unlock_irqrestore(&queue->irqlock,flags);
}
重點(diǎn)關(guān)注:uvc_video_enable
/*
*Enableordisablethevideostream.
*/
intuvc_video_enable(structuvc_streaming*stream,intenable)
{
intret;
if(!enable){
uvc_uninit_video(stream,1);
if(stream->intf->num_altsetting>1){
usb_set_interface(stream->dev->udev,
stream->intfnum,0);
}else{
/*UVCdoesn'tspecifyhowtoinformabulk-baseddevice
*whenthevideostreamisstopped.Windowssendsa
*CLEAR_FEATURE(HALT)requesttothevideostreaming
*bulkendpoint,mimicthesamebehaviour.
*/
unsignedintepnum=stream->header.bEndpointAddress
&USB_ENDPOINT_NUMBER_MASK;
unsignedintdir=stream->header.bEndpointAddress
&USB_ENDPOINT_DIR_MASK;
unsignedintpipe;
pipe=usb_sndbulkpipe(stream->dev->udev,epnum)|dir;
usb_clear_halt(stream->dev->udev,pipe);
}
uvc_video_clock_cleanup(stream);
return0;
}
ret=uvc_video_clock_init(stream);
if(ret0)
??return?ret;
?/*?Commit?the?streaming?parameters.?*/
?ret?=?uvc_commit_video(stream,?&stream->ctrl);
if(ret0)
??goto?error_commit;
?ret?=?uvc_init_video(stream,?GFP_KERNEL);
?if?(ret?0)
??goto?error_video;
?return?0;
error_video:
?usb_set_interface(stream->dev->udev,stream->intfnum,0);
error_commit:
uvc_video_clock_cleanup(stream);
returnret;
}
分析代碼可知:
首先判斷是否關(guān)流操作;
如果是,判斷接口的可選配置是否大于1,如果大于1,發(fā)送usb_set_interface(intfnum,0) 關(guān)流,否則發(fā)送usb_clear_halt 請求;
如果是開流操作,發(fā)送commit 請求
然后初始化 video
/*
*Initializeisochronous/bulkURBsandallocatetransferbuffers.
*/
staticintuvc_init_video(structuvc_streaming*stream,gfp_tgfp_flags)
{
structusb_interface*intf=stream->intf;
structusb_host_endpoint*ep;
unsignedinti;
intret;
stream->sequence=-1;
stream->last_fid=-1;
stream->bulk.header_size=0;
stream->bulk.skip_payload=0;
stream->bulk.payload_size=0;
uvc_video_stats_start(stream);
if(intf->num_altsetting>1){
structusb_host_endpoint*best_ep=NULL;
unsignedintbest_psize=UINT_MAX;
unsignedintbandwidth;
unsignedintuninitialized_var(altsetting);
intintfnum=stream->intfnum;
/*Isochronousendpoint,selectthealternatesetting.*/
bandwidth=stream->ctrl.dwMaxPayloadTransferSize;
if(bandwidth==0){
uvc_trace(UVC_TRACE_VIDEO,"Devicerequestednull"
"bandwidth,defaultingtolowest.
");
bandwidth=1;
}else{
uvc_trace(UVC_TRACE_VIDEO,"Devicerequested%u"
"B/framebandwidth.
",bandwidth);
}
for(i=0;inum_altsetting;++i){
structusb_host_interface*alts;
unsignedintpsize;
alts=&intf->altsetting[i];
ep=uvc_find_endpoint(alts,
stream->header.bEndpointAddress);
if(ep==NULL)
continue;
/*Checkifthebandwidthishighenough.*/
psize=uvc_endpoint_max_bpi(stream->dev->udev,ep);
if(psize>=bandwidth&&psize<=?best_psize)?{
????altsetting?=?alts->desc.bAlternateSetting;
best_psize=psize;
best_ep=ep;
}
}
if(best_ep==NULL){
uvc_trace(UVC_TRACE_VIDEO,"Nofastenoughaltsetting"
"forrequestedbandwidth.
");
return-EIO;
}
uvc_trace(UVC_TRACE_VIDEO,"Selectingalternatesetting%u"
"(%uB/framebandwidth).
",altsetting,best_psize);
ret=usb_set_interface(stream->dev->udev,intfnum,altsetting);
if(ret0)
???return?ret;
??ret?=?uvc_init_video_isoc(stream,?best_ep,?gfp_flags);
?}?else?{
??/*?Bulk?endpoint,?proceed?to?URB?initialization.?*/
??ep?=?uvc_find_endpoint(&intf->altsetting[0],
stream->header.bEndpointAddress);
if(ep==NULL)
return-EIO;
/*Rejectbrokendescriptors.*/
if(usb_endpoint_maxp(&ep->desc)==0)
return-EIO;
ret=uvc_init_video_bulk(stream,ep,gfp_flags);
}
if(ret0)
??return?ret;
?/*?Submit?the?URBs.?*/
?for?(i?=?0;?i?urb[i],gfp_flags);
if(ret0)?{
???uvc_printk(KERN_ERR,?"Failed?to?submit?URB?%u?"
?????"(%d).
",?i,?ret);
???uvc_uninit_video(stream,?1);
???return?ret;
??}
?}
?/*?The?Logitech?C920?temporarily?forgets?that?it?should?not?be?adjusting
??*?Exposure?Absolute?during?init?so?restore?controls?to?stored?values.
??*/
?if?(stream->dev->quirks&UVC_QUIRK_RESTORE_CTRLS_ON_INIT)
uvc_ctrl_restore_values(stream->dev);
return0;
}
從這段代碼可以看出,如果altsetting 大于1 走同步傳輸,發(fā)送usb_set_interface(intfnum, altsetting) ,選擇合適帶寬配置。然后初始化同步傳輸管道。
否則,初始化 同步傳輸管道,提交傳輸。
3. 其他注意點(diǎn)
對比同步傳輸和批量傳輸我們可以發(fā)現(xiàn),對于uvc 批量傳輸, 由于沒有同步傳輸類似的多個(gè)可選配置,所以沒法靈活控制開流關(guān)流操作。特別是在linux 平臺下,要切換不同的格式和分辨率的時(shí)候沒有同步傳輸方便。
故,筆者覺得同步傳輸適合傳固定數(shù)據(jù),或者對usb camera 做中轉(zhuǎn)使用比較合適。
對于批量傳輸如果能充分發(fā)送usb 吞吐量,(USB2.0)一個(gè)微幀傳輸13個(gè)packet,理論帶寬將近50MB/s, 筆者實(shí)際測試能達(dá)到47MB/s,對于YUYV圖像能夠極大提高幀率。
-
接口
+關(guān)注
關(guān)注
33文章
9356瀏覽量
155828 -
數(shù)據(jù)傳輸
+關(guān)注
關(guān)注
9文章
2056瀏覽量
67093 -
控制設(shè)備
+關(guān)注
關(guān)注
0文章
145瀏覽量
11385
原文標(biāo)題:UVC 批量傳輸技術(shù)探討
文章出處:【微信號:漫談嵌入式,微信公眾號:漫談嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
淺談剩余電流動(dòng)作繼電器的在低壓配電系統(tǒng)中的應(yīng)用
求如何用NI 6002 的電壓輸出通道控制外圍設(shè)備的動(dòng)作,如控制24V繼電器開合!
請問如何控制DAQ助手的開和關(guān)
轉(zhuǎn)帖 剩余電流動(dòng)作保護(hù)器(RCD)的分類、作用、以及接線方式!
剩余電流動(dòng)作繼電器的應(yīng)用探討
開/關(guān)溫度控制模擬開源分享
如何控制IoT ONE Nixie時(shí)鐘背光-開/關(guān)
如何控制IoT ONE Nixie時(shí)鐘背光-開/關(guān)和顏色
單按鈕開/關(guān)電源控制

如何知道設(shè)備控制設(shè)備的開流和關(guān)流動(dòng)作
評論