diff options
Diffstat (limited to 'drivers/media/video/videodev.c')
-rw-r--r-- | drivers/media/video/videodev.c | 105 |
1 files changed, 103 insertions, 2 deletions
diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 43152297e6d5..763e178555d0 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c | |||
@@ -190,10 +190,15 @@ video_usercopy(struct inode *inode, struct file *file, | |||
190 | void *mbuf = NULL; | 190 | void *mbuf = NULL; |
191 | void *parg = NULL; | 191 | void *parg = NULL; |
192 | int err = -EINVAL; | 192 | int err = -EINVAL; |
193 | int is_ext_ctrl; | ||
194 | size_t ctrls_size = 0; | ||
195 | void __user *user_ptr = NULL; | ||
193 | 196 | ||
194 | #ifdef __OLD_VIDIOC_ | 197 | #ifdef __OLD_VIDIOC_ |
195 | cmd = video_fix_command(cmd); | 198 | cmd = video_fix_command(cmd); |
196 | #endif | 199 | #endif |
200 | is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || | ||
201 | cmd == VIDIOC_TRY_EXT_CTRLS); | ||
197 | 202 | ||
198 | /* Copy arguments into temp kernel buffer */ | 203 | /* Copy arguments into temp kernel buffer */ |
199 | switch (_IOC_DIR(cmd)) { | 204 | switch (_IOC_DIR(cmd)) { |
@@ -215,19 +220,47 @@ video_usercopy(struct inode *inode, struct file *file, | |||
215 | 220 | ||
216 | err = -EFAULT; | 221 | err = -EFAULT; |
217 | if (_IOC_DIR(cmd) & _IOC_WRITE) | 222 | if (_IOC_DIR(cmd) & _IOC_WRITE) |
218 | if (copy_from_user(parg, (void __user *)arg, | 223 | if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) |
219 | _IOC_SIZE(cmd))) | ||
220 | goto out; | 224 | goto out; |
221 | break; | 225 | break; |
222 | } | 226 | } |
227 | if (is_ext_ctrl) { | ||
228 | struct v4l2_ext_controls *p = parg; | ||
229 | |||
230 | /* In case of an error, tell the caller that it wasn't | ||
231 | a specific control that caused it. */ | ||
232 | p->error_idx = p->count; | ||
233 | user_ptr = (void __user *)p->controls; | ||
234 | if (p->count) { | ||
235 | ctrls_size = sizeof(struct v4l2_ext_control) * p->count; | ||
236 | /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ | ||
237 | mbuf = kmalloc(ctrls_size, GFP_KERNEL); | ||
238 | err = -ENOMEM; | ||
239 | if (NULL == mbuf) | ||
240 | goto out_ext_ctrl; | ||
241 | err = -EFAULT; | ||
242 | if (copy_from_user(mbuf, user_ptr, ctrls_size)) | ||
243 | goto out_ext_ctrl; | ||
244 | p->controls = mbuf; | ||
245 | } | ||
246 | } | ||
223 | 247 | ||
224 | /* call driver */ | 248 | /* call driver */ |
225 | err = func(inode, file, cmd, parg); | 249 | err = func(inode, file, cmd, parg); |
226 | if (err == -ENOIOCTLCMD) | 250 | if (err == -ENOIOCTLCMD) |
227 | err = -EINVAL; | 251 | err = -EINVAL; |
252 | if (is_ext_ctrl) { | ||
253 | struct v4l2_ext_controls *p = parg; | ||
254 | |||
255 | p->controls = (void *)user_ptr; | ||
256 | if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) | ||
257 | err = -EFAULT; | ||
258 | goto out_ext_ctrl; | ||
259 | } | ||
228 | if (err < 0) | 260 | if (err < 0) |
229 | goto out; | 261 | goto out; |
230 | 262 | ||
263 | out_ext_ctrl: | ||
231 | /* Copy results into user buffer */ | 264 | /* Copy results into user buffer */ |
232 | switch (_IOC_DIR(cmd)) | 265 | switch (_IOC_DIR(cmd)) |
233 | { | 266 | { |
@@ -993,6 +1026,39 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, | |||
993 | ret=vfd->vidioc_s_ctrl(file, fh, p); | 1026 | ret=vfd->vidioc_s_ctrl(file, fh, p); |
994 | break; | 1027 | break; |
995 | } | 1028 | } |
1029 | case VIDIOC_G_EXT_CTRLS: | ||
1030 | { | ||
1031 | struct v4l2_ext_controls *p = arg; | ||
1032 | |||
1033 | if (vfd->vidioc_g_ext_ctrls) { | ||
1034 | dbgarg(cmd, "count=%d\n", p->count); | ||
1035 | |||
1036 | ret=vfd->vidioc_g_ext_ctrls(file, fh, p); | ||
1037 | } | ||
1038 | break; | ||
1039 | } | ||
1040 | case VIDIOC_S_EXT_CTRLS: | ||
1041 | { | ||
1042 | struct v4l2_ext_controls *p = arg; | ||
1043 | |||
1044 | if (vfd->vidioc_s_ext_ctrls) { | ||
1045 | dbgarg(cmd, "count=%d\n", p->count); | ||
1046 | |||
1047 | ret=vfd->vidioc_s_ext_ctrls(file, fh, p); | ||
1048 | } | ||
1049 | break; | ||
1050 | } | ||
1051 | case VIDIOC_TRY_EXT_CTRLS: | ||
1052 | { | ||
1053 | struct v4l2_ext_controls *p = arg; | ||
1054 | |||
1055 | if (vfd->vidioc_try_ext_ctrls) { | ||
1056 | dbgarg(cmd, "count=%d\n", p->count); | ||
1057 | |||
1058 | ret=vfd->vidioc_try_ext_ctrls(file, fh, p); | ||
1059 | } | ||
1060 | break; | ||
1061 | } | ||
996 | case VIDIOC_QUERYMENU: | 1062 | case VIDIOC_QUERYMENU: |
997 | { | 1063 | { |
998 | struct v4l2_querymenu *p=arg; | 1064 | struct v4l2_querymenu *p=arg; |
@@ -1326,10 +1392,15 @@ int video_ioctl2 (struct inode *inode, struct file *file, | |||
1326 | void *mbuf = NULL; | 1392 | void *mbuf = NULL; |
1327 | void *parg = NULL; | 1393 | void *parg = NULL; |
1328 | int err = -EINVAL; | 1394 | int err = -EINVAL; |
1395 | int is_ext_ctrl; | ||
1396 | size_t ctrls_size = 0; | ||
1397 | void __user *user_ptr = NULL; | ||
1329 | 1398 | ||
1330 | #ifdef __OLD_VIDIOC_ | 1399 | #ifdef __OLD_VIDIOC_ |
1331 | cmd = video_fix_command(cmd); | 1400 | cmd = video_fix_command(cmd); |
1332 | #endif | 1401 | #endif |
1402 | is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS || | ||
1403 | cmd == VIDIOC_TRY_EXT_CTRLS); | ||
1333 | 1404 | ||
1334 | /* Copy arguments into temp kernel buffer */ | 1405 | /* Copy arguments into temp kernel buffer */ |
1335 | switch (_IOC_DIR(cmd)) { | 1406 | switch (_IOC_DIR(cmd)) { |
@@ -1356,13 +1427,43 @@ int video_ioctl2 (struct inode *inode, struct file *file, | |||
1356 | break; | 1427 | break; |
1357 | } | 1428 | } |
1358 | 1429 | ||
1430 | if (is_ext_ctrl) { | ||
1431 | struct v4l2_ext_controls *p = parg; | ||
1432 | |||
1433 | /* In case of an error, tell the caller that it wasn't | ||
1434 | a specific control that caused it. */ | ||
1435 | p->error_idx = p->count; | ||
1436 | user_ptr = (void __user *)p->controls; | ||
1437 | if (p->count) { | ||
1438 | ctrls_size = sizeof(struct v4l2_ext_control) * p->count; | ||
1439 | /* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */ | ||
1440 | mbuf = kmalloc(ctrls_size, GFP_KERNEL); | ||
1441 | err = -ENOMEM; | ||
1442 | if (NULL == mbuf) | ||
1443 | goto out_ext_ctrl; | ||
1444 | err = -EFAULT; | ||
1445 | if (copy_from_user(mbuf, user_ptr, ctrls_size)) | ||
1446 | goto out_ext_ctrl; | ||
1447 | p->controls = mbuf; | ||
1448 | } | ||
1449 | } | ||
1450 | |||
1359 | /* Handles IOCTL */ | 1451 | /* Handles IOCTL */ |
1360 | err = __video_do_ioctl(inode, file, cmd, parg); | 1452 | err = __video_do_ioctl(inode, file, cmd, parg); |
1361 | if (err == -ENOIOCTLCMD) | 1453 | if (err == -ENOIOCTLCMD) |
1362 | err = -EINVAL; | 1454 | err = -EINVAL; |
1455 | if (is_ext_ctrl) { | ||
1456 | struct v4l2_ext_controls *p = parg; | ||
1457 | |||
1458 | p->controls = (void *)user_ptr; | ||
1459 | if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size)) | ||
1460 | err = -EFAULT; | ||
1461 | goto out_ext_ctrl; | ||
1462 | } | ||
1363 | if (err < 0) | 1463 | if (err < 0) |
1364 | goto out; | 1464 | goto out; |
1365 | 1465 | ||
1466 | out_ext_ctrl: | ||
1366 | /* Copy results into user buffer */ | 1467 | /* Copy results into user buffer */ |
1367 | switch (_IOC_DIR(cmd)) | 1468 | switch (_IOC_DIR(cmd)) |
1368 | { | 1469 | { |