在某些平台设备,如果同时使用多个uvc camera进行数据采集或者是同一个USB,既用作OTG功能,又用作USB host功能,会因为USB 带宽的的限制,导致camera无法正常使用,使用失败的现象如下:
uvcvideo: Failed to submit URB 0 (-28).
No space left on device
在通过VIDIOC_STREAMON开启流数据传输时,将会产生以上错误,这个是由与USB的带宽不足导致的,出现这样的问题,可以尝试将分辨率调低,或者换为编码格式输出,如果还是不行,可以通过手动限制uvc camera的输出带宽。
uvc驱动会在注册uvc camera的时候,在注册video节点之前,查询uvc camera实际硬件的设备情况,并保存好,然后当使用的时候,设置完输出分辨率、格式等参数之后,在VIDIOC_STREAMON的时候,会根据设置的分辨率以及格式、注册video节点时查询得到的信息计算所需带宽,然后将该带宽与uvc camera实际情况比较反馈得到一个合适的带宽设置到uvc camera中。
那么如何查看uvc camera支持的带宽信息呢?先通过lsusb命令查看得到uvc camera的厂家信息,比如得到下面的信息:
Bus 001 Device 041: ID 045e:0779 Microsoft Corp. LifeCam HD-3000
得知厂家id之后,比如从上面的信息可得知usb id为0x045e,接着通过lsusb -v -d 045e: >info.txt命令,将查询uvc camera的详细信息输出到info.txt。在info.txt中,通过搜索bAlternateSetting可以看到类似以下的信息:
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 3
bNumEndpoints 1
bInterfaceClass 14 Video
bInterfaceSubClass 2 Video Streaming
bInterfaceProtocol 0
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 5
Transfer Type Isochronous
Synch Type Asynchronous
Usage Type Data
wMaxPacketSize 0x0200 1x 512 bytes
bInterval 1
Interface Descriptor:
bLength 9
bDescriptorType 4
bInterfaceNumber 1
bAlternateSetting 4
bNumEndpoints 1
bInterfaceClass 14 Video
bInterfaceSubClass 2 Video Streaming
bInterfaceProtocol 0
iInterface 0
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 5
Transfer Type Isochronous
Synch Type Asynchronous
Usage Type Data
wMaxPacketSize 0x0400 1x 1024 bytes
bInterval 1
其中的wMaxPacketSize变量说明的就是该uvc camera支持的带宽,它会支持多种带宽,以适应不同的USB传输速率。
在Linux内核中的uvc驱动就有usb带宽的匹配设置过程,代码位于drivers/media/usb/uvc下的uvc_video.c中,在uvc_init_video()函数中,就会根据所设置的分辨率以及格式等参数,计算得到的带宽与uvc camera硬件支持的带宽进行一个适配,从而设置有效参数。
/*
* Initialize isochronous/bulk URBs and allocate transfer buffers.
*/
static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
{
struct usb_interface *intf = stream->intf;
struct usb_host_endpoint *ep;
unsigned int i;
int ret;
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) {
struct usb_host_endpoint *best_ep = NULL;
unsigned int best_psize = UINT_MAX;
unsigned int bandwidth;
unsigned int uninitialized_var(altsetting);
int intfnum = stream->intfnum;
/* Isochronous endpoint, select the alternate setting. */
bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
if (bandwidth == 0) {
uvc_trace(UVC_TRACE_VIDEO, "Device requested null "
"bandwidth, defaulting to lowest.\n");
bandwidth = 1;
} else {
uvc_trace(UVC_TRACE_VIDEO, "Device requested %u "
"B/frame bandwidth.\n", bandwidth);
}
for (i = 0; i < intf->num_altsetting; ++i) {
struct usb_host_interface *alts;
unsigned int psize;
alts = &intf->altsetting[i];
ep = uvc_find_endpoint(alts,
stream->header.bEndpointAddress);
if (ep == NULL)
continue;
/* Check if the bandwidth is high enough. */
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, "No fast enough alt setting "
"for requested bandwidth.\n");
return -EIO;
}
uvc_trace(UVC_TRACE_VIDEO, "Selecting alternate setting %u "
"(%u B/frame bandwidth).\n", 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 {
...
}
...
return 0;
}
在上述函数中,有以下这个操作:
/* Isochronous endpoint, select the alternate setting. */
bandwidth = stream->ctrl.dwMaxPayloadTransferSize;
bandWidth就是根据分辨率以及格式得到的所需带宽,我们可以手动的修改这个值,将它调小,降低usb带宽,从而在带宽有限的情况下可以有效的使用uvc camera,但是需要注意,并不是可以随便的修改该值,因为最终还需要通过该值与uvc camera的实际支持的带宽进行匹配,即上面通过lsusb得到的info.txt中的wMaxPacketSize值进行适配。wMaxPacketSize代表着usb传输数据时,每个包的数据量大小,当改小时,降低带宽,相应的分辨率以及帧率也都会降低,这个可以尝试修改,得到一个最合适的带宽。
为了方便调试,看到相应的带宽设置参数,可通过修改drivers/media/usb/uvc中的uvc_driver.c的uvc_trace_param值,将其修改为如下
unsigned int uvc_trace_param = UVC_TRACE_VIDEO;
这样就可以将与带宽相关的信息打印出来。
在嵌入式中,如果出现这样的问题,如果是uvc camera本身是支持高分辨率的,一般都是嵌入式的usb驱动没有做好导致的。建议检查usb驱动,是不是标准的hci驱动,同时,有没有调试过相应驱动的同步传输。
转载请注明出处!