diff options
author | Martin Rubli <martin_rubli@logitech.com> | 2010-10-02 18:10:16 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2011-05-20 08:30:26 -0400 |
commit | fe78d187fe792fac5d190b19a2806c23df28891e (patch) | |
tree | dafe6b4217b6a731b8f107df9694c2dcc58071fe /drivers/media | |
parent | 837d50b5648347b1a011f42e474eeb5f671cc259 (diff) |
[media] uvcvideo: Add UVCIOC_CTRL_QUERY ioctl
This ioctl extends UVCIOC_CTRL_GET/SET by not only allowing to get/set
XU controls but to also send arbitrary UVC commands to XU controls,
namely GET_CUR, SET_CUR, GET_MIN, GET_MAX, GET_RES, GET_LEN, GET_INFO
and GET_DEF. This is required for applications to work with XU controls,
so that they can properly query the size and allocate the necessary
buffers.
Signed-off-by: Martin Rubli <martin_rubli@logitech.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 92 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvc_v4l2.c | 19 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvcvideo.h | 13 |
3 files changed, 88 insertions, 36 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 59f8a9ad3796..47175ccd2f01 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c | |||
@@ -1344,32 +1344,33 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev, | |||
1344 | } | 1344 | } |
1345 | 1345 | ||
1346 | int uvc_xu_ctrl_query(struct uvc_video_chain *chain, | 1346 | int uvc_xu_ctrl_query(struct uvc_video_chain *chain, |
1347 | struct uvc_xu_control *xctrl, int set) | 1347 | struct uvc_xu_control_query *xqry) |
1348 | { | 1348 | { |
1349 | struct uvc_entity *entity; | 1349 | struct uvc_entity *entity; |
1350 | struct uvc_control *ctrl = NULL; | 1350 | struct uvc_control *ctrl; |
1351 | unsigned int i, found = 0; | 1351 | unsigned int i, found = 0; |
1352 | int restore = 0; | 1352 | __u32 reqflags; |
1353 | __u8 *data; | 1353 | __u16 size; |
1354 | __u8 *data = NULL; | ||
1354 | int ret; | 1355 | int ret; |
1355 | 1356 | ||
1356 | /* Find the extension unit. */ | 1357 | /* Find the extension unit. */ |
1357 | list_for_each_entry(entity, &chain->entities, chain) { | 1358 | list_for_each_entry(entity, &chain->entities, chain) { |
1358 | if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && | 1359 | if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && |
1359 | entity->id == xctrl->unit) | 1360 | entity->id == xqry->unit) |
1360 | break; | 1361 | break; |
1361 | } | 1362 | } |
1362 | 1363 | ||
1363 | if (entity->id != xctrl->unit) { | 1364 | if (entity->id != xqry->unit) { |
1364 | uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", | 1365 | uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", |
1365 | xctrl->unit); | 1366 | xqry->unit); |
1366 | return -EINVAL; | 1367 | return -ENOENT; |
1367 | } | 1368 | } |
1368 | 1369 | ||
1369 | /* Find the control and perform delayed initialization if needed. */ | 1370 | /* Find the control and perform delayed initialization if needed. */ |
1370 | for (i = 0; i < entity->ncontrols; ++i) { | 1371 | for (i = 0; i < entity->ncontrols; ++i) { |
1371 | ctrl = &entity->controls[i]; | 1372 | ctrl = &entity->controls[i]; |
1372 | if (ctrl->index == xctrl->selector - 1) { | 1373 | if (ctrl->index == xqry->selector - 1) { |
1373 | found = 1; | 1374 | found = 1; |
1374 | break; | 1375 | break; |
1375 | } | 1376 | } |
@@ -1377,8 +1378,8 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, | |||
1377 | 1378 | ||
1378 | if (!found) { | 1379 | if (!found) { |
1379 | uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n", | 1380 | uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n", |
1380 | entity->extension.guidExtensionCode, xctrl->selector); | 1381 | entity->extension.guidExtensionCode, xqry->selector); |
1381 | return -EINVAL; | 1382 | return -ENOENT; |
1382 | } | 1383 | } |
1383 | 1384 | ||
1384 | if (mutex_lock_interruptible(&chain->ctrl_mutex)) | 1385 | if (mutex_lock_interruptible(&chain->ctrl_mutex)) |
@@ -1390,43 +1391,72 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, | |||
1390 | goto done; | 1391 | goto done; |
1391 | } | 1392 | } |
1392 | 1393 | ||
1393 | /* Validate control data size. */ | 1394 | /* Validate the required buffer size and flags for the request */ |
1394 | if (ctrl->info.size != xctrl->size) { | 1395 | reqflags = 0; |
1396 | size = ctrl->info.size; | ||
1397 | |||
1398 | switch (xqry->query) { | ||
1399 | case UVC_GET_CUR: | ||
1400 | reqflags = UVC_CONTROL_GET_CUR; | ||
1401 | break; | ||
1402 | case UVC_GET_MIN: | ||
1403 | reqflags = UVC_CONTROL_GET_MIN; | ||
1404 | break; | ||
1405 | case UVC_GET_MAX: | ||
1406 | reqflags = UVC_CONTROL_GET_MAX; | ||
1407 | break; | ||
1408 | case UVC_GET_DEF: | ||
1409 | reqflags = UVC_CONTROL_GET_DEF; | ||
1410 | break; | ||
1411 | case UVC_GET_RES: | ||
1412 | reqflags = UVC_CONTROL_GET_RES; | ||
1413 | break; | ||
1414 | case UVC_SET_CUR: | ||
1415 | reqflags = UVC_CONTROL_SET_CUR; | ||
1416 | break; | ||
1417 | case UVC_GET_LEN: | ||
1418 | size = 2; | ||
1419 | break; | ||
1420 | case UVC_GET_INFO: | ||
1421 | size = 1; | ||
1422 | break; | ||
1423 | default: | ||
1395 | ret = -EINVAL; | 1424 | ret = -EINVAL; |
1396 | goto done; | 1425 | goto done; |
1397 | } | 1426 | } |
1398 | 1427 | ||
1399 | if ((set && !(ctrl->info.flags & UVC_CONTROL_SET_CUR)) || | 1428 | if (size != xqry->size) { |
1400 | (!set && !(ctrl->info.flags & UVC_CONTROL_GET_CUR))) { | 1429 | ret = -ENOBUFS; |
1401 | ret = -EINVAL; | ||
1402 | goto done; | 1430 | goto done; |
1403 | } | 1431 | } |
1404 | 1432 | ||
1405 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), | 1433 | if (reqflags && !(ctrl->info.flags & reqflags)) { |
1406 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | 1434 | ret = -EBADRQC; |
1407 | ctrl->info.size); | 1435 | goto done; |
1408 | data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT); | 1436 | } |
1409 | restore = set; | ||
1410 | 1437 | ||
1411 | if (set && copy_from_user(data, xctrl->data, xctrl->size)) { | 1438 | data = kmalloc(size, GFP_KERNEL); |
1439 | if (data == NULL) { | ||
1440 | ret = -ENOMEM; | ||
1441 | goto done; | ||
1442 | } | ||
1443 | |||
1444 | if (xqry->query == UVC_SET_CUR && | ||
1445 | copy_from_user(data, xqry->data, size)) { | ||
1412 | ret = -EFAULT; | 1446 | ret = -EFAULT; |
1413 | goto done; | 1447 | goto done; |
1414 | } | 1448 | } |
1415 | 1449 | ||
1416 | ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR, | 1450 | ret = uvc_query_ctrl(chain->dev, xqry->query, xqry->unit, |
1417 | xctrl->unit, chain->dev->intfnum, xctrl->selector, | 1451 | chain->dev->intfnum, xqry->selector, data, size); |
1418 | data, xctrl->size); | ||
1419 | if (ret < 0) | 1452 | if (ret < 0) |
1420 | goto done; | 1453 | goto done; |
1421 | 1454 | ||
1422 | if (!set && copy_to_user(xctrl->data, data, xctrl->size)) | 1455 | if (xqry->query != UVC_SET_CUR && |
1456 | copy_to_user(xqry->data, data, size)) | ||
1423 | ret = -EFAULT; | 1457 | ret = -EFAULT; |
1424 | done: | 1458 | done: |
1425 | if (ret && restore) | 1459 | kfree(data); |
1426 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | ||
1427 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), | ||
1428 | xctrl->size); | ||
1429 | |||
1430 | mutex_unlock(&chain->ctrl_mutex); | 1460 | mutex_unlock(&chain->ctrl_mutex); |
1431 | return ret; | 1461 | return ret; |
1432 | } | 1462 | } |
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 9005a8d9d5f8..74323362c8e6 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c | |||
@@ -1029,10 +1029,23 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
1029 | cmd == UVCIOC_CTRL_MAP_OLD); | 1029 | cmd == UVCIOC_CTRL_MAP_OLD); |
1030 | 1030 | ||
1031 | case UVCIOC_CTRL_GET: | 1031 | case UVCIOC_CTRL_GET: |
1032 | return uvc_xu_ctrl_query(chain, arg, 0); | ||
1033 | |||
1034 | case UVCIOC_CTRL_SET: | 1032 | case UVCIOC_CTRL_SET: |
1035 | return uvc_xu_ctrl_query(chain, arg, 1); | 1033 | { |
1034 | struct uvc_xu_control *xctrl = arg; | ||
1035 | struct uvc_xu_control_query xqry = { | ||
1036 | .unit = xctrl->unit, | ||
1037 | .selector = xctrl->selector, | ||
1038 | .query = cmd == UVCIOC_CTRL_GET | ||
1039 | ? UVC_GET_CUR : UVC_SET_CUR, | ||
1040 | .size = xctrl->size, | ||
1041 | .data = xctrl->data, | ||
1042 | }; | ||
1043 | |||
1044 | return uvc_xu_ctrl_query(chain, &xqry); | ||
1045 | } | ||
1046 | |||
1047 | case UVCIOC_CTRL_QUERY: | ||
1048 | return uvc_xu_ctrl_query(chain, arg); | ||
1036 | 1049 | ||
1037 | default: | 1050 | default: |
1038 | uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); | 1051 | uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); |
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 45f01e7e13d2..a3f4d308358a 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h | |||
@@ -57,7 +57,7 @@ struct uvc_xu_control_mapping { | |||
57 | 57 | ||
58 | __u8 size; | 58 | __u8 size; |
59 | __u8 offset; | 59 | __u8 offset; |
60 | enum v4l2_ctrl_type v4l2_type; | 60 | __u32 v4l2_type; |
61 | __u32 data_type; | 61 | __u32 data_type; |
62 | 62 | ||
63 | struct uvc_menu_info __user *menu_info; | 63 | struct uvc_menu_info __user *menu_info; |
@@ -73,11 +73,20 @@ struct uvc_xu_control { | |||
73 | __u8 __user *data; | 73 | __u8 __user *data; |
74 | }; | 74 | }; |
75 | 75 | ||
76 | struct uvc_xu_control_query { | ||
77 | __u8 unit; | ||
78 | __u8 selector; | ||
79 | __u8 query; | ||
80 | __u16 size; | ||
81 | __u8 __user *data; | ||
82 | }; | ||
83 | |||
76 | #define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) | 84 | #define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info) |
77 | #define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old) | 85 | #define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old) |
78 | #define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) | 86 | #define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping) |
79 | #define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) | 87 | #define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control) |
80 | #define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) | 88 | #define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control) |
89 | #define UVCIOC_CTRL_QUERY _IOWR('U', 5, struct uvc_xu_control_query) | ||
81 | 90 | ||
82 | #ifdef __KERNEL__ | 91 | #ifdef __KERNEL__ |
83 | 92 | ||
@@ -638,7 +647,7 @@ extern int uvc_ctrl_set(struct uvc_video_chain *chain, | |||
638 | struct v4l2_ext_control *xctrl); | 647 | struct v4l2_ext_control *xctrl); |
639 | 648 | ||
640 | extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain, | 649 | extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain, |
641 | struct uvc_xu_control *ctrl, int set); | 650 | struct uvc_xu_control_query *xqry); |
642 | 651 | ||
643 | /* Utility functions */ | 652 | /* Utility functions */ |
644 | extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, | 653 | extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, |