首页 > 技术知识 > 正文

1.描述符布局

如何知道设备控制设备的开流和关流动作-设备控制器的定义

如图为 bulk 传输描述符布局,相对于同步传输,批量传输只有一个可选择的配置,没有备用配置。

VideoControl :无变化

VideoStream:只有一个 bAlternateSetting(删除alt=1描述符)。同时支持bulk in 端点。

需要修改的地方:

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挂一个bulk端点*/ .bInterfaceClass=USB_CLASS_VIDEO, .bInterfaceSubClass=UVC_SC_VIDEOSTREAMING, .bInterfaceProtocol=0x00, .iInterface=0, };

端点描述符:

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. 控制流程

根据USB规范可知,同步传输方式是只要带中带有同步端点的接口,系统会定时从设备中读取数据,无论设备中是否有数据。而如要停止数据的传输,只需要选中不带有同步端点的接口即可。

USB同步传输这种灵活的数据传输方式是依靠视频流接口的转换接口即我们常说的备份接口实现的。在默认情况下数据不传输时,视频数据流接口和备份接口ID为0,其它的备份接口是可根据视频数据传输的大小可按需选择。

我们知道,批量传输只有一个可选择的altsetting ,那么如何知道设备控制设备的开流和关流动作呢?

2.1 stream on

使用视频流接口的VS_COMMIT_CONTROL 提交给设备,让其以指定的数据格式进行数据采样。

如何知道设备控制设备的开流和关流动作-设备控制器的定义1

2.2 stream off

关流操作,通过抓包可以看到,通过发送一个clear_halt 请求,来中断流的操作。

如何知道设备控制设备的开流和关流动作-设备控制器的定义2

2.3 代码分析

基于 linux 4.14.281 内核版本:分析host 端uvc 开关流流程

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; }

关流操作: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); }

重点关注: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{ /*UVCdoesntspecifyhowtoinformabulk-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(ret< 0)   return ret;  /* Commit the streaming parameters. */  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; }

分析代码可知:

首先判断是否关流操作;

如果是,判断接口的可选配置是否大于1,如果大于1,发送usb_set_interface(intfnum,0) 关流,否则发送usb_clear_halt 请求;

如果是开流操作,发送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;i< intf->num_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(ret< 0)    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(ret< 0)   return ret;  /* Submit the URBs. */  for (i = 0; i < UVC_URBS; ++i) {   ret = usb_submit_urb(stream->urb[i],gfp_flags); if(ret< 0) {    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 走同步传输,发送usb_set_interface(intfnum, altsetting) ,选择合适带宽配置。然后初始化同步传输管道。

否则,初始化 同步传输管道,提交传输。

3. 其他注意点

对比同步传输和批量传输我们可以发现,对于uvc 批量传输, 由于没有同步传输类似的多个可选配置,所以没法灵活控制开流关流操作。特别是在linux 平台下,要切换不同的格式和分辨率的时候没有同步传输方便。

故,笔者觉得同步传输适合传固定数据,或者对usb camera 做中转使用比较合适。

对于批量传输如果能充分发送usb 吞吐量,(USB2.0)一个微帧传输13个packet,理论带宽将近50MB/s, 笔者实际测试能达到47MB/s,对于YUYV图像能够极大提高帧率。

审核编辑:彭静

猜你喜欢