diff options
author | Guennadi Liakhovetski <lg@denx.de> | 2008-12-18 10:28:54 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-12-30 06:40:22 -0500 |
commit | 1c3bb7431d16f7486a8523d54380bad89c485dc8 (patch) | |
tree | 89dd8e65f627df726e70de771130398ba73148b3 /drivers/media/video/soc_camera.c | |
parent | bf507158eb27ea94aca300b28ecee60fdbb40007 (diff) |
V4L/DVB (10083): soc-camera: unify locking, play nicer with videobuf locking
Move mutex from host drivers to camera device object, take into account
videobuf locking.
Signed-off-by: Guennadi Liakhovetski <lg@denx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/soc_camera.c')
-rw-r--r-- | drivers/media/video/soc_camera.c | 99 |
1 files changed, 83 insertions, 16 deletions
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index d5613cdd93a6..e869670dbae5 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c | |||
@@ -33,7 +33,6 @@ | |||
33 | static LIST_HEAD(hosts); | 33 | static LIST_HEAD(hosts); |
34 | static LIST_HEAD(devices); | 34 | static LIST_HEAD(devices); |
35 | static DEFINE_MUTEX(list_lock); | 35 | static DEFINE_MUTEX(list_lock); |
36 | static DEFINE_MUTEX(video_lock); | ||
37 | 36 | ||
38 | const struct soc_camera_data_format *soc_camera_format_by_fourcc( | 37 | const struct soc_camera_data_format *soc_camera_format_by_fourcc( |
39 | struct soc_camera_device *icd, unsigned int fourcc) | 38 | struct soc_camera_device *icd, unsigned int fourcc) |
@@ -270,8 +269,10 @@ static int soc_camera_open(struct inode *inode, struct file *file) | |||
270 | if (!icf) | 269 | if (!icf) |
271 | return -ENOMEM; | 270 | return -ENOMEM; |
272 | 271 | ||
273 | /* Protect against icd->remove() until we module_get() both drivers. */ | 272 | /* |
274 | mutex_lock(&video_lock); | 273 | * It is safe to dereference these pointers now as long as a user has |
274 | * the video device open - we are protected by the held cdev reference. | ||
275 | */ | ||
275 | 276 | ||
276 | vdev = video_devdata(file); | 277 | vdev = video_devdata(file); |
277 | icd = container_of(vdev->parent, struct soc_camera_device, dev); | 278 | icd = container_of(vdev->parent, struct soc_camera_device, dev); |
@@ -289,6 +290,9 @@ static int soc_camera_open(struct inode *inode, struct file *file) | |||
289 | goto emgi; | 290 | goto emgi; |
290 | } | 291 | } |
291 | 292 | ||
293 | /* Protect against icd->remove() until we module_get() both drivers. */ | ||
294 | mutex_lock(&icd->video_lock); | ||
295 | |||
292 | icf->icd = icd; | 296 | icf->icd = icd; |
293 | icd->use_count++; | 297 | icd->use_count++; |
294 | 298 | ||
@@ -304,7 +308,7 @@ static int soc_camera_open(struct inode *inode, struct file *file) | |||
304 | } | 308 | } |
305 | } | 309 | } |
306 | 310 | ||
307 | mutex_unlock(&video_lock); | 311 | mutex_unlock(&icd->video_lock); |
308 | 312 | ||
309 | file->private_data = icf; | 313 | file->private_data = icf; |
310 | dev_dbg(&icd->dev, "camera device open\n"); | 314 | dev_dbg(&icd->dev, "camera device open\n"); |
@@ -313,16 +317,16 @@ static int soc_camera_open(struct inode *inode, struct file *file) | |||
313 | 317 | ||
314 | return 0; | 318 | return 0; |
315 | 319 | ||
316 | /* All errors are entered with the video_lock held */ | 320 | /* First two errors are entered with the .video_lock held */ |
317 | eiciadd: | 321 | eiciadd: |
318 | soc_camera_free_user_formats(icd); | 322 | soc_camera_free_user_formats(icd); |
319 | eiufmt: | 323 | eiufmt: |
320 | icd->use_count--; | 324 | icd->use_count--; |
325 | mutex_unlock(&icd->video_lock); | ||
321 | module_put(ici->ops->owner); | 326 | module_put(ici->ops->owner); |
322 | emgi: | 327 | emgi: |
323 | module_put(icd->ops->owner); | 328 | module_put(icd->ops->owner); |
324 | emgd: | 329 | emgd: |
325 | mutex_unlock(&video_lock); | ||
326 | vfree(icf); | 330 | vfree(icf); |
327 | return ret; | 331 | return ret; |
328 | } | 332 | } |
@@ -334,15 +338,16 @@ static int soc_camera_close(struct inode *inode, struct file *file) | |||
334 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 338 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
335 | struct video_device *vdev = icd->vdev; | 339 | struct video_device *vdev = icd->vdev; |
336 | 340 | ||
337 | mutex_lock(&video_lock); | 341 | mutex_lock(&icd->video_lock); |
338 | icd->use_count--; | 342 | icd->use_count--; |
339 | if (!icd->use_count) { | 343 | if (!icd->use_count) { |
340 | ici->ops->remove(icd); | 344 | ici->ops->remove(icd); |
341 | soc_camera_free_user_formats(icd); | 345 | soc_camera_free_user_formats(icd); |
342 | } | 346 | } |
347 | mutex_unlock(&icd->video_lock); | ||
348 | |||
343 | module_put(icd->ops->owner); | 349 | module_put(icd->ops->owner); |
344 | module_put(ici->ops->owner); | 350 | module_put(ici->ops->owner); |
345 | mutex_unlock(&video_lock); | ||
346 | 351 | ||
347 | vfree(icf); | 352 | vfree(icf); |
348 | 353 | ||
@@ -424,18 +429,27 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, | |||
424 | if (ret < 0) | 429 | if (ret < 0) |
425 | return ret; | 430 | return ret; |
426 | 431 | ||
432 | mutex_lock(&icf->vb_vidq.vb_lock); | ||
433 | |||
434 | if (videobuf_queue_is_busy(&icf->vb_vidq)) { | ||
435 | dev_err(&icd->dev, "S_FMT denied: queue busy\n"); | ||
436 | ret = -EBUSY; | ||
437 | goto unlock; | ||
438 | } | ||
439 | |||
427 | rect.left = icd->x_current; | 440 | rect.left = icd->x_current; |
428 | rect.top = icd->y_current; | 441 | rect.top = icd->y_current; |
429 | rect.width = pix->width; | 442 | rect.width = pix->width; |
430 | rect.height = pix->height; | 443 | rect.height = pix->height; |
431 | ret = ici->ops->set_fmt(icd, pix->pixelformat, &rect); | 444 | ret = ici->ops->set_fmt(icd, pix->pixelformat, &rect); |
432 | if (ret < 0) { | 445 | if (ret < 0) { |
433 | return ret; | 446 | goto unlock; |
434 | } else if (!icd->current_fmt || | 447 | } else if (!icd->current_fmt || |
435 | icd->current_fmt->fourcc != pixfmt) { | 448 | icd->current_fmt->fourcc != pixfmt) { |
436 | dev_err(&ici->dev, | 449 | dev_err(&ici->dev, |
437 | "Host driver hasn't set up current format correctly!\n"); | 450 | "Host driver hasn't set up current format correctly!\n"); |
438 | return -EINVAL; | 451 | ret = -EINVAL; |
452 | goto unlock; | ||
439 | } | 453 | } |
440 | 454 | ||
441 | icd->width = rect.width; | 455 | icd->width = rect.width; |
@@ -449,7 +463,12 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, | |||
449 | icd->width, icd->height); | 463 | icd->width, icd->height); |
450 | 464 | ||
451 | /* set physical bus parameters */ | 465 | /* set physical bus parameters */ |
452 | return ici->ops->set_bus_param(icd, pixfmt); | 466 | ret = ici->ops->set_bus_param(icd, pixfmt); |
467 | |||
468 | unlock: | ||
469 | mutex_unlock(&icf->vb_vidq.vb_lock); | ||
470 | |||
471 | return ret; | ||
453 | } | 472 | } |
454 | 473 | ||
455 | static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, | 474 | static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv, |
@@ -510,6 +529,7 @@ static int soc_camera_streamon(struct file *file, void *priv, | |||
510 | { | 529 | { |
511 | struct soc_camera_file *icf = file->private_data; | 530 | struct soc_camera_file *icf = file->private_data; |
512 | struct soc_camera_device *icd = icf->icd; | 531 | struct soc_camera_device *icd = icf->icd; |
532 | int ret; | ||
513 | 533 | ||
514 | WARN_ON(priv != file->private_data); | 534 | WARN_ON(priv != file->private_data); |
515 | 535 | ||
@@ -518,10 +538,16 @@ static int soc_camera_streamon(struct file *file, void *priv, | |||
518 | if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 538 | if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
519 | return -EINVAL; | 539 | return -EINVAL; |
520 | 540 | ||
541 | mutex_lock(&icd->video_lock); | ||
542 | |||
521 | icd->ops->start_capture(icd); | 543 | icd->ops->start_capture(icd); |
522 | 544 | ||
523 | /* This calls buf_queue from host driver's videobuf_queue_ops */ | 545 | /* This calls buf_queue from host driver's videobuf_queue_ops */ |
524 | return videobuf_streamon(&icf->vb_vidq); | 546 | ret = videobuf_streamon(&icf->vb_vidq); |
547 | |||
548 | mutex_unlock(&icd->video_lock); | ||
549 | |||
550 | return ret; | ||
525 | } | 551 | } |
526 | 552 | ||
527 | static int soc_camera_streamoff(struct file *file, void *priv, | 553 | static int soc_camera_streamoff(struct file *file, void *priv, |
@@ -537,12 +563,16 @@ static int soc_camera_streamoff(struct file *file, void *priv, | |||
537 | if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 563 | if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
538 | return -EINVAL; | 564 | return -EINVAL; |
539 | 565 | ||
566 | mutex_lock(&icd->video_lock); | ||
567 | |||
540 | /* This calls buf_release from host driver's videobuf_queue_ops for all | 568 | /* This calls buf_release from host driver's videobuf_queue_ops for all |
541 | * remaining buffers. When the last buffer is freed, stop capture */ | 569 | * remaining buffers. When the last buffer is freed, stop capture */ |
542 | videobuf_streamoff(&icf->vb_vidq); | 570 | videobuf_streamoff(&icf->vb_vidq); |
543 | 571 | ||
544 | icd->ops->stop_capture(icd); | 572 | icd->ops->stop_capture(icd); |
545 | 573 | ||
574 | mutex_unlock(&icd->video_lock); | ||
575 | |||
546 | return 0; | 576 | return 0; |
547 | } | 577 | } |
548 | 578 | ||
@@ -654,6 +684,9 @@ static int soc_camera_s_crop(struct file *file, void *fh, | |||
654 | if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 684 | if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
655 | return -EINVAL; | 685 | return -EINVAL; |
656 | 686 | ||
687 | /* Cropping is allowed during a running capture, guard consistency */ | ||
688 | mutex_lock(&icf->vb_vidq.vb_lock); | ||
689 | |||
657 | ret = ici->ops->set_fmt(icd, 0, &a->c); | 690 | ret = ici->ops->set_fmt(icd, 0, &a->c); |
658 | if (!ret) { | 691 | if (!ret) { |
659 | icd->width = a->c.width; | 692 | icd->width = a->c.width; |
@@ -662,6 +695,8 @@ static int soc_camera_s_crop(struct file *file, void *fh, | |||
662 | icd->y_current = a->c.top; | 695 | icd->y_current = a->c.top; |
663 | } | 696 | } |
664 | 697 | ||
698 | mutex_unlock(&icf->vb_vidq.vb_lock); | ||
699 | |||
665 | return ret; | 700 | return ret; |
666 | } | 701 | } |
667 | 702 | ||
@@ -775,11 +810,32 @@ static int soc_camera_probe(struct device *dev) | |||
775 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 810 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
776 | int ret; | 811 | int ret; |
777 | 812 | ||
813 | /* | ||
814 | * Possible race scenario: | ||
815 | * modprobe <camera-host-driver> triggers __func__ | ||
816 | * at this moment respective <camera-sensor-driver> gets rmmod'ed | ||
817 | * to protect take module references. | ||
818 | */ | ||
819 | |||
820 | if (!try_module_get(icd->ops->owner)) { | ||
821 | dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); | ||
822 | ret = -EINVAL; | ||
823 | goto emgd; | ||
824 | } | ||
825 | |||
826 | if (!try_module_get(ici->ops->owner)) { | ||
827 | dev_err(&icd->dev, "Couldn't lock capture bus driver.\n"); | ||
828 | ret = -EINVAL; | ||
829 | goto emgi; | ||
830 | } | ||
831 | |||
832 | mutex_lock(&icd->video_lock); | ||
833 | |||
778 | /* We only call ->add() here to activate and probe the camera. | 834 | /* We only call ->add() here to activate and probe the camera. |
779 | * We shall ->remove() and deactivate it immediately afterwards. */ | 835 | * We shall ->remove() and deactivate it immediately afterwards. */ |
780 | ret = ici->ops->add(icd); | 836 | ret = ici->ops->add(icd); |
781 | if (ret < 0) | 837 | if (ret < 0) |
782 | return ret; | 838 | goto eiadd; |
783 | 839 | ||
784 | ret = icd->ops->probe(icd); | 840 | ret = icd->ops->probe(icd); |
785 | if (ret >= 0) { | 841 | if (ret >= 0) { |
@@ -793,6 +849,12 @@ static int soc_camera_probe(struct device *dev) | |||
793 | } | 849 | } |
794 | ici->ops->remove(icd); | 850 | ici->ops->remove(icd); |
795 | 851 | ||
852 | eiadd: | ||
853 | mutex_unlock(&icd->video_lock); | ||
854 | module_put(ici->ops->owner); | ||
855 | emgi: | ||
856 | module_put(icd->ops->owner); | ||
857 | emgd: | ||
796 | return ret; | 858 | return ret; |
797 | } | 859 | } |
798 | 860 | ||
@@ -966,6 +1028,7 @@ int soc_camera_device_register(struct soc_camera_device *icd) | |||
966 | icd->dev.release = dummy_release; | 1028 | icd->dev.release = dummy_release; |
967 | icd->use_count = 0; | 1029 | icd->use_count = 0; |
968 | icd->host_priv = NULL; | 1030 | icd->host_priv = NULL; |
1031 | mutex_init(&icd->video_lock); | ||
969 | 1032 | ||
970 | return scan_add_device(icd); | 1033 | return scan_add_device(icd); |
971 | } | 1034 | } |
@@ -1012,6 +1075,10 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { | |||
1012 | #endif | 1075 | #endif |
1013 | }; | 1076 | }; |
1014 | 1077 | ||
1078 | /* | ||
1079 | * Usually called from the struct soc_camera_ops .probe() method, i.e., from | ||
1080 | * soc_camera_probe() above with .video_lock held | ||
1081 | */ | ||
1015 | int soc_camera_video_start(struct soc_camera_device *icd) | 1082 | int soc_camera_video_start(struct soc_camera_device *icd) |
1016 | { | 1083 | { |
1017 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 1084 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
@@ -1027,7 +1094,7 @@ int soc_camera_video_start(struct soc_camera_device *icd) | |||
1027 | dev_dbg(&ici->dev, "Allocated video_device %p\n", vdev); | 1094 | dev_dbg(&ici->dev, "Allocated video_device %p\n", vdev); |
1028 | 1095 | ||
1029 | strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); | 1096 | strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); |
1030 | /* Maybe better &ici->dev */ | 1097 | |
1031 | vdev->parent = &icd->dev; | 1098 | vdev->parent = &icd->dev; |
1032 | vdev->current_norm = V4L2_STD_UNKNOWN; | 1099 | vdev->current_norm = V4L2_STD_UNKNOWN; |
1033 | vdev->fops = &soc_camera_fops; | 1100 | vdev->fops = &soc_camera_fops; |
@@ -1061,10 +1128,10 @@ void soc_camera_video_stop(struct soc_camera_device *icd) | |||
1061 | if (!icd->dev.parent || !vdev) | 1128 | if (!icd->dev.parent || !vdev) |
1062 | return; | 1129 | return; |
1063 | 1130 | ||
1064 | mutex_lock(&video_lock); | 1131 | mutex_lock(&icd->video_lock); |
1065 | video_unregister_device(vdev); | 1132 | video_unregister_device(vdev); |
1066 | icd->vdev = NULL; | 1133 | icd->vdev = NULL; |
1067 | mutex_unlock(&video_lock); | 1134 | mutex_unlock(&icd->video_lock); |
1068 | } | 1135 | } |
1069 | EXPORT_SYMBOL(soc_camera_video_stop); | 1136 | EXPORT_SYMBOL(soc_camera_video_stop); |
1070 | 1137 | ||