diff options
author | Alexey Klimov <klimov.linux@gmail.com> | 2008-11-18 22:36:29 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2008-12-30 06:38:05 -0500 |
commit | 3480130a502a528373f98fb852d2b3fc5f73e58b (patch) | |
tree | 331f7e5d900aec5a184b73bf858318f5fc54ae7b /drivers/media/radio/radio-mr800.c | |
parent | c7abfb47c9e19d63ce6f757afcb392c69ce09765 (diff) |
V4L/DVB (9655): radio-mr800: fix unplug
This patch fixes problems(kernel oopses) with unplug of device while
it's working.
Patch adds disconnect_lock mutex, changes usb_amradio_close and
usb_amradio_disconnect functions and adds a lot of safety checks.
Signed-off-by: Alexey Klimov <klimov.linux@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio/radio-mr800.c')
-rw-r--r-- | drivers/media/radio/radio-mr800.c | 62 |
1 files changed, 54 insertions, 8 deletions
diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 61760019b033..7b7a1cfb121d 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c | |||
@@ -141,6 +141,7 @@ struct amradio_device { | |||
141 | 141 | ||
142 | unsigned char *buffer; | 142 | unsigned char *buffer; |
143 | struct mutex lock; /* buffer locking */ | 143 | struct mutex lock; /* buffer locking */ |
144 | struct mutex disconnect_lock; | ||
144 | int curfreq; | 145 | int curfreq; |
145 | int stereo; | 146 | int stereo; |
146 | int users; | 147 | int users; |
@@ -207,6 +208,10 @@ static int amradio_stop(struct amradio_device *radio) | |||
207 | int retval; | 208 | int retval; |
208 | int size; | 209 | int size; |
209 | 210 | ||
211 | /* safety check */ | ||
212 | if (radio->removed) | ||
213 | return -EIO; | ||
214 | |||
210 | mutex_lock(&radio->lock); | 215 | mutex_lock(&radio->lock); |
211 | 216 | ||
212 | radio->buffer[0] = 0x00; | 217 | radio->buffer[0] = 0x00; |
@@ -240,6 +245,10 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) | |||
240 | int size; | 245 | int size; |
241 | unsigned short freq_send = 0x13 + (freq >> 3) / 25; | 246 | unsigned short freq_send = 0x13 + (freq >> 3) / 25; |
242 | 247 | ||
248 | /* safety check */ | ||
249 | if (radio->removed) | ||
250 | return -EIO; | ||
251 | |||
243 | mutex_lock(&radio->lock); | 252 | mutex_lock(&radio->lock); |
244 | 253 | ||
245 | radio->buffer[0] = 0x00; | 254 | radio->buffer[0] = 0x00; |
@@ -293,18 +302,16 @@ static void usb_amradio_disconnect(struct usb_interface *intf) | |||
293 | { | 302 | { |
294 | struct amradio_device *radio = usb_get_intfdata(intf); | 303 | struct amradio_device *radio = usb_get_intfdata(intf); |
295 | 304 | ||
305 | mutex_lock(&radio->disconnect_lock); | ||
306 | radio->removed = 1; | ||
296 | usb_set_intfdata(intf, NULL); | 307 | usb_set_intfdata(intf, NULL); |
297 | 308 | ||
298 | if (radio) { | 309 | if (radio->users == 0) { |
299 | video_unregister_device(radio->videodev); | 310 | video_unregister_device(radio->videodev); |
300 | radio->videodev = NULL; | 311 | kfree(radio->buffer); |
301 | if (radio->users) { | 312 | kfree(radio); |
302 | kfree(radio->buffer); | ||
303 | kfree(radio); | ||
304 | } else { | ||
305 | radio->removed = 1; | ||
306 | } | ||
307 | } | 313 | } |
314 | mutex_unlock(&radio->disconnect_lock); | ||
308 | } | 315 | } |
309 | 316 | ||
310 | /* vidioc_querycap - query device capabilities */ | 317 | /* vidioc_querycap - query device capabilities */ |
@@ -325,6 +332,10 @@ static int vidioc_g_tuner(struct file *file, void *priv, | |||
325 | { | 332 | { |
326 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); | 333 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); |
327 | 334 | ||
335 | /* safety check */ | ||
336 | if (radio->removed) | ||
337 | return -EIO; | ||
338 | |||
328 | if (v->index > 0) | 339 | if (v->index > 0) |
329 | return -EINVAL; | 340 | return -EINVAL; |
330 | 341 | ||
@@ -351,6 +362,12 @@ static int vidioc_g_tuner(struct file *file, void *priv, | |||
351 | static int vidioc_s_tuner(struct file *file, void *priv, | 362 | static int vidioc_s_tuner(struct file *file, void *priv, |
352 | struct v4l2_tuner *v) | 363 | struct v4l2_tuner *v) |
353 | { | 364 | { |
365 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); | ||
366 | |||
367 | /* safety check */ | ||
368 | if (radio->removed) | ||
369 | return -EIO; | ||
370 | |||
354 | if (v->index > 0) | 371 | if (v->index > 0) |
355 | return -EINVAL; | 372 | return -EINVAL; |
356 | return 0; | 373 | return 0; |
@@ -362,6 +379,10 @@ static int vidioc_s_frequency(struct file *file, void *priv, | |||
362 | { | 379 | { |
363 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); | 380 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); |
364 | 381 | ||
382 | /* safety check */ | ||
383 | if (radio->removed) | ||
384 | return -EIO; | ||
385 | |||
365 | radio->curfreq = f->frequency; | 386 | radio->curfreq = f->frequency; |
366 | if (amradio_setfreq(radio, radio->curfreq) < 0) | 387 | if (amradio_setfreq(radio, radio->curfreq) < 0) |
367 | amradio_dev_warn(&radio->videodev->dev, | 388 | amradio_dev_warn(&radio->videodev->dev, |
@@ -375,6 +396,10 @@ static int vidioc_g_frequency(struct file *file, void *priv, | |||
375 | { | 396 | { |
376 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); | 397 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); |
377 | 398 | ||
399 | /* safety check */ | ||
400 | if (radio->removed) | ||
401 | return -EIO; | ||
402 | |||
378 | f->type = V4L2_TUNER_RADIO; | 403 | f->type = V4L2_TUNER_RADIO; |
379 | f->frequency = radio->curfreq; | 404 | f->frequency = radio->curfreq; |
380 | return 0; | 405 | return 0; |
@@ -401,6 +426,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv, | |||
401 | { | 426 | { |
402 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); | 427 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); |
403 | 428 | ||
429 | /* safety check */ | ||
430 | if (radio->removed) | ||
431 | return -EIO; | ||
432 | |||
404 | switch (ctrl->id) { | 433 | switch (ctrl->id) { |
405 | case V4L2_CID_AUDIO_MUTE: | 434 | case V4L2_CID_AUDIO_MUTE: |
406 | ctrl->value = radio->muted; | 435 | ctrl->value = radio->muted; |
@@ -415,6 +444,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv, | |||
415 | { | 444 | { |
416 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); | 445 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); |
417 | 446 | ||
447 | /* safety check */ | ||
448 | if (radio->removed) | ||
449 | return -EIO; | ||
450 | |||
418 | switch (ctrl->id) { | 451 | switch (ctrl->id) { |
419 | case V4L2_CID_AUDIO_MUTE: | 452 | case V4L2_CID_AUDIO_MUTE: |
420 | if (ctrl->value) { | 453 | if (ctrl->value) { |
@@ -500,14 +533,26 @@ static int usb_amradio_open(struct inode *inode, struct file *file) | |||
500 | static int usb_amradio_close(struct inode *inode, struct file *file) | 533 | static int usb_amradio_close(struct inode *inode, struct file *file) |
501 | { | 534 | { |
502 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); | 535 | struct amradio_device *radio = video_get_drvdata(video_devdata(file)); |
536 | int retval; | ||
503 | 537 | ||
504 | if (!radio) | 538 | if (!radio) |
505 | return -ENODEV; | 539 | return -ENODEV; |
540 | |||
541 | mutex_lock(&radio->disconnect_lock); | ||
506 | radio->users = 0; | 542 | radio->users = 0; |
507 | if (radio->removed) { | 543 | if (radio->removed) { |
544 | video_unregister_device(radio->videodev); | ||
508 | kfree(radio->buffer); | 545 | kfree(radio->buffer); |
509 | kfree(radio); | 546 | kfree(radio); |
547 | |||
548 | } else { | ||
549 | retval = amradio_stop(radio); | ||
550 | if (retval < 0) | ||
551 | amradio_dev_warn(&radio->videodev->dev, | ||
552 | "amradio_stop failed\n"); | ||
510 | } | 553 | } |
554 | |||
555 | mutex_unlock(&radio->disconnect_lock); | ||
511 | return 0; | 556 | return 0; |
512 | } | 557 | } |
513 | 558 | ||
@@ -607,6 +652,7 @@ static int usb_amradio_probe(struct usb_interface *intf, | |||
607 | radio->usbdev = interface_to_usbdev(intf); | 652 | radio->usbdev = interface_to_usbdev(intf); |
608 | radio->curfreq = 95.16 * FREQ_MUL; | 653 | radio->curfreq = 95.16 * FREQ_MUL; |
609 | 654 | ||
655 | mutex_init(&radio->disconnect_lock); | ||
610 | mutex_init(&radio->lock); | 656 | mutex_init(&radio->lock); |
611 | 657 | ||
612 | video_set_drvdata(radio->videodev, radio); | 658 | video_set_drvdata(radio->videodev, radio); |