diff options
-rw-r--r-- | drivers/media/video/soc_camera.c | 52 | ||||
-rw-r--r-- | include/media/soc_camera.h | 2 |
2 files changed, 41 insertions, 13 deletions
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index c947525c3f2f..322f837bcfea 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c | |||
@@ -179,11 +179,9 @@ static int soc_camera_dqbuf(struct file *file, void *priv, | |||
179 | 179 | ||
180 | static int soc_camera_open(struct inode *inode, struct file *file) | 180 | static int soc_camera_open(struct inode *inode, struct file *file) |
181 | { | 181 | { |
182 | struct video_device *vdev = video_devdata(file); | 182 | struct video_device *vdev; |
183 | struct soc_camera_device *icd = container_of(vdev->dev, | 183 | struct soc_camera_device *icd; |
184 | struct soc_camera_device, dev); | 184 | struct soc_camera_host *ici; |
185 | struct soc_camera_host *ici = | ||
186 | to_soc_camera_host(icd->dev.parent); | ||
187 | struct soc_camera_file *icf; | 185 | struct soc_camera_file *icf; |
188 | int ret; | 186 | int ret; |
189 | 187 | ||
@@ -191,7 +189,12 @@ static int soc_camera_open(struct inode *inode, struct file *file) | |||
191 | if (!icf) | 189 | if (!icf) |
192 | return -ENOMEM; | 190 | return -ENOMEM; |
193 | 191 | ||
194 | icf->icd = icd; | 192 | /* Protect against icd->remove() until we module_get() both drivers. */ |
193 | mutex_lock(&video_lock); | ||
194 | |||
195 | vdev = video_devdata(file); | ||
196 | icd = container_of(vdev->dev, struct soc_camera_device, dev); | ||
197 | ici = to_soc_camera_host(icd->dev.parent); | ||
195 | 198 | ||
196 | if (!try_module_get(icd->ops->owner)) { | 199 | if (!try_module_get(icd->ops->owner)) { |
197 | dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); | 200 | dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); |
@@ -205,6 +208,22 @@ static int soc_camera_open(struct inode *inode, struct file *file) | |||
205 | goto emgi; | 208 | goto emgi; |
206 | } | 209 | } |
207 | 210 | ||
211 | icd->use_count++; | ||
212 | |||
213 | icf->icd = icd; | ||
214 | |||
215 | /* Now we really have to activate the camera */ | ||
216 | if (icd->use_count == 1) { | ||
217 | ret = ici->add(icd); | ||
218 | if (ret < 0) { | ||
219 | dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); | ||
220 | icd->use_count--; | ||
221 | goto eiciadd; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | mutex_unlock(&video_lock); | ||
226 | |||
208 | file->private_data = icf; | 227 | file->private_data = icf; |
209 | dev_dbg(&icd->dev, "camera device open\n"); | 228 | dev_dbg(&icd->dev, "camera device open\n"); |
210 | 229 | ||
@@ -216,9 +235,13 @@ static int soc_camera_open(struct inode *inode, struct file *file) | |||
216 | 235 | ||
217 | return 0; | 236 | return 0; |
218 | 237 | ||
238 | /* All errors are entered with the video_lock held */ | ||
239 | eiciadd: | ||
240 | module_put(ici->owner); | ||
219 | emgi: | 241 | emgi: |
220 | module_put(icd->ops->owner); | 242 | module_put(icd->ops->owner); |
221 | emgd: | 243 | emgd: |
244 | mutex_unlock(&video_lock); | ||
222 | vfree(icf); | 245 | vfree(icf); |
223 | return ret; | 246 | return ret; |
224 | } | 247 | } |
@@ -230,8 +253,14 @@ static int soc_camera_close(struct inode *inode, struct file *file) | |||
230 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); | 253 | struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); |
231 | struct video_device *vdev = icd->vdev; | 254 | struct video_device *vdev = icd->vdev; |
232 | 255 | ||
256 | mutex_lock(&video_lock); | ||
257 | icd->use_count--; | ||
258 | if (!icd->use_count) | ||
259 | ici->remove(icd); | ||
233 | module_put(icd->ops->owner); | 260 | module_put(icd->ops->owner); |
234 | module_put(ici->owner); | 261 | module_put(ici->owner); |
262 | mutex_unlock(&video_lock); | ||
263 | |||
235 | vfree(file->private_data); | 264 | vfree(file->private_data); |
236 | 265 | ||
237 | dev_dbg(vdev->dev, "camera device close\n"); | 266 | dev_dbg(vdev->dev, "camera device close\n"); |
@@ -673,14 +702,14 @@ static int soc_camera_probe(struct device *dev) | |||
673 | if (!icd->probe) | 702 | if (!icd->probe) |
674 | return -ENODEV; | 703 | return -ENODEV; |
675 | 704 | ||
705 | /* We only call ->add() here to activate and probe the camera. | ||
706 | * We shall ->remove() and deactivate it immediately afterwards. */ | ||
676 | ret = ici->add(icd); | 707 | ret = ici->add(icd); |
677 | if (ret < 0) | 708 | if (ret < 0) |
678 | return ret; | 709 | return ret; |
679 | 710 | ||
680 | ret = icd->probe(icd); | 711 | ret = icd->probe(icd); |
681 | if (ret < 0) | 712 | if (ret >= 0) { |
682 | ici->remove(icd); | ||
683 | else { | ||
684 | const struct v4l2_queryctrl *qctrl; | 713 | const struct v4l2_queryctrl *qctrl; |
685 | 714 | ||
686 | qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_GAIN); | 715 | qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_GAIN); |
@@ -689,6 +718,7 @@ static int soc_camera_probe(struct device *dev) | |||
689 | icd->exposure = qctrl ? qctrl->default_value : | 718 | icd->exposure = qctrl ? qctrl->default_value : |
690 | (unsigned short)~0; | 719 | (unsigned short)~0; |
691 | } | 720 | } |
721 | ici->remove(icd); | ||
692 | 722 | ||
693 | return ret; | 723 | return ret; |
694 | } | 724 | } |
@@ -698,14 +728,10 @@ static int soc_camera_probe(struct device *dev) | |||
698 | static int soc_camera_remove(struct device *dev) | 728 | static int soc_camera_remove(struct device *dev) |
699 | { | 729 | { |
700 | struct soc_camera_device *icd = to_soc_camera_dev(dev); | 730 | struct soc_camera_device *icd = to_soc_camera_dev(dev); |
701 | struct soc_camera_host *ici = | ||
702 | to_soc_camera_host(icd->dev.parent); | ||
703 | 731 | ||
704 | if (icd->remove) | 732 | if (icd->remove) |
705 | icd->remove(icd); | 733 | icd->remove(icd); |
706 | 734 | ||
707 | ici->remove(icd); | ||
708 | |||
709 | return 0; | 735 | return 0; |
710 | } | 736 | } |
711 | 737 | ||
diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 69aba7188e44..c886b1e64872 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h | |||
@@ -41,6 +41,8 @@ struct soc_camera_device { | |||
41 | int (*probe)(struct soc_camera_device *icd); | 41 | int (*probe)(struct soc_camera_device *icd); |
42 | void (*remove)(struct soc_camera_device *icd); | 42 | void (*remove)(struct soc_camera_device *icd); |
43 | struct module *owner; | 43 | struct module *owner; |
44 | /* soc_camera.c private count. Only accessed with video_lock held */ | ||
45 | int use_count; | ||
44 | }; | 46 | }; |
45 | 47 | ||
46 | struct soc_camera_file { | 48 | struct soc_camera_file { |