前段時間有個讀者咨詢UVC bulk 傳輸實現,接著這個機會重新梳理一遍UVC bulk 傳輸實現思路,同時對比ISO 與 Bulk 實現不同。有關bulk 傳輸實現請先看前文
閱讀此文前提:假設讀者,已知曉UVC 協議,以及UVC 設備驅動框架
1. 描述符布局
1.1 ISO 傳輸
ISO 傳輸模式下的描述符布局如上圖所示:
一個配置
兩個接口:Video Control / Video Stream
接口0:VideoControl。處理 UVC CT/PU/XU 等處理,主要用來控制接口1:VideoStream。處理UVC 視頻流控制。
一個VideoControl 可以關聯一個或者多個流接口。每個流接口下面,關聯2個多或者多個接口配置。
對于ISO 傳輸,流接口下有多個備用接口,用來控制視頻流的傳輸。一般用alt0 關流,altx 開流。
1.2 BULK 傳輸
BULK 傳輸模式下的描述符布局如上圖所示:
一個配置
兩個接口:Video Control / Video Stream
接口0:VideoControl。處理 UVC CT/PU/XU 等處理,主要用來控制接口1:VideoStream。處理UVC 視頻流控制。
對于BULK 傳輸, 與ISO 模式最大的不同,是流接口只有一個alt0 接口配置。故,對其視頻流的控制流程,相比較ISO 模式比較復雜一些。
2. 控制流程
2.1 ISO 傳輸
根據USB規范可知,同步傳輸方式是只要選中中帶有同步端點的接口,系統會定時從設備中讀取數據,無論設備中是否有數據。而如要停止數據的傳輸,只需要選中不帶有同步端點的接口即可。
USB同步傳輸這種靈活的數據傳輸方式是依靠視頻流接口的轉換接口即我們常說的備份接口實現的。
Stream ON:
Stream OFF:
整個視頻流的控制流程(開流/關流),可以在設備收到set_alt1/0 后進行處理。
2.2 BULK 傳輸
從描述符上分析 UVC 的 bulk 傳輸只有一個備用接口。故無法通過ISO 模式下通過備用接口進行開關流控制。
StreamOn:
StreamOff
通過抓包對比發現,開流過程通過uvc probe 與commit 流程處理。關流過程主機會下發一個clear_halt 請求。在clear_halt 請求里面做關流的后處理。
通過對比發現,對于ISO 傳輸,針對UVC 的設備描述修改基本不大,難處理的是UVC 的控制流程,特別是視頻流的處理,接下來我們會以linux 平臺介紹如何在iso 基礎上修改為 bulk 模式
3. 源碼分析
我們以linux 平臺舉例,其他平臺只要熟悉uvc 底層協議,即可快速遷移。
3.1 host 端
源碼位置:driver/media/uvc/uvc_queue.c
uvc 使能:
intuvc_video_enable(structuvc_streaming*stream,intenable) { intret; if(!enable){ ... 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. */ ...... usb_clear_halt(stream->dev->udev,pipe); } ...... return0; } /*Committhestreamingparameters.*/ 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; }
StreamOn:
uvc_start_streaming()// ret=uvc_video_enable(stream,1);
StreamOff:
uvc_stop_streaming(); uvc_video_enable(stream,0);
從host 端源碼分析和我們猜想得到了印證:
開流發送commit 請求
關流發送clear_halt 請求
視頻傳輸:源碼參考:driver/media/usb/uvc_video.c
//usbcomplete中斷 staticvoiduvc_video_complete(structurb*urb) { structuvc_streaming*stream=urb->context; structuvc_video_queue*queue=&stream->queue; structuvc_buffer*buf=NULL; unsignedlongflags; intret; switch(urb->status){ case0: break; default: uvc_printk(KERN_WARNING,"Non-zerostatus(%d)invideo" "completionhandler. ",urb->status); /*fallthrough*/ case-ENOENT:/*usb_kill_urb()called.*/ if(stream->frozen) return; /*fallthrough*/ case-ECONNRESET:/*usb_unlink_urb()called.*/ case-ESHUTDOWN:/*Theendpointisbeingdisabled.*/ uvc_queue_cancel(queue,urb->status==-ESHUTDOWN); return; } spin_lock_irqsave(&queue->irqlock,flags); if(!list_empty(&queue->irqqueue)) buf=list_first_entry(&queue->irqqueue,structuvc_buffer, queue); spin_unlock_irqrestore(&queue->irqlock,flags); /*視頻解碼:視頻傳輸關鍵*/ stream->decode(urb,stream,buf); /*提交urb*/ if((ret=usb_submit_urb(urb,GFP_ATOMIC))0)?{ ??uvc_printk(KERN_ERR,?"Failed?to?resubmit?video?URB?(%d). ", ???ret); ?} }
host 端視頻數據解碼:
stream->decode(urb,stream,buf); uvc_video_decode_bulk(); uvc_video_decode_start();//解析uvcheader uvc_video_decode_data();//解析視頻數據 uvc_video_decode_end();//幀結束標記
3.2 device 端
源碼位置:driver/usb/gadget/function
ISO 開關流
staticintuvc_function_set_alt(structusb_function*f,unsignedinterface,unsignedalt) { ....... /*VideoControlprocess*/ if(interface==uvc->control_intf){ /*復位控制端點*/ usb_ep_disable(uvc->control_ep); ... usb_ep_enable(uvc->control_ep); if(uvc->state==UVC_STATE_DISCONNECTED){ /*提交uvc狀態*/ memset(&v4l2_event,0,sizof(v4l2_evetn); v4l2_event.type=UVC_EVENT_CONNECT; uvc_event->speed=cdev->gadget->speed; v4l2_event_queue(&uvc->vdev,&v4l2_event); uvc->state=UVC_STATE_CONNECTED; } return0; } /*VideoStreamprocess*/ if(interface!=uvc->streaming_intf){ return-EINVAL; } /*判斷端點是否為bulk 端點:*/ if(usb_endpoint_xfer_bulk(&uvc->desc.vs_ep)){ /*使能端點*/ usb_ep_enable(uvc->video_ep); returnalt?-EIVAL:0; } /*ISO開關流處理*/ switch(alt){ case0: if(uvc->state!=UVC_STATE_STREAMING) return0; if(uvc->video.ep) usb_ep_disable(uvc->video.ep); /*提交應用:關流*/ memset(&v4l2_event,0,sizeof(v4l2_event)); v4l2_event.type=UVC_EVENT_STREAMOFF; v4l2_event_queue(&uvc->vdev,&v4l2_event); uvc->state=UVC_STATE_CONNECTED; return0; case1: if(uvc->state!=UVC_STATE_CONNECTED) return0; if(!uvc->video.ep) return-EINVAL; INFO(cdev,"resetUVC "); usb_ep_disable(uvc->video.ep); ret=config_ep_by_speed(f->config->cdev->gadget, &(uvc->func),uvc->video.ep); if(ret) returnret; usb_ep_enable(uvc->video.ep); /*開流*/ memset(&v4l2_event,0,sizeof(v4l2_event)); v4l2_event.type=UVC_EVENT_STREAMON; v4l2_event_queue(&uvc->vdev,&v4l2_event); returnUSB_GADGET_DELAYED_STATUS; default: return-EINVAL; } }
bulk 傳輸開關流
uvc_function_setup(structusb_function*f,conststructusb_ctrlrequest*ctrl) { structuvc_device*uvc=to_uvc(f); structv4l2_eventv4l2_event; structuvc_event*uvc_event=(void*)&v4l2_event.u.data; /*printk(KERN_INFO"setuprequest%02x%02xvalue%04xindex%04x%04x ", *ctrl->bRequestType,ctrl->bRequest,le16_to_cpu(ctrl->wValue), *le16_to_cpu(ctrl->wIndex),le16_to_cpu(ctrl->wLength)); */ if((ctrl->bRequestType&USB_TYPE_MASK)!=USB_TYPE_CLASS){ /* bulk 傳輸關流:需要修改udc 控制器,將usb 控制器clear_halt 注冊到uvc function 驅動里面*/ if(usb_endpoint_xfer_bulk(&uvc->desc.vs_ep)){ memset(&v4l2_event,0,sizeof(v4l2_event)); v4l2_event.type=UVC_EVENT_STREANOFF; memcpy(&uvc_event->req,ctrl,sizeof(uvc_event->req)); v4l2_event_queue(&uvc->vdev,&v4l2_event); }else{ INFO(f->config->cdev,"invalidrequesttype "); return-EINVAL; } } /*Stalltoobigrequests.*/ if(le16_to_cpu(ctrl->wLength)>UVC_MAX_REQUEST_SIZE) return-EINVAL; /*Tellthecompletecallbacktogenerateaneventforthenextrequest *thatwillbeenqueuedbyUVCIOC_SEND_RESPONSE. */ uvc->event_setup_out=!(ctrl->bRequestType&USB_DIR_IN); uvc->event_length=le16_to_cpu(ctrl->wLength); /* bulk 傳輸開流:通過uvc probe 和 commit 提交分辨率和幀率控制*/ memset(&v4l2_event,0,sizeof(v4l2_event)); v4l2_event.type=UVC_EVENT_SETUP; memcpy(&uvc_event->req,ctrl,sizeof(uvc_event->req)); v4l2_event_queue(&uvc->vdev,&v4l2_event); return0; }
4. 總結
整個bulk 傳輸控制流程如上圖所示。
對于linux 平臺而言,需要關心的有兩點:1)如何將底層clear_halt 請求 與uvc_function 請求關聯上,而不影響其他端點;2)如何將clear_halt 請求提交到應用層去處理。這一步不是非必須的。
對于Rtos 平臺大同小異,只要理解了整個開關流流程,處理起來自然簡單許多。
-
接口
+關注
關注
33文章
8497瀏覽量
150834 -
usb
+關注
關注
60文章
7895瀏覽量
263984 -
Bulk
+關注
關注
0文章
8瀏覽量
8643 -
uvc
+關注
關注
1文章
126瀏覽量
14506
原文標題:UVC Bulk 傳輸實現細節
文章出處:【微信號:漫談嵌入式,微信公眾號:漫談嵌入式】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論