aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeti Fliegl <deti@fliegl.de>2006-01-09 12:25:23 -0500
committerMauro Carvalho Chehab <mchehab@brturbo.com.br>2006-01-09 12:25:23 -0500
commite4acba3c01ca8b275dda22664191c30ee352a38e (patch)
tree8bc95d18a8df0743938f14ffcaf552532f63f425
parent6a5bdd322e5366730803beb59adca7ebb144d7e4 (diff)
DVB (2401): USB hot unplug Oops fix.
- USB hot unplug Oops fix. Signed-off-by: Deti Fliegl <deti@fliegl.de> Signed-off-by: Johannes Stezenbach <js@linuxtv.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@brturbo.com.br>
-rw-r--r--drivers/media/dvb/cinergyT2/cinergyT2.c71
1 files changed, 47 insertions, 24 deletions
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
index e2598bbc2bb5..c4b4c5b6b7c8 100644
--- a/drivers/media/dvb/cinergyT2/cinergyT2.c
+++ b/drivers/media/dvb/cinergyT2/cinergyT2.c
@@ -131,6 +131,8 @@ struct cinergyt2 {
131 131
132 wait_queue_head_t poll_wq; 132 wait_queue_head_t poll_wq;
133 int pending_fe_events; 133 int pending_fe_events;
134 int disconnect_pending;
135 atomic_t inuse;
134 136
135 void *streambuf; 137 void *streambuf;
136 dma_addr_t streambuf_dmahandle; 138 dma_addr_t streambuf_dmahandle;
@@ -343,7 +345,7 @@ static int cinergyt2_start_feed(struct dvb_demux_feed *dvbdmxfeed)
343 struct dvb_demux *demux = dvbdmxfeed->demux; 345 struct dvb_demux *demux = dvbdmxfeed->demux;
344 struct cinergyt2 *cinergyt2 = demux->priv; 346 struct cinergyt2 *cinergyt2 = demux->priv;
345 347
346 if (down_interruptible(&cinergyt2->sem)) 348 if (cinergyt2->disconnect_pending || down_interruptible(&cinergyt2->sem))
347 return -ERESTARTSYS; 349 return -ERESTARTSYS;
348 350
349 if (cinergyt2->streaming == 0) 351 if (cinergyt2->streaming == 0)
@@ -359,7 +361,7 @@ static int cinergyt2_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
359 struct dvb_demux *demux = dvbdmxfeed->demux; 361 struct dvb_demux *demux = dvbdmxfeed->demux;
360 struct cinergyt2 *cinergyt2 = demux->priv; 362 struct cinergyt2 *cinergyt2 = demux->priv;
361 363
362 if (down_interruptible(&cinergyt2->sem)) 364 if (cinergyt2->disconnect_pending || down_interruptible(&cinergyt2->sem))
363 return -ERESTARTSYS; 365 return -ERESTARTSYS;
364 366
365 if (--cinergyt2->streaming == 0) 367 if (--cinergyt2->streaming == 0)
@@ -479,23 +481,37 @@ static int cinergyt2_open (struct inode *inode, struct file *file)
479{ 481{
480 struct dvb_device *dvbdev = file->private_data; 482 struct dvb_device *dvbdev = file->private_data;
481 struct cinergyt2 *cinergyt2 = dvbdev->priv; 483 struct cinergyt2 *cinergyt2 = dvbdev->priv;
482 int err; 484 int err = -ERESTARTSYS;
483 485
484 if ((err = dvb_generic_open(inode, file))) 486 if (cinergyt2->disconnect_pending || down_interruptible(&cinergyt2->sem))
487 return -ERESTARTSYS;
488
489 if ((err = dvb_generic_open(inode, file))) {
490 up(&cinergyt2->sem);
485 return err; 491 return err;
492 }
486 493
487 if (down_interruptible(&cinergyt2->sem))
488 return -ERESTARTSYS;
489 494
490 if ((file->f_flags & O_ACCMODE) != O_RDONLY) { 495 if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
491 cinergyt2_sleep(cinergyt2, 0); 496 cinergyt2_sleep(cinergyt2, 0);
492 schedule_delayed_work(&cinergyt2->query_work, HZ/2); 497 schedule_delayed_work(&cinergyt2->query_work, HZ/2);
493 } 498 }
494 499
500 atomic_inc(&cinergyt2->inuse);
501
495 up(&cinergyt2->sem); 502 up(&cinergyt2->sem);
496 return 0; 503 return 0;
497} 504}
498 505
506static void cinergyt2_unregister(struct cinergyt2 *cinergyt2)
507{
508 dvb_unregister_device(cinergyt2->fedev);
509 dvb_unregister_adapter(&cinergyt2->adapter);
510
511 cinergyt2_free_stream_urbs(cinergyt2);
512 kfree(cinergyt2);
513}
514
499static int cinergyt2_release (struct inode *inode, struct file *file) 515static int cinergyt2_release (struct inode *inode, struct file *file)
500{ 516{
501 struct dvb_device *dvbdev = file->private_data; 517 struct dvb_device *dvbdev = file->private_data;
@@ -504,7 +520,7 @@ static int cinergyt2_release (struct inode *inode, struct file *file)
504 if (down_interruptible(&cinergyt2->sem)) 520 if (down_interruptible(&cinergyt2->sem))
505 return -ERESTARTSYS; 521 return -ERESTARTSYS;
506 522
507 if ((file->f_flags & O_ACCMODE) != O_RDONLY) { 523 if (!cinergyt2->disconnect_pending && (file->f_flags & O_ACCMODE) != O_RDONLY) {
508 cancel_delayed_work(&cinergyt2->query_work); 524 cancel_delayed_work(&cinergyt2->query_work);
509 flush_scheduled_work(); 525 flush_scheduled_work();
510 cinergyt2_sleep(cinergyt2, 1); 526 cinergyt2_sleep(cinergyt2, 1);
@@ -512,6 +528,11 @@ static int cinergyt2_release (struct inode *inode, struct file *file)
512 528
513 up(&cinergyt2->sem); 529 up(&cinergyt2->sem);
514 530
531 if (atomic_dec_and_test(&cinergyt2->inuse) && cinergyt2->disconnect_pending) {
532 warn("delayed unregister in release");
533 cinergyt2_unregister(cinergyt2);
534 }
535
515 return dvb_generic_release(inode, file); 536 return dvb_generic_release(inode, file);
516} 537}
517 538
@@ -519,7 +540,14 @@ static unsigned int cinergyt2_poll (struct file *file, struct poll_table_struct
519{ 540{
520 struct dvb_device *dvbdev = file->private_data; 541 struct dvb_device *dvbdev = file->private_data;
521 struct cinergyt2 *cinergyt2 = dvbdev->priv; 542 struct cinergyt2 *cinergyt2 = dvbdev->priv;
543
544 if (cinergyt2->disconnect_pending || down_interruptible(&cinergyt2->sem))
545 return -ERESTARTSYS;
546
522 poll_wait(file, &cinergyt2->poll_wq, wait); 547 poll_wait(file, &cinergyt2->poll_wq, wait);
548
549 up(&cinergyt2->sem);
550
523 return (POLLIN | POLLRDNORM | POLLPRI); 551 return (POLLIN | POLLRDNORM | POLLPRI);
524} 552}
525 553
@@ -585,7 +613,7 @@ static int cinergyt2_ioctl (struct inode *inode, struct file *file,
585 if (copy_from_user(&p, (void __user*) arg, sizeof(p))) 613 if (copy_from_user(&p, (void __user*) arg, sizeof(p)))
586 return -EFAULT; 614 return -EFAULT;
587 615
588 if (down_interruptible(&cinergyt2->sem)) 616 if (cinergyt2->disconnect_pending || down_interruptible(&cinergyt2->sem))
589 return -ERESTARTSYS; 617 return -ERESTARTSYS;
590 618
591 param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS; 619 param->cmd = CINERGYT2_EP1_SET_TUNER_PARAMETERS;
@@ -696,7 +724,7 @@ static void cinergyt2_query_rc (void *data)
696 struct cinergyt2_rc_event rc_events[12]; 724 struct cinergyt2_rc_event rc_events[12];
697 int n, len, i; 725 int n, len, i;
698 726
699 if (down_interruptible(&cinergyt2->sem)) 727 if (cinergyt2->disconnect_pending || down_interruptible(&cinergyt2->sem))
700 return; 728 return;
701 729
702 len = cinergyt2_command(cinergyt2, buf, sizeof(buf), 730 len = cinergyt2_command(cinergyt2, buf, sizeof(buf),
@@ -791,7 +819,6 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2)
791static void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2) 819static void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2)
792{ 820{
793 cancel_delayed_work(&cinergyt2->rc_query_work); 821 cancel_delayed_work(&cinergyt2->rc_query_work);
794 flush_scheduled_work();
795 input_unregister_device(cinergyt2->rc_input_dev); 822 input_unregister_device(cinergyt2->rc_input_dev);
796} 823}
797 824
@@ -822,7 +849,7 @@ static void cinergyt2_query (void *data)
822 uint8_t lock_bits; 849 uint8_t lock_bits;
823 uint32_t unc; 850 uint32_t unc;
824 851
825 if (down_interruptible(&cinergyt2->sem)) 852 if (cinergyt2->disconnect_pending || down_interruptible(&cinergyt2->sem))
826 return; 853 return;
827 854
828 unc = s->uncorrected_block_count; 855 unc = s->uncorrected_block_count;
@@ -922,28 +949,25 @@ static void cinergyt2_disconnect (struct usb_interface *intf)
922{ 949{
923 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); 950 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
924 951
925 if (down_interruptible(&cinergyt2->sem)) 952 flush_scheduled_work();
926 return;
927 953
928 cinergyt2_unregister_rc(cinergyt2); 954 cinergyt2_unregister_rc(cinergyt2);
929 955
956 cancel_delayed_work(&cinergyt2->query_work);
957 wake_up_interruptible(&cinergyt2->poll_wq);
958
930 cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx); 959 cinergyt2->demux.dmx.close(&cinergyt2->demux.dmx);
931 dvb_net_release(&cinergyt2->dvbnet); 960 cinergyt2->disconnect_pending = 1;
932 dvb_dmxdev_release(&cinergyt2->dmxdev);
933 dvb_dmx_release(&cinergyt2->demux);
934 dvb_unregister_device(cinergyt2->fedev);
935 dvb_unregister_adapter(&cinergyt2->adapter);
936 961
937 cinergyt2_free_stream_urbs(cinergyt2); 962 if (!atomic_read(&cinergyt2->inuse))
938 up(&cinergyt2->sem); 963 cinergyt2_unregister(cinergyt2);
939 kfree(cinergyt2);
940} 964}
941 965
942static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state) 966static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state)
943{ 967{
944 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); 968 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
945 969
946 if (down_interruptible(&cinergyt2->sem)) 970 if (cinergyt2->disconnect_pending || down_interruptible(&cinergyt2->sem))
947 return -ERESTARTSYS; 971 return -ERESTARTSYS;
948 972
949 if (state.event > PM_EVENT_ON) { 973 if (state.event > PM_EVENT_ON) {
@@ -966,7 +990,7 @@ static int cinergyt2_resume (struct usb_interface *intf)
966 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf); 990 struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
967 struct dvbt_set_parameters_msg *param = &cinergyt2->param; 991 struct dvbt_set_parameters_msg *param = &cinergyt2->param;
968 992
969 if (down_interruptible(&cinergyt2->sem)) 993 if (cinergyt2->disconnect_pending || down_interruptible(&cinergyt2->sem))
970 return -ERESTARTSYS; 994 return -ERESTARTSYS;
971 995
972 if (!cinergyt2->sleeping) { 996 if (!cinergyt2->sleeping) {
@@ -1019,4 +1043,3 @@ module_exit (cinergyt2_exit);
1019 1043
1020MODULE_LICENSE("GPL"); 1044MODULE_LICENSE("GPL");
1021MODULE_AUTHOR("Holger Waechtler, Daniel Mack"); 1045MODULE_AUTHOR("Holger Waechtler, Daniel Mack");
1022