LINUX&UVC视频控制请求与V4L2映射关系
2024-03-28
142
0
从 Linux&UVC驱动栈 https://www.usbzh.com/article/detail-1322.html 一文可知,UVC驱动其实是一个中间层驱动程序,其启着承上启下的功能。
- 在UVC驱动的下层是USBCore驱动,其实现的是USB设备初始化及通讯相关的功能实现。
- 在UVC驱动的上层,则是V4L2,是LINUX关于视频流的通用驱动程序。假如从Windows的角度来讲,V4L2是类驱动程序,UVC驱动是端口驱动程序。这和Windows系统中HIDCLASS.SYS和HIDUSB.SYS的关系类似。
在UVC视频控制请求描述符和UVC视频流控制请求中,存在着大量的UVC特定类请求。如果只是单纯的是UVC驱动,其实完全可以不需要V4L2,但由于存在其它总线的相机的问题,如MIPI之类。所以关于发向这些UVC拓扑结构或者视频流接口的特定类请求需要在V4L2这里有一个更加广泛而统一的接口。这些统一的调用接口在系统层应可以更加方便的使用于其它总线的设备。这就类似于系统层所有设备都被当作文件对待一下,都可以支持VFS层想关的fops相关回调函数一样。
从LINUX&UVC驱动的代码来看,主要汲到以下请求:
static const u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING;
static const u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA;
static const u8 uvc_media_transport_input_guid[16] = UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT;
static int uvc_entity_match_guid(const struct uvc_entity *entity,
const u8 guid[16])
{
switch (UVC_ENTITY_TYPE(entity)) {
case UVC_ITT_CAMERA:
return memcmp(uvc_camera_guid, guid, 16) == 0;
case UVC_ITT_MEDIA_TRANSPORT_INPUT:
return memcmp(uvc_media_transport_input_guid, guid, 16) == 0;
case UVC_VC_PROCESSING_UNIT:
return memcmp(uvc_processing_guid, guid, 16) == 0;
case UVC_VC_EXTENSION_UNIT:
return memcmp(entity->extension.guidExtensionCode,guid, 16) == 0;
default:
return 0;
}
}
- UVC_ITT_CAMERA:UVC视频输入端相机特定类请求,如PTZ
- UVC_ITT_MEDIA_TRANSPORT_INPUT:这种类型的设备本人没有接触过。不做分析。
- UVC_VC_PROCESSING_UNIT:UVC控制接口UVC特定类请求。
- UVC_VC_EXTENSION_UNIT:UVC用于用户自定义通讯的扩展单元请求。
从定义来看,除过扩展单元以外,其它所有请求都定义了统一的GUID,用于惟一的标识。而扩展单元可由固件开发者自行定义。这样就做到了UVC请求在格式上的统一。
那么,为什么要做这些处理呢?这是因为不同的相机固件对以上的拓扑结构支持的功能是不同的。以UVC_ITT_CAMERA为示例。其最多可能支持以下的请求:
bmControls:使用位图来表示支持的视频流。
D0:扫描模式 //扫描模式(逐行扫描或隔行扫描)
D1:自动曝光模式
D2:自动曝光优先级
D3:曝光时间(绝对值)
D4:曝光时间(相对)
D5:焦点(绝对)
D6:焦点(相对)
D7:虹膜光圈(绝对)
D8:虹膜光圈(相对)
D9:缩放(绝对)
D10:缩放(相对)
D11:PanTilt(绝对) PT是左右和上下移动
D12:PanTilt(相对)
D13:滚动(绝对)
D14:滚动(相对)
D15:预留
D16:预留
D17:自动对焦
D18:隐私
D19:专注,简单
D20:窗口
D21:关注区域,感兴趣区域
D22–D23:保留,设置为零
详见
- UVC 相机终端描述符 https://www.usbzh.com/article/detail-1.html
- UVC 相机终端控制请求 https://www.usbzh.com/article/detail-47.html
但固件一般大概率是不会支持全部的,故只会根据其设备的实际情况选择性的支持。例如这里可能只支持PTZ相关的,其余不支持。故在设备初始化的时候,会对这此是否支持进行处理:
/*
* Initialize device controls.
*/
int uvc_ctrl_init_device(struct uvc_device *dev)
{
...
/* Walk the entities list and instantiate controls */
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;
}
/* Remove bogus/blacklisted controls */
uvc_ctrl_prune_entity(dev, entity);
/* Count supported controls and allocate the controls array */
ncontrols = memweight(bmControls, bControlSize);
if (ncontrols == 0)
continue;
//分配控制请求
entity->controls = kcalloc(ncontrols, sizeof(*ctrl), GFP_KERNEL);
if (entity->controls == NULL)
return -ENOMEM;
entity->ncontrols = ncontrols;
/* 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进行初始化
uvc_ctrl_init_ctrl(dev, ctrl);
ctrl++;
}
}
return 0;
}
在uvc_ctrl_init_ctrl函数中,通过uvc_ctrl_add_info调用uvc_ctrl_get_flags获取该选择子支持的操作。该操作是通过GET_INFO特定类请求实现的。
static int uvc_ctrl_get_flags(struct uvc_device *dev,
const struct uvc_control *ctrl,
struct uvc_control_info *info)
{
u8 *data;
int ret;
data = kmalloc(1, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,info->selector, data, 1);
if (!ret)
info->flags |= (data[0] & UVC_CONTROL_CAP_GET ? UVC_CTRL_FLAG_GET_CUR : 0)
| (data[0] & UVC_CONTROL_CAP_SET ? UVC_CTRL_FLAG_SET_CUR : 0)
| (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ? UVC_CTRL_FLAG_AUTO_UPDATE : 0)
| (data[0] & UVC_CONTROL_CAP_ASYNCHRONOUS ?UVC_CTRL_FLAG_ASYNCHRONOUS : 0);
kfree(data);
return ret;
}