diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2010-10-02 08:04:53 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-10-20 23:18:26 -0400 |
commit | 27a61c13ec5d9c1da5286fd1697ab5930b916b4f (patch) | |
tree | fc2853c64f1558c94d1665c9c1537d2823e43c43 /drivers/media/video/uvc | |
parent | b5977a58c330ac6afdc64bab9b1dd674f11c0635 (diff) |
[media] uvcvideo: Fix uvc_query_v4l2_ctrl() and uvc_xu_ctrl_query() locking
Take the ctrl_mutex mutex before touching control information in those
functions.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/uvc')
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 63 | ||||
-rw-r--r-- | drivers/media/video/uvc/uvcvideo.h | 2 |
2 files changed, 39 insertions, 26 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 0d310c422412..f169f7736677 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c | |||
@@ -863,9 +863,15 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, | |||
863 | unsigned int i; | 863 | unsigned int i; |
864 | int ret; | 864 | int ret; |
865 | 865 | ||
866 | ret = mutex_lock_interruptible(&chain->ctrl_mutex); | ||
867 | if (ret < 0) | ||
868 | return -ERESTARTSYS; | ||
869 | |||
866 | ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); | 870 | ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); |
867 | if (ctrl == NULL) | 871 | if (ctrl == NULL) { |
868 | return -EINVAL; | 872 | ret = -EINVAL; |
873 | goto done; | ||
874 | } | ||
869 | 875 | ||
870 | memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); | 876 | memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); |
871 | v4l2_ctrl->id = mapping->id; | 877 | v4l2_ctrl->id = mapping->id; |
@@ -881,7 +887,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, | |||
881 | if (!ctrl->cached) { | 887 | if (!ctrl->cached) { |
882 | ret = uvc_ctrl_populate_cache(chain, ctrl); | 888 | ret = uvc_ctrl_populate_cache(chain, ctrl); |
883 | if (ret < 0) | 889 | if (ret < 0) |
884 | return ret; | 890 | goto done; |
885 | } | 891 | } |
886 | 892 | ||
887 | if (ctrl->info.flags & UVC_CONTROL_GET_DEF) { | 893 | if (ctrl->info.flags & UVC_CONTROL_GET_DEF) { |
@@ -903,19 +909,19 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, | |||
903 | } | 909 | } |
904 | } | 910 | } |
905 | 911 | ||
906 | return 0; | 912 | goto done; |
907 | 913 | ||
908 | case V4L2_CTRL_TYPE_BOOLEAN: | 914 | case V4L2_CTRL_TYPE_BOOLEAN: |
909 | v4l2_ctrl->minimum = 0; | 915 | v4l2_ctrl->minimum = 0; |
910 | v4l2_ctrl->maximum = 1; | 916 | v4l2_ctrl->maximum = 1; |
911 | v4l2_ctrl->step = 1; | 917 | v4l2_ctrl->step = 1; |
912 | return 0; | 918 | goto done; |
913 | 919 | ||
914 | case V4L2_CTRL_TYPE_BUTTON: | 920 | case V4L2_CTRL_TYPE_BUTTON: |
915 | v4l2_ctrl->minimum = 0; | 921 | v4l2_ctrl->minimum = 0; |
916 | v4l2_ctrl->maximum = 0; | 922 | v4l2_ctrl->maximum = 0; |
917 | v4l2_ctrl->step = 0; | 923 | v4l2_ctrl->step = 0; |
918 | return 0; | 924 | goto done; |
919 | 925 | ||
920 | default: | 926 | default: |
921 | break; | 927 | break; |
@@ -933,7 +939,9 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, | |||
933 | v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, | 939 | v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, |
934 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); | 940 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); |
935 | 941 | ||
936 | return 0; | 942 | done: |
943 | mutex_unlock(&chain->ctrl_mutex); | ||
944 | return ret; | ||
937 | } | 945 | } |
938 | 946 | ||
939 | 947 | ||
@@ -1295,6 +1303,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, | |||
1295 | struct uvc_entity *entity; | 1303 | struct uvc_entity *entity; |
1296 | struct uvc_control *ctrl = NULL; | 1304 | struct uvc_control *ctrl = NULL; |
1297 | unsigned int i, found = 0; | 1305 | unsigned int i, found = 0; |
1306 | int restore = 0; | ||
1298 | __u8 *data; | 1307 | __u8 *data; |
1299 | int ret; | 1308 | int ret; |
1300 | 1309 | ||
@@ -1326,44 +1335,48 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, | |||
1326 | return -EINVAL; | 1335 | return -EINVAL; |
1327 | } | 1336 | } |
1328 | 1337 | ||
1338 | if (mutex_lock_interruptible(&chain->ctrl_mutex)) | ||
1339 | return -ERESTARTSYS; | ||
1340 | |||
1329 | ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl); | 1341 | ret = uvc_ctrl_init_xu_ctrl(chain->dev, ctrl); |
1330 | if (ret < 0) | 1342 | if (ret < 0) { |
1331 | return -ENOENT; | 1343 | ret = -ENOENT; |
1344 | goto done; | ||
1345 | } | ||
1332 | 1346 | ||
1333 | /* Validate control data size. */ | 1347 | /* Validate control data size. */ |
1334 | if (ctrl->info.size != xctrl->size) | 1348 | if (ctrl->info.size != xctrl->size) { |
1335 | return -EINVAL; | 1349 | ret = -EINVAL; |
1350 | goto done; | ||
1351 | } | ||
1336 | 1352 | ||
1337 | if ((set && !(ctrl->info.flags & UVC_CONTROL_SET_CUR)) || | 1353 | if ((set && !(ctrl->info.flags & UVC_CONTROL_SET_CUR)) || |
1338 | (!set && !(ctrl->info.flags & UVC_CONTROL_GET_CUR))) | 1354 | (!set && !(ctrl->info.flags & UVC_CONTROL_GET_CUR))) { |
1339 | return -EINVAL; | 1355 | ret = -EINVAL; |
1340 | 1356 | goto done; | |
1341 | if (mutex_lock_interruptible(&chain->ctrl_mutex)) | 1357 | } |
1342 | return -ERESTARTSYS; | ||
1343 | 1358 | ||
1344 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), | 1359 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), |
1345 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | 1360 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), |
1346 | xctrl->size); | 1361 | ctrl->info.size); |
1347 | data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT); | 1362 | data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT); |
1363 | restore = set; | ||
1348 | 1364 | ||
1349 | if (set && copy_from_user(data, xctrl->data, xctrl->size)) { | 1365 | if (set && copy_from_user(data, xctrl->data, xctrl->size)) { |
1350 | ret = -EFAULT; | 1366 | ret = -EFAULT; |
1351 | goto out; | 1367 | goto done; |
1352 | } | 1368 | } |
1353 | 1369 | ||
1354 | ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR, | 1370 | ret = uvc_query_ctrl(chain->dev, set ? UVC_SET_CUR : UVC_GET_CUR, |
1355 | xctrl->unit, chain->dev->intfnum, xctrl->selector, | 1371 | xctrl->unit, chain->dev->intfnum, xctrl->selector, |
1356 | data, xctrl->size); | 1372 | data, xctrl->size); |
1357 | if (ret < 0) | 1373 | if (ret < 0) |
1358 | goto out; | 1374 | goto done; |
1359 | 1375 | ||
1360 | if (!set && copy_to_user(xctrl->data, data, xctrl->size)) { | 1376 | if (!set && copy_to_user(xctrl->data, data, xctrl->size)) |
1361 | ret = -EFAULT; | 1377 | ret = -EFAULT; |
1362 | goto out; | 1378 | done: |
1363 | } | 1379 | if (ret && restore) |
1364 | |||
1365 | out: | ||
1366 | if (ret) | ||
1367 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | 1380 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), |
1368 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), | 1381 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), |
1369 | xctrl->size); | 1382 | xctrl->size); |
diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 7d67d95de838..d97cf6d6a4f9 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h | |||
@@ -413,7 +413,7 @@ struct uvc_video_chain { | |||
413 | struct uvc_entity *processing; /* Processing unit */ | 413 | struct uvc_entity *processing; /* Processing unit */ |
414 | struct uvc_entity *selector; /* Selector unit */ | 414 | struct uvc_entity *selector; /* Selector unit */ |
415 | 415 | ||
416 | struct mutex ctrl_mutex; | 416 | struct mutex ctrl_mutex; /* Protects ctrl.info */ |
417 | }; | 417 | }; |
418 | 418 | ||
419 | struct uvc_streaming { | 419 | struct uvc_streaming { |