aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorMartin Rubli <martin_rubli@logitech.com>2010-10-02 18:10:16 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-05-20 08:30:26 -0400
commitfe78d187fe792fac5d190b19a2806c23df28891e (patch)
treedafe6b4217b6a731b8f107df9694c2dcc58071fe /drivers/media
parent837d50b5648347b1a011f42e474eeb5f671cc259 (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.c92
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c19
-rw-r--r--drivers/media/video/uvc/uvcvideo.h13
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
1346int uvc_xu_ctrl_query(struct uvc_video_chain *chain, 1346int 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;
1424done: 1458done:
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
76struct 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
640extern int uvc_xu_ctrl_query(struct uvc_video_chain *chain, 649extern 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 */
644extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator, 653extern void uvc_simplify_fraction(uint32_t *numerator, uint32_t *denominator,