diff options
Diffstat (limited to 'drivers/media/radio/radio-si470x.c')
-rw-r--r-- | drivers/media/radio/radio-si470x.c | 483 |
1 files changed, 345 insertions, 138 deletions
diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index 77354ca6e8e9..dc93a882b385 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c | |||
@@ -24,6 +24,19 @@ | |||
24 | 24 | ||
25 | 25 | ||
26 | /* | 26 | /* |
27 | * User Notes: | ||
28 | * - USB Audio is provided by the alsa snd_usb_audio module. | ||
29 | * For listing you have to redirect the sound, for example using: | ||
30 | * arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B - | ||
31 | * - regarding module parameters in /sys/module/radio_si470x/parameters: | ||
32 | * the contents of read-only files (0444) are not updated, even if | ||
33 | * space, band and de are changed using private video controls | ||
34 | * - increase tune_timeout, if you often get -EIO errors | ||
35 | * - hw_freq_seek returns -EAGAIN, when timed out or band limit is reached | ||
36 | */ | ||
37 | |||
38 | |||
39 | /* | ||
27 | * History: | 40 | * History: |
28 | * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net> | 41 | * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net> |
29 | * Version 1.0.0 | 42 | * Version 1.0.0 |
@@ -85,10 +98,14 @@ | |||
85 | * Oliver Neukum <oliver@neukum.org> | 98 | * Oliver Neukum <oliver@neukum.org> |
86 | * Version 1.0.7 | 99 | * Version 1.0.7 |
87 | * - usb autosuspend support | 100 | * - usb autosuspend support |
88 | * - unplugging fixed | 101 | * - unplugging fixed |
102 | * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net> | ||
103 | * Version 1.0.8 | ||
104 | * - hardware frequency seek support | ||
105 | * - afc indication | ||
106 | * - more safety checks, let si470x_get_freq return errno | ||
89 | * | 107 | * |
90 | * ToDo: | 108 | * ToDo: |
91 | * - add seeking support | ||
92 | * - add firmware download/update support | 109 | * - add firmware download/update support |
93 | * - RDS support: interrupt mode, instead of polling | 110 | * - RDS support: interrupt mode, instead of polling |
94 | * - add LED status output (check if that's not already done in firmware) | 111 | * - add LED status output (check if that's not already done in firmware) |
@@ -98,10 +115,10 @@ | |||
98 | /* driver definitions */ | 115 | /* driver definitions */ |
99 | #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" | 116 | #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" |
100 | #define DRIVER_NAME "radio-si470x" | 117 | #define DRIVER_NAME "radio-si470x" |
101 | #define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 7) | 118 | #define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 8) |
102 | #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" | 119 | #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" |
103 | #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" | 120 | #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" |
104 | #define DRIVER_VERSION "1.0.7" | 121 | #define DRIVER_VERSION "1.0.8" |
105 | 122 | ||
106 | 123 | ||
107 | /* kernel includes */ | 124 | /* kernel includes */ |
@@ -175,6 +192,11 @@ static unsigned int tune_timeout = 3000; | |||
175 | module_param(tune_timeout, uint, 0); | 192 | module_param(tune_timeout, uint, 0); |
176 | MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*"); | 193 | MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*"); |
177 | 194 | ||
195 | /* Seek timeout */ | ||
196 | static unsigned int seek_timeout = 5000; | ||
197 | module_param(seek_timeout, uint, 0); | ||
198 | MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*"); | ||
199 | |||
178 | /* RDS buffer blocks */ | 200 | /* RDS buffer blocks */ |
179 | static unsigned int rds_buf = 100; | 201 | static unsigned int rds_buf = 100; |
180 | module_param(rds_buf, uint, 0); | 202 | module_param(rds_buf, uint, 0); |
@@ -425,7 +447,8 @@ struct si470x_device { | |||
425 | 447 | ||
426 | /* driver management */ | 448 | /* driver management */ |
427 | unsigned int users; | 449 | unsigned int users; |
428 | unsigned char disconnected; | 450 | unsigned char disconnected; |
451 | struct mutex disconnect_lock; | ||
429 | 452 | ||
430 | /* Silabs internal registers (0..15) */ | 453 | /* Silabs internal registers (0..15) */ |
431 | unsigned short registers[RADIO_REGISTER_NUM]; | 454 | unsigned short registers[RADIO_REGISTER_NUM]; |
@@ -442,12 +465,6 @@ struct si470x_device { | |||
442 | 465 | ||
443 | 466 | ||
444 | /* | 467 | /* |
445 | * Lock to prevent kfree of data before all users have releases the device. | ||
446 | */ | ||
447 | static DEFINE_MUTEX(open_close_lock); | ||
448 | |||
449 | |||
450 | /* | ||
451 | * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, | 468 | * The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, |
452 | * 62.5 kHz otherwise. | 469 | * 62.5 kHz otherwise. |
453 | * The tuner is able to have a channel spacing of 50, 100 or 200 kHz. | 470 | * The tuner is able to have a channel spacing of 50, 100 or 200 kHz. |
@@ -476,11 +493,11 @@ static int si470x_get_report(struct si470x_device *radio, void *buf, int size) | |||
476 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | 493 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, |
477 | report[0], 2, | 494 | report[0], 2, |
478 | buf, size, usb_timeout); | 495 | buf, size, usb_timeout); |
496 | |||
479 | if (retval < 0) | 497 | if (retval < 0) |
480 | printk(KERN_WARNING DRIVER_NAME | 498 | printk(KERN_WARNING DRIVER_NAME |
481 | ": si470x_get_report: usb_control_msg returned %d\n", | 499 | ": si470x_get_report: usb_control_msg returned %d\n", |
482 | retval); | 500 | retval); |
483 | |||
484 | return retval; | 501 | return retval; |
485 | } | 502 | } |
486 | 503 | ||
@@ -499,11 +516,11 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size) | |||
499 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | 516 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, |
500 | report[0], 2, | 517 | report[0], 2, |
501 | buf, size, usb_timeout); | 518 | buf, size, usb_timeout); |
519 | |||
502 | if (retval < 0) | 520 | if (retval < 0) |
503 | printk(KERN_WARNING DRIVER_NAME | 521 | printk(KERN_WARNING DRIVER_NAME |
504 | ": si470x_set_report: usb_control_msg returned %d\n", | 522 | ": si470x_set_report: usb_control_msg returned %d\n", |
505 | retval); | 523 | retval); |
506 | |||
507 | return retval; | 524 | return retval; |
508 | } | 525 | } |
509 | 526 | ||
@@ -521,8 +538,7 @@ static int si470x_get_register(struct si470x_device *radio, int regnr) | |||
521 | retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); | 538 | retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); |
522 | 539 | ||
523 | if (retval >= 0) | 540 | if (retval >= 0) |
524 | radio->registers[regnr] = be16_to_cpu(get_unaligned( | 541 | radio->registers[regnr] = get_unaligned_be16(&buf[1]); |
525 | (unsigned short *) &buf[1])); | ||
526 | 542 | ||
527 | return (retval < 0) ? -EINVAL : 0; | 543 | return (retval < 0) ? -EINVAL : 0; |
528 | } | 544 | } |
@@ -537,8 +553,7 @@ static int si470x_set_register(struct si470x_device *radio, int regnr) | |||
537 | int retval; | 553 | int retval; |
538 | 554 | ||
539 | buf[0] = REGISTER_REPORT(regnr); | 555 | buf[0] = REGISTER_REPORT(regnr); |
540 | put_unaligned(cpu_to_be16(radio->registers[regnr]), | 556 | put_unaligned_be16(radio->registers[regnr], &buf[1]); |
541 | (unsigned short *) &buf[1]); | ||
542 | 557 | ||
543 | retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); | 558 | retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); |
544 | 559 | ||
@@ -561,9 +576,8 @@ static int si470x_get_all_registers(struct si470x_device *radio) | |||
561 | 576 | ||
562 | if (retval >= 0) | 577 | if (retval >= 0) |
563 | for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) | 578 | for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) |
564 | radio->registers[regnr] = be16_to_cpu(get_unaligned( | 579 | radio->registers[regnr] = get_unaligned_be16( |
565 | (unsigned short *) | 580 | &buf[regnr * RADIO_REGISTER_SIZE + 1]); |
566 | &buf[regnr * RADIO_REGISTER_SIZE + 1])); | ||
567 | 581 | ||
568 | return (retval < 0) ? -EINVAL : 0; | 582 | return (retval < 0) ? -EINVAL : 0; |
569 | } | 583 | } |
@@ -585,7 +599,7 @@ static int si470x_get_rds_registers(struct si470x_device *radio) | |||
585 | usb_rcvintpipe(radio->usbdev, 1), | 599 | usb_rcvintpipe(radio->usbdev, 1), |
586 | (void *) &buf, sizeof(buf), &size, usb_timeout); | 600 | (void *) &buf, sizeof(buf), &size, usb_timeout); |
587 | if (size != sizeof(buf)) | 601 | if (size != sizeof(buf)) |
588 | printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " | 602 | printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " |
589 | "return size differs: %d != %zu\n", size, sizeof(buf)); | 603 | "return size differs: %d != %zu\n", size, sizeof(buf)); |
590 | if (retval < 0) | 604 | if (retval < 0) |
591 | printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " | 605 | printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " |
@@ -594,8 +608,8 @@ static int si470x_get_rds_registers(struct si470x_device *radio) | |||
594 | if (retval >= 0) | 608 | if (retval >= 0) |
595 | for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) | 609 | for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) |
596 | radio->registers[STATUSRSSI + regnr] = | 610 | radio->registers[STATUSRSSI + regnr] = |
597 | be16_to_cpu(get_unaligned((unsigned short *) | 611 | get_unaligned_be16( |
598 | &buf[regnr * RADIO_REGISTER_SIZE + 1])); | 612 | &buf[regnr * RADIO_REGISTER_SIZE + 1]); |
599 | 613 | ||
600 | return (retval < 0) ? -EINVAL : 0; | 614 | return (retval < 0) ? -EINVAL : 0; |
601 | } | 615 | } |
@@ -615,33 +629,39 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) | |||
615 | radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; | 629 | radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; |
616 | retval = si470x_set_register(radio, CHANNEL); | 630 | retval = si470x_set_register(radio, CHANNEL); |
617 | if (retval < 0) | 631 | if (retval < 0) |
618 | return retval; | 632 | goto done; |
619 | 633 | ||
620 | /* wait till seek operation has completed */ | 634 | /* wait till tune operation has completed */ |
621 | timeout = jiffies + msecs_to_jiffies(tune_timeout); | 635 | timeout = jiffies + msecs_to_jiffies(tune_timeout); |
622 | do { | 636 | do { |
623 | retval = si470x_get_register(radio, STATUSRSSI); | 637 | retval = si470x_get_register(radio, STATUSRSSI); |
624 | if (retval < 0) | 638 | if (retval < 0) |
625 | return retval; | 639 | goto stop; |
626 | timed_out = time_after(jiffies, timeout); | 640 | timed_out = time_after(jiffies, timeout); |
627 | } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && | 641 | } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && |
628 | (!timed_out)); | 642 | (!timed_out)); |
643 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) | ||
644 | printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n"); | ||
629 | if (timed_out) | 645 | if (timed_out) |
630 | printk(KERN_WARNING DRIVER_NAME | 646 | printk(KERN_WARNING DRIVER_NAME |
631 | ": seek does not finish after %u ms\n", tune_timeout); | 647 | ": tune timed out after %u ms\n", tune_timeout); |
632 | 648 | ||
649 | stop: | ||
633 | /* stop tuning */ | 650 | /* stop tuning */ |
634 | radio->registers[CHANNEL] &= ~CHANNEL_TUNE; | 651 | radio->registers[CHANNEL] &= ~CHANNEL_TUNE; |
635 | return si470x_set_register(radio, CHANNEL); | 652 | retval = si470x_set_register(radio, CHANNEL); |
653 | |||
654 | done: | ||
655 | return retval; | ||
636 | } | 656 | } |
637 | 657 | ||
638 | 658 | ||
639 | /* | 659 | /* |
640 | * si470x_get_freq - get the frequency | 660 | * si470x_get_freq - get the frequency |
641 | */ | 661 | */ |
642 | static unsigned int si470x_get_freq(struct si470x_device *radio) | 662 | static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) |
643 | { | 663 | { |
644 | unsigned int spacing, band_bottom, freq; | 664 | unsigned int spacing, band_bottom; |
645 | unsigned short chan; | 665 | unsigned short chan; |
646 | int retval; | 666 | int retval; |
647 | 667 | ||
@@ -667,14 +687,12 @@ static unsigned int si470x_get_freq(struct si470x_device *radio) | |||
667 | 687 | ||
668 | /* read channel */ | 688 | /* read channel */ |
669 | retval = si470x_get_register(radio, READCHAN); | 689 | retval = si470x_get_register(radio, READCHAN); |
670 | if (retval < 0) | ||
671 | return retval; | ||
672 | chan = radio->registers[READCHAN] & READCHAN_READCHAN; | 690 | chan = radio->registers[READCHAN] & READCHAN_READCHAN; |
673 | 691 | ||
674 | /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ | 692 | /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ |
675 | freq = chan * spacing + band_bottom; | 693 | *freq = chan * spacing + band_bottom; |
676 | 694 | ||
677 | return freq; | 695 | return retval; |
678 | } | 696 | } |
679 | 697 | ||
680 | 698 | ||
@@ -714,6 +732,62 @@ static int si470x_set_freq(struct si470x_device *radio, unsigned int freq) | |||
714 | 732 | ||
715 | 733 | ||
716 | /* | 734 | /* |
735 | * si470x_set_seek - set seek | ||
736 | */ | ||
737 | static int si470x_set_seek(struct si470x_device *radio, | ||
738 | unsigned int wrap_around, unsigned int seek_upward) | ||
739 | { | ||
740 | int retval = 0; | ||
741 | unsigned long timeout; | ||
742 | bool timed_out = 0; | ||
743 | |||
744 | /* start seeking */ | ||
745 | radio->registers[POWERCFG] |= POWERCFG_SEEK; | ||
746 | if (wrap_around == 1) | ||
747 | radio->registers[POWERCFG] &= ~POWERCFG_SKMODE; | ||
748 | else | ||
749 | radio->registers[POWERCFG] |= POWERCFG_SKMODE; | ||
750 | if (seek_upward == 1) | ||
751 | radio->registers[POWERCFG] |= POWERCFG_SEEKUP; | ||
752 | else | ||
753 | radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; | ||
754 | retval = si470x_set_register(radio, POWERCFG); | ||
755 | if (retval < 0) | ||
756 | goto done; | ||
757 | |||
758 | /* wait till seek operation has completed */ | ||
759 | timeout = jiffies + msecs_to_jiffies(seek_timeout); | ||
760 | do { | ||
761 | retval = si470x_get_register(radio, STATUSRSSI); | ||
762 | if (retval < 0) | ||
763 | goto stop; | ||
764 | timed_out = time_after(jiffies, timeout); | ||
765 | } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) && | ||
766 | (!timed_out)); | ||
767 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) | ||
768 | printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n"); | ||
769 | if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) | ||
770 | printk(KERN_WARNING DRIVER_NAME | ||
771 | ": seek failed / band limit reached\n"); | ||
772 | if (timed_out) | ||
773 | printk(KERN_WARNING DRIVER_NAME | ||
774 | ": seek timed out after %u ms\n", seek_timeout); | ||
775 | |||
776 | stop: | ||
777 | /* stop seeking */ | ||
778 | radio->registers[POWERCFG] &= ~POWERCFG_SEEK; | ||
779 | retval = si470x_set_register(radio, POWERCFG); | ||
780 | |||
781 | done: | ||
782 | /* try again, if timed out */ | ||
783 | if ((retval == 0) && timed_out) | ||
784 | retval = -EAGAIN; | ||
785 | |||
786 | return retval; | ||
787 | } | ||
788 | |||
789 | |||
790 | /* | ||
717 | * si470x_start - switch on radio | 791 | * si470x_start - switch on radio |
718 | */ | 792 | */ |
719 | static int si470x_start(struct si470x_device *radio) | 793 | static int si470x_start(struct si470x_device *radio) |
@@ -725,27 +799,30 @@ static int si470x_start(struct si470x_device *radio) | |||
725 | POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; | 799 | POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; |
726 | retval = si470x_set_register(radio, POWERCFG); | 800 | retval = si470x_set_register(radio, POWERCFG); |
727 | if (retval < 0) | 801 | if (retval < 0) |
728 | return retval; | 802 | goto done; |
729 | 803 | ||
730 | /* sysconfig 1 */ | 804 | /* sysconfig 1 */ |
731 | radio->registers[SYSCONFIG1] = SYSCONFIG1_DE; | 805 | radio->registers[SYSCONFIG1] = SYSCONFIG1_DE; |
732 | retval = si470x_set_register(radio, SYSCONFIG1); | 806 | retval = si470x_set_register(radio, SYSCONFIG1); |
733 | if (retval < 0) | 807 | if (retval < 0) |
734 | return retval; | 808 | goto done; |
735 | 809 | ||
736 | /* sysconfig 2 */ | 810 | /* sysconfig 2 */ |
737 | radio->registers[SYSCONFIG2] = | 811 | radio->registers[SYSCONFIG2] = |
738 | (0x3f << 8) | /* SEEKTH */ | 812 | (0x3f << 8) | /* SEEKTH */ |
739 | (band << 6) | /* BAND */ | 813 | ((band << 6) & SYSCONFIG2_BAND) | /* BAND */ |
740 | (space << 4) | /* SPACE */ | 814 | ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */ |
741 | 15; /* VOLUME (max) */ | 815 | 15; /* VOLUME (max) */ |
742 | retval = si470x_set_register(radio, SYSCONFIG2); | 816 | retval = si470x_set_register(radio, SYSCONFIG2); |
743 | if (retval < 0) | 817 | if (retval < 0) |
744 | return retval; | 818 | goto done; |
745 | 819 | ||
746 | /* reset last channel */ | 820 | /* reset last channel */ |
747 | return si470x_set_chan(radio, | 821 | retval = si470x_set_chan(radio, |
748 | radio->registers[CHANNEL] & CHANNEL_CHAN); | 822 | radio->registers[CHANNEL] & CHANNEL_CHAN); |
823 | |||
824 | done: | ||
825 | return retval; | ||
749 | } | 826 | } |
750 | 827 | ||
751 | 828 | ||
@@ -760,13 +837,16 @@ static int si470x_stop(struct si470x_device *radio) | |||
760 | radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; | 837 | radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS; |
761 | retval = si470x_set_register(radio, SYSCONFIG1); | 838 | retval = si470x_set_register(radio, SYSCONFIG1); |
762 | if (retval < 0) | 839 | if (retval < 0) |
763 | return retval; | 840 | goto done; |
764 | 841 | ||
765 | /* powercfg */ | 842 | /* powercfg */ |
766 | radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; | 843 | radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; |
767 | /* POWERCFG_ENABLE has to automatically go low */ | 844 | /* POWERCFG_ENABLE has to automatically go low */ |
768 | radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; | 845 | radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; |
769 | return si470x_set_register(radio, POWERCFG); | 846 | retval = si470x_set_register(radio, POWERCFG); |
847 | |||
848 | done: | ||
849 | return retval; | ||
770 | } | 850 | } |
771 | 851 | ||
772 | 852 | ||
@@ -843,7 +923,7 @@ static void si470x_rds(struct si470x_device *radio) | |||
843 | }; | 923 | }; |
844 | 924 | ||
845 | /* Fill the V4L2 RDS buffer */ | 925 | /* Fill the V4L2 RDS buffer */ |
846 | put_unaligned(cpu_to_le16(rds), (unsigned short *) &tmpbuf); | 926 | put_unaligned_le16(rds, &tmpbuf); |
847 | tmpbuf[2] = blocknum; /* offset name */ | 927 | tmpbuf[2] = blocknum; /* offset name */ |
848 | tmpbuf[2] |= blocknum << 3; /* received offset */ | 928 | tmpbuf[2] |= blocknum << 3; /* received offset */ |
849 | if (bler > max_rds_errors) | 929 | if (bler > max_rds_errors) |
@@ -883,8 +963,9 @@ static void si470x_work(struct work_struct *work) | |||
883 | struct si470x_device *radio = container_of(work, struct si470x_device, | 963 | struct si470x_device *radio = container_of(work, struct si470x_device, |
884 | work.work); | 964 | work.work); |
885 | 965 | ||
886 | if (radio->disconnected) | 966 | /* safety checks */ |
887 | return; | 967 | if (radio->disconnected) |
968 | return; | ||
888 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) | 969 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) |
889 | return; | 970 | return; |
890 | 971 | ||
@@ -917,11 +998,15 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, | |||
917 | 998 | ||
918 | /* block if no new data available */ | 999 | /* block if no new data available */ |
919 | while (radio->wr_index == radio->rd_index) { | 1000 | while (radio->wr_index == radio->rd_index) { |
920 | if (file->f_flags & O_NONBLOCK) | 1001 | if (file->f_flags & O_NONBLOCK) { |
921 | return -EWOULDBLOCK; | 1002 | retval = -EWOULDBLOCK; |
1003 | goto done; | ||
1004 | } | ||
922 | if (wait_event_interruptible(radio->read_queue, | 1005 | if (wait_event_interruptible(radio->read_queue, |
923 | radio->wr_index != radio->rd_index) < 0) | 1006 | radio->wr_index != radio->rd_index) < 0) { |
924 | return -EINTR; | 1007 | retval = -EINTR; |
1008 | goto done; | ||
1009 | } | ||
925 | } | 1010 | } |
926 | 1011 | ||
927 | /* calculate block count from byte count */ | 1012 | /* calculate block count from byte count */ |
@@ -950,6 +1035,7 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, | |||
950 | } | 1035 | } |
951 | mutex_unlock(&radio->lock); | 1036 | mutex_unlock(&radio->lock); |
952 | 1037 | ||
1038 | done: | ||
953 | return retval; | 1039 | return retval; |
954 | } | 1040 | } |
955 | 1041 | ||
@@ -961,6 +1047,7 @@ static unsigned int si470x_fops_poll(struct file *file, | |||
961 | struct poll_table_struct *pts) | 1047 | struct poll_table_struct *pts) |
962 | { | 1048 | { |
963 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1049 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1050 | int retval = 0; | ||
964 | 1051 | ||
965 | /* switch on rds reception */ | 1052 | /* switch on rds reception */ |
966 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { | 1053 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { |
@@ -972,9 +1059,9 @@ static unsigned int si470x_fops_poll(struct file *file, | |||
972 | poll_wait(file, &radio->read_queue, pts); | 1059 | poll_wait(file, &radio->read_queue, pts); |
973 | 1060 | ||
974 | if (radio->rd_index != radio->wr_index) | 1061 | if (radio->rd_index != radio->wr_index) |
975 | return POLLIN | POLLRDNORM; | 1062 | retval = POLLIN | POLLRDNORM; |
976 | 1063 | ||
977 | return 0; | 1064 | return retval; |
978 | } | 1065 | } |
979 | 1066 | ||
980 | 1067 | ||
@@ -991,17 +1078,18 @@ static int si470x_fops_open(struct inode *inode, struct file *file) | |||
991 | retval = usb_autopm_get_interface(radio->intf); | 1078 | retval = usb_autopm_get_interface(radio->intf); |
992 | if (retval < 0) { | 1079 | if (retval < 0) { |
993 | radio->users--; | 1080 | radio->users--; |
994 | return -EIO; | 1081 | retval = -EIO; |
1082 | goto done; | ||
995 | } | 1083 | } |
996 | 1084 | ||
997 | if (radio->users == 1) { | 1085 | if (radio->users == 1) { |
998 | retval = si470x_start(radio); | 1086 | retval = si470x_start(radio); |
999 | if (retval < 0) | 1087 | if (retval < 0) |
1000 | usb_autopm_put_interface(radio->intf); | 1088 | usb_autopm_put_interface(radio->intf); |
1001 | return retval; | ||
1002 | } | 1089 | } |
1003 | 1090 | ||
1004 | return 0; | 1091 | done: |
1092 | return retval; | ||
1005 | } | 1093 | } |
1006 | 1094 | ||
1007 | 1095 | ||
@@ -1011,20 +1099,23 @@ static int si470x_fops_open(struct inode *inode, struct file *file) | |||
1011 | static int si470x_fops_release(struct inode *inode, struct file *file) | 1099 | static int si470x_fops_release(struct inode *inode, struct file *file) |
1012 | { | 1100 | { |
1013 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1101 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1014 | int retval = 0; | 1102 | int retval = 0; |
1015 | 1103 | ||
1016 | if (!radio) | 1104 | /* safety check */ |
1017 | return -ENODEV; | 1105 | if (!radio) { |
1106 | retval = -ENODEV; | ||
1107 | goto done; | ||
1108 | } | ||
1018 | 1109 | ||
1019 | mutex_lock(&open_close_lock); | 1110 | mutex_lock(&radio->disconnect_lock); |
1020 | radio->users--; | 1111 | radio->users--; |
1021 | if (radio->users == 0) { | 1112 | if (radio->users == 0) { |
1022 | if (radio->disconnected) { | 1113 | if (radio->disconnected) { |
1023 | video_unregister_device(radio->videodev); | 1114 | video_unregister_device(radio->videodev); |
1024 | kfree(radio->buffer); | 1115 | kfree(radio->buffer); |
1025 | kfree(radio); | 1116 | kfree(radio); |
1026 | goto done; | 1117 | goto unlock; |
1027 | } | 1118 | } |
1028 | 1119 | ||
1029 | /* stop rds reception */ | 1120 | /* stop rds reception */ |
1030 | cancel_delayed_work_sync(&radio->work); | 1121 | cancel_delayed_work_sync(&radio->work); |
@@ -1036,9 +1127,11 @@ static int si470x_fops_release(struct inode *inode, struct file *file) | |||
1036 | usb_autopm_put_interface(radio->intf); | 1127 | usb_autopm_put_interface(radio->intf); |
1037 | } | 1128 | } |
1038 | 1129 | ||
1130 | unlock: | ||
1131 | mutex_unlock(&radio->disconnect_lock); | ||
1132 | |||
1039 | done: | 1133 | done: |
1040 | mutex_unlock(&open_close_lock); | 1134 | return retval; |
1041 | return retval; | ||
1042 | } | 1135 | } |
1043 | 1136 | ||
1044 | 1137 | ||
@@ -1116,7 +1209,8 @@ static int si470x_vidioc_querycap(struct file *file, void *priv, | |||
1116 | strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); | 1209 | strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); |
1117 | sprintf(capability->bus_info, "USB"); | 1210 | sprintf(capability->bus_info, "USB"); |
1118 | capability->version = DRIVER_KERNEL_VERSION; | 1211 | capability->version = DRIVER_KERNEL_VERSION; |
1119 | capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | 1212 | capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | |
1213 | V4L2_CAP_TUNER | V4L2_CAP_RADIO; | ||
1120 | 1214 | ||
1121 | return 0; | 1215 | return 0; |
1122 | } | 1216 | } |
@@ -1125,7 +1219,7 @@ static int si470x_vidioc_querycap(struct file *file, void *priv, | |||
1125 | /* | 1219 | /* |
1126 | * si470x_vidioc_g_input - get input | 1220 | * si470x_vidioc_g_input - get input |
1127 | */ | 1221 | */ |
1128 | static int si470x_vidioc_g_input(struct file *filp, void *priv, | 1222 | static int si470x_vidioc_g_input(struct file *file, void *priv, |
1129 | unsigned int *i) | 1223 | unsigned int *i) |
1130 | { | 1224 | { |
1131 | *i = 0; | 1225 | *i = 0; |
@@ -1137,12 +1231,18 @@ static int si470x_vidioc_g_input(struct file *filp, void *priv, | |||
1137 | /* | 1231 | /* |
1138 | * si470x_vidioc_s_input - set input | 1232 | * si470x_vidioc_s_input - set input |
1139 | */ | 1233 | */ |
1140 | static int si470x_vidioc_s_input(struct file *filp, void *priv, unsigned int i) | 1234 | static int si470x_vidioc_s_input(struct file *file, void *priv, unsigned int i) |
1141 | { | 1235 | { |
1236 | int retval = 0; | ||
1237 | |||
1238 | /* safety checks */ | ||
1142 | if (i != 0) | 1239 | if (i != 0) |
1143 | return -EINVAL; | 1240 | retval = -EINVAL; |
1144 | 1241 | ||
1145 | return 0; | 1242 | if (retval < 0) |
1243 | printk(KERN_WARNING DRIVER_NAME | ||
1244 | ": set input failed with %d\n", retval); | ||
1245 | return retval; | ||
1146 | } | 1246 | } |
1147 | 1247 | ||
1148 | 1248 | ||
@@ -1155,17 +1255,22 @@ static int si470x_vidioc_queryctrl(struct file *file, void *priv, | |||
1155 | unsigned char i; | 1255 | unsigned char i; |
1156 | int retval = -EINVAL; | 1256 | int retval = -EINVAL; |
1157 | 1257 | ||
1258 | /* safety checks */ | ||
1259 | if (!qc->id) | ||
1260 | goto done; | ||
1261 | |||
1158 | for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) { | 1262 | for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) { |
1159 | if (qc->id && qc->id == si470x_v4l2_queryctrl[i].id) { | 1263 | if (qc->id == si470x_v4l2_queryctrl[i].id) { |
1160 | memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc)); | 1264 | memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc)); |
1161 | retval = 0; | 1265 | retval = 0; |
1162 | break; | 1266 | break; |
1163 | } | 1267 | } |
1164 | } | 1268 | } |
1269 | |||
1270 | done: | ||
1165 | if (retval < 0) | 1271 | if (retval < 0) |
1166 | printk(KERN_WARNING DRIVER_NAME | 1272 | printk(KERN_WARNING DRIVER_NAME |
1167 | ": query control failed with %d\n", retval); | 1273 | ": query controls failed with %d\n", retval); |
1168 | |||
1169 | return retval; | 1274 | return retval; |
1170 | } | 1275 | } |
1171 | 1276 | ||
@@ -1177,9 +1282,13 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, | |||
1177 | struct v4l2_control *ctrl) | 1282 | struct v4l2_control *ctrl) |
1178 | { | 1283 | { |
1179 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1284 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1285 | int retval = 0; | ||
1180 | 1286 | ||
1181 | if (radio->disconnected) | 1287 | /* safety checks */ |
1182 | return -EIO; | 1288 | if (radio->disconnected) { |
1289 | retval = -EIO; | ||
1290 | goto done; | ||
1291 | } | ||
1183 | 1292 | ||
1184 | switch (ctrl->id) { | 1293 | switch (ctrl->id) { |
1185 | case V4L2_CID_AUDIO_VOLUME: | 1294 | case V4L2_CID_AUDIO_VOLUME: |
@@ -1190,9 +1299,15 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv, | |||
1190 | ctrl->value = ((radio->registers[POWERCFG] & | 1299 | ctrl->value = ((radio->registers[POWERCFG] & |
1191 | POWERCFG_DMUTE) == 0) ? 1 : 0; | 1300 | POWERCFG_DMUTE) == 0) ? 1 : 0; |
1192 | break; | 1301 | break; |
1302 | default: | ||
1303 | retval = -EINVAL; | ||
1193 | } | 1304 | } |
1194 | 1305 | ||
1195 | return 0; | 1306 | done: |
1307 | if (retval < 0) | ||
1308 | printk(KERN_WARNING DRIVER_NAME | ||
1309 | ": get control failed with %d\n", retval); | ||
1310 | return retval; | ||
1196 | } | 1311 | } |
1197 | 1312 | ||
1198 | 1313 | ||
@@ -1203,10 +1318,13 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, | |||
1203 | struct v4l2_control *ctrl) | 1318 | struct v4l2_control *ctrl) |
1204 | { | 1319 | { |
1205 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1320 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1206 | int retval; | 1321 | int retval = 0; |
1207 | 1322 | ||
1208 | if (radio->disconnected) | 1323 | /* safety checks */ |
1209 | return -EIO; | 1324 | if (radio->disconnected) { |
1325 | retval = -EIO; | ||
1326 | goto done; | ||
1327 | } | ||
1210 | 1328 | ||
1211 | switch (ctrl->id) { | 1329 | switch (ctrl->id) { |
1212 | case V4L2_CID_AUDIO_VOLUME: | 1330 | case V4L2_CID_AUDIO_VOLUME: |
@@ -1224,10 +1342,11 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, | |||
1224 | default: | 1342 | default: |
1225 | retval = -EINVAL; | 1343 | retval = -EINVAL; |
1226 | } | 1344 | } |
1345 | |||
1346 | done: | ||
1227 | if (retval < 0) | 1347 | if (retval < 0) |
1228 | printk(KERN_WARNING DRIVER_NAME | 1348 | printk(KERN_WARNING DRIVER_NAME |
1229 | ": set control failed with %d\n", retval); | 1349 | ": set control failed with %d\n", retval); |
1230 | |||
1231 | return retval; | 1350 | return retval; |
1232 | } | 1351 | } |
1233 | 1352 | ||
@@ -1238,13 +1357,22 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv, | |||
1238 | static int si470x_vidioc_g_audio(struct file *file, void *priv, | 1357 | static int si470x_vidioc_g_audio(struct file *file, void *priv, |
1239 | struct v4l2_audio *audio) | 1358 | struct v4l2_audio *audio) |
1240 | { | 1359 | { |
1241 | if (audio->index > 1) | 1360 | int retval = 0; |
1242 | return -EINVAL; | 1361 | |
1362 | /* safety checks */ | ||
1363 | if (audio->index != 0) { | ||
1364 | retval = -EINVAL; | ||
1365 | goto done; | ||
1366 | } | ||
1243 | 1367 | ||
1244 | strcpy(audio->name, "Radio"); | 1368 | strcpy(audio->name, "Radio"); |
1245 | audio->capability = V4L2_AUDCAP_STEREO; | 1369 | audio->capability = V4L2_AUDCAP_STEREO; |
1246 | 1370 | ||
1247 | return 0; | 1371 | done: |
1372 | if (retval < 0) | ||
1373 | printk(KERN_WARNING DRIVER_NAME | ||
1374 | ": get audio failed with %d\n", retval); | ||
1375 | return retval; | ||
1248 | } | 1376 | } |
1249 | 1377 | ||
1250 | 1378 | ||
@@ -1254,10 +1382,19 @@ static int si470x_vidioc_g_audio(struct file *file, void *priv, | |||
1254 | static int si470x_vidioc_s_audio(struct file *file, void *priv, | 1382 | static int si470x_vidioc_s_audio(struct file *file, void *priv, |
1255 | struct v4l2_audio *audio) | 1383 | struct v4l2_audio *audio) |
1256 | { | 1384 | { |
1257 | if (audio->index != 0) | 1385 | int retval = 0; |
1258 | return -EINVAL; | ||
1259 | 1386 | ||
1260 | return 0; | 1387 | /* safety checks */ |
1388 | if (audio->index != 0) { | ||
1389 | retval = -EINVAL; | ||
1390 | goto done; | ||
1391 | } | ||
1392 | |||
1393 | done: | ||
1394 | if (retval < 0) | ||
1395 | printk(KERN_WARNING DRIVER_NAME | ||
1396 | ": set audio failed with %d\n", retval); | ||
1397 | return retval; | ||
1261 | } | 1398 | } |
1262 | 1399 | ||
1263 | 1400 | ||
@@ -1268,20 +1405,23 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, | |||
1268 | struct v4l2_tuner *tuner) | 1405 | struct v4l2_tuner *tuner) |
1269 | { | 1406 | { |
1270 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1407 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1271 | int retval; | 1408 | int retval = 0; |
1272 | 1409 | ||
1273 | if (radio->disconnected) | 1410 | /* safety checks */ |
1274 | return -EIO; | 1411 | if (radio->disconnected) { |
1275 | if (tuner->index > 0) | 1412 | retval = -EIO; |
1276 | return -EINVAL; | 1413 | goto done; |
1414 | } | ||
1415 | if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) { | ||
1416 | retval = -EINVAL; | ||
1417 | goto done; | ||
1418 | } | ||
1277 | 1419 | ||
1278 | /* read status rssi */ | ||
1279 | retval = si470x_get_register(radio, STATUSRSSI); | 1420 | retval = si470x_get_register(radio, STATUSRSSI); |
1280 | if (retval < 0) | 1421 | if (retval < 0) |
1281 | return retval; | 1422 | goto done; |
1282 | 1423 | ||
1283 | strcpy(tuner->name, "FM"); | 1424 | strcpy(tuner->name, "FM"); |
1284 | tuner->type = V4L2_TUNER_RADIO; | ||
1285 | switch (band) { | 1425 | switch (band) { |
1286 | /* 0: 87.5 - 108 MHz (USA, Europe, default) */ | 1426 | /* 0: 87.5 - 108 MHz (USA, Europe, default) */ |
1287 | default: | 1427 | default: |
@@ -1313,9 +1453,14 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, | |||
1313 | * 0x0101; | 1453 | * 0x0101; |
1314 | 1454 | ||
1315 | /* automatic frequency control: -1: freq to low, 1 freq to high */ | 1455 | /* automatic frequency control: -1: freq to low, 1 freq to high */ |
1316 | tuner->afc = 0; | 1456 | /* AFCRL does only indicate that freq. differs, not if too low/high */ |
1457 | tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; | ||
1317 | 1458 | ||
1318 | return 0; | 1459 | done: |
1460 | if (retval < 0) | ||
1461 | printk(KERN_WARNING DRIVER_NAME | ||
1462 | ": get tuner failed with %d\n", retval); | ||
1463 | return retval; | ||
1319 | } | 1464 | } |
1320 | 1465 | ||
1321 | 1466 | ||
@@ -1326,12 +1471,17 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, | |||
1326 | struct v4l2_tuner *tuner) | 1471 | struct v4l2_tuner *tuner) |
1327 | { | 1472 | { |
1328 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1473 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1329 | int retval; | 1474 | int retval = 0; |
1330 | 1475 | ||
1331 | if (radio->disconnected) | 1476 | /* safety checks */ |
1332 | return -EIO; | 1477 | if (radio->disconnected) { |
1333 | if (tuner->index > 0) | 1478 | retval = -EIO; |
1334 | return -EINVAL; | 1479 | goto done; |
1480 | } | ||
1481 | if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) { | ||
1482 | retval = -EINVAL; | ||
1483 | goto done; | ||
1484 | } | ||
1335 | 1485 | ||
1336 | if (tuner->audmode == V4L2_TUNER_MODE_MONO) | 1486 | if (tuner->audmode == V4L2_TUNER_MODE_MONO) |
1337 | radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ | 1487 | radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ |
@@ -1339,10 +1489,11 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, | |||
1339 | radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ | 1489 | radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ |
1340 | 1490 | ||
1341 | retval = si470x_set_register(radio, POWERCFG); | 1491 | retval = si470x_set_register(radio, POWERCFG); |
1492 | |||
1493 | done: | ||
1342 | if (retval < 0) | 1494 | if (retval < 0) |
1343 | printk(KERN_WARNING DRIVER_NAME | 1495 | printk(KERN_WARNING DRIVER_NAME |
1344 | ": set tuner failed with %d\n", retval); | 1496 | ": set tuner failed with %d\n", retval); |
1345 | |||
1346 | return retval; | 1497 | return retval; |
1347 | } | 1498 | } |
1348 | 1499 | ||
@@ -1354,14 +1505,25 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, | |||
1354 | struct v4l2_frequency *freq) | 1505 | struct v4l2_frequency *freq) |
1355 | { | 1506 | { |
1356 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1507 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1508 | int retval = 0; | ||
1357 | 1509 | ||
1358 | if (radio->disconnected) | 1510 | /* safety checks */ |
1359 | return -EIO; | 1511 | if (radio->disconnected) { |
1512 | retval = -EIO; | ||
1513 | goto done; | ||
1514 | } | ||
1515 | if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) { | ||
1516 | retval = -EINVAL; | ||
1517 | goto done; | ||
1518 | } | ||
1360 | 1519 | ||
1361 | freq->type = V4L2_TUNER_RADIO; | 1520 | retval = si470x_get_freq(radio, &freq->frequency); |
1362 | freq->frequency = si470x_get_freq(radio); | ||
1363 | 1521 | ||
1364 | return 0; | 1522 | done: |
1523 | if (retval < 0) | ||
1524 | printk(KERN_WARNING DRIVER_NAME | ||
1525 | ": get frequency failed with %d\n", retval); | ||
1526 | return retval; | ||
1365 | } | 1527 | } |
1366 | 1528 | ||
1367 | 1529 | ||
@@ -1372,19 +1534,55 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, | |||
1372 | struct v4l2_frequency *freq) | 1534 | struct v4l2_frequency *freq) |
1373 | { | 1535 | { |
1374 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | 1536 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); |
1375 | int retval; | 1537 | int retval = 0; |
1376 | 1538 | ||
1377 | if (radio->disconnected) | 1539 | /* safety checks */ |
1378 | return -EIO; | 1540 | if (radio->disconnected) { |
1379 | if (freq->type != V4L2_TUNER_RADIO) | 1541 | retval = -EIO; |
1380 | return -EINVAL; | 1542 | goto done; |
1543 | } | ||
1544 | if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) { | ||
1545 | retval = -EINVAL; | ||
1546 | goto done; | ||
1547 | } | ||
1381 | 1548 | ||
1382 | retval = si470x_set_freq(radio, freq->frequency); | 1549 | retval = si470x_set_freq(radio, freq->frequency); |
1550 | |||
1551 | done: | ||
1383 | if (retval < 0) | 1552 | if (retval < 0) |
1384 | printk(KERN_WARNING DRIVER_NAME | 1553 | printk(KERN_WARNING DRIVER_NAME |
1385 | ": set frequency failed with %d\n", retval); | 1554 | ": set frequency failed with %d\n", retval); |
1555 | return retval; | ||
1556 | } | ||
1386 | 1557 | ||
1387 | return 0; | 1558 | |
1559 | /* | ||
1560 | * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek | ||
1561 | */ | ||
1562 | static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, | ||
1563 | struct v4l2_hw_freq_seek *seek) | ||
1564 | { | ||
1565 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1566 | int retval = 0; | ||
1567 | |||
1568 | /* safety checks */ | ||
1569 | if (radio->disconnected) { | ||
1570 | retval = -EIO; | ||
1571 | goto done; | ||
1572 | } | ||
1573 | if ((seek->tuner != 0) && (seek->type != V4L2_TUNER_RADIO)) { | ||
1574 | retval = -EINVAL; | ||
1575 | goto done; | ||
1576 | } | ||
1577 | |||
1578 | retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); | ||
1579 | |||
1580 | done: | ||
1581 | if (retval < 0) | ||
1582 | printk(KERN_WARNING DRIVER_NAME | ||
1583 | ": set hardware frequency seek failed with %d\n", | ||
1584 | retval); | ||
1585 | return retval; | ||
1388 | } | 1586 | } |
1389 | 1587 | ||
1390 | 1588 | ||
@@ -1408,6 +1606,7 @@ static struct video_device si470x_viddev_template = { | |||
1408 | .vidioc_s_tuner = si470x_vidioc_s_tuner, | 1606 | .vidioc_s_tuner = si470x_vidioc_s_tuner, |
1409 | .vidioc_g_frequency = si470x_vidioc_g_frequency, | 1607 | .vidioc_g_frequency = si470x_vidioc_g_frequency, |
1410 | .vidioc_s_frequency = si470x_vidioc_s_frequency, | 1608 | .vidioc_s_frequency = si470x_vidioc_s_frequency, |
1609 | .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, | ||
1411 | .owner = THIS_MODULE, | 1610 | .owner = THIS_MODULE, |
1412 | }; | 1611 | }; |
1413 | 1612 | ||
@@ -1424,31 +1623,36 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
1424 | const struct usb_device_id *id) | 1623 | const struct usb_device_id *id) |
1425 | { | 1624 | { |
1426 | struct si470x_device *radio; | 1625 | struct si470x_device *radio; |
1427 | int retval = -ENOMEM; | 1626 | int retval = 0; |
1428 | 1627 | ||
1429 | /* private data allocation */ | 1628 | /* private data allocation and initialization */ |
1430 | radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); | 1629 | radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); |
1431 | if (!radio) | 1630 | if (!radio) { |
1631 | retval = -ENOMEM; | ||
1432 | goto err_initial; | 1632 | goto err_initial; |
1633 | } | ||
1634 | radio->users = 0; | ||
1635 | radio->disconnected = 0; | ||
1636 | radio->usbdev = interface_to_usbdev(intf); | ||
1637 | radio->intf = intf; | ||
1638 | mutex_init(&radio->disconnect_lock); | ||
1639 | mutex_init(&radio->lock); | ||
1433 | 1640 | ||
1434 | /* video device allocation */ | 1641 | /* video device allocation and initialization */ |
1435 | radio->videodev = video_device_alloc(); | 1642 | radio->videodev = video_device_alloc(); |
1436 | if (!radio->videodev) | 1643 | if (!radio->videodev) { |
1644 | retval = -ENOMEM; | ||
1437 | goto err_radio; | 1645 | goto err_radio; |
1438 | 1646 | } | |
1439 | /* initial configuration */ | ||
1440 | memcpy(radio->videodev, &si470x_viddev_template, | 1647 | memcpy(radio->videodev, &si470x_viddev_template, |
1441 | sizeof(si470x_viddev_template)); | 1648 | sizeof(si470x_viddev_template)); |
1442 | radio->users = 0; | ||
1443 | radio->usbdev = interface_to_usbdev(intf); | ||
1444 | radio->intf = intf; | ||
1445 | mutex_init(&radio->lock); | ||
1446 | video_set_drvdata(radio->videodev, radio); | 1649 | video_set_drvdata(radio->videodev, radio); |
1447 | 1650 | ||
1448 | /* show some infos about the specific device */ | 1651 | /* show some infos about the specific device */ |
1449 | retval = -EIO; | 1652 | if (si470x_get_all_registers(radio) < 0) { |
1450 | if (si470x_get_all_registers(radio) < 0) | 1653 | retval = -EIO; |
1451 | goto err_all; | 1654 | goto err_all; |
1655 | } | ||
1452 | printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", | 1656 | printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", |
1453 | radio->registers[DEVICEID], radio->registers[CHIPID]); | 1657 | radio->registers[DEVICEID], radio->registers[CHIPID]); |
1454 | 1658 | ||
@@ -1474,8 +1678,10 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
1474 | /* rds buffer allocation */ | 1678 | /* rds buffer allocation */ |
1475 | radio->buf_size = rds_buf * 3; | 1679 | radio->buf_size = rds_buf * 3; |
1476 | radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); | 1680 | radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); |
1477 | if (!radio->buffer) | 1681 | if (!radio->buffer) { |
1682 | retval = -EIO; | ||
1478 | goto err_all; | 1683 | goto err_all; |
1684 | } | ||
1479 | 1685 | ||
1480 | /* rds buffer configuration */ | 1686 | /* rds buffer configuration */ |
1481 | radio->wr_index = 0; | 1687 | radio->wr_index = 0; |
@@ -1487,6 +1693,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
1487 | 1693 | ||
1488 | /* register video device */ | 1694 | /* register video device */ |
1489 | if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) { | 1695 | if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) { |
1696 | retval = -EIO; | ||
1490 | printk(KERN_WARNING DRIVER_NAME | 1697 | printk(KERN_WARNING DRIVER_NAME |
1491 | ": Could not register video device\n"); | 1698 | ": Could not register video device\n"); |
1492 | goto err_all; | 1699 | goto err_all; |
@@ -1546,16 +1753,16 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) | |||
1546 | { | 1753 | { |
1547 | struct si470x_device *radio = usb_get_intfdata(intf); | 1754 | struct si470x_device *radio = usb_get_intfdata(intf); |
1548 | 1755 | ||
1549 | mutex_lock(&open_close_lock); | 1756 | mutex_lock(&radio->disconnect_lock); |
1550 | radio->disconnected = 1; | 1757 | radio->disconnected = 1; |
1551 | cancel_delayed_work_sync(&radio->work); | 1758 | cancel_delayed_work_sync(&radio->work); |
1552 | usb_set_intfdata(intf, NULL); | 1759 | usb_set_intfdata(intf, NULL); |
1553 | if (radio->users == 0) { | 1760 | if (radio->users == 0) { |
1554 | video_unregister_device(radio->videodev); | 1761 | video_unregister_device(radio->videodev); |
1555 | kfree(radio->buffer); | 1762 | kfree(radio->buffer); |
1556 | kfree(radio); | 1763 | kfree(radio); |
1557 | } | 1764 | } |
1558 | mutex_unlock(&open_close_lock); | 1765 | mutex_unlock(&radio->disconnect_lock); |
1559 | } | 1766 | } |
1560 | 1767 | ||
1561 | 1768 | ||