aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio/radio-mr800.c
diff options
context:
space:
mode:
authorAlexey Klimov <klimov.linux@gmail.com>2008-11-18 22:36:29 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2008-12-30 06:38:05 -0500
commit3480130a502a528373f98fb852d2b3fc5f73e58b (patch)
tree331f7e5d900aec5a184b73bf858318f5fc54ae7b /drivers/media/radio/radio-mr800.c
parentc7abfb47c9e19d63ce6f757afcb392c69ce09765 (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.c62
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,
351static int vidioc_s_tuner(struct file *file, void *priv, 362static 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)
500static int usb_amradio_close(struct inode *inode, struct file *file) 533static 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);