diff options
Diffstat (limited to 'drivers/media/radio/si470x/radio-si470x-usb.c')
-rw-r--r-- | drivers/media/radio/si470x/radio-si470x-usb.c | 265 |
1 files changed, 113 insertions, 152 deletions
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index b7debb67932a..e9f638761296 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c | |||
@@ -367,23 +367,6 @@ static int si470x_get_scratch_page_versions(struct si470x_device *radio) | |||
367 | 367 | ||
368 | 368 | ||
369 | /************************************************************************** | 369 | /************************************************************************** |
370 | * General Driver Functions - DISCONNECT_CHECK | ||
371 | **************************************************************************/ | ||
372 | |||
373 | /* | ||
374 | * si470x_disconnect_check - check whether radio disconnects | ||
375 | */ | ||
376 | int si470x_disconnect_check(struct si470x_device *radio) | ||
377 | { | ||
378 | if (radio->disconnected) | ||
379 | return -EIO; | ||
380 | else | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | |||
385 | |||
386 | /************************************************************************** | ||
387 | * RDS Driver Functions | 370 | * RDS Driver Functions |
388 | **************************************************************************/ | 371 | **************************************************************************/ |
389 | 372 | ||
@@ -414,9 +397,6 @@ static void si470x_int_in_callback(struct urb *urb) | |||
414 | } | 397 | } |
415 | } | 398 | } |
416 | 399 | ||
417 | /* safety checks */ | ||
418 | if (radio->disconnected) | ||
419 | return; | ||
420 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) | 400 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) |
421 | goto resubmit; | 401 | goto resubmit; |
422 | 402 | ||
@@ -501,112 +481,30 @@ resubmit: | |||
501 | } | 481 | } |
502 | 482 | ||
503 | 483 | ||
504 | |||
505 | /************************************************************************** | ||
506 | * File Operations Interface | ||
507 | **************************************************************************/ | ||
508 | |||
509 | /* | ||
510 | * si470x_fops_open - file open | ||
511 | */ | ||
512 | int si470x_fops_open(struct file *file) | 484 | int si470x_fops_open(struct file *file) |
513 | { | 485 | { |
514 | struct si470x_device *radio = video_drvdata(file); | 486 | return v4l2_fh_open(file); |
515 | int retval; | ||
516 | |||
517 | mutex_lock(&radio->lock); | ||
518 | radio->users++; | ||
519 | |||
520 | retval = usb_autopm_get_interface(radio->intf); | ||
521 | if (retval < 0) { | ||
522 | radio->users--; | ||
523 | retval = -EIO; | ||
524 | goto done; | ||
525 | } | ||
526 | |||
527 | if (radio->users == 1) { | ||
528 | /* start radio */ | ||
529 | retval = si470x_start(radio); | ||
530 | if (retval < 0) { | ||
531 | usb_autopm_put_interface(radio->intf); | ||
532 | goto done; | ||
533 | } | ||
534 | |||
535 | /* initialize interrupt urb */ | ||
536 | usb_fill_int_urb(radio->int_in_urb, radio->usbdev, | ||
537 | usb_rcvintpipe(radio->usbdev, | ||
538 | radio->int_in_endpoint->bEndpointAddress), | ||
539 | radio->int_in_buffer, | ||
540 | le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), | ||
541 | si470x_int_in_callback, | ||
542 | radio, | ||
543 | radio->int_in_endpoint->bInterval); | ||
544 | |||
545 | radio->int_in_running = 1; | ||
546 | mb(); | ||
547 | |||
548 | retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); | ||
549 | if (retval) { | ||
550 | dev_info(&radio->intf->dev, | ||
551 | "submitting int urb failed (%d)\n", retval); | ||
552 | radio->int_in_running = 0; | ||
553 | usb_autopm_put_interface(radio->intf); | ||
554 | } | ||
555 | } | ||
556 | |||
557 | done: | ||
558 | mutex_unlock(&radio->lock); | ||
559 | return retval; | ||
560 | } | 487 | } |
561 | 488 | ||
562 | |||
563 | /* | ||
564 | * si470x_fops_release - file release | ||
565 | */ | ||
566 | int si470x_fops_release(struct file *file) | 489 | int si470x_fops_release(struct file *file) |
567 | { | 490 | { |
568 | struct si470x_device *radio = video_drvdata(file); | 491 | return v4l2_fh_release(file); |
569 | int retval = 0; | 492 | } |
570 | |||
571 | /* safety check */ | ||
572 | if (!radio) { | ||
573 | retval = -ENODEV; | ||
574 | goto done; | ||
575 | } | ||
576 | |||
577 | mutex_lock(&radio->lock); | ||
578 | radio->users--; | ||
579 | if (radio->users == 0) { | ||
580 | /* shutdown interrupt handler */ | ||
581 | if (radio->int_in_running) { | ||
582 | radio->int_in_running = 0; | ||
583 | if (radio->int_in_urb) | ||
584 | usb_kill_urb(radio->int_in_urb); | ||
585 | } | ||
586 | |||
587 | if (radio->disconnected) { | ||
588 | video_unregister_device(radio->videodev); | ||
589 | kfree(radio->int_in_buffer); | ||
590 | kfree(radio->buffer); | ||
591 | mutex_unlock(&radio->lock); | ||
592 | kfree(radio); | ||
593 | goto done; | ||
594 | } | ||
595 | 493 | ||
596 | /* cancel read processes */ | 494 | static void si470x_usb_release(struct v4l2_device *v4l2_dev) |
597 | wake_up_interruptible(&radio->read_queue); | 495 | { |
496 | struct si470x_device *radio = | ||
497 | container_of(v4l2_dev, struct si470x_device, v4l2_dev); | ||
598 | 498 | ||
599 | /* stop radio */ | 499 | usb_free_urb(radio->int_in_urb); |
600 | retval = si470x_stop(radio); | 500 | v4l2_ctrl_handler_free(&radio->hdl); |
601 | usb_autopm_put_interface(radio->intf); | 501 | v4l2_device_unregister(&radio->v4l2_dev); |
602 | } | 502 | kfree(radio->int_in_buffer); |
603 | mutex_unlock(&radio->lock); | 503 | kfree(radio->buffer); |
604 | done: | 504 | kfree(radio); |
605 | return retval; | ||
606 | } | 505 | } |
607 | 506 | ||
608 | 507 | ||
609 | |||
610 | /************************************************************************** | 508 | /************************************************************************** |
611 | * Video4Linux Interface | 509 | * Video4Linux Interface |
612 | **************************************************************************/ | 510 | **************************************************************************/ |
@@ -623,13 +521,45 @@ int si470x_vidioc_querycap(struct file *file, void *priv, | |||
623 | strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); | 521 | strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); |
624 | usb_make_path(radio->usbdev, capability->bus_info, | 522 | usb_make_path(radio->usbdev, capability->bus_info, |
625 | sizeof(capability->bus_info)); | 523 | sizeof(capability->bus_info)); |
626 | capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | | 524 | capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | |
627 | V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; | 525 | V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; |
628 | 526 | capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; | |
629 | return 0; | 527 | return 0; |
630 | } | 528 | } |
631 | 529 | ||
632 | 530 | ||
531 | static int si470x_start_usb(struct si470x_device *radio) | ||
532 | { | ||
533 | int retval; | ||
534 | |||
535 | /* start radio */ | ||
536 | retval = si470x_start(radio); | ||
537 | if (retval < 0) | ||
538 | return retval; | ||
539 | |||
540 | v4l2_ctrl_handler_setup(&radio->hdl); | ||
541 | |||
542 | /* initialize interrupt urb */ | ||
543 | usb_fill_int_urb(radio->int_in_urb, radio->usbdev, | ||
544 | usb_rcvintpipe(radio->usbdev, | ||
545 | radio->int_in_endpoint->bEndpointAddress), | ||
546 | radio->int_in_buffer, | ||
547 | le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), | ||
548 | si470x_int_in_callback, | ||
549 | radio, | ||
550 | radio->int_in_endpoint->bInterval); | ||
551 | |||
552 | radio->int_in_running = 1; | ||
553 | mb(); | ||
554 | |||
555 | retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); | ||
556 | if (retval) { | ||
557 | dev_info(&radio->intf->dev, | ||
558 | "submitting int urb failed (%d)\n", retval); | ||
559 | radio->int_in_running = 0; | ||
560 | } | ||
561 | return retval; | ||
562 | } | ||
633 | 563 | ||
634 | /************************************************************************** | 564 | /************************************************************************** |
635 | * USB Interface | 565 | * USB Interface |
@@ -653,8 +583,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
653 | retval = -ENOMEM; | 583 | retval = -ENOMEM; |
654 | goto err_initial; | 584 | goto err_initial; |
655 | } | 585 | } |
656 | radio->users = 0; | ||
657 | radio->disconnected = 0; | ||
658 | radio->usbdev = interface_to_usbdev(intf); | 586 | radio->usbdev = interface_to_usbdev(intf); |
659 | radio->intf = intf; | 587 | radio->intf = intf; |
660 | mutex_init(&radio->lock); | 588 | mutex_init(&radio->lock); |
@@ -691,20 +619,35 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
691 | goto err_intbuffer; | 619 | goto err_intbuffer; |
692 | } | 620 | } |
693 | 621 | ||
694 | /* video device allocation and initialization */ | 622 | radio->v4l2_dev.release = si470x_usb_release; |
695 | radio->videodev = video_device_alloc(); | 623 | retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); |
696 | if (!radio->videodev) { | 624 | if (retval < 0) { |
697 | retval = -ENOMEM; | 625 | dev_err(&intf->dev, "couldn't register v4l2_device\n"); |
698 | goto err_urb; | 626 | goto err_urb; |
699 | } | 627 | } |
700 | memcpy(radio->videodev, &si470x_viddev_template, | 628 | |
701 | sizeof(si470x_viddev_template)); | 629 | v4l2_ctrl_handler_init(&radio->hdl, 2); |
702 | video_set_drvdata(radio->videodev, radio); | 630 | v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, |
631 | V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); | ||
632 | v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, | ||
633 | V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 15); | ||
634 | if (radio->hdl.error) { | ||
635 | retval = radio->hdl.error; | ||
636 | dev_err(&intf->dev, "couldn't register control\n"); | ||
637 | goto err_dev; | ||
638 | } | ||
639 | radio->videodev = si470x_viddev_template; | ||
640 | radio->videodev.ctrl_handler = &radio->hdl; | ||
641 | radio->videodev.lock = &radio->lock; | ||
642 | radio->videodev.v4l2_dev = &radio->v4l2_dev; | ||
643 | radio->videodev.release = video_device_release_empty; | ||
644 | set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); | ||
645 | video_set_drvdata(&radio->videodev, radio); | ||
703 | 646 | ||
704 | /* get device and chip versions */ | 647 | /* get device and chip versions */ |
705 | if (si470x_get_all_registers(radio) < 0) { | 648 | if (si470x_get_all_registers(radio) < 0) { |
706 | retval = -EIO; | 649 | retval = -EIO; |
707 | goto err_video; | 650 | goto err_ctrl; |
708 | } | 651 | } |
709 | dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", | 652 | dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", |
710 | radio->registers[DEVICEID], radio->registers[CHIPID]); | 653 | radio->registers[DEVICEID], radio->registers[CHIPID]); |
@@ -721,7 +664,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
721 | /* get software and hardware versions */ | 664 | /* get software and hardware versions */ |
722 | if (si470x_get_scratch_page_versions(radio) < 0) { | 665 | if (si470x_get_scratch_page_versions(radio) < 0) { |
723 | retval = -EIO; | 666 | retval = -EIO; |
724 | goto err_video; | 667 | goto err_ctrl; |
725 | } | 668 | } |
726 | dev_info(&intf->dev, "software version %d, hardware version %d\n", | 669 | dev_info(&intf->dev, "software version %d, hardware version %d\n", |
727 | radio->software_version, radio->hardware_version); | 670 | radio->software_version, radio->hardware_version); |
@@ -764,28 +707,35 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
764 | radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); | 707 | radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); |
765 | if (!radio->buffer) { | 708 | if (!radio->buffer) { |
766 | retval = -EIO; | 709 | retval = -EIO; |
767 | goto err_video; | 710 | goto err_ctrl; |
768 | } | 711 | } |
769 | 712 | ||
770 | /* rds buffer configuration */ | 713 | /* rds buffer configuration */ |
771 | radio->wr_index = 0; | 714 | radio->wr_index = 0; |
772 | radio->rd_index = 0; | 715 | radio->rd_index = 0; |
773 | init_waitqueue_head(&radio->read_queue); | 716 | init_waitqueue_head(&radio->read_queue); |
717 | usb_set_intfdata(intf, radio); | ||
718 | |||
719 | /* start radio */ | ||
720 | retval = si470x_start_usb(radio); | ||
721 | if (retval < 0) | ||
722 | goto err_all; | ||
774 | 723 | ||
775 | /* register video device */ | 724 | /* register video device */ |
776 | retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, | 725 | retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, |
777 | radio_nr); | 726 | radio_nr); |
778 | if (retval) { | 727 | if (retval) { |
779 | dev_warn(&intf->dev, "Could not register video device\n"); | 728 | dev_err(&intf->dev, "Could not register video device\n"); |
780 | goto err_all; | 729 | goto err_all; |
781 | } | 730 | } |
782 | usb_set_intfdata(intf, radio); | ||
783 | 731 | ||
784 | return 0; | 732 | return 0; |
785 | err_all: | 733 | err_all: |
786 | kfree(radio->buffer); | 734 | kfree(radio->buffer); |
787 | err_video: | 735 | err_ctrl: |
788 | video_device_release(radio->videodev); | 736 | v4l2_ctrl_handler_free(&radio->hdl); |
737 | err_dev: | ||
738 | v4l2_device_unregister(&radio->v4l2_dev); | ||
789 | err_urb: | 739 | err_urb: |
790 | usb_free_urb(radio->int_in_urb); | 740 | usb_free_urb(radio->int_in_urb); |
791 | err_intbuffer: | 741 | err_intbuffer: |
@@ -803,8 +753,22 @@ err_initial: | |||
803 | static int si470x_usb_driver_suspend(struct usb_interface *intf, | 753 | static int si470x_usb_driver_suspend(struct usb_interface *intf, |
804 | pm_message_t message) | 754 | pm_message_t message) |
805 | { | 755 | { |
756 | struct si470x_device *radio = usb_get_intfdata(intf); | ||
757 | |||
806 | dev_info(&intf->dev, "suspending now...\n"); | 758 | dev_info(&intf->dev, "suspending now...\n"); |
807 | 759 | ||
760 | /* shutdown interrupt handler */ | ||
761 | if (radio->int_in_running) { | ||
762 | radio->int_in_running = 0; | ||
763 | if (radio->int_in_urb) | ||
764 | usb_kill_urb(radio->int_in_urb); | ||
765 | } | ||
766 | |||
767 | /* cancel read processes */ | ||
768 | wake_up_interruptible(&radio->read_queue); | ||
769 | |||
770 | /* stop radio */ | ||
771 | si470x_stop(radio); | ||
808 | return 0; | 772 | return 0; |
809 | } | 773 | } |
810 | 774 | ||
@@ -814,9 +778,12 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf, | |||
814 | */ | 778 | */ |
815 | static int si470x_usb_driver_resume(struct usb_interface *intf) | 779 | static int si470x_usb_driver_resume(struct usb_interface *intf) |
816 | { | 780 | { |
781 | struct si470x_device *radio = usb_get_intfdata(intf); | ||
782 | |||
817 | dev_info(&intf->dev, "resuming now...\n"); | 783 | dev_info(&intf->dev, "resuming now...\n"); |
818 | 784 | ||
819 | return 0; | 785 | /* start radio */ |
786 | return si470x_start_usb(radio); | ||
820 | } | 787 | } |
821 | 788 | ||
822 | 789 | ||
@@ -828,28 +795,22 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) | |||
828 | struct si470x_device *radio = usb_get_intfdata(intf); | 795 | struct si470x_device *radio = usb_get_intfdata(intf); |
829 | 796 | ||
830 | mutex_lock(&radio->lock); | 797 | mutex_lock(&radio->lock); |
831 | radio->disconnected = 1; | 798 | v4l2_device_disconnect(&radio->v4l2_dev); |
799 | video_unregister_device(&radio->videodev); | ||
832 | usb_set_intfdata(intf, NULL); | 800 | usb_set_intfdata(intf, NULL); |
833 | if (radio->users == 0) { | 801 | mutex_unlock(&radio->lock); |
834 | /* set led to disconnect state */ | 802 | v4l2_device_put(&radio->v4l2_dev); |
835 | si470x_set_led_state(radio, BLINK_ORANGE_LED); | ||
836 | |||
837 | /* Free data structures. */ | ||
838 | usb_free_urb(radio->int_in_urb); | ||
839 | |||
840 | kfree(radio->int_in_buffer); | ||
841 | video_unregister_device(radio->videodev); | ||
842 | kfree(radio->buffer); | ||
843 | mutex_unlock(&radio->lock); | ||
844 | kfree(radio); | ||
845 | } else { | ||
846 | mutex_unlock(&radio->lock); | ||
847 | } | ||
848 | } | 803 | } |
849 | 804 | ||
850 | 805 | ||
851 | /* | 806 | /* |
852 | * si470x_usb_driver - usb driver interface | 807 | * si470x_usb_driver - usb driver interface |
808 | * | ||
809 | * A note on suspend/resume: this driver had only empty suspend/resume | ||
810 | * functions, and when I tried to test suspend/resume it always disconnected | ||
811 | * instead of resuming (using my ADS InstantFM stick). So I've decided to | ||
812 | * remove these callbacks until someone else with better hardware can | ||
813 | * implement and test this. | ||
853 | */ | 814 | */ |
854 | static struct usb_driver si470x_usb_driver = { | 815 | static struct usb_driver si470x_usb_driver = { |
855 | .name = DRIVER_NAME, | 816 | .name = DRIVER_NAME, |
@@ -857,8 +818,8 @@ static struct usb_driver si470x_usb_driver = { | |||
857 | .disconnect = si470x_usb_driver_disconnect, | 818 | .disconnect = si470x_usb_driver_disconnect, |
858 | .suspend = si470x_usb_driver_suspend, | 819 | .suspend = si470x_usb_driver_suspend, |
859 | .resume = si470x_usb_driver_resume, | 820 | .resume = si470x_usb_driver_resume, |
821 | .reset_resume = si470x_usb_driver_resume, | ||
860 | .id_table = si470x_usb_driver_id_table, | 822 | .id_table = si470x_usb_driver_id_table, |
861 | .supports_autosuspend = 1, | ||
862 | }; | 823 | }; |
863 | 824 | ||
864 | module_usb_driver(si470x_usb_driver); | 825 | module_usb_driver(si470x_usb_driver); |