在某些平台设备,如果同时使用多个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驱动,同时,有没有调试过相应驱动的同步传输。

 

转载请注明出处!