diff options
author | Alexey Klimov <klimov.linux@gmail.com> | 2008-12-27 19:32:49 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-12-30 06:40:09 -0500 |
commit | 3a0efc3200386b9288e1d3d3be0a9f5d6f286906 (patch) | |
tree | 650ed52ad72760b8f3bcadcfd104b00ecc68d678 /drivers/media | |
parent | f2ce9179eab9a1551e91e3c97498e9350a087e39 (diff) |
V4L/DVB (10054): dsbr100: fix unplug oops
This patch corrects unplug procedure. Patch adds
usb_dsbr100_video_device_release, new macros - videodev_to_radio, mutex
lock and a lot of safety checks.
Struct video_device videodev is embedded in dsbr100_device structure.
Signed-off-by: Alexey Klimov <klimov.linux@gmail.com>
Signed-off-by: Douglas Schilling Landgraf <dougsland@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/radio/dsbr100.c | 106 |
1 files changed, 77 insertions, 29 deletions
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index eafa547ca96b..84914fb267be 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c | |||
@@ -145,6 +145,7 @@ devices, that would be 76 and 91. */ | |||
145 | #define FREQ_MAX 108.0 | 145 | #define FREQ_MAX 108.0 |
146 | #define FREQ_MUL 16000 | 146 | #define FREQ_MUL 16000 |
147 | 147 | ||
148 | #define videodev_to_radio(d) container_of(d, struct dsbr100_device, videodev) | ||
148 | 149 | ||
149 | static int usb_dsbr100_probe(struct usb_interface *intf, | 150 | static int usb_dsbr100_probe(struct usb_interface *intf, |
150 | const struct usb_device_id *id); | 151 | const struct usb_device_id *id); |
@@ -161,8 +162,9 @@ module_param(radio_nr, int, 0); | |||
161 | /* Data for one (physical) device */ | 162 | /* Data for one (physical) device */ |
162 | struct dsbr100_device { | 163 | struct dsbr100_device { |
163 | struct usb_device *usbdev; | 164 | struct usb_device *usbdev; |
164 | struct video_device *videodev; | 165 | struct video_device videodev; |
165 | u8 *transfer_buffer; | 166 | u8 *transfer_buffer; |
167 | struct mutex lock; /* buffer locking */ | ||
166 | int curfreq; | 168 | int curfreq; |
167 | int stereo; | 169 | int stereo; |
168 | int users; | 170 | int users; |
@@ -195,6 +197,7 @@ static struct usb_driver usb_dsbr100_driver = { | |||
195 | /* switch on radio */ | 197 | /* switch on radio */ |
196 | static int dsbr100_start(struct dsbr100_device *radio) | 198 | static int dsbr100_start(struct dsbr100_device *radio) |
197 | { | 199 | { |
200 | mutex_lock(&radio->lock); | ||
198 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | 201 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), |
199 | USB_REQ_GET_STATUS, | 202 | USB_REQ_GET_STATUS, |
200 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | 203 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
@@ -202,9 +205,13 @@ static int dsbr100_start(struct dsbr100_device *radio) | |||
202 | usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | 205 | usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), |
203 | DSB100_ONOFF, | 206 | DSB100_ONOFF, |
204 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | 207 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
205 | 0x01, 0x00, radio->transfer_buffer, 8, 300) < 0) | 208 | 0x01, 0x00, radio->transfer_buffer, 8, 300) < 0) { |
209 | mutex_unlock(&radio->lock); | ||
206 | return -1; | 210 | return -1; |
211 | } | ||
212 | |||
207 | radio->muted=0; | 213 | radio->muted=0; |
214 | mutex_unlock(&radio->lock); | ||
208 | return (radio->transfer_buffer)[0]; | 215 | return (radio->transfer_buffer)[0]; |
209 | } | 216 | } |
210 | 217 | ||
@@ -212,6 +219,7 @@ static int dsbr100_start(struct dsbr100_device *radio) | |||
212 | /* switch off radio */ | 219 | /* switch off radio */ |
213 | static int dsbr100_stop(struct dsbr100_device *radio) | 220 | static int dsbr100_stop(struct dsbr100_device *radio) |
214 | { | 221 | { |
222 | mutex_lock(&radio->lock); | ||
215 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | 223 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), |
216 | USB_REQ_GET_STATUS, | 224 | USB_REQ_GET_STATUS, |
217 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | 225 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
@@ -219,9 +227,13 @@ static int dsbr100_stop(struct dsbr100_device *radio) | |||
219 | usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | 227 | usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), |
220 | DSB100_ONOFF, | 228 | DSB100_ONOFF, |
221 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | 229 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
222 | 0x00, 0x00, radio->transfer_buffer, 8, 300) < 0) | 230 | 0x00, 0x00, radio->transfer_buffer, 8, 300) < 0) { |
231 | mutex_unlock(&radio->lock); | ||
223 | return -1; | 232 | return -1; |
233 | } | ||
234 | |||
224 | radio->muted=1; | 235 | radio->muted=1; |
236 | mutex_unlock(&radio->lock); | ||
225 | return (radio->transfer_buffer)[0]; | 237 | return (radio->transfer_buffer)[0]; |
226 | } | 238 | } |
227 | 239 | ||
@@ -229,6 +241,7 @@ static int dsbr100_stop(struct dsbr100_device *radio) | |||
229 | static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) | 241 | static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) |
230 | { | 242 | { |
231 | freq = (freq / 16 * 80) / 1000 + 856; | 243 | freq = (freq / 16 * 80) / 1000 + 856; |
244 | mutex_lock(&radio->lock); | ||
232 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | 245 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), |
233 | DSB100_TUNE, | 246 | DSB100_TUNE, |
234 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | 247 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
@@ -243,9 +256,12 @@ static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) | |||
243 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | 256 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
244 | 0x00, 0x24, radio->transfer_buffer, 8, 300) < 0) { | 257 | 0x00, 0x24, radio->transfer_buffer, 8, 300) < 0) { |
245 | radio->stereo = -1; | 258 | radio->stereo = -1; |
259 | mutex_unlock(&radio->lock); | ||
246 | return -1; | 260 | return -1; |
247 | } | 261 | } |
262 | |||
248 | radio->stereo = !((radio->transfer_buffer)[0] & 0x01); | 263 | radio->stereo = !((radio->transfer_buffer)[0] & 0x01); |
264 | mutex_unlock(&radio->lock); | ||
249 | return (radio->transfer_buffer)[0]; | 265 | return (radio->transfer_buffer)[0]; |
250 | } | 266 | } |
251 | 267 | ||
@@ -253,6 +269,7 @@ static int dsbr100_setfreq(struct dsbr100_device *radio, int freq) | |||
253 | sees a stereo signal or not. Pity. */ | 269 | sees a stereo signal or not. Pity. */ |
254 | static void dsbr100_getstat(struct dsbr100_device *radio) | 270 | static void dsbr100_getstat(struct dsbr100_device *radio) |
255 | { | 271 | { |
272 | mutex_lock(&radio->lock); | ||
256 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), | 273 | if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), |
257 | USB_REQ_GET_STATUS, | 274 | USB_REQ_GET_STATUS, |
258 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, | 275 | USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, |
@@ -260,6 +277,7 @@ static void dsbr100_getstat(struct dsbr100_device *radio) | |||
260 | radio->stereo = -1; | 277 | radio->stereo = -1; |
261 | else | 278 | else |
262 | radio->stereo = !(radio->transfer_buffer[0] & 0x01); | 279 | radio->stereo = !(radio->transfer_buffer[0] & 0x01); |
280 | mutex_unlock(&radio->lock); | ||
263 | } | 281 | } |
264 | 282 | ||
265 | 283 | ||
@@ -274,16 +292,12 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf) | |||
274 | struct dsbr100_device *radio = usb_get_intfdata(intf); | 292 | struct dsbr100_device *radio = usb_get_intfdata(intf); |
275 | 293 | ||
276 | usb_set_intfdata (intf, NULL); | 294 | usb_set_intfdata (intf, NULL); |
277 | if (radio) { | 295 | |
278 | video_unregister_device(radio->videodev); | 296 | mutex_lock(&radio->lock); |
279 | radio->videodev = NULL; | 297 | radio->removed = 1; |
280 | if (radio->users) { | 298 | mutex_unlock(&radio->lock); |
281 | kfree(radio->transfer_buffer); | 299 | |
282 | kfree(radio); | 300 | video_unregister_device(&radio->videodev); |
283 | } else { | ||
284 | radio->removed = 1; | ||
285 | } | ||
286 | } | ||
287 | } | 301 | } |
288 | 302 | ||
289 | 303 | ||
@@ -303,6 +317,10 @@ static int vidioc_g_tuner(struct file *file, void *priv, | |||
303 | { | 317 | { |
304 | struct dsbr100_device *radio = video_drvdata(file); | 318 | struct dsbr100_device *radio = video_drvdata(file); |
305 | 319 | ||
320 | /* safety check */ | ||
321 | if (radio->removed) | ||
322 | return -EIO; | ||
323 | |||
306 | if (v->index > 0) | 324 | if (v->index > 0) |
307 | return -EINVAL; | 325 | return -EINVAL; |
308 | 326 | ||
@@ -324,6 +342,12 @@ static int vidioc_g_tuner(struct file *file, void *priv, | |||
324 | static int vidioc_s_tuner(struct file *file, void *priv, | 342 | static int vidioc_s_tuner(struct file *file, void *priv, |
325 | struct v4l2_tuner *v) | 343 | struct v4l2_tuner *v) |
326 | { | 344 | { |
345 | struct dsbr100_device *radio = video_drvdata(file); | ||
346 | |||
347 | /* safety check */ | ||
348 | if (radio->removed) | ||
349 | return -EIO; | ||
350 | |||
327 | if (v->index > 0) | 351 | if (v->index > 0) |
328 | return -EINVAL; | 352 | return -EINVAL; |
329 | 353 | ||
@@ -335,6 +359,10 @@ static int vidioc_s_frequency(struct file *file, void *priv, | |||
335 | { | 359 | { |
336 | struct dsbr100_device *radio = video_drvdata(file); | 360 | struct dsbr100_device *radio = video_drvdata(file); |
337 | 361 | ||
362 | /* safety check */ | ||
363 | if (radio->removed) | ||
364 | return -EIO; | ||
365 | |||
338 | radio->curfreq = f->frequency; | 366 | radio->curfreq = f->frequency; |
339 | if (dsbr100_setfreq(radio, radio->curfreq) == -1) | 367 | if (dsbr100_setfreq(radio, radio->curfreq) == -1) |
340 | dev_warn(&radio->usbdev->dev, "Set frequency failed\n"); | 368 | dev_warn(&radio->usbdev->dev, "Set frequency failed\n"); |
@@ -346,6 +374,10 @@ static int vidioc_g_frequency(struct file *file, void *priv, | |||
346 | { | 374 | { |
347 | struct dsbr100_device *radio = video_drvdata(file); | 375 | struct dsbr100_device *radio = video_drvdata(file); |
348 | 376 | ||
377 | /* safety check */ | ||
378 | if (radio->removed) | ||
379 | return -EIO; | ||
380 | |||
349 | f->type = V4L2_TUNER_RADIO; | 381 | f->type = V4L2_TUNER_RADIO; |
350 | f->frequency = radio->curfreq; | 382 | f->frequency = radio->curfreq; |
351 | return 0; | 383 | return 0; |
@@ -370,6 +402,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv, | |||
370 | { | 402 | { |
371 | struct dsbr100_device *radio = video_drvdata(file); | 403 | struct dsbr100_device *radio = video_drvdata(file); |
372 | 404 | ||
405 | /* safety check */ | ||
406 | if (radio->removed) | ||
407 | return -EIO; | ||
408 | |||
373 | switch (ctrl->id) { | 409 | switch (ctrl->id) { |
374 | case V4L2_CID_AUDIO_MUTE: | 410 | case V4L2_CID_AUDIO_MUTE: |
375 | ctrl->value = radio->muted; | 411 | ctrl->value = radio->muted; |
@@ -383,6 +419,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv, | |||
383 | { | 419 | { |
384 | struct dsbr100_device *radio = video_drvdata(file); | 420 | struct dsbr100_device *radio = video_drvdata(file); |
385 | 421 | ||
422 | /* safety check */ | ||
423 | if (radio->removed) | ||
424 | return -EIO; | ||
425 | |||
386 | switch (ctrl->id) { | 426 | switch (ctrl->id) { |
387 | case V4L2_CID_AUDIO_MUTE: | 427 | case V4L2_CID_AUDIO_MUTE: |
388 | if (ctrl->value) { | 428 | if (ctrl->value) { |
@@ -464,13 +504,19 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file) | |||
464 | static int usb_dsbr100_close(struct inode *inode, struct file *file) | 504 | static int usb_dsbr100_close(struct inode *inode, struct file *file) |
465 | { | 505 | { |
466 | struct dsbr100_device *radio = video_drvdata(file); | 506 | struct dsbr100_device *radio = video_drvdata(file); |
507 | int retval; | ||
467 | 508 | ||
468 | if (!radio) | 509 | if (!radio) |
469 | return -ENODEV; | 510 | return -ENODEV; |
511 | |||
470 | radio->users = 0; | 512 | radio->users = 0; |
471 | if (radio->removed) { | 513 | if (!radio->removed) { |
472 | kfree(radio->transfer_buffer); | 514 | retval = dsbr100_stop(radio); |
473 | kfree(radio); | 515 | if (retval == -1) { |
516 | dev_warn(&radio->usbdev->dev, | ||
517 | "dsbr100_stop failed\n"); | ||
518 | } | ||
519 | |||
474 | } | 520 | } |
475 | return 0; | 521 | return 0; |
476 | } | 522 | } |
@@ -505,6 +551,14 @@ static int usb_dsbr100_resume(struct usb_interface *intf) | |||
505 | return 0; | 551 | return 0; |
506 | } | 552 | } |
507 | 553 | ||
554 | static void usb_dsbr100_video_device_release(struct video_device *videodev) | ||
555 | { | ||
556 | struct dsbr100_device *radio = videodev_to_radio(videodev); | ||
557 | |||
558 | kfree(radio->transfer_buffer); | ||
559 | kfree(radio); | ||
560 | } | ||
561 | |||
508 | /* File system interface */ | 562 | /* File system interface */ |
509 | static const struct file_operations usb_dsbr100_fops = { | 563 | static const struct file_operations usb_dsbr100_fops = { |
510 | .owner = THIS_MODULE, | 564 | .owner = THIS_MODULE, |
@@ -533,11 +587,11 @@ static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { | |||
533 | }; | 587 | }; |
534 | 588 | ||
535 | /* V4L2 interface */ | 589 | /* V4L2 interface */ |
536 | static struct video_device dsbr100_videodev_template = { | 590 | static struct video_device dsbr100_videodev_data = { |
537 | .name = "D-Link DSB-R 100", | 591 | .name = "D-Link DSB-R 100", |
538 | .fops = &usb_dsbr100_fops, | 592 | .fops = &usb_dsbr100_fops, |
539 | .ioctl_ops = &usb_dsbr100_ioctl_ops, | 593 | .ioctl_ops = &usb_dsbr100_ioctl_ops, |
540 | .release = video_device_release, | 594 | .release = usb_dsbr100_video_device_release, |
541 | }; | 595 | }; |
542 | 596 | ||
543 | /* check if the device is present and register with v4l and | 597 | /* check if the device is present and register with v4l and |
@@ -558,23 +612,17 @@ static int usb_dsbr100_probe(struct usb_interface *intf, | |||
558 | kfree(radio); | 612 | kfree(radio); |
559 | return -ENOMEM; | 613 | return -ENOMEM; |
560 | } | 614 | } |
561 | radio->videodev = video_device_alloc(); | ||
562 | 615 | ||
563 | if (!(radio->videodev)) { | 616 | mutex_init(&radio->lock); |
564 | kfree(radio->transfer_buffer); | 617 | radio->videodev = dsbr100_videodev_data; |
565 | kfree(radio); | 618 | |
566 | return -ENOMEM; | ||
567 | } | ||
568 | memcpy(radio->videodev, &dsbr100_videodev_template, | ||
569 | sizeof(dsbr100_videodev_template)); | ||
570 | radio->removed = 0; | 619 | radio->removed = 0; |
571 | radio->users = 0; | 620 | radio->users = 0; |
572 | radio->usbdev = interface_to_usbdev(intf); | 621 | radio->usbdev = interface_to_usbdev(intf); |
573 | radio->curfreq = FREQ_MIN * FREQ_MUL; | 622 | radio->curfreq = FREQ_MIN * FREQ_MUL; |
574 | video_set_drvdata(radio->videodev, radio); | 623 | video_set_drvdata(&radio->videodev, radio); |
575 | if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr) < 0) { | 624 | if (video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr) < 0) { |
576 | dev_warn(&intf->dev, "Could not register video device\n"); | 625 | dev_warn(&intf->dev, "Could not register video device\n"); |
577 | video_device_release(radio->videodev); | ||
578 | kfree(radio->transfer_buffer); | 626 | kfree(radio->transfer_buffer); |
579 | kfree(radio); | 627 | kfree(radio); |
580 | return -EIO; | 628 | return -EIO; |