最近做了一个项目通过uvc协议控制按键实现截图功能,供货商修改了uvcvideo属性的saturation(饱和度),当按键按下时修改了cmos侧的saturation为1,应用检测这个状态,当检测到1时,截一张图,并把saturation值设为0。实际使用,在windows上用amcap软件测试是没有问题的,在linux下使用guvcview软件测试时发现,按键按下后,软件并不能读到1,切换到windows,再切换回ubuntu时状态值变为了1,按键按下后才切换到windows的由此判断按键按下后saturation是上报到cmos侧了,但是ubuntu并不能读到这个值,使用开发板是相同的问题,使用v4l2-ctl 也不能读到1。分析后明白应该是驱动里面为了usb减少总线占用,使用了本地映射的方式,通过v4l2-ctl读取值时是直接读取的本地映射的值,并不是通过ioctrl从设备侧读到的真实的值。通过分析源码发现果然这是这样,附源码:
int uvc_ctrl_get(struct uvc_video_chain *chain,
struct v4l2_ext_control *xctrl)
{
struct uvc_control *ctrl;
struct uvc_control_mapping *mapping;
struct uvc_menu_info *menu;
unsigned int i;
int ret;
ctrl = uvc_find_control(chain, xctrl->id, &mapping);
if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
return -EINVAL;
if (!ctrl->loaded) {
ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
chain->dev->intfnum, ctrl->info.selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info.size);
if (ret < 0)
return ret;
ctrl->loaded = 1;
}
xctrl->value = mapping->get(mapping, UVC_GET_CUR,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
menu = mapping->menu_info;
for (i = 0; i < mapping->menu_count; ++i, ++menu) {
if (menu->value == xctrl->value) {
xctrl->value = i;
break;
}
}
}
return 0;
}
通过!ctrl->loaded
判断,如果是第一次加载,就通过ioctrl从设备测读取,后面就不再执行这一段代码,也就是不通过ioctrl从设备侧读属性值了。
修改代码如下:
int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl)
{
struct uvc_control *ctrl;
struct uvc_control_mapping *mapping;
struct uvc_menu_info *menu;
unsigned int i;
int ret;
ctrl = uvc_find_control(chain, xctrl->id, &mapping);
if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0)
return -EINVAL;
// 移除loaded状态检查,每次直接查询
ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id,
chain->dev->intfnum, ctrl->info.selector,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT),
ctrl->info.size);
if (ret < 0)
return ret;
// 保留原逻辑的其余部分
xctrl->value = mapping->get(mapping, UVC_GET_CUR,
uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT));
if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) {
menu = mapping->menu_info;
for (i = 0; i < mapping->menu_count; ++i, ++menu) {
if (menu->value == xctrl->value) {
xctrl->value = i;
break;
}
}
}
return 0;
}