V4L/DVB: uvcvideo: Don't use stack-based buffers for USB transfers
Data buffers on the stack are not allowed for USB I/O. Use dynamically allocated buffers instead when querying control length and control capabilities. The control capabilities are now also stored in the uvc_control structure. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
committed by
Mauro Carvalho Chehab
parent
1b4e21c4f6
commit
b30ece5394
@@ -643,7 +643,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
|
|||||||
|
|
||||||
static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
|
static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id)
|
||||||
{
|
{
|
||||||
return ctrl->data + id * ctrl->info->size;
|
return ctrl->uvc_data + id * ctrl->info->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int uvc_test_bit(const __u8 *data, int bit)
|
static inline int uvc_test_bit(const __u8 *data, int bit)
|
||||||
@@ -1293,13 +1293,15 @@ int uvc_ctrl_resume_device(struct uvc_device *dev)
|
|||||||
* Control and mapping handling
|
* Control and mapping handling
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void uvc_ctrl_add_ctrl(struct uvc_device *dev,
|
static int uvc_ctrl_add_ctrl(struct uvc_device *dev,
|
||||||
struct uvc_control_info *info)
|
struct uvc_control_info *info)
|
||||||
{
|
{
|
||||||
struct uvc_entity *entity;
|
struct uvc_entity *entity;
|
||||||
struct uvc_control *ctrl = NULL;
|
struct uvc_control *ctrl = NULL;
|
||||||
int ret, found = 0;
|
int ret = 0, found = 0;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
u8 *uvc_info;
|
||||||
|
u8 *uvc_data;
|
||||||
|
|
||||||
list_for_each_entry(entity, &dev->entities, list) {
|
list_for_each_entry(entity, &dev->entities, list) {
|
||||||
if (!uvc_entity_match_guid(entity, info->entity))
|
if (!uvc_entity_match_guid(entity, info->entity))
|
||||||
@@ -1318,56 +1320,69 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
|
uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL);
|
||||||
|
if (uvc_data == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
uvc_info = uvc_data + info->size * UVC_CTRL_DATA_LAST;
|
||||||
|
|
||||||
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
|
if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
|
||||||
/* Check if the device control information and length match
|
/* Check if the device control information and length match
|
||||||
* the user supplied information.
|
* the user supplied information.
|
||||||
*/
|
*/
|
||||||
__le16 size;
|
|
||||||
__u8 _info;
|
|
||||||
|
|
||||||
ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id,
|
ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id,
|
||||||
dev->intfnum, info->selector, (__u8 *)&size, 2);
|
dev->intfnum, info->selector, uvc_data, 2);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
uvc_trace(UVC_TRACE_CONTROL,
|
uvc_trace(UVC_TRACE_CONTROL,
|
||||||
"GET_LEN failed on control %pUl/%u (%d).\n",
|
"GET_LEN failed on control %pUl/%u (%d).\n",
|
||||||
info->entity, info->selector, ret);
|
info->entity, info->selector, ret);
|
||||||
return;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info->size != le16_to_cpu(size)) {
|
if (info->size != le16_to_cpu(*(__le16 *)uvc_data)) {
|
||||||
uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size "
|
uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size "
|
||||||
"doesn't match user supplied value.\n",
|
"doesn't match user supplied value.\n",
|
||||||
info->entity, info->selector);
|
info->entity, info->selector);
|
||||||
return;
|
ret = -EINVAL;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id,
|
ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id,
|
||||||
dev->intfnum, info->selector, &_info, 1);
|
dev->intfnum, info->selector, uvc_info, 1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
uvc_trace(UVC_TRACE_CONTROL,
|
uvc_trace(UVC_TRACE_CONTROL,
|
||||||
"GET_INFO failed on control %pUl/%u (%d).\n",
|
"GET_INFO failed on control %pUl/%u (%d).\n",
|
||||||
info->entity, info->selector, ret);
|
info->entity, info->selector, ret);
|
||||||
return;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((info->flags & UVC_CONTROL_GET_CUR) &&
|
if (((info->flags & UVC_CONTROL_GET_CUR) &&
|
||||||
!(_info & UVC_CONTROL_CAP_GET)) ||
|
!(*uvc_info & UVC_CONTROL_CAP_GET)) ||
|
||||||
((info->flags & UVC_CONTROL_SET_CUR) &&
|
((info->flags & UVC_CONTROL_SET_CUR) &&
|
||||||
!(_info & UVC_CONTROL_CAP_SET))) {
|
!(*uvc_info & UVC_CONTROL_CAP_SET))) {
|
||||||
uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags "
|
uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags "
|
||||||
"don't match supported operations.\n",
|
"don't match supported operations.\n",
|
||||||
info->entity, info->selector);
|
info->entity, info->selector);
|
||||||
return;
|
ret = -EINVAL;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctrl->info = info;
|
ctrl->info = info;
|
||||||
ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_DATA_LAST, GFP_KERNEL);
|
ctrl->uvc_data = uvc_data;
|
||||||
|
ctrl->uvc_info = uvc_info;
|
||||||
|
|
||||||
uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
|
uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s "
|
||||||
"entity %u\n", ctrl->info->entity, ctrl->info->selector,
|
"entity %u\n", ctrl->info->entity, ctrl->info->selector,
|
||||||
dev->udev->devpath, entity->id);
|
dev->udev->devpath, entity->id);
|
||||||
|
|
||||||
|
done:
|
||||||
|
if (ret < 0)
|
||||||
|
kfree(uvc_data);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1600,7 +1615,7 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev)
|
|||||||
|
|
||||||
list_for_each_entry(entity, &dev->entities, list) {
|
list_for_each_entry(entity, &dev->entities, list) {
|
||||||
for (i = 0; i < entity->ncontrols; ++i)
|
for (i = 0; i < entity->ncontrols; ++i)
|
||||||
kfree(entity->controls[i].data);
|
kfree(entity->controls[i].uvc_data);
|
||||||
|
|
||||||
kfree(entity->controls);
|
kfree(entity->controls);
|
||||||
}
|
}
|
||||||
|
@@ -1028,6 +1028,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
|
|||||||
if (!capable(CAP_SYS_ADMIN))
|
if (!capable(CAP_SYS_ADMIN))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
|
if (xinfo->size == 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
info = kzalloc(sizeof *info, GFP_KERNEL);
|
info = kzalloc(sizeof *info, GFP_KERNEL);
|
||||||
if (info == NULL)
|
if (info == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@@ -262,7 +262,8 @@ struct uvc_control {
|
|||||||
modified : 1,
|
modified : 1,
|
||||||
cached : 1;
|
cached : 1;
|
||||||
|
|
||||||
__u8 *data;
|
__u8 *uvc_data;
|
||||||
|
__u8 *uvc_info;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uvc_format_desc {
|
struct uvc_format_desc {
|
||||||
|
Reference in New Issue
Block a user