diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2010-06-18 10:31:24 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-08-02 14:34:10 -0400 |
commit | b30ece53946ad2b79304ee5cfdb18b361dc3a3fc (patch) | |
tree | 80dc24b56b1a38d03d50e8077ae275ec02391847 | |
parent | 1b4e21c4f62eae6bdcb3e7bfdfc52171a24f3689 (diff) |
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>
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 51 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_v4l2.c | 3 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvcvideo.h | 3 |
3 files changed, 38 insertions, 19 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index fa06cf512ecf..a350fad0db43 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c | |||
@@ -643,7 +643,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { | |||
643 | 643 | ||
644 | static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) | 644 | static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) |
645 | { | 645 | { |
646 | return ctrl->data + id * ctrl->info->size; | 646 | return ctrl->uvc_data + id * ctrl->info->size; |
647 | } | 647 | } |
648 | 648 | ||
649 | static inline int uvc_test_bit(const __u8 *data, int bit) | 649 | static inline int uvc_test_bit(const __u8 *data, int bit) |
@@ -1293,13 +1293,15 @@ int uvc_ctrl_resume_device(struct uvc_device *dev) | |||
1293 | * Control and mapping handling | 1293 | * Control and mapping handling |
1294 | */ | 1294 | */ |
1295 | 1295 | ||
1296 | static void uvc_ctrl_add_ctrl(struct uvc_device *dev, | 1296 | static int uvc_ctrl_add_ctrl(struct uvc_device *dev, |
1297 | struct uvc_control_info *info) | 1297 | struct uvc_control_info *info) |
1298 | { | 1298 | { |
1299 | struct uvc_entity *entity; | 1299 | struct uvc_entity *entity; |
1300 | struct uvc_control *ctrl = NULL; | 1300 | struct uvc_control *ctrl = NULL; |
1301 | int ret, found = 0; | 1301 | int ret = 0, found = 0; |
1302 | unsigned int i; | 1302 | unsigned int i; |
1303 | u8 *uvc_info; | ||
1304 | u8 *uvc_data; | ||
1303 | 1305 | ||
1304 | list_for_each_entry(entity, &dev->entities, list) { | 1306 | list_for_each_entry(entity, &dev->entities, list) { |
1305 | if (!uvc_entity_match_guid(entity, info->entity)) | 1307 | if (!uvc_entity_match_guid(entity, info->entity)) |
@@ -1318,56 +1320,69 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, | |||
1318 | } | 1320 | } |
1319 | 1321 | ||
1320 | if (!found) | 1322 | if (!found) |
1321 | return; | 1323 | return 0; |
1324 | |||
1325 | uvc_data = kmalloc(info->size * UVC_CTRL_DATA_LAST + 1, GFP_KERNEL); | ||
1326 | if (uvc_data == NULL) | ||
1327 | return -ENOMEM; | ||
1328 | |||
1329 | uvc_info = uvc_data + info->size * UVC_CTRL_DATA_LAST; | ||
1322 | 1330 | ||
1323 | if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { | 1331 | if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) { |
1324 | /* Check if the device control information and length match | 1332 | /* Check if the device control information and length match |
1325 | * the user supplied information. | 1333 | * the user supplied information. |
1326 | */ | 1334 | */ |
1327 | __le16 size; | ||
1328 | __u8 _info; | ||
1329 | |||
1330 | ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, | 1335 | ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, |
1331 | dev->intfnum, info->selector, (__u8 *)&size, 2); | 1336 | dev->intfnum, info->selector, uvc_data, 2); |
1332 | if (ret < 0) { | 1337 | if (ret < 0) { |
1333 | uvc_trace(UVC_TRACE_CONTROL, | 1338 | uvc_trace(UVC_TRACE_CONTROL, |
1334 | "GET_LEN failed on control %pUl/%u (%d).\n", | 1339 | "GET_LEN failed on control %pUl/%u (%d).\n", |
1335 | info->entity, info->selector, ret); | 1340 | info->entity, info->selector, ret); |
1336 | return; | 1341 | goto done; |
1337 | } | 1342 | } |
1338 | 1343 | ||
1339 | if (info->size != le16_to_cpu(size)) { | 1344 | if (info->size != le16_to_cpu(*(__le16 *)uvc_data)) { |
1340 | uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size " | 1345 | uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size " |
1341 | "doesn't match user supplied value.\n", | 1346 | "doesn't match user supplied value.\n", |
1342 | info->entity, info->selector); | 1347 | info->entity, info->selector); |
1343 | return; | 1348 | ret = -EINVAL; |
1349 | goto done; | ||
1344 | } | 1350 | } |
1345 | 1351 | ||
1346 | ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, | 1352 | ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, |
1347 | dev->intfnum, info->selector, &_info, 1); | 1353 | dev->intfnum, info->selector, uvc_info, 1); |
1348 | if (ret < 0) { | 1354 | if (ret < 0) { |
1349 | uvc_trace(UVC_TRACE_CONTROL, | 1355 | uvc_trace(UVC_TRACE_CONTROL, |
1350 | "GET_INFO failed on control %pUl/%u (%d).\n", | 1356 | "GET_INFO failed on control %pUl/%u (%d).\n", |
1351 | info->entity, info->selector, ret); | 1357 | info->entity, info->selector, ret); |
1352 | return; | 1358 | goto done; |
1353 | } | 1359 | } |
1354 | 1360 | ||
1355 | if (((info->flags & UVC_CONTROL_GET_CUR) && | 1361 | if (((info->flags & UVC_CONTROL_GET_CUR) && |
1356 | !(_info & UVC_CONTROL_CAP_GET)) || | 1362 | !(*uvc_info & UVC_CONTROL_CAP_GET)) || |
1357 | ((info->flags & UVC_CONTROL_SET_CUR) && | 1363 | ((info->flags & UVC_CONTROL_SET_CUR) && |
1358 | !(_info & UVC_CONTROL_CAP_SET))) { | 1364 | !(*uvc_info & UVC_CONTROL_CAP_SET))) { |
1359 | uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags " | 1365 | uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags " |
1360 | "don't match supported operations.\n", | 1366 | "don't match supported operations.\n", |
1361 | info->entity, info->selector); | 1367 | info->entity, info->selector); |
1362 | return; | 1368 | ret = -EINVAL; |
1369 | goto done; | ||
1363 | } | 1370 | } |
1364 | } | 1371 | } |
1365 | 1372 | ||
1366 | ctrl->info = info; | 1373 | ctrl->info = info; |
1367 | ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_DATA_LAST, GFP_KERNEL); | 1374 | ctrl->uvc_data = uvc_data; |
1375 | ctrl->uvc_info = uvc_info; | ||
1376 | |||
1368 | uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s " | 1377 | uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s " |
1369 | "entity %u\n", ctrl->info->entity, ctrl->info->selector, | 1378 | "entity %u\n", ctrl->info->entity, ctrl->info->selector, |
1370 | dev->udev->devpath, entity->id); | 1379 | dev->udev->devpath, entity->id); |
1380 | |||
1381 | done: | ||
1382 | if (ret < 0) | ||
1383 | kfree(uvc_data); | ||
1384 | |||
1385 | return ret; | ||
1371 | } | 1386 | } |
1372 | 1387 | ||
1373 | /* | 1388 | /* |
@@ -1600,7 +1615,7 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev) | |||
1600 | 1615 | ||
1601 | list_for_each_entry(entity, &dev->entities, list) { | 1616 | list_for_each_entry(entity, &dev->entities, list) { |
1602 | for (i = 0; i < entity->ncontrols; ++i) | 1617 | for (i = 0; i < entity->ncontrols; ++i) |
1603 | kfree(entity->controls[i].data); | 1618 | kfree(entity->controls[i].uvc_data); |
1604 | 1619 | ||
1605 | kfree(entity->controls); | 1620 | kfree(entity->controls); |
1606 | } | 1621 | } |
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 485a89912cef..369ce06be035 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c | |||
@@ -1028,6 +1028,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
1028 | if (!capable(CAP_SYS_ADMIN)) | 1028 | if (!capable(CAP_SYS_ADMIN)) |
1029 | return -EPERM; | 1029 | return -EPERM; |
1030 | 1030 | ||
1031 | if (xinfo->size == 0) | ||
1032 | return -EINVAL; | ||
1033 | |||
1031 | info = kzalloc(sizeof *info, GFP_KERNEL); | 1034 | info = kzalloc(sizeof *info, GFP_KERNEL); |
1032 | if (info == NULL) | 1035 | if (info == NULL) |
1033 | return -ENOMEM; | 1036 | return -ENOMEM; |
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 14f77e42fd45..47b20e7e3786 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h | |||
@@ -262,7 +262,8 @@ struct uvc_control { | |||
262 | modified : 1, | 262 | modified : 1, |
263 | cached : 1; | 263 | cached : 1; |
264 | 264 | ||
265 | __u8 *data; | 265 | __u8 *uvc_data; |
266 | __u8 *uvc_info; | ||
266 | }; | 267 | }; |
267 | 268 | ||
268 | struct uvc_format_desc { | 269 | struct uvc_format_desc { |