前段时间有个读者咨询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(ret< 0) 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文章
8550浏览量
150960 -
usb
+关注
关注
60文章
7923浏览量
264266 -
Bulk
+关注
关注
0文章
8浏览量
8646 -
uvc
+关注
关注
1文章
127浏览量
14519
原文标题:UVC Bulk 传输实现细节
文章出处:【微信号:漫谈嵌入式,微信公众号:漫谈嵌入式】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论