diff options
author | Tobias Lorenz <tobias.lorenz@gmx.net> | 2009-06-20 18:00:06 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-09-12 11:17:11 -0400 |
commit | 721f59ed612477a9f83f3f3a222a14d16505c1a4 (patch) | |
tree | 3fba0f8d336103debf9e49097bb99b706a6adaf9 /drivers/media/radio | |
parent | 86d710146fb9975f04c505ec78caa43d227c1018 (diff) |
V4L/DVB (12142): radio-si470x: Add suport for RDS endpoint interrupt mode
Add support for interrupt mode for RDS endpoint, instead of polling.
Improves RDS reception significantly
Signed-off-by: Edouard Lafargue <edouard@lafargue.name>
Acked-by: Tobias Lorenz <tobias.lorenz@gmx.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/radio-si470x.c | 350 |
1 files changed, 192 insertions, 158 deletions
diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c index e85f318b4d2b..41f304325074 100644 --- a/drivers/media/radio/radio-si470x.c +++ b/drivers/media/radio/radio-si470x.c | |||
@@ -106,20 +106,24 @@ | |||
106 | * Tobias Lorenz <tobias.lorenz@gmx.net> | 106 | * Tobias Lorenz <tobias.lorenz@gmx.net> |
107 | * - add LED status output | 107 | * - add LED status output |
108 | * - get HW/SW version from scratchpad | 108 | * - get HW/SW version from scratchpad |
109 | * 2009-06-16 Edouard Lafargue <edouard@lafargue.name> | ||
110 | * Version 1.0.10 | ||
111 | * - add support for interrupt mode for RDS endpoint, | ||
112 | * instead of polling. | ||
113 | * Improves RDS reception significantly | ||
109 | * | 114 | * |
110 | * ToDo: | 115 | * ToDo: |
111 | * - add firmware download/update support | 116 | * - add firmware download/update support |
112 | * - RDS support: interrupt mode, instead of polling | ||
113 | */ | 117 | */ |
114 | 118 | ||
115 | 119 | ||
116 | /* driver definitions */ | 120 | /* driver definitions */ |
117 | #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" | 121 | #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" |
118 | #define DRIVER_NAME "radio-si470x" | 122 | #define DRIVER_NAME "radio-si470x" |
119 | #define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 9) | 123 | #define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10) |
120 | #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" | 124 | #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" |
121 | #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" | 125 | #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" |
122 | #define DRIVER_VERSION "1.0.9" | 126 | #define DRIVER_VERSION "1.0.10" |
123 | 127 | ||
124 | 128 | ||
125 | /* kernel includes */ | 129 | /* kernel includes */ |
@@ -218,16 +222,6 @@ static unsigned short max_rds_errors = 1; | |||
218 | module_param(max_rds_errors, ushort, 0644); | 222 | module_param(max_rds_errors, ushort, 0644); |
219 | MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); | 223 | MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); |
220 | 224 | ||
221 | /* RDS poll frequency */ | ||
222 | static unsigned int rds_poll_time = 40; | ||
223 | /* 40 is used by the original USBRadio.exe */ | ||
224 | /* 50 is used by radio-cadet */ | ||
225 | /* 75 should be okay */ | ||
226 | /* 80 is the usual RDS receive interval */ | ||
227 | module_param(rds_poll_time, uint, 0644); | ||
228 | MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*"); | ||
229 | |||
230 | |||
231 | 225 | ||
232 | /************************************************************************** | 226 | /************************************************************************** |
233 | * Register Definitions | 227 | * Register Definitions |
@@ -450,6 +444,12 @@ struct si470x_device { | |||
450 | struct usb_interface *intf; | 444 | struct usb_interface *intf; |
451 | struct video_device *videodev; | 445 | struct video_device *videodev; |
452 | 446 | ||
447 | /* Interrupt endpoint handling */ | ||
448 | char *int_in_buffer; | ||
449 | struct usb_endpoint_descriptor *int_in_endpoint; | ||
450 | struct urb *int_in_urb; | ||
451 | int int_in_running; | ||
452 | |||
453 | /* driver management */ | 453 | /* driver management */ |
454 | unsigned int users; | 454 | unsigned int users; |
455 | unsigned char disconnected; | 455 | unsigned char disconnected; |
@@ -459,7 +459,6 @@ struct si470x_device { | |||
459 | unsigned short registers[RADIO_REGISTER_NUM]; | 459 | unsigned short registers[RADIO_REGISTER_NUM]; |
460 | 460 | ||
461 | /* RDS receive buffer */ | 461 | /* RDS receive buffer */ |
462 | struct delayed_work work; | ||
463 | wait_queue_head_t read_queue; | 462 | wait_queue_head_t read_queue; |
464 | struct mutex lock; /* buffer locking */ | 463 | struct mutex lock; /* buffer locking */ |
465 | unsigned char *buffer; /* size is always multiple of three */ | 464 | unsigned char *buffer; /* size is always multiple of three */ |
@@ -865,43 +864,6 @@ static int si470x_get_all_registers(struct si470x_device *radio) | |||
865 | 864 | ||
866 | 865 | ||
867 | /************************************************************************** | 866 | /************************************************************************** |
868 | * General Driver Functions - RDS_REPORT | ||
869 | **************************************************************************/ | ||
870 | |||
871 | /* | ||
872 | * si470x_get_rds_registers - read rds registers | ||
873 | */ | ||
874 | static int si470x_get_rds_registers(struct si470x_device *radio) | ||
875 | { | ||
876 | unsigned char buf[RDS_REPORT_SIZE]; | ||
877 | int retval; | ||
878 | int size; | ||
879 | unsigned char regnr; | ||
880 | |||
881 | buf[0] = RDS_REPORT; | ||
882 | |||
883 | retval = usb_interrupt_msg(radio->usbdev, | ||
884 | usb_rcvintpipe(radio->usbdev, 1), | ||
885 | (void *) &buf, sizeof(buf), &size, usb_timeout); | ||
886 | if (size != sizeof(buf)) | ||
887 | printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " | ||
888 | "return size differs: %d != %zu\n", size, sizeof(buf)); | ||
889 | if (retval < 0) | ||
890 | printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: " | ||
891 | "usb_interrupt_msg returned %d\n", retval); | ||
892 | |||
893 | if (retval >= 0) | ||
894 | for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) | ||
895 | radio->registers[STATUSRSSI + regnr] = | ||
896 | get_unaligned_be16( | ||
897 | &buf[regnr * RADIO_REGISTER_SIZE + 1]); | ||
898 | |||
899 | return (retval < 0) ? -EINVAL : 0; | ||
900 | } | ||
901 | |||
902 | |||
903 | |||
904 | /************************************************************************** | ||
905 | * General Driver Functions - LED_REPORT | 867 | * General Driver Functions - LED_REPORT |
906 | **************************************************************************/ | 868 | **************************************************************************/ |
907 | 869 | ||
@@ -959,102 +921,118 @@ static int si470x_get_scratch_page_versions(struct si470x_device *radio) | |||
959 | **************************************************************************/ | 921 | **************************************************************************/ |
960 | 922 | ||
961 | /* | 923 | /* |
962 | * si470x_rds - rds processing function | 924 | * si470x_int_in_callback - rds callback and processing function |
925 | * | ||
926 | * TODO: do we need to use mutex locks in some sections? | ||
963 | */ | 927 | */ |
964 | static void si470x_rds(struct si470x_device *radio) | 928 | static void si470x_int_in_callback(struct urb *urb) |
965 | { | 929 | { |
930 | struct si470x_device *radio = urb->context; | ||
931 | unsigned char buf[RDS_REPORT_SIZE]; | ||
932 | int retval; | ||
933 | unsigned char regnr; | ||
966 | unsigned char blocknum; | 934 | unsigned char blocknum; |
967 | unsigned short bler; /* rds block errors */ | 935 | unsigned short bler; /* rds block errors */ |
968 | unsigned short rds; | 936 | unsigned short rds; |
969 | unsigned char tmpbuf[3]; | 937 | unsigned char tmpbuf[3]; |
970 | 938 | ||
971 | /* get rds blocks */ | 939 | if (urb->status) { |
972 | if (si470x_get_rds_registers(radio) < 0) | 940 | if (urb->status == -ENOENT || |
973 | return; | 941 | urb->status == -ECONNRESET || |
974 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { | 942 | urb->status == -ESHUTDOWN) { |
975 | /* No RDS group ready */ | 943 | return; |
976 | return; | 944 | } else { |
977 | } | 945 | printk(KERN_WARNING DRIVER_NAME |
978 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) { | 946 | ": non-zero urb status (%d)\n", urb->status); |
979 | /* RDS decoder not synchronized */ | 947 | goto resubmit; /* Maybe we can recover. */ |
980 | return; | ||
981 | } | ||
982 | |||
983 | /* copy all four RDS blocks to internal buffer */ | ||
984 | mutex_lock(&radio->lock); | ||
985 | for (blocknum = 0; blocknum < 4; blocknum++) { | ||
986 | switch (blocknum) { | ||
987 | default: | ||
988 | bler = (radio->registers[STATUSRSSI] & | ||
989 | STATUSRSSI_BLERA) >> 9; | ||
990 | rds = radio->registers[RDSA]; | ||
991 | break; | ||
992 | case 1: | ||
993 | bler = (radio->registers[READCHAN] & | ||
994 | READCHAN_BLERB) >> 14; | ||
995 | rds = radio->registers[RDSB]; | ||
996 | break; | ||
997 | case 2: | ||
998 | bler = (radio->registers[READCHAN] & | ||
999 | READCHAN_BLERC) >> 12; | ||
1000 | rds = radio->registers[RDSC]; | ||
1001 | break; | ||
1002 | case 3: | ||
1003 | bler = (radio->registers[READCHAN] & | ||
1004 | READCHAN_BLERD) >> 10; | ||
1005 | rds = radio->registers[RDSD]; | ||
1006 | break; | ||
1007 | }; | ||
1008 | |||
1009 | /* Fill the V4L2 RDS buffer */ | ||
1010 | put_unaligned_le16(rds, &tmpbuf); | ||
1011 | tmpbuf[2] = blocknum; /* offset name */ | ||
1012 | tmpbuf[2] |= blocknum << 3; /* received offset */ | ||
1013 | if (bler > max_rds_errors) | ||
1014 | tmpbuf[2] |= 0x80; /* uncorrectable errors */ | ||
1015 | else if (bler > 0) | ||
1016 | tmpbuf[2] |= 0x40; /* corrected error(s) */ | ||
1017 | |||
1018 | /* copy RDS block to internal buffer */ | ||
1019 | memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); | ||
1020 | radio->wr_index += 3; | ||
1021 | |||
1022 | /* wrap write pointer */ | ||
1023 | if (radio->wr_index >= radio->buf_size) | ||
1024 | radio->wr_index = 0; | ||
1025 | |||
1026 | /* check for overflow */ | ||
1027 | if (radio->wr_index == radio->rd_index) { | ||
1028 | /* increment and wrap read pointer */ | ||
1029 | radio->rd_index += 3; | ||
1030 | if (radio->rd_index >= radio->buf_size) | ||
1031 | radio->rd_index = 0; | ||
1032 | } | 948 | } |
1033 | } | 949 | } |
1034 | mutex_unlock(&radio->lock); | ||
1035 | |||
1036 | /* wake up read queue */ | ||
1037 | if (radio->wr_index != radio->rd_index) | ||
1038 | wake_up_interruptible(&radio->read_queue); | ||
1039 | } | ||
1040 | |||
1041 | |||
1042 | /* | ||
1043 | * si470x_work - rds work function | ||
1044 | */ | ||
1045 | static void si470x_work(struct work_struct *work) | ||
1046 | { | ||
1047 | struct si470x_device *radio = container_of(work, struct si470x_device, | ||
1048 | work.work); | ||
1049 | 950 | ||
1050 | /* safety checks */ | 951 | /* safety checks */ |
1051 | if (radio->disconnected) | 952 | if (radio->disconnected) |
1052 | return; | 953 | return; |
1053 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) | 954 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) |
1054 | return; | 955 | goto resubmit; |
1055 | 956 | ||
1056 | si470x_rds(radio); | 957 | if (urb->actual_length > 0) { |
1057 | schedule_delayed_work(&radio->work, msecs_to_jiffies(rds_poll_time)); | 958 | /* Update RDS registers with URB data */ |
959 | buf[0] = RDS_REPORT; | ||
960 | for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) | ||
961 | radio->registers[STATUSRSSI + regnr] = | ||
962 | get_unaligned_be16(&radio->int_in_buffer[ | ||
963 | regnr * RADIO_REGISTER_SIZE + 1]); | ||
964 | /* get rds blocks */ | ||
965 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { | ||
966 | /* No RDS group ready, better luck next time */ | ||
967 | goto resubmit; | ||
968 | } | ||
969 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) { | ||
970 | /* RDS decoder not synchronized */ | ||
971 | goto resubmit; | ||
972 | } | ||
973 | for (blocknum = 0; blocknum < 4; blocknum++) { | ||
974 | switch (blocknum) { | ||
975 | default: | ||
976 | bler = (radio->registers[STATUSRSSI] & | ||
977 | STATUSRSSI_BLERA) >> 9; | ||
978 | rds = radio->registers[RDSA]; | ||
979 | break; | ||
980 | case 1: | ||
981 | bler = (radio->registers[READCHAN] & | ||
982 | READCHAN_BLERB) >> 14; | ||
983 | rds = radio->registers[RDSB]; | ||
984 | break; | ||
985 | case 2: | ||
986 | bler = (radio->registers[READCHAN] & | ||
987 | READCHAN_BLERC) >> 12; | ||
988 | rds = radio->registers[RDSC]; | ||
989 | break; | ||
990 | case 3: | ||
991 | bler = (radio->registers[READCHAN] & | ||
992 | READCHAN_BLERD) >> 10; | ||
993 | rds = radio->registers[RDSD]; | ||
994 | break; | ||
995 | }; | ||
996 | |||
997 | /* Fill the V4L2 RDS buffer */ | ||
998 | put_unaligned_le16(rds, &tmpbuf); | ||
999 | tmpbuf[2] = blocknum; /* offset name */ | ||
1000 | tmpbuf[2] |= blocknum << 3; /* received offset */ | ||
1001 | if (bler > max_rds_errors) | ||
1002 | tmpbuf[2] |= 0x80; /* uncorrectable errors */ | ||
1003 | else if (bler > 0) | ||
1004 | tmpbuf[2] |= 0x40; /* corrected error(s) */ | ||
1005 | |||
1006 | /* copy RDS block to internal buffer */ | ||
1007 | memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); | ||
1008 | radio->wr_index += 3; | ||
1009 | |||
1010 | /* wrap write pointer */ | ||
1011 | if (radio->wr_index >= radio->buf_size) | ||
1012 | radio->wr_index = 0; | ||
1013 | |||
1014 | /* check for overflow */ | ||
1015 | if (radio->wr_index == radio->rd_index) { | ||
1016 | /* increment and wrap read pointer */ | ||
1017 | radio->rd_index += 3; | ||
1018 | if (radio->rd_index >= radio->buf_size) | ||
1019 | radio->rd_index = 0; | ||
1020 | } | ||
1021 | } | ||
1022 | if (radio->wr_index != radio->rd_index) | ||
1023 | wake_up_interruptible(&radio->read_queue); | ||
1024 | } | ||
1025 | |||
1026 | resubmit: | ||
1027 | /* Resubmit if we're still running. */ | ||
1028 | if (radio->int_in_running && radio->usbdev) { | ||
1029 | retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC); | ||
1030 | if (retval) { | ||
1031 | printk(KERN_WARNING DRIVER_NAME | ||
1032 | ": resubmitting urb failed (%d)", retval); | ||
1033 | radio->int_in_running = 0; | ||
1034 | } | ||
1035 | } | ||
1058 | } | 1036 | } |
1059 | 1037 | ||
1060 | 1038 | ||
@@ -1076,8 +1054,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, | |||
1076 | /* switch on rds reception */ | 1054 | /* switch on rds reception */ |
1077 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { | 1055 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { |
1078 | si470x_rds_on(radio); | 1056 | si470x_rds_on(radio); |
1079 | schedule_delayed_work(&radio->work, | ||
1080 | msecs_to_jiffies(rds_poll_time)); | ||
1081 | } | 1057 | } |
1082 | 1058 | ||
1083 | /* block if no new data available */ | 1059 | /* block if no new data available */ |
@@ -1136,8 +1112,6 @@ static unsigned int si470x_fops_poll(struct file *file, | |||
1136 | /* switch on rds reception */ | 1112 | /* switch on rds reception */ |
1137 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { | 1113 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) { |
1138 | si470x_rds_on(radio); | 1114 | si470x_rds_on(radio); |
1139 | schedule_delayed_work(&radio->work, | ||
1140 | msecs_to_jiffies(rds_poll_time)); | ||
1141 | } | 1115 | } |
1142 | 1116 | ||
1143 | poll_wait(file, &radio->read_queue, pts); | 1117 | poll_wait(file, &radio->read_queue, pts); |
@@ -1167,11 +1141,37 @@ static int si470x_fops_open(struct file *file) | |||
1167 | goto done; | 1141 | goto done; |
1168 | } | 1142 | } |
1169 | 1143 | ||
1144 | printk(KERN_INFO DRIVER_NAME | ||
1145 | ": Opened radio (users now: %i)\n", radio->users); | ||
1146 | |||
1170 | if (radio->users == 1) { | 1147 | if (radio->users == 1) { |
1171 | /* start radio */ | 1148 | /* start radio */ |
1172 | retval = si470x_start(radio); | 1149 | retval = si470x_start(radio); |
1173 | if (retval < 0) | 1150 | if (retval < 0) { |
1174 | usb_autopm_put_interface(radio->intf); | 1151 | usb_autopm_put_interface(radio->intf); |
1152 | goto done; | ||
1153 | } | ||
1154 | /* Initialize interrupt URB. */ | ||
1155 | usb_fill_int_urb(radio->int_in_urb, radio->usbdev, | ||
1156 | usb_rcvintpipe(radio->usbdev, | ||
1157 | radio->int_in_endpoint->bEndpointAddress), | ||
1158 | radio->int_in_buffer, | ||
1159 | le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), | ||
1160 | si470x_int_in_callback, | ||
1161 | radio, | ||
1162 | radio->int_in_endpoint->bInterval); | ||
1163 | |||
1164 | radio->int_in_running = 1; | ||
1165 | mb(); | ||
1166 | |||
1167 | retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); | ||
1168 | if (retval) { | ||
1169 | printk(KERN_INFO DRIVER_NAME | ||
1170 | ": submitting int urb failed (%d)\n", retval); | ||
1171 | radio->int_in_running = 0; | ||
1172 | usb_autopm_put_interface(radio->intf); | ||
1173 | } | ||
1174 | |||
1175 | } | 1175 | } |
1176 | 1176 | ||
1177 | done: | 1177 | done: |
@@ -1196,17 +1196,25 @@ static int si470x_fops_release(struct file *file) | |||
1196 | 1196 | ||
1197 | mutex_lock(&radio->disconnect_lock); | 1197 | mutex_lock(&radio->disconnect_lock); |
1198 | radio->users--; | 1198 | radio->users--; |
1199 | printk(KERN_INFO DRIVER_NAME | ||
1200 | ": Closed radio (remaining users:%i)\n", radio->users); | ||
1199 | if (radio->users == 0) { | 1201 | if (radio->users == 0) { |
1202 | |||
1203 | /* Shutdown Interrupt handler */ | ||
1204 | if (radio->int_in_running) { | ||
1205 | radio->int_in_running = 0; | ||
1206 | if (radio->int_in_urb) | ||
1207 | usb_kill_urb(radio->int_in_urb); | ||
1208 | } | ||
1209 | |||
1200 | if (radio->disconnected) { | 1210 | if (radio->disconnected) { |
1201 | video_unregister_device(radio->videodev); | 1211 | video_unregister_device(radio->videodev); |
1212 | kfree(radio->int_in_buffer); | ||
1202 | kfree(radio->buffer); | 1213 | kfree(radio->buffer); |
1203 | kfree(radio); | 1214 | kfree(radio); |
1204 | goto unlock; | 1215 | goto unlock; |
1205 | } | 1216 | } |
1206 | 1217 | ||
1207 | /* stop rds reception */ | ||
1208 | cancel_delayed_work_sync(&radio->work); | ||
1209 | |||
1210 | /* cancel read processes */ | 1218 | /* cancel read processes */ |
1211 | wake_up_interruptible(&radio->read_queue); | 1219 | wake_up_interruptible(&radio->read_queue); |
1212 | 1220 | ||
@@ -1657,7 +1665,9 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
1657 | const struct usb_device_id *id) | 1665 | const struct usb_device_id *id) |
1658 | { | 1666 | { |
1659 | struct si470x_device *radio; | 1667 | struct si470x_device *radio; |
1660 | int retval = 0; | 1668 | struct usb_host_interface *iface_desc; |
1669 | struct usb_endpoint_descriptor *endpoint; | ||
1670 | int i, int_end_size, retval = 0; | ||
1661 | 1671 | ||
1662 | /* private data allocation and initialization */ | 1672 | /* private data allocation and initialization */ |
1663 | radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); | 1673 | radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); |
@@ -1672,11 +1682,45 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
1672 | mutex_init(&radio->disconnect_lock); | 1682 | mutex_init(&radio->disconnect_lock); |
1673 | mutex_init(&radio->lock); | 1683 | mutex_init(&radio->lock); |
1674 | 1684 | ||
1685 | iface_desc = intf->cur_altsetting; | ||
1686 | |||
1687 | /* Set up interrupt endpoint information. */ | ||
1688 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | ||
1689 | endpoint = &iface_desc->endpoint[i].desc; | ||
1690 | if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == | ||
1691 | USB_DIR_IN) && ((endpoint->bmAttributes & | ||
1692 | USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) | ||
1693 | radio->int_in_endpoint = endpoint; | ||
1694 | } | ||
1695 | if (!radio->int_in_endpoint) { | ||
1696 | printk(KERN_INFO DRIVER_NAME | ||
1697 | ": could not find interrupt in endpoint\n"); | ||
1698 | retval = -EIO; | ||
1699 | goto err_radio; | ||
1700 | } | ||
1701 | |||
1702 | int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize); | ||
1703 | |||
1704 | radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL); | ||
1705 | if (!radio->int_in_buffer) { | ||
1706 | printk(KERN_INFO DRIVER_NAME | ||
1707 | "could not allocate int_in_buffer"); | ||
1708 | retval = -ENOMEM; | ||
1709 | goto err_radio; | ||
1710 | } | ||
1711 | |||
1712 | radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
1713 | if (!radio->int_in_urb) { | ||
1714 | printk(KERN_INFO DRIVER_NAME "could not allocate int_in_urb"); | ||
1715 | retval = -ENOMEM; | ||
1716 | goto err_intbuffer; | ||
1717 | } | ||
1718 | |||
1675 | /* video device allocation and initialization */ | 1719 | /* video device allocation and initialization */ |
1676 | radio->videodev = video_device_alloc(); | 1720 | radio->videodev = video_device_alloc(); |
1677 | if (!radio->videodev) { | 1721 | if (!radio->videodev) { |
1678 | retval = -ENOMEM; | 1722 | retval = -ENOMEM; |
1679 | goto err_radio; | 1723 | goto err_intbuffer; |
1680 | } | 1724 | } |
1681 | memcpy(radio->videodev, &si470x_viddev_template, | 1725 | memcpy(radio->videodev, &si470x_viddev_template, |
1682 | sizeof(si470x_viddev_template)); | 1726 | sizeof(si470x_viddev_template)); |
@@ -1734,9 +1778,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
1734 | radio->rd_index = 0; | 1778 | radio->rd_index = 0; |
1735 | init_waitqueue_head(&radio->read_queue); | 1779 | init_waitqueue_head(&radio->read_queue); |
1736 | 1780 | ||
1737 | /* prepare rds work function */ | ||
1738 | INIT_DELAYED_WORK(&radio->work, si470x_work); | ||
1739 | |||
1740 | /* register video device */ | 1781 | /* register video device */ |
1741 | retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr); | 1782 | retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr); |
1742 | if (retval) { | 1783 | if (retval) { |
@@ -1751,6 +1792,8 @@ err_all: | |||
1751 | kfree(radio->buffer); | 1792 | kfree(radio->buffer); |
1752 | err_video: | 1793 | err_video: |
1753 | video_device_release(radio->videodev); | 1794 | video_device_release(radio->videodev); |
1795 | err_intbuffer: | ||
1796 | kfree(radio->int_in_buffer); | ||
1754 | err_radio: | 1797 | err_radio: |
1755 | kfree(radio); | 1798 | kfree(radio); |
1756 | err_initial: | 1799 | err_initial: |
@@ -1764,12 +1807,8 @@ err_initial: | |||
1764 | static int si470x_usb_driver_suspend(struct usb_interface *intf, | 1807 | static int si470x_usb_driver_suspend(struct usb_interface *intf, |
1765 | pm_message_t message) | 1808 | pm_message_t message) |
1766 | { | 1809 | { |
1767 | struct si470x_device *radio = usb_get_intfdata(intf); | ||
1768 | |||
1769 | printk(KERN_INFO DRIVER_NAME ": suspending now...\n"); | 1810 | printk(KERN_INFO DRIVER_NAME ": suspending now...\n"); |
1770 | 1811 | ||
1771 | cancel_delayed_work_sync(&radio->work); | ||
1772 | |||
1773 | return 0; | 1812 | return 0; |
1774 | } | 1813 | } |
1775 | 1814 | ||
@@ -1779,16 +1818,8 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf, | |||
1779 | */ | 1818 | */ |
1780 | static int si470x_usb_driver_resume(struct usb_interface *intf) | 1819 | static int si470x_usb_driver_resume(struct usb_interface *intf) |
1781 | { | 1820 | { |
1782 | struct si470x_device *radio = usb_get_intfdata(intf); | ||
1783 | |||
1784 | printk(KERN_INFO DRIVER_NAME ": resuming now...\n"); | 1821 | printk(KERN_INFO DRIVER_NAME ": resuming now...\n"); |
1785 | 1822 | ||
1786 | mutex_lock(&radio->lock); | ||
1787 | if (radio->users && radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) | ||
1788 | schedule_delayed_work(&radio->work, | ||
1789 | msecs_to_jiffies(rds_poll_time)); | ||
1790 | mutex_unlock(&radio->lock); | ||
1791 | |||
1792 | return 0; | 1823 | return 0; |
1793 | } | 1824 | } |
1794 | 1825 | ||
@@ -1802,12 +1833,15 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) | |||
1802 | 1833 | ||
1803 | mutex_lock(&radio->disconnect_lock); | 1834 | mutex_lock(&radio->disconnect_lock); |
1804 | radio->disconnected = 1; | 1835 | radio->disconnected = 1; |
1805 | cancel_delayed_work_sync(&radio->work); | ||
1806 | usb_set_intfdata(intf, NULL); | 1836 | usb_set_intfdata(intf, NULL); |
1807 | if (radio->users == 0) { | 1837 | if (radio->users == 0) { |
1808 | /* set led to disconnect state */ | 1838 | /* set led to disconnect state */ |
1809 | si470x_set_led_state(radio, BLINK_ORANGE_LED); | 1839 | si470x_set_led_state(radio, BLINK_ORANGE_LED); |
1810 | 1840 | ||
1841 | /* Free data structures. */ | ||
1842 | usb_free_urb(radio->int_in_urb); | ||
1843 | |||
1844 | kfree(radio->int_in_buffer); | ||
1811 | video_unregister_device(radio->videodev); | 1845 | video_unregister_device(radio->videodev); |
1812 | kfree(radio->buffer); | 1846 | kfree(radio->buffer); |
1813 | kfree(radio); | 1847 | kfree(radio); |