Skip to content

Commit

Permalink
[media] uvcvideo: Delay initialization of XU controls
Browse files Browse the repository at this point in the history
XU controls initialization requires querying the device for control
information. As some buggy UVC devices will crash when queried
repeatedly in a tight loop, delay XU controls initialization until first
use.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Laurent Pinchart authored and Mauro Carvalho Chehab committed Oct 21, 2010
1 parent 071c8bb commit 52c58ad
Showing 1 changed file with 107 additions and 87 deletions.
194 changes: 107 additions & 87 deletions drivers/media/video/uvc/uvc_ctrl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,90 @@ int uvc_ctrl_set(struct uvc_video_chain *chain,
* Dynamic controls
*/

/*
* Query control information (size and flags) for XU controls.
*/
static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
const struct uvc_control *ctrl, struct uvc_control_info *info)
{
u8 *data;
int ret;

data = kmalloc(2, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;

memcpy(info->entity, ctrl->entity->extension.guidExtensionCode,
sizeof(info->entity));
info->index = ctrl->index;
info->selector = ctrl->index + 1;

/* Query and verify the control length (GET_LEN) */
ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum,
info->selector, data, 2);
if (ret < 0) {
uvc_trace(UVC_TRACE_CONTROL,
"GET_LEN failed on control %pUl/%u (%d).\n",
info->entity, info->selector, ret);
goto done;
}

info->size = le16_to_cpup((__le16 *)data);

/* Query the control information (GET_INFO) */
ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
info->selector, data, 1);
if (ret < 0) {
uvc_trace(UVC_TRACE_CONTROL,
"GET_INFO failed on control %pUl/%u (%d).\n",
info->entity, info->selector, ret);
goto done;
}

info->flags = UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX
| UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF
| (data[0] & UVC_CONTROL_CAP_GET ? UVC_CONTROL_GET_CUR : 0)
| (data[0] & UVC_CONTROL_CAP_SET ? UVC_CONTROL_SET_CUR : 0)
| (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
UVC_CONTROL_AUTO_UPDATE : 0);

uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, "
"flags { get %u set %u auto %u }.\n",
info->entity, info->selector, info->size,
(info->flags & UVC_CONTROL_GET_CUR) ? 1 : 0,
(info->flags & UVC_CONTROL_SET_CUR) ? 1 : 0,
(info->flags & UVC_CONTROL_AUTO_UPDATE) ? 1 : 0);

done:
kfree(data);
return ret;
}

static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
const struct uvc_control_info *info);

static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
struct uvc_control *ctrl)
{
struct uvc_control_info info;
int ret;

if (ctrl->initialized)
return 0;

ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info);
if (ret < 0)
return ret;

ret = uvc_ctrl_add_info(dev, ctrl, &info);
if (ret < 0)
uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize control "
"%pUl/%u on device %s entity %u\n", info.entity,
info.selector, dev->udev->devpath, ctrl->entity->id);

return ret;
}

int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control *xctrl, int set)
{
Expand All @@ -1186,13 +1270,10 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
return -EINVAL;
}

/* Find the control. */
/* Find the control and perform delayed initialization if needed. */
for (i = 0; i < entity->ncontrols; ++i) {
ctrl = &entity->controls[i];
if (!ctrl->initialized)
continue;

if (ctrl->info.selector == xctrl->selector) {
if (ctrl->index == xctrl->selector - 1) {
found = 1;
break;
}
Expand All @@ -1204,6 +1285,10 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
return -EINVAL;
}

ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl);
if (ret < 0)
return -ENOENT;

/* Validate control data size. */
if (ctrl->info.size != xctrl->size)
return -EINVAL;
Expand Down Expand Up @@ -1294,65 +1379,6 @@ int uvc_ctrl_resume_device(struct uvc_device *dev)
* Control and mapping handling
*/

/*
* Query control information (size and flags) for XU controls.
*/
static int uvc_ctrl_fill_xu_info(struct uvc_device *dev,
const struct uvc_control *ctrl, struct uvc_control_info *info)
{
u8 *data;
int ret;

data = kmalloc(2, GFP_KERNEL);
if (data == NULL)
return -ENOMEM;

memcpy(info->entity, ctrl->entity->extension.guidExtensionCode,
sizeof(info->entity));
info->index = ctrl->index;
info->selector = ctrl->index + 1;

/* Query and verify the control length (GET_LEN) */
ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum,
info->selector, data, 2);
if (ret < 0) {
uvc_trace(UVC_TRACE_CONTROL,
"GET_LEN failed on control %pUl/%u (%d).\n",
info->entity, info->selector, ret);
goto done;
}

info->size = le16_to_cpup((__le16 *)data);

/* Query the control information (GET_INFO) */
ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum,
info->selector, data, 1);
if (ret < 0) {
uvc_trace(UVC_TRACE_CONTROL,
"GET_INFO failed on control %pUl/%u (%d).\n",
info->entity, info->selector, ret);
goto done;
}

info->flags = UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX
| UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF
| (data[0] & UVC_CONTROL_CAP_GET ? UVC_CONTROL_GET_CUR : 0)
| (data[0] & UVC_CONTROL_CAP_SET ? UVC_CONTROL_SET_CUR : 0)
| (data[0] & UVC_CONTROL_CAP_AUTOUPDATE ?
UVC_CONTROL_AUTO_UPDATE : 0);

uvc_trace(UVC_TRACE_CONTROL, "XU control %pUl/%u queried: len %u, "
"flags { get %u set %u auto %u }.\n",
info->entity, info->selector, info->size,
(info->flags & UVC_CONTROL_GET_CUR) ? 1 : 0,
(info->flags & UVC_CONTROL_SET_CUR) ? 1 : 0,
(info->flags & UVC_CONTROL_AUTO_UPDATE) ? 1 : 0);

done:
kfree(data);
return ret;
}

/*
* Add control information to a given control.
*/
Expand Down Expand Up @@ -1434,7 +1460,7 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,

if (mapping->id & ~V4L2_CTRL_ID_MASK) {
uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', control "
"control id 0x%08x is invalid.\n", mapping->name,
"id 0x%08x is invalid.\n", mapping->name,
mapping->id);
return -EINVAL;
}
Expand All @@ -1443,13 +1469,13 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
list_for_each_entry(entity, &dev->entities, list) {
unsigned int i;

if (!uvc_entity_match_guid(entity, mapping->entity))
if (UVC_ENTITY_TYPE(entity) != UVC_VC_EXTENSION_UNIT ||
!uvc_entity_match_guid(entity, mapping->entity))
continue;

for (i = 0; i < entity->ncontrols; ++i) {
ctrl = &entity->controls[i];
if (ctrl->initialized &&
ctrl->info.selector == mapping->selector) {
if (ctrl->index == mapping->selector - 1) {
found = 1;
break;
}
Expand All @@ -1464,6 +1490,13 @@ int uvc_ctrl_add_mapping(struct uvc_video_chain *chain,
if (mutex_lock_interruptible(&chain->ctrl_mutex))
return -ERESTARTSYS;

/* Perform delayed initialization of XU controls */
ret = uvc_ctrl_init_xu_ctrl(dev, ctrl);
if (ret < 0) {
ret = -ENOENT;
goto done;
}

list_for_each_entry(map, &ctrl->info.mappings, list) {
if (mapping->id == map->id) {
uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', "
Expand Down Expand Up @@ -1567,26 +1600,13 @@ static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl)
const struct uvc_control_mapping *mend =
mapping + ARRAY_SIZE(uvc_ctrl_mappings);

/* Query XU controls for control information */
if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT) {
struct uvc_control_info info;
int ret;

ret = uvc_ctrl_fill_xu_info(dev, ctrl, &info);
if (ret < 0)
return;

ret = uvc_ctrl_add_info(dev, ctrl, &info);
if (ret < 0) {
/* Skip the control */
uvc_trace(UVC_TRACE_CONTROL, "Failed to initialize "
"control %pUl/%u on device %s entity %u\n",
info.entity, info.selector, dev->udev->devpath,
ctrl->entity->id);
memset(ctrl, 0, sizeof(*ctrl));
}
/* XU controls initialization requires querying the device for control
* information. As some buggy UVC devices will crash when queried
* repeatedly in a tight loop, delay XU controls initialization until
* first use.
*/
if (UVC_ENTITY_TYPE(ctrl->entity) == UVC_VC_EXTENSION_UNIT)
return;
}

for (; info < iend; ++info) {
if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
Expand Down

0 comments on commit 52c58ad

Please sign in to comment.