Linux源码分析UVC摄像头的初始化流程分析
2021-04-01
2040
0
UVC摄像头的初始化发生在硬件被接入USB集线器中,设备初USB驱动识别为摄像头的后续初始化流程。
和Windows的AddDevice驱动函数一样,Linux设备的创建和侦测是通过int uvc_probe函数实现的。其函数的调用关系如下:
//linux/v5.11.11/source/drivers/media/usb/uvc/uvc_driver.c#L2098
uvc_probe
uvc_register_chains
uvc_register_terms
uvc_register_video_device
uvc_register_video
uvc_video_init
uvc_video_init的代码如下:
int uvc_video_init(struct uvc_streaming *stream)
{
struct uvc_streaming_control *probe = &stream->ctrl;
struct uvc_format *format = NULL;
struct uvc_frame *frame = NULL;
struct uvc_urb *uvc_urb;
unsigned int i;
int ret;
if (stream->nformats == 0) {
uvc_printk(KERN_INFO, "No supported video formats found.\n");
return -EINVAL;
}
atomic_set(&stream->active, 0);
/* Alternate setting 0 should be the default, yet the XBox Live Vision
* Cam (and possibly other devices) crash or otherwise misbehave if
* they don't receive a SET_INTERFACE request before any other video
* control request.
*/
usb_set_interface(stream->dev->udev, stream->intfnum, 0);
/* Set the streaming probe control with default streaming parameters
* retrieved from the device. Webcams that don't support GET_DEF
* requests on the probe control will just keep their current streaming
* parameters.
*/
if (uvc_get_video_ctrl(stream, probe, 1, UVC_GET_DEF) == 0)
uvc_set_video_ctrl(stream, probe, 1);
/* Initialize the streaming parameters with the probe control current
* value. This makes sure SET_CUR requests on the streaming commit
* control will always use values retrieved from a successful GET_CUR
* request on the probe control, as required by the UVC specification.
*/
ret = uvc_get_video_ctrl(stream, probe, 1, UVC_GET_CUR);
if (ret < 0)
return ret;
/* Check if the default format descriptor exists. Use the first
* available format otherwise.
*/
for (i = stream->nformats; i > 0; --i) {
format = &stream->format[i-1];
if (format->index == probe->bFormatIndex)
break;
}
if (format->nframes == 0) {
uvc_printk(KERN_INFO, "No frame descriptor found for the "
"default format.\n");
return -EINVAL;
}
/* Zero bFrameIndex might be correct. Stream-based formats (including
* MPEG-2 TS and DV) do not support frames but have a dummy frame
* descriptor with bFrameIndex set to zero. If the default frame
* descriptor is not found, use the first available frame.
*/
for (i = format->nframes; i > 0; --i) {
frame = &format->frame[i-1];
if (frame->bFrameIndex == probe->bFrameIndex)
break;
}
probe->bFormatIndex = format->index;
probe->bFrameIndex = frame->bFrameIndex;
stream->def_format = format;
stream->cur_format = format;
stream->cur_frame = frame;
/* Select the video decoding function */
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)
stream->decode = uvc_video_decode_isight;
else if (stream->intf->num_altsetting > 1)
stream->decode = uvc_video_decode_isoc;
else
stream->decode = uvc_video_decode_bulk;
} else {
if (stream->intf->num_altsetting == 1)
stream->decode = uvc_video_encode_bulk;
else {
uvc_printk(KERN_INFO, "Isochronous endpoints are not "
"supported for video output devices.\n");
return -EINVAL;
}
}
/* Prepare asynchronous work items. */
for_each_uvc_urb(uvc_urb, stream)
INIT_WORK(&uvc_urb->work, uvc_video_copy_data_work);
return 0;
}
从代码来看,UVC设备的初始化的第一步是选择视频流的接口,通过函数usb_set_interface实现。
usb_set_interface(stream->dev->udev, stream->intfnum, 0);
然后使用VS_PROBE_CONTROL选择子和GET_DEF请求类型获取默认的视频帧格式类型,然后设置当前SET_CUR的属性信息。
后续的代码就相对简单,是对获取的默认视频编码格式和帧格式进行校验,校验完成后就保存当前的状态信息。
Windows下的UVC相机初始化
本人手中有一UVC摄像头,其UVC控制接口(ID=1)中有相机终端描述符(ID=1),处理单元描述符(ID=2)输出终端描述符(ID=3)和一个扩展单元描述符(ID=0x0b).
为什么这个摄像头的UVC控制接口ID=1?这是因为本人手中的这个相机摄像头是一个复合设备,接口描述符ID=0的是一个HID设备。
现将设备插入电脑的枚举过程抓包显示如下:(只抓了前面少部分字节)
Device Length Phase Data
------ -------- ----- ---------------------------------------------------- ---------
44.0 CTL 80 06 00 01 00 00 12 00 GET DESCR
44.0 18 IN 12 01 00 02 ef 02 01 40 6b 1d 00 01 09 04 04 0a .......@k
44.0 CTL 80 06 00 02 00 00 09 00 GET DESCR
44.0 9 IN 09 02 bb 00 02 01 07 80 fa .........
44.0 CTL 80 06 00 02 00 00 bb 00 GET DESCR
44.0 187 IN 09 02 bb 00 02 01 07 80 fa 09 04 01 00 01 0e 01 .........
44.0 CTL 00 09 01 00 00 00 00 00 SET CONFI
44.0 CTL 01 0b 00 00 02 00 00 00 SET INTER
//参见相机终端描述符 先择子为CT_ZOOM_ABSOLUTE_CONTROL
44.0 CTL a1 86 00 0b 01 01 01 00 GET INFO
44.0 1 IN 13 .
44.0 CTL a1 82 00 0b 01 01 02 00 GET MIN
44.0 2 IN 00 00 ..
44.0 CTL a1 83 00 0b 01 01 02 00 GET MAX
44.0 2 IN d0 02 ..
44.0 CTL a1 84 00 0b 01 01 02 00 GET RES
44.0 2 IN 01 00 ..
44.0 CTL a1 87 00 0b 01 01 02 00 GET DEF
44.0 2 IN 00 00 ..
//CT_ZOOM_RELATIVE_CONTROL
44.0 CTL a1 86 00 0c 01 01 01 00 GET INFO
44.0 1 IN 03 .
44.0 CTL a1 82 00 0c 01 01 03 00 GET MIN
44.0 3 IN 00 00 00 ...
44.0 CTL a1 83 00 0c 01 01 03 00 GET MAX
44.0 3 IN 00 00 01 ...
44.0 CTL a1 84 00 0c 01 01 03 00 GET RES
44.0 3 IN 00 00 01 ...
44.0 CTL a1 87 00 0c 01 01 03 00 GET DEF
44.0 3 IN 00 00 01 ...
//CT_PANTILT_ABSOLUTE_CONTROL
44.0 CTL a1 86 00 0d 01 01 01 00 GET INFO
44.0 1 IN 13 .
44.0 CTL a1 82 00 0d 01 01 08 00 GET MIN
44.0 8 IN 80 39 ec ff 60 e7 f4 ff
44.0 CTL a1 83 00 0d 01 01 08 00 GET MAX
44.0 8 IN 80 c6 13 00 a0 18 0b 00 ........
44.0 CTL a1 84 00 0d 01 01 08 00 GET RES
44.0 8 IN 10 0e 00 00 10 0e 00 00 ........
44.0 CTL a1 87 00 0d 01 01 08 00 GET DEF
44.0 8 IN 00 00 00 00 00 00 00 00 ........
44.0 CTL a1 86 00 0e 01 01 01 00 GET INFO
44.0 1 IN 03 .
//CT_PANTILT_RELATIVE_CONTROL
44.0 CTL a1 82 00 0e 01 01 04 00 GET MIN
44.0 4 IN 00 01 00 01 ....
44.0 CTL a1 83 00 0e 01 01 04 00 GET MAX
44.0 4 IN 00 01 00 01 ....
44.0 CTL a1 84 00 0e 01 01 04 00 GET RES
44.0 4 IN 00 01 00 01 ....
44.0 CTL a1 87 00 0e 01 01 04 00 GET DEF
44.0 4 IN 00 01 00 01 ....
44.0 CTL a1 85 00 01 01 0b 02 00 GET LEN
44.0 2 IN 01 00 ..
//参见扩展单元描述符 ,支持1-5的选择子
44.0 CTL a1 86 00 01 01 0b 01 00 GET INFO
44.0 1 IN 02 .
44.0 CTL a1 82 00 01 01 0b 01 00 GET MIN
44.0 1 IN 00 .
44.0 CTL a1 83 00 01 01 0b 01 00 GET MAX
44.0 1 IN ff .
44.0 CTL a1 84 00 01 01 0b 01 00 GET RES
44.0 1 IN 01 .
44.0 CTL a1 87 00 01 01 0b 01 00 GET DEF
44.0 1 IN 00 .
44.0 CTL a1 85 00 02 01 0b 02 00 GET LEN
44.0 2 IN 01 00 ..
44.0 CTL a1 86 00 02 01 0b 01 00 GET INFO
44.0 1 IN 02 .
44.0 CTL a1 82 00 02 01 0b 01 00 GET MIN
44.0 1 IN 00 .
44.0 CTL a1 83 00 02 01 0b 01 00 GET MAX
44.0 1 IN ff .
44.0 CTL a1 84 00 02 01 0b 01 00 GET RES
44.0 1 IN 01 .
44.0 CTL a1 87 00 02 01 0b 01 00 GET DEF
44.0 1 IN 00 .
44.0 CTL a1 85 00 03 01 0b 02 00 GET LEN
44.0 2 IN 01 00 ..
44.0 CTL a1 86 00 03 01 0b 01 00 GET INFO
44.0 1 IN 02 .
44.0 CTL a1 82 00 03 01 0b 01 00 GET MIN
44.0 1 IN 00 .
44.0 CTL a1 83 00 03 01 0b 01 00 GET MAX
44.0 1 IN ff .
44.0 CTL a1 84 00 03 01 0b 01 00 GET RES
44.0 1 IN 01 .
44.0 CTL a1 87 00 03 01 0b 01 00 GET DEF
44.0 1 IN 00 .
44.0 CTL a1 85 00 04 01 0b 02 00 GET LEN
44.0 2 IN 01 00 ..
44.0 CTL a1 86 00 04 01 0b 01 00 GET INFO
44.0 1 IN 02 .
44.0 CTL a1 82 00 04 01 0b 01 00 GET MIN
44.0 1 IN 00 .
44.0 CTL a1 83 00 04 01 0b 01 00 GET MAX
44.0 1 IN ff .
44.0 CTL a1 84 00 04 01 0b 01 00 GET RES
44.0 1 IN 01 .
44.0 CTL a1 87 00 04 01 0b 01 00 GET DEF
44.0 1 IN 00 .
44.0 CTL a1 85 00 05 01 0b 02 00 GET LEN
44.0 2 IN 01 00 ..
44.0 CTL a1 86 00 05 01 0b 01 00 GET INFO
44.0 1 IN 02 .
44.0 CTL a1 82 00 05 01 0b 01 00 GET MIN
44.0 1 IN 00 .
44.0 CTL a1 83 00 05 01 0b 01 00 GET MAX
44.0 1 IN ff .
44.0 CTL a1 84 00 05 01 0b 01 00 GET RES
44.0 1 IN 01 .
44.0 CTL a1 87 00 05 01 0b 01 00 GET DEF
44.0 1 IN 00 .
而在打开摄像头的时候,才进行相关参数的获取.这也许是Windows和Linux的区别吧。
Device Length Phase Data
------ -------- ----- ---------------------------------------------------- --------------
44.0 CTL a1 81 00 01 02 00 22 00 GET CUR
44.0 34 IN 01 00 01 01 15 16 05 00 00 00 00 00 00 00 00 00 ..............
44.0 CTL 21 01 00 01 02 00 22 00 SET CUR
44.0 34 OUT 01 00 01 01 15 16 05 00 00 00 00 00 00 00 00 00 ..............
44.0 CTL a1 81 00 01 02 00 22 00 GET CUR
44.0 34 IN 01 00 01 01 15 16 05 00 00 00 00 00 00 00 00 00 ..............
44.0 CTL a1 83 00 01 02 00 22 00 GET MAX
44.0 34 IN 01 00 01 01 15 16 05 00 00 00 00 00 00 00 00 00 ..............
44.0 CTL a1 82 00 01 02 00 22 00 GET MIN
44.0 34 IN 01 00 01 01 15 16 05 00 00 00 00 00 00 00 00 00 ..............
44.0 CTL 21 01 00 02 02 00 22 00 SET CUR
44.0 34 OUT 01 00 01 01 15 16 05 00 00 00 00 00 00 00 00 00 ..............
44.0 CTL 01 0b 00 00 02 00 00 00 SET INTERFACE
`