aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio
diff options
context:
space:
mode:
authorTobias Lorenz <tobias.lorenz@gmx.net>2008-01-25 03:14:57 -0500
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-01-25 16:05:14 -0500
commit2fb8840663cf0e476549104a2c09caa0fb3b4bc9 (patch)
treed3e0ac80a5e5483918cfefac73647b910e6e2419 /drivers/media/radio
parent8bf5e5ca394441e56f68300dc4e7b26b79ddfe0b (diff)
V4L/DVB (7062): radio-si570x: Some fixes and new USB ID addition
- avoid poss. locking when doing copy_to_user which may sleep - RDS is automatically activated on read now - code cleaned of unnecessary rds_commands - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified (thanks to Guillaume RAMOUSSE) Signed-off-by: Tobias Lorenz <tobias.lorenz@gmx.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r--drivers/media/radio/radio-si470x.c291
1 files changed, 156 insertions, 135 deletions
diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c
index d54fe6405351..8e4bd4769048 100644
--- a/drivers/media/radio/radio-si470x.c
+++ b/drivers/media/radio/radio-si470x.c
@@ -55,13 +55,17 @@
55 * - applied all checkpatch.pl v1.12 suggestions 55 * - applied all checkpatch.pl v1.12 suggestions
56 * except the warning about the too long lines with bit comments 56 * except the warning about the too long lines with bit comments
57 * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl) 57 * - renamed FMRADIO to RADIO to cut line length (checkpatch.pl)
58 * 2008-01-22 Tobias Lorenz <tobias.lorenz@gmx.net>
59 * Version 1.0.4
60 * - avoid poss. locking when doing copy_to_user which may sleep
61 * - RDS is automatically activated on read now
62 * - code cleaned of unnecessary rds_commands
63 * - USB Vendor/Product ID for ADS/Tech FM Radio Receiver verified
64 * (thanks to Guillaume RAMOUSSE)
58 * 65 *
59 * ToDo: 66 * ToDo:
60 * - check USB Vendor/Product ID for ADS/Tech FM Radio Receiver
61 * (formerly Instant FM Music) (RDX-155-EF) is 06e1:a155
62 * - add seeking support 67 * - add seeking support
63 * - add firmware download/update support 68 * - add firmware download/update support
64 * - add possibility to switch off RDS
65 * - RDS support: interrupt mode, instead of polling 69 * - RDS support: interrupt mode, instead of polling
66 * - add LED status output (check if that's not already done in firmware) 70 * - add LED status output (check if that's not already done in firmware)
67 */ 71 */
@@ -70,7 +74,7 @@
70/* driver definitions */ 74/* driver definitions */
71#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" 75#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
72#define DRIVER_NAME "radio-si470x" 76#define DRIVER_NAME "radio-si470x"
73#define DRIVER_VERSION KERNEL_VERSION(1, 0, 3) 77#define DRIVER_VERSION KERNEL_VERSION(1, 0, 4)
74#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" 78#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
75#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" 79#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
76 80
@@ -93,6 +97,8 @@
93static struct usb_device_id si470x_usb_driver_id_table[] = { 97static struct usb_device_id si470x_usb_driver_id_table[] = {
94 /* Silicon Labs USB FM Radio Reference Design */ 98 /* Silicon Labs USB FM Radio Reference Design */
95 { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, 99 { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
100 /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */
101 { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
96 /* Terminating entry */ 102 /* Terminating entry */
97 { } 103 { }
98}; 104};
@@ -159,6 +165,7 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*");
159/* RDS poll frequency */ 165/* RDS poll frequency */
160static int rds_poll_time = 40; 166static int rds_poll_time = 40;
161/* 40 is used by the original USBRadio.exe */ 167/* 40 is used by the original USBRadio.exe */
168/* 50 is used by radio-cadet */
162/* 75 should be okay */ 169/* 75 should be okay */
163/* 80 is the usual RDS receive interval */ 170/* 80 is the usual RDS receive interval */
164module_param(rds_poll_time, int, 0); 171module_param(rds_poll_time, int, 0);
@@ -399,16 +406,13 @@ struct si470x_device {
399 406
400 /* RDS receive buffer */ 407 /* RDS receive buffer */
401 struct work_struct work; 408 struct work_struct work;
409 wait_queue_head_t read_queue;
402 struct timer_list timer; 410 struct timer_list timer;
403 spinlock_t lock; /* buffer locking */ 411 spinlock_t lock; /* buffer locking */
404 unsigned char *buffer; 412 unsigned char *buffer; /* size is always multiple of three */
405 unsigned int buf_size; 413 unsigned int buf_size;
406 unsigned int rd_index; 414 unsigned int rd_index;
407 unsigned int wr_index; 415 unsigned int wr_index;
408 unsigned int block_count;
409 unsigned char last_blocknum;
410 wait_queue_head_t read_queue;
411 int data_available_for_read;
412}; 416};
413 417
414 418
@@ -658,8 +662,7 @@ static int si470x_start(struct si470x_device *radio)
658 return retval; 662 return retval;
659 663
660 /* sysconfig 1 */ 664 /* sysconfig 1 */
661 radio->registers[SYSCONFIG1] = 665 radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
662 SYSCONFIG1_DE | SYSCONFIG1_RDS;
663 retval = si470x_set_register(radio, SYSCONFIG1); 666 retval = si470x_set_register(radio, SYSCONFIG1);
664 if (retval < 0) 667 if (retval < 0)
665 return retval; 668 return retval;
@@ -685,6 +688,14 @@ static int si470x_start(struct si470x_device *radio)
685 */ 688 */
686static int si470x_stop(struct si470x_device *radio) 689static int si470x_stop(struct si470x_device *radio)
687{ 690{
691 int retval;
692
693 /* sysconfig 1 */
694 radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
695 retval = si470x_set_register(radio, SYSCONFIG1);
696 if (retval < 0)
697 return retval;
698
688 /* powercfg */ 699 /* powercfg */
689 radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; 700 radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
690 /* POWERCFG_ENABLE has to automatically go low */ 701 /* POWERCFG_ENABLE has to automatically go low */
@@ -693,6 +704,17 @@ static int si470x_stop(struct si470x_device *radio)
693} 704}
694 705
695 706
707/*
708 * si470x_rds_on - switch on rds reception
709 */
710static int si470x_rds_on(struct si470x_device *radio)
711{
712 /* sysconfig 1 */
713 radio->registers[SYSCONFIG1] |= SYSCONFIG1_RDS;
714 return si470x_set_register(radio, SYSCONFIG1);
715}
716
717
696 718
697/************************************************************************** 719/**************************************************************************
698 * RDS Driver Functions 720 * RDS Driver Functions
@@ -703,15 +725,13 @@ static int si470x_stop(struct si470x_device *radio)
703 */ 725 */
704static void si470x_rds(struct si470x_device *radio) 726static void si470x_rds(struct si470x_device *radio)
705{ 727{
706 unsigned long flags;
707 unsigned char tmpbuf[3]; 728 unsigned char tmpbuf[3];
708 unsigned char blocknum; 729 unsigned char blocknum;
709 unsigned char bler; /* RDS block errors */ 730 unsigned char bler; /* rds block errors */
710 unsigned short rds; 731 unsigned short rds;
711 unsigned int i; 732 unsigned int i;
712 733
713 if (radio->users == 0) 734 /* get rds blocks */
714 return;
715 if (si470x_get_rds_registers(radio) < 0) 735 if (si470x_get_rds_registers(radio) < 0)
716 return; 736 return;
717 if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { 737 if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) {
@@ -723,63 +743,67 @@ static void si470x_rds(struct si470x_device *radio)
723 return; 743 return;
724 } 744 }
725 745
726 for (blocknum = 0; blocknum < 4; blocknum++) { 746 /* copy four RDS blocks to internal buffer */
727 switch (blocknum) { 747 if (spin_trylock(&radio->lock)) {
728 default: 748 /* process each rds block */
729 bler = (radio->registers[STATUSRSSI] & 749 for (blocknum = 0; blocknum < 4; blocknum++) {
730 STATUSRSSI_BLERA) >> 9; 750 switch (blocknum) {
731 rds = radio->registers[RDSA]; 751 default:
732 break; 752 bler = (radio->registers[STATUSRSSI] &
733 case 1: 753 STATUSRSSI_BLERA) >> 9;
734 bler = (radio->registers[READCHAN] & 754 rds = radio->registers[RDSA];
735 READCHAN_BLERB) >> 14; 755 break;
736 rds = radio->registers[RDSB]; 756 case 1:
737 break; 757 bler = (radio->registers[READCHAN] &
738 case 2: 758 READCHAN_BLERB) >> 14;
739 bler = (radio->registers[READCHAN] & 759 rds = radio->registers[RDSB];
740 READCHAN_BLERC) >> 12; 760 break;
741 rds = radio->registers[RDSC]; 761 case 2:
742 break; 762 bler = (radio->registers[READCHAN] &
743 case 3: 763 READCHAN_BLERC) >> 12;
744 bler = (radio->registers[READCHAN] & 764 rds = radio->registers[RDSC];
745 READCHAN_BLERD) >> 10; 765 break;
746 rds = radio->registers[RDSD]; 766 case 3:
747 break; 767 bler = (radio->registers[READCHAN] &
748 }; 768 READCHAN_BLERD) >> 10;
749 769 rds = radio->registers[RDSD];
750 /* Fill the V4L2 RDS buffer */ 770 break;
751 tmpbuf[0] = rds & 0x00ff; /* LSB */ 771 };
752 tmpbuf[1] = (rds & 0xff00) >> 8;/* MSB */ 772
753 tmpbuf[2] = blocknum; /* offset name */ 773 /* Fill the V4L2 RDS buffer */
754 tmpbuf[2] |= blocknum << 3; /* received offset */ 774 tmpbuf[0] = rds & 0x00ff; /* LSB */
755 if (bler > max_rds_errors) 775 tmpbuf[1] = (rds & 0xff00) >> 8;/* MSB */
756 tmpbuf[2] |= 0x80; /* uncorrectable errors */ 776 tmpbuf[2] = blocknum; /* offset name */
757 else if (bler > 0) 777 tmpbuf[2] |= blocknum << 3; /* received offset */
758 tmpbuf[2] |= 0x40; /* corrected error(s) */ 778 if (bler > max_rds_errors)
759 779 tmpbuf[2] |= 0x80; /* uncorrectable errors */
760 spin_lock_irqsave(&radio->lock, flags); 780 else if (bler > 0)
761 781 tmpbuf[2] |= 0x40; /* corrected error(s) */
762 /* copy RDS block to internal buffer */ 782
763 for (i = 0; i < 3; i++) { 783 /* copy RDS block to internal buffer */
764 radio->buffer[radio->wr_index] = tmpbuf[i]; 784 for (i = 0; i < 3; i++) {
765 radio->wr_index++; 785 radio->buffer[radio->wr_index] = tmpbuf[i];
786 radio->wr_index++;
787 }
788
789 /* wrap write pointer */
790 if (radio->wr_index >= radio->buf_size)
791 radio->wr_index = 0;
792
793 /* check for overflow */
794 if (radio->wr_index == radio->rd_index) {
795 /* increment and wrap read pointer */
796 radio->rd_index += 3;
797 if (radio->rd_index >= radio->buf_size)
798 radio->rd_index = 0;
799 }
766 } 800 }
767 801 spin_unlock(&radio->lock);
768 if (radio->wr_index >= radio->buf_size)
769 radio->wr_index = 0;
770
771 if (radio->wr_index == radio->rd_index) {
772 radio->rd_index += 3;
773 if (radio->rd_index >= radio->buf_size)
774 radio->rd_index = 0;
775 } else
776 radio->block_count++;
777
778 spin_unlock_irqrestore(&radio->lock, flags);
779 } 802 }
780 803
781 radio->data_available_for_read = 1; 804 /* wake up read queue */
782 wake_up_interruptible(&radio->read_queue); 805 if (radio->wr_index != radio->rd_index)
806 wake_up_interruptible(&radio->read_queue);
783} 807}
784 808
785 809
@@ -795,14 +819,14 @@ static void si470x_timer(unsigned long data)
795 819
796 820
797/* 821/*
798 * si470x_timer - rds work function 822 * si470x_work - rds work function
799 */ 823 */
800static void si470x_work(struct work_struct *work) 824static void si470x_work(struct work_struct *work)
801{ 825{
802 struct si470x_device *radio = container_of(work, struct si470x_device, 826 struct si470x_device *radio = container_of(work, struct si470x_device,
803 work); 827 work);
804 828
805 if (radio->users == 0) 829 if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
806 return; 830 return;
807 831
808 si470x_rds(radio); 832 si470x_rds(radio);
@@ -822,53 +846,52 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf,
822 size_t count, loff_t *ppos) 846 size_t count, loff_t *ppos)
823{ 847{
824 struct si470x_device *radio = video_get_drvdata(video_devdata(file)); 848 struct si470x_device *radio = video_get_drvdata(video_devdata(file));
825 struct rds_command cmd; 849 int retval = 0;
826 unsigned long flags; 850 unsigned int block_count = 0;
827 unsigned int i;
828 unsigned int rd_blocks;
829
830 cmd.block_count = count / 3; /* each RDS block needs 3 bytes */
831 cmd.result = 0;
832 cmd.buffer = buf;
833 cmd.instance = file;
834
835 /* copy RDS block out of internal buffer */
836 while (!radio->data_available_for_read) {
837 if (wait_event_interruptible(radio->read_queue,
838 radio->data_available_for_read) < 0)
839 return -EINTR;
840 }
841 851
842 spin_lock_irqsave(&radio->lock, flags); 852 /* switch on rds reception */
843 rd_blocks = cmd.block_count; 853 if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
844 if (rd_blocks > radio->block_count) 854 si470x_rds_on(radio);
845 rd_blocks = radio->block_count; 855 schedule_work(&radio->work);
856 }
846 857
847 if (!rd_blocks) { 858 /* block if no new data available */
848 spin_unlock_irqrestore(&radio->lock, flags); 859 while (radio->wr_index == radio->rd_index) {
849 return cmd.result; 860 if (file->f_flags & O_NONBLOCK)
861 return -EWOULDBLOCK;
862 interruptible_sleep_on(&radio->read_queue);
850 } 863 }
851 864
852 for (i = 0; i < rd_blocks; i++) { 865 /* calculate block count from byte count */
853 /* copy RDS block to user buffer */ 866 count /= 3;
854 if (radio->rd_index == radio->wr_index) 867
855 break; 868 /* copy RDS block out of internal buffer and to user buffer */
869 if (spin_trylock(&radio->lock)) {
870 while (block_count < count) {
871 if (radio->rd_index == radio->wr_index)
872 break;
856 873
857 if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3)) 874 /* always transfer rds complete blocks */
858 break; 875 if (copy_to_user(buf,
876 &radio->buffer[radio->rd_index], 3))
877 /* retval = -EFAULT; */
878 break;
859 879
860 radio->rd_index += 3; 880 /* increment and wrap read pointer */
861 if (radio->rd_index >= radio->buf_size) 881 radio->rd_index += 3;
862 radio->rd_index = 0; 882 if (radio->rd_index >= radio->buf_size)
863 radio->block_count--; 883 radio->rd_index = 0;
884
885 /* increment counters */
886 block_count++;
887 buf += 3;
888 retval += 3;
889 }
864 890
865 buf += 3; 891 spin_unlock(&radio->lock);
866 cmd.result += 3;
867 } 892 }
868 radio->data_available_for_read = (radio->block_count > 0);
869 spin_unlock_irqrestore(&radio->lock, flags);
870 893
871 return cmd.result; 894 return retval;
872} 895}
873 896
874 897
@@ -879,14 +902,19 @@ static unsigned int si470x_fops_poll(struct file *file,
879 struct poll_table_struct *pts) 902 struct poll_table_struct *pts)
880{ 903{
881 struct si470x_device *radio = video_get_drvdata(video_devdata(file)); 904 struct si470x_device *radio = video_get_drvdata(video_devdata(file));
882 int retval;
883 905
884 retval = 0; 906 /* switch on rds reception */
885 if (radio->data_available_for_read) 907 if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
886 retval = POLLIN | POLLRDNORM; 908 si470x_rds_on(radio);
909 schedule_work(&radio->work);
910 }
911
887 poll_wait(file, &radio->read_queue, pts); 912 poll_wait(file, &radio->read_queue, pts);
888 913
889 return retval; 914 if (radio->rd_index != radio->wr_index)
915 return POLLIN | POLLRDNORM;
916
917 return 0;
890} 918}
891 919
892 920
@@ -895,17 +923,11 @@ static unsigned int si470x_fops_poll(struct file *file,
895 */ 923 */
896static int si470x_fops_open(struct inode *inode, struct file *file) 924static int si470x_fops_open(struct inode *inode, struct file *file)
897{ 925{
898 int retval;
899 struct si470x_device *radio = video_get_drvdata(video_devdata(file)); 926 struct si470x_device *radio = video_get_drvdata(video_devdata(file));
900 927
901 radio->users++; 928 radio->users++;
902 if (radio->users == 1) { 929 if (radio->users == 1)
903 retval = si470x_start(radio); 930 return si470x_start(radio);
904 if (retval < 0)
905 return retval;
906
907 schedule_work(&radio->work);
908 }
909 931
910 return 0; 932 return 0;
911} 933}
@@ -916,7 +938,6 @@ static int si470x_fops_open(struct inode *inode, struct file *file)
916 */ 938 */
917static int si470x_fops_release(struct inode *inode, struct file *file) 939static int si470x_fops_release(struct inode *inode, struct file *file)
918{ 940{
919 int retval;
920 struct si470x_device *radio = video_get_drvdata(video_devdata(file)); 941 struct si470x_device *radio = video_get_drvdata(video_devdata(file));
921 942
922 if (!radio) 943 if (!radio)
@@ -924,12 +945,14 @@ static int si470x_fops_release(struct inode *inode, struct file *file)
924 945
925 radio->users--; 946 radio->users--;
926 if (radio->users == 0) { 947 if (radio->users == 0) {
927 radio->data_available_for_read = 1; /* ? */ 948 /* stop rds reception */
928 wake_up_interruptible(&radio->read_queue); /* ? */ 949 del_timer_sync(&radio->timer);
950 flush_scheduled_work();
929 951
930 retval = si470x_stop(radio); 952 /* cancel read processes */
931 if (retval < 0) 953 wake_up_interruptible(&radio->read_queue);
932 return retval; 954
955 return si470x_stop(radio);
933 } 956 }
934 957
935 return 0; 958 return 0;
@@ -1314,9 +1337,10 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
1314 < RADIO_SW_VERSION_CURRENT) 1337 < RADIO_SW_VERSION_CURRENT)
1315 printk(KERN_WARNING DRIVER_NAME 1338 printk(KERN_WARNING DRIVER_NAME
1316 ": This driver is known to work with chip version %d, " 1339 ": This driver is known to work with chip version %d, "
1317 "but the device has firmware %d. If you have some " 1340 "but the device has firmware %d.\n"
1318 "trouble using this driver, please report to V4L ML " 1341 DRIVER_NAME
1319 "at video4linux-list@redhat.com\n", 1342 "If you have some trouble using this driver, please "
1343 "report to V4L ML at video4linux-list@redhat.com\n",
1320 radio->registers[CHIPID] & CHIPID_FIRMWARE, 1344 radio->registers[CHIPID] & CHIPID_FIRMWARE,
1321 RADIO_SW_VERSION_CURRENT); 1345 RADIO_SW_VERSION_CURRENT);
1322 1346
@@ -1331,12 +1355,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
1331 kfree(radio); 1355 kfree(radio);
1332 return -ENOMEM; 1356 return -ENOMEM;
1333 } 1357 }
1334 radio->block_count = 0;
1335 radio->wr_index = 0; 1358 radio->wr_index = 0;
1336 radio->rd_index = 0; 1359 radio->rd_index = 0;
1337 radio->last_blocknum = 0xff;
1338 init_waitqueue_head(&radio->read_queue); 1360 init_waitqueue_head(&radio->read_queue);
1339 radio->data_available_for_read = 0;
1340 1361
1341 /* prepare polling via eventd */ 1362 /* prepare polling via eventd */
1342 INIT_WORK(&radio->work, si470x_work); 1363 INIT_WORK(&radio->work, si470x_work);
@@ -1408,4 +1429,4 @@ module_exit(si470x_module_exit);
1408MODULE_LICENSE("GPL"); 1429MODULE_LICENSE("GPL");
1409MODULE_AUTHOR(DRIVER_AUTHOR); 1430MODULE_AUTHOR(DRIVER_AUTHOR);
1410MODULE_DESCRIPTION(DRIVER_DESC); 1431MODULE_DESCRIPTION(DRIVER_DESC);
1411MODULE_VERSION("1.0.3"); 1432MODULE_VERSION("1.0.4");