文章将会同步到我的微信公众号:Android部落格html
UVC协议文档网址:www.usb.org/documents?s…linux
主要下载USB Video Class 1_5,关注下载zip包中的UVC 1.5 Class specification.pdf文件,里面有接口相关的解释。android
Android盒子控制摄像头项目已经差很少4年了,不少知识点已经忘记,如今从新温固一遍,下面两篇文章能够帮助回顾。git
my.oschina.net/u/2007478/b…api
下边两个网址中能够找到代码中各类结构体的解释:微信
www.kernel.org/doc/html/v4…markdown
咱们先从Android官网git clone一下kernel的源码:ide
android.googlesource.com/kernel/gold…
git clone android.googlesource.com/kernel/gold…
clone到本地以后就能够经过Source Insight查看源码了。查看源码以前先project -> rebuild project,这样代码中各对象之间能够点击跳转。
goldfish\drivers\media\usb\uvc\uvc_driver.c
static int __init uvc_init(void) {
int ret;
ret = usb_register(&uvc_driver.driver);
return 0;
}
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo",
.probe = uvc_probe,
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
.reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
};
复制代码
在入口函数uvc_init
中,核心的一行是usb_register
,也就是注册USB设备,在注册完成以后会调用uvc_probe
函数。
goldfish\include\linux\usb.h
struct usb_driver {
const char *name;
int (*probe) (struct usb_interface *intf,
const struct usb_device_id *id);
复制代码
看看这个uvc_probe
函数:
goldfish\drivers\media\usb\uvc\uvc_driver.c
static int uvc_probe(struct usb_interface *intf, const struct usb_device_id *id) {
struct usb_device *udev = interface_to_usbdev(intf);
struct uvc_device *dev;
int ret;
if (id->idVendor && id->idProduct)
uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
"(%04x:%04x)\n", udev->devpath, id->idVendor,
id->idProduct);
else
uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
udev->devpath);
uvc_parse_control(dev);//1
v4l2_device_register(&intf->dev, &dev->vdev);//2
uvc_ctrl_init_device(dev);//3
uvc_scan_device(dev);
uvc_register_chains(dev);
usb_set_intfdata(intf, dev);
ret = uvc_status_init(dev);
usb_enable_autosuspend(udev);
}
复制代码
uvc_parse_control
会根据设备的vendorId和productId去对特定厂商的摄像头作一些适配。v4l2_device_register
,该方法将设备注册到v4l2,v4l2是Video for linux2的简称,为linux中关于视频设备的内核驱动。该方法在goldfish\drivers\media\v4l2-core\v4l2-device.c
中。uvc_ctrl_init_device
,初始化设备控制。这里是咱们须要重点关注的。能够先跟踪一下这个调用栈。
goldfish\drivers\media\usb\uvc\uvc_ctrl.c
int uvc_ctrl_init_device(struct uvc_device *dev) {
list_for_each_entry(entity, &dev->entities, list) {
struct uvc_control *ctrl;
unsigned int bControlSize = 0, ncontrols;
__u8 *bmControls = NULL;
//第一部分
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
bmControls = entity->extension.bmControls;
bControlSize = entity->extension.bControlSize;
} else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) {
bmControls = entity->processing.bmControls;
bControlSize = entity->processing.bControlSize;
} else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
bmControls = entity->camera.bmControls;
bControlSize = entity->camera.bControlSize;
}
//第二部分
/* Initialize all supported controls */
ctrl = entity->controls;
for (i = 0; i < bControlSize * 8; ++i) {
if (uvc_test_bit(bmControls, i) == 0)
continue;
ctrl->entity = entity;
ctrl->index = i;
uvc_ctrl_init_ctrl(dev, ctrl);
ctrl++;
}
}
}
复制代码
用于区分终端类型,重点关注UVC_ITT_CAMERA
类型,看看这个类型在UVC协议文档里面的定义:
摄像头传感器,仅用于描述摄像头终端。那么代码里面的描述是:
goldfish\include\uapi\linux\usb\video.h
/* B.2. Input Terminal Types */
#define UVC_ITT_VENDOR_SPECIFIC 0x0200
#define UVC_ITT_CAMERA 0x0201
#define UVC_ITT_MEDIA_TRANSPORT_INPUT 0x0202
复制代码
从第一部分中取出camera.bmControls
和camera.bControlSize
,这两个变量是干吗的呢,仍是看协议文档:
bControlSize
是对应着bmControls
的位大小; bmControls
对应着摄像头支持的控制参数,若是控制参数位置为1,表示支持该控制。
接下来调用uvc_ctrl_init_ctrl
方法:
goldfish\drivers\media\usb\uvc\uvc_ctrl.c
static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl) {
const struct uvc_control_info *info = uvc_ctrls;
const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls);
const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
const struct uvc_control_mapping *mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings);
for (; info < iend; ++info) {
if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
ctrl->index == info->index) {
uvc_ctrl_add_info(dev, ctrl, info);
break;
}
}
if (!ctrl->initialized)
return;
for (; mapping < mend; ++mapping) {
if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
ctrl->info.selector == mapping->selector)
__uvc_ctrl_add_mapping(dev, ctrl, mapping);
}
}
复制代码
uvc_control_info
,是一个静态数组。能够理解为一个实体类别下对应着多个控制功能,每一个功能有对应着不一样的操做方式,以当前项目须要用到的功能举例:static struct uvc_control_info uvc_ctrls[] = {
{
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
.index = 11,
.size = 8,
.flags = UVC_CTRL_FLAG_SET_CUR
| UVC_CTRL_FLAG_GET_RANGE
| UVC_CTRL_FLAG_RESTORE
| UVC_CTRL_FLAG_AUTO_UPDATE,
},
{
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_RELATIVE_CONTROL,
.index = 12,
.size = 4,
.flags = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
| UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
| UVC_CTRL_FLAG_GET_DEF
| UVC_CTRL_FLAG_AUTO_UPDATE,
},
}
复制代码
entity 实体是一种类型,UVC_GUID_UVC_CAMERA
只是其中一种,还有UVC_GUID_UVC_PROCESSING
等。
selector 对应的是实体下的一种功能,好比相对绝对转动。
index 对应着在uvc_ctrls
中的序号。
/* Bit index in bmControls */
UVC_CT_PANTILT_ABSOLUTE_CONTROL
和UVC_CT_PANTILT_RELATIVE_CONTROL
控制,看看在协议文档中的定义:对于PanTilt Absolute来讲,高四位表明着左右的角度,低四位表明着上下的角度,都是有符号整数,总共八位,因此size为8。
对于PanTilt Relative来讲,总共四位,每一位表明不一样的控制属性,第一位表示左右相对;第二位表示左右控制的速度;第三位表示上下相对;第四位表示上下的速度。因此size为4。
selector
支持的功能操做:详细的解释以下:
uvc_ctrl_mappings是uvc_control_mapping
类型的结构体变量,也是一个静态的结构体,简略看下里面定义了啥:
static struct uvc_control_mapping uvc_ctrl_mappings[] = {
{
.id = V4L2_CID_PAN_ABSOLUTE,
.name = "Pan (Absolute)",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
.size = 32,
.offset = 0,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
},
{
.id = V4L2_CID_TILT_ABSOLUTE,
.name = "Tilt (Absolute)",
.entity = UVC_GUID_UVC_CAMERA,
.selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
.size = 32,
.offset = 32,
.v4l2_type = V4L2_CTRL_TYPE_INTEGER,
.data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
},
}
复制代码
能够看到这里的id
开头都是V4L2
,而entity
和selector
都对应着uvc_control_info uvc_ctrls
中定义的entity
和selector
。
另外v4l2_type
对应着设置的数据类型,data_type
则定义了数据为有符号仍是无符号。
这个结构体从个人理解来看,就是将UVC定义的控制,映射到v4l2,并创建二者之间的关系。
这个方法核心就一行代码:
goldfish\drivers\media\usb\uvc\uvc_ctrl.c
static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl, const struct uvc_control_info *info) {
ctrl->info = *info;
}
复制代码
将上一步uvc_control_info
变量给到uvc_control
的info。uvc_control
的定义以下:
goldfish\drivers\media\usb\uvc\uvcvideo.h
struct uvc_control {
struct uvc_entity *entity;
struct uvc_control_info info;
__u8 index; /* Used to match the uvc_control entry with a uvc_control_info. */
__u8 dirty:1,
loaded:1,
modified:1,
cached:1,
initialized:1;
__u8 *uvc_data;
};
复制代码
一样的,将uvc_control_mapping
数据赋值到uvc_control
对象中:
goldfish\drivers\media\usb\uvc\uvc_ctrl.c
static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
struct uvc_control *ctrl, const struct uvc_control_mapping *mapping)
{
struct uvc_control_mapping *map;
map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
list_add_tail(&map->list, &ctrl->info.mappings);
}
复制代码
uvc_control_info
的mappings
做为链表头,将map->list
添加到后面。
在初始化的过程当中能够将UVC协议的文档跟代码创建联系,以帮助理解代码的逻辑。待理解了各类数据类型定义的原理及流程以后,发现其实现了UVC与V4L2的链接,这样下一步的工做就比较好开展了。
UVC初始化的部分到这里告一段落,接下来要根据具体需求作一些定制的工做。