diff options
author | Deti Fliegl <deti@fliegl.de> | 2006-01-09 12:25:23 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@brturbo.com.br> | 2006-01-09 12:25:23 -0500 |
commit | e4acba3c01ca8b275dda22664191c30ee352a38e (patch) | |
tree | 8bc95d18a8df0743938f14ffcaf552532f63f425 | |
parent | 6a5bdd322e5366730803beb59adca7ebb144d7e4 (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.c | 71 |
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 | ||
506 | static 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 | |||
499 | static int cinergyt2_release (struct inode *inode, struct file *file) | 515 | static 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) | |||
791 | static void cinergyt2_unregister_rc(struct cinergyt2 *cinergyt2) | 819 | static 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 | ||
942 | static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state) | 966 | static 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 | ||
1020 | MODULE_LICENSE("GPL"); | 1044 | MODULE_LICENSE("GPL"); |
1021 | MODULE_AUTHOR("Holger Waechtler, Daniel Mack"); | 1045 | MODULE_AUTHOR("Holger Waechtler, Daniel Mack"); |
1022 | |||