diff options
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/radio/Kconfig | 10 | ||||
-rw-r--r-- | drivers/media/radio/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/radio/radio-si470x.c | 1440 |
3 files changed, 1451 insertions, 0 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 11e962f1a97f..8d5214f18cf0 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig | |||
@@ -351,4 +351,14 @@ config USB_DSBR | |||
351 | To compile this driver as a module, choose M here: the | 351 | To compile this driver as a module, choose M here: the |
352 | module will be called dsbr100. | 352 | module will be called dsbr100. |
353 | 353 | ||
354 | config USB_SI470X | ||
355 | tristate "Silicon Labs Si470x FM Radio Receiver support" | ||
356 | depends on USB && VIDEO_V4L2 | ||
357 | ---help--- | ||
358 | Say Y here if you want to connect this type of radio to your | ||
359 | computer's USB port. | ||
360 | |||
361 | To compile this driver as a module, choose M here: the | ||
362 | module will be called radio-silabs. | ||
363 | |||
354 | endif # RADIO_ADAPTERS | 364 | endif # RADIO_ADAPTERS |
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index cf55a18e3ddf..f36cf4e63b6c 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile | |||
@@ -21,5 +21,6 @@ obj-$(CONFIG_RADIO_GEMTEK_PCI) += radio-gemtek-pci.o | |||
21 | obj-$(CONFIG_RADIO_TRUST) += radio-trust.o | 21 | obj-$(CONFIG_RADIO_TRUST) += radio-trust.o |
22 | obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o | 22 | obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o |
23 | obj-$(CONFIG_USB_DSBR) += dsbr100.o | 23 | obj-$(CONFIG_USB_DSBR) += dsbr100.o |
24 | obj-$(CONFIG_USB_SI470X) := radio-si470x.o | ||
24 | 25 | ||
25 | EXTRA_CFLAGS += -Isound | 26 | EXTRA_CFLAGS += -Isound |
diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c new file mode 100644 index 000000000000..eccbc750e140 --- /dev/null +++ b/drivers/media/radio/radio-si470x.c | |||
@@ -0,0 +1,1440 @@ | |||
1 | /* | ||
2 | * drivers/media/radio/radio-si470x.c | ||
3 | * | ||
4 | * Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers: | ||
5 | * - Silicon Labs USB FM Radio Reference Design | ||
6 | * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF) | ||
7 | * | ||
8 | * Copyright (c) 2007 Tobias Lorenz <tobias.lorenz@gmx.net> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | |||
26 | /* | ||
27 | * History: | ||
28 | * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net> | ||
29 | * Version 1.0.0 | ||
30 | * - First working version | ||
31 | * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net> | ||
32 | * Version 1.0.1 | ||
33 | * - Improved error handling, every function now returns errno | ||
34 | * - Improved multi user access (start/mute/stop) | ||
35 | * - Channel doesn't get lost anymore after start/mute/stop | ||
36 | * - RDS support added (polling mode via interrupt EP 1) | ||
37 | * - marked default module parameters with *value* | ||
38 | * - switched from bit structs to bit masks | ||
39 | * - header file cleaned and integrated | ||
40 | * 2008-01-14 Tobias Lorenz <tobias.lorenz@gmx.net> | ||
41 | * Version 1.0.2 | ||
42 | * - hex values are now lower case | ||
43 | * - commented USB ID for ADS/Tech moved on todo list | ||
44 | * - blacklisted si470x in hid-quirks.c | ||
45 | * - rds buffer handling functions integrated into *_work, *_read | ||
46 | * - rds_command in si470x_poll exchanged against simple retval | ||
47 | * - check for firmware version 15 | ||
48 | * - code order and prototypes still remain the same | ||
49 | * - spacing and bottom of band codes remain the same | ||
50 | * | ||
51 | * ToDo: | ||
52 | * - check USB Vendor/Product ID for ADS/Tech FM Radio Receiver | ||
53 | * (formerly Instant FM Music) (RDX-155-EF) is 06e1:a155 | ||
54 | * - add seeking support | ||
55 | * - add firmware download/update support | ||
56 | * - add possibility to switch off RDS | ||
57 | * - RDS support: interrupt mode, instead of polling | ||
58 | * - add LED status output | ||
59 | */ | ||
60 | |||
61 | /* driver definitions */ | ||
62 | #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" | ||
63 | #define DRIVER_NAME "radio-si470x" | ||
64 | #define DRIVER_VERSION KERNEL_VERSION(1, 0, 2) | ||
65 | #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" | ||
66 | #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" | ||
67 | |||
68 | |||
69 | /* kernel includes */ | ||
70 | #include <linux/kernel.h> | ||
71 | #include <linux/module.h> | ||
72 | #include <linux/init.h> | ||
73 | #include <linux/slab.h> | ||
74 | #include <linux/input.h> | ||
75 | #include <linux/videodev2.h> | ||
76 | #include <media/v4l2-common.h> | ||
77 | #include <linux/usb.h> | ||
78 | #include <linux/hid.h> | ||
79 | #include <linux/version.h> | ||
80 | #include <media/rds.h> | ||
81 | |||
82 | |||
83 | /* Radio Nr */ | ||
84 | static int radio_nr = -1; | ||
85 | module_param(radio_nr, int, 0); | ||
86 | MODULE_PARM_DESC(radio_nr, "Radio Nr"); | ||
87 | |||
88 | /* Spacing (kHz) */ | ||
89 | /* 0: 200 kHz (USA, Australia) */ | ||
90 | /* 1: 100 kHz (Europe, Japan) */ | ||
91 | /* 2: 50 kHz */ | ||
92 | static int space = 2; | ||
93 | module_param(space, int, 0); | ||
94 | MODULE_PARM_DESC(radio_nr, "Spacing: 0=200kHz 1=100kHz *2=50kHz*"); | ||
95 | |||
96 | /* Bottom of Band (MHz) */ | ||
97 | /* 0: 87.5 - 108 MHz (USA, Europe)*/ | ||
98 | /* 1: 76 - 108 MHz (Japan wide band) */ | ||
99 | /* 2: 76 - 90 MHz (Japan) */ | ||
100 | static int band = 1; | ||
101 | module_param(band, int, 0); | ||
102 | MODULE_PARM_DESC(radio_nr, "Band: 0=87.5..108MHz *1=76..108MHz* 2=76..90MHz"); | ||
103 | |||
104 | /* De-emphasis */ | ||
105 | /* 0: 75 us (USA) */ | ||
106 | /* 1: 50 us (Europe, Australia, Japan) */ | ||
107 | static int de = 1; | ||
108 | module_param(de, int, 0); | ||
109 | MODULE_PARM_DESC(radio_nr, "De-emphasis: 0=75us *1=50us*"); | ||
110 | |||
111 | /* USB timeout */ | ||
112 | static int usb_timeout = 500; | ||
113 | module_param(usb_timeout, int, 0); | ||
114 | MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*"); | ||
115 | |||
116 | /* Seek retries */ | ||
117 | static int seek_retries = 100; | ||
118 | module_param(seek_retries, int, 0); | ||
119 | MODULE_PARM_DESC(seek_retries, "Seek retries: *100*"); | ||
120 | |||
121 | /* RDS buffer blocks */ | ||
122 | static int rds_buf = 100; | ||
123 | module_param(rds_buf, int, 0); | ||
124 | MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*"); | ||
125 | |||
126 | /* RDS maximum block errors */ | ||
127 | static int max_rds_errors = 1; | ||
128 | /* 0 means 0 errors requiring correction */ | ||
129 | /* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */ | ||
130 | /* 2 means 3-5 errors requiring correction */ | ||
131 | /* 3 means 6+ errors or errors in checkword, correction not possible */ | ||
132 | module_param(max_rds_errors, int, 0); | ||
133 | MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); | ||
134 | |||
135 | /* RDS poll frequency */ | ||
136 | static int rds_poll_time = 40; | ||
137 | /* 40 is used by the original USBRadio.exe */ | ||
138 | /* 75 should be okay */ | ||
139 | /* 80 is the usual RDS receive interval */ | ||
140 | module_param(rds_poll_time, int, 0); | ||
141 | MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*"); | ||
142 | |||
143 | |||
144 | |||
145 | /************************************************************************** | ||
146 | * Register Definitions | ||
147 | **************************************************************************/ | ||
148 | #define FMRADIO_REGISTER_SIZE 2 /* 16 register bit width */ | ||
149 | #define FMRADIO_REGISTER_NUM 16 /* DEVICEID ... RDSD */ | ||
150 | #define RDS_REGISTER_NUM 6 /* STATUSRSSI ... RDSD */ | ||
151 | |||
152 | #define DEVICEID 0 /* Device ID */ | ||
153 | #define DEVICEID_PN 0xf000 /* bits 15..12: Part Number */ | ||
154 | #define DEVICEID_MFGID 0x0fff /* bits 11..00: Manufacturer ID */ | ||
155 | |||
156 | #define CHIPID 1 /* Chip ID */ | ||
157 | #define CHIPID_REV 0xfc00 /* bits 15..10: Chip Version */ | ||
158 | #define CHIPID_DEV 0x0200 /* bits 09..09: Device */ | ||
159 | #define CHIPID_FIRMWARE 0x01ff /* bits 08..00: Firmware Version */ | ||
160 | |||
161 | #define POWERCFG 2 /* Power Configuration */ | ||
162 | #define POWERCFG_DSMUTE 0x8000 /* bits 15..15: Softmute Disable */ | ||
163 | #define POWERCFG_DMUTE 0x4000 /* bits 14..14: Mute Disable */ | ||
164 | #define POWERCFG_MONO 0x2000 /* bits 13..13: Mono Select */ | ||
165 | #define POWERCFG_RDSM 0x0800 /* bits 11..11: RDS Mode (Si4701 only) */ | ||
166 | #define POWERCFG_SKMODE 0x0400 /* bits 10..10: Seek Mode */ | ||
167 | #define POWERCFG_SEEKUP 0x0200 /* bits 09..09: Seek Direction */ | ||
168 | #define POWERCFG_SEEK 0x0100 /* bits 08..08: Seek */ | ||
169 | #define POWERCFG_DISABLE 0x0040 /* bits 06..06: Powerup Disable */ | ||
170 | #define POWERCFG_ENABLE 0x0001 /* bits 00..00: Powerup Enable */ | ||
171 | |||
172 | #define CHANNEL 3 /* Channel */ | ||
173 | #define CHANNEL_TUNE 0x8000 /* bits 15..15: Tune */ | ||
174 | #define CHANNEL_CHAN 0x03ff /* bits 09..00: Channel Select */ | ||
175 | |||
176 | #define SYSCONFIG1 4 /* System Configuration 1 */ | ||
177 | #define SYSCONFIG1_RDSIEN 0x8000 /* bits 15..15: RDS Interrupt Enable (Si4701 only) */ | ||
178 | #define SYSCONFIG1_STCIEN 0x4000 /* bits 14..14: Seek/Tune Complete Interrupt Enable */ | ||
179 | #define SYSCONFIG1_RDS 0x1000 /* bits 12..12: RDS Enable (Si4701 only) */ | ||
180 | #define SYSCONFIG1_DE 0x0800 /* bits 11..11: De-emphasis (0=75us 1=50us) */ | ||
181 | #define SYSCONFIG1_AGCD 0x0400 /* bits 10..10: AGC Disable */ | ||
182 | #define SYSCONFIG1_BLNDADJ 0x00c0 /* bits 07..06: Stereo/Mono Blend Level Adjustment */ | ||
183 | #define SYSCONFIG1_GPIO3 0x0030 /* bits 05..04: General Purpose I/O 3 */ | ||
184 | #define SYSCONFIG1_GPIO2 0x000c /* bits 03..02: General Purpose I/O 2 */ | ||
185 | #define SYSCONFIG1_GPIO1 0x0003 /* bits 01..00: General Purpose I/O 1 */ | ||
186 | |||
187 | #define SYSCONFIG2 5 /* System Configuration 2 */ | ||
188 | #define SYSCONFIG2_SEEKTH 0xff00 /* bits 15..08: RSSI Seek Threshold */ | ||
189 | #define SYSCONFIG2_BAND 0x0080 /* bits 07..06: Band Select */ | ||
190 | #define SYSCONFIG2_SPACE 0x0030 /* bits 05..04: Channel Spacing */ | ||
191 | #define SYSCONFIG2_VOLUME 0x000f /* bits 03..00: Volume */ | ||
192 | |||
193 | #define SYSCONFIG3 6 /* System Configuration 3 */ | ||
194 | #define SYSCONFIG3_SMUTER 0xc000 /* bits 15..14: Softmute Attack/Recover Rate */ | ||
195 | #define SYSCONFIG3_SMUTEA 0x3000 /* bits 13..12: Softmute Attenuation */ | ||
196 | #define SYSCONFIG3_SKSNR 0x00f0 /* bits 07..04: Seek SNR Threshold */ | ||
197 | #define SYSCONFIG3_SKCNT 0x000f /* bits 03..00: Seek FM Impulse Detection Threshold */ | ||
198 | |||
199 | #define TEST1 7 /* Test 1 */ | ||
200 | #define TEST1_AHIZEN 0x4000 /* bits 14..14: Audio High-Z Enable */ | ||
201 | |||
202 | #define TEST2 8 /* Test 2 */ | ||
203 | /* TEST2 only contains reserved bits */ | ||
204 | |||
205 | #define BOOTCONFIG 9 /* Boot Configuration */ | ||
206 | /* BOOTCONFIG only contains reserved bits */ | ||
207 | |||
208 | #define STATUSRSSI 10 /* Status RSSI */ | ||
209 | #define STATUSRSSI_RDSR 0x8000 /* bits 15..15: RDS Ready (Si4701 only) */ | ||
210 | #define STATUSRSSI_STC 0x4000 /* bits 14..14: Seek/Tune Complete */ | ||
211 | #define STATUSRSSI_SF 0x2000 /* bits 13..13: Seek Fail/Band Limit */ | ||
212 | #define STATUSRSSI_AFCRL 0x1000 /* bits 12..12: AFC Rail */ | ||
213 | #define STATUSRSSI_RDSS 0x0800 /* bits 11..11: RDS Synchronized (Si4701 only) */ | ||
214 | #define STATUSRSSI_BLERA 0x0600 /* bits 10..09: RDS Block A Errors (Si4701 only) */ | ||
215 | #define STATUSRSSI_ST 0x0100 /* bits 08..08: Stereo Indicator */ | ||
216 | #define STATUSRSSI_RSSI 0x00ff /* bits 07..00: RSSI (Received Signal Strength Indicator) */ | ||
217 | |||
218 | #define READCHAN 11 /* Read Channel */ | ||
219 | #define READCHAN_BLERB 0xc000 /* bits 15..14: RDS Block D Errors (Si4701 only) */ | ||
220 | #define READCHAN_BLERC 0x3000 /* bits 13..12: RDS Block C Errors (Si4701 only) */ | ||
221 | #define READCHAN_BLERD 0x0c00 /* bits 11..10: RDS Block B Errors (Si4701 only) */ | ||
222 | #define READCHAN_READCHAN 0x03ff /* bits 09..00: Read Channel */ | ||
223 | |||
224 | #define RDSA 12 /* RDSA */ | ||
225 | #define RDSA_RDSA 0xffff /* bits 15..00: RDS Block A Data (Si4701 only) */ | ||
226 | |||
227 | #define RDSB 13 /* RDSB */ | ||
228 | #define RDSB_RDSB 0xffff /* bits 15..00: RDS Block B Data (Si4701 only) */ | ||
229 | |||
230 | #define RDSC 14 /* RDSC */ | ||
231 | #define RDSC_RDSC 0xffff /* bits 15..00: RDS Block C Data (Si4701 only) */ | ||
232 | |||
233 | #define RDSD 15 /* RDSD */ | ||
234 | #define RDSD_RDSD 0xffff /* bits 15..00: RDS Block D Data (Si4701 only) */ | ||
235 | |||
236 | |||
237 | |||
238 | /************************************************************************** | ||
239 | * USB HID reports | ||
240 | **************************************************************************/ | ||
241 | |||
242 | /* Reports 1-16 give direct read/write access to the 16 Si470x registers */ | ||
243 | /* with the (REPORT_ID - 1) corresponding to the register address across USB */ | ||
244 | /* endpoint 0 using GET_REPORT and SET_REPORT */ | ||
245 | #define REGISTER_REPORT_SIZE (FMRADIO_REGISTER_SIZE + 1) | ||
246 | #define REGISTER_REPORT(reg) ((reg) + 1) | ||
247 | |||
248 | /* Report 17 gives direct read/write access to the entire Si470x register */ | ||
249 | /* map across endpoint 0 using GET_REPORT and SET_REPORT */ | ||
250 | #define ENTIRE_REPORT_SIZE (FMRADIO_REGISTER_NUM * FMRADIO_REGISTER_SIZE + 1) | ||
251 | #define ENTIRE_REPORT 17 | ||
252 | |||
253 | /* Report 18 is used to send the lowest 6 Si470x registers up the HID */ | ||
254 | /* interrupt endpoint 1 to Windows every 20 milliseconds for status */ | ||
255 | #define RDS_REPORT_SIZE (RDS_REGISTER_NUM * FMRADIO_REGISTER_SIZE + 1) | ||
256 | #define RDS_REPORT 18 | ||
257 | |||
258 | /* Report 19: LED state */ | ||
259 | #define LED_REPORT_SIZE 3 | ||
260 | #define LED_REPORT 19 | ||
261 | |||
262 | /* Report 19: stream */ | ||
263 | #define STREAM_REPORT_SIZE 3 | ||
264 | #define STREAM_REPORT 19 | ||
265 | |||
266 | /* Report 20: scratch */ | ||
267 | #define SCRATCH_PAGE_SIZE 63 | ||
268 | #define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1) | ||
269 | #define SCRATCH_REPORT 20 | ||
270 | |||
271 | /* Reports 19-22: flash upgrade of the C8051F321 */ | ||
272 | #define WRITE_REPORT 19 | ||
273 | #define FLASH_REPORT 20 | ||
274 | #define CRC_REPORT 21 | ||
275 | #define RESPONSE_REPORT 22 | ||
276 | |||
277 | /* Report 23: currently unused, but can accept 60 byte reports on the HID */ | ||
278 | /* interrupt out endpoint 2 every 1 millisecond */ | ||
279 | #define UNUSED_REPORT 23 | ||
280 | |||
281 | |||
282 | |||
283 | /************************************************************************** | ||
284 | * software/hardware versions | ||
285 | **************************************************************************/ | ||
286 | #define FMRADIO_SW_VERSION_NOT_BOOTLOADABLE 6 | ||
287 | #define FMRADIO_SW_VERSION 7 | ||
288 | #define FMRADIO_SW_VERSION_CURRENT 15 | ||
289 | #define FMRADIO_HW_VERSION 1 | ||
290 | |||
291 | #define SCRATCH_PAGE_SW_VERSION 1 | ||
292 | #define SCRATCH_PAGE_HW_VERSION 2 | ||
293 | |||
294 | |||
295 | |||
296 | /************************************************************************** | ||
297 | * LED State definitions | ||
298 | **************************************************************************/ | ||
299 | #define LED_COMMAND 0x35 | ||
300 | |||
301 | #define NO_CHANGE_LED 0x00 | ||
302 | #define ALL_COLOR_LED 0x01 /* streaming state */ | ||
303 | #define BLINK_GREEN_LED 0x02 /* connect state */ | ||
304 | #define BLINK_RED_LED 0x04 | ||
305 | #define BLINK_ORANGE_LED 0x10 /* disconnect state */ | ||
306 | #define SOLID_GREEN_LED 0x20 /* tuning/seeking state */ | ||
307 | #define SOLID_RED_LED 0x40 /* bootload state */ | ||
308 | #define SOLID_ORANGE_LED 0x80 | ||
309 | |||
310 | |||
311 | |||
312 | /************************************************************************** | ||
313 | * Stream State definitions | ||
314 | **************************************************************************/ | ||
315 | #define STREAM_COMMAND 0x36 | ||
316 | #define STREAM_VIDPID 0x00 | ||
317 | #define STREAM_AUDIO 0xff | ||
318 | |||
319 | |||
320 | |||
321 | /************************************************************************** | ||
322 | * bootloader / flash commands | ||
323 | **************************************************************************/ | ||
324 | |||
325 | /* Unique ID sent to bootloader and required to put into a bootload state */ | ||
326 | #define UNIQUE_BL_ID 0x34 | ||
327 | |||
328 | /* Mask for the flash data */ | ||
329 | #define FLASH_DATA_MASK 0x55 | ||
330 | |||
331 | /* Bootloader commands */ | ||
332 | #define GET_SW_VERSION_COMMAND 0x00 | ||
333 | #define SET_PAGE_COMMAND 0x01 | ||
334 | #define ERASE_PAGE_COMMAND 0x02 | ||
335 | #define WRITE_PAGE_COMMAND 0x03 | ||
336 | #define CRC_ON_PAGE_COMMAND 0x04 | ||
337 | #define READ_FLASH_BYTE_COMMAND 0x05 | ||
338 | #define RESET_DEVICE_COMMAND 0x06 | ||
339 | #define GET_HW_VERSION_COMMAND 0x07 | ||
340 | #define BLANK 0xff | ||
341 | |||
342 | /* Bootloader command responses */ | ||
343 | #define COMMAND_OK 0x01 | ||
344 | #define COMMAND_FAILED 0x02 | ||
345 | #define COMMAND_PENDING 0x03 | ||
346 | |||
347 | /* Buffer sizes */ | ||
348 | #define COMMAND_BUFFER_SIZE 4 | ||
349 | #define RESPONSE_BUFFER_SIZE 2 | ||
350 | #define FLASH_BUFFER_SIZE 64 | ||
351 | #define CRC_BUFFER_SIZE 3 | ||
352 | |||
353 | |||
354 | |||
355 | /************************************************************************** | ||
356 | * Driver private definitions | ||
357 | **************************************************************************/ | ||
358 | |||
359 | /* private data */ | ||
360 | struct si470x_device { | ||
361 | /* reference to USB and video device */ | ||
362 | struct usb_device *usbdev; | ||
363 | struct video_device *videodev; | ||
364 | |||
365 | /* are these really necessary ? */ | ||
366 | int users; | ||
367 | |||
368 | /* report buffer (maximum 64 bytes) */ | ||
369 | unsigned char buf[64]; | ||
370 | |||
371 | /* Silabs internal registers (0..15) */ | ||
372 | unsigned short registers[FMRADIO_REGISTER_NUM]; | ||
373 | |||
374 | /* RDS receive buffer */ | ||
375 | struct work_struct work; | ||
376 | struct timer_list timer; | ||
377 | spinlock_t lock; | ||
378 | unsigned char *buffer; | ||
379 | unsigned int buf_size; | ||
380 | unsigned int rd_index; | ||
381 | unsigned int wr_index; | ||
382 | unsigned int block_count; | ||
383 | unsigned char last_blocknum; | ||
384 | wait_queue_head_t read_queue; | ||
385 | int data_available_for_read; | ||
386 | }; | ||
387 | |||
388 | /* register acccess functions */ | ||
389 | static int si470x_get_report(struct si470x_device *radio, int size); | ||
390 | static int si470x_set_report(struct si470x_device *radio, int size); | ||
391 | static int si470x_get_register(struct si470x_device *radio, int regnr); | ||
392 | static int si470x_set_register(struct si470x_device *radio, int regnr); | ||
393 | static int si470x_get_all_registers(struct si470x_device *radio); | ||
394 | static int si470x_get_rds_registers(struct si470x_device *radio); | ||
395 | |||
396 | /* high-level functions */ | ||
397 | static int si470x_start(struct si470x_device *radio); | ||
398 | static int si470x_stop(struct si470x_device *radio); | ||
399 | static int si470x_set_chan(struct si470x_device *radio, int chan); | ||
400 | static int si470x_get_freq(struct si470x_device *radio); | ||
401 | static int si470x_set_freq(struct si470x_device *radio, int freq); | ||
402 | |||
403 | /* RDS functions */ | ||
404 | static void si470x_rds(struct si470x_device *radio); | ||
405 | static void si470x_timer(unsigned long data); | ||
406 | static void si470x_work(struct work_struct *work); | ||
407 | |||
408 | |||
409 | |||
410 | /************************************************************************** | ||
411 | * USB interface definitions | ||
412 | **************************************************************************/ | ||
413 | |||
414 | /* USB device ID list (Vendor, Product, Class, SubClass, Protocol) */ | ||
415 | static struct usb_device_id si470x_usb_driver_id_table[] = { | ||
416 | /* Silicon Labs USB FM Radio Reference Design */ | ||
417 | { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, | ||
418 | /* Terminating entry */ | ||
419 | { } | ||
420 | }; | ||
421 | MODULE_DEVICE_TABLE (usb, si470x_usb_driver_id_table); | ||
422 | |||
423 | /* USB driver functions */ | ||
424 | static int si470x_usb_driver_probe(struct usb_interface *intf, | ||
425 | const struct usb_device_id *id); | ||
426 | static void si470x_usb_driver_disconnect(struct usb_interface *intf); | ||
427 | |||
428 | /* USB driver interface */ | ||
429 | static struct usb_driver si470x_usb_driver = { | ||
430 | .name = DRIVER_NAME, | ||
431 | .probe = si470x_usb_driver_probe, | ||
432 | .disconnect = si470x_usb_driver_disconnect, | ||
433 | .id_table = si470x_usb_driver_id_table, | ||
434 | }; | ||
435 | |||
436 | |||
437 | |||
438 | /************************************************************************** | ||
439 | * Video4Linux interface definitions | ||
440 | **************************************************************************/ | ||
441 | |||
442 | /* The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW, */ | ||
443 | /* 62.5 kHz otherwise. */ | ||
444 | /* The tuner is able to have a channel spacing of 50, 100 or 200 kHz. */ | ||
445 | /* tuner->capability is therefore set to V4L2_TUNER_CAP_LOW */ | ||
446 | /* The FREQ_MUL is then: 1 MHz / 62.5 Hz = 16000 */ | ||
447 | #define FREQ_MUL (1000000 / 62.5) | ||
448 | |||
449 | /* File operations functions */ | ||
450 | static ssize_t si470x_fops_read(struct file *file, char __user *buf, | ||
451 | size_t count, loff_t *ppos); | ||
452 | static unsigned int si470x_fops_poll(struct file *file, | ||
453 | struct poll_table_struct *pts); | ||
454 | static int si470x_fops_open(struct inode *inode, struct file *file); | ||
455 | static int si470x_fops_release(struct inode *inode, struct file *file); | ||
456 | |||
457 | /* File operations interface */ | ||
458 | static const struct file_operations si470x_fops = { | ||
459 | .owner = THIS_MODULE, | ||
460 | .llseek = no_llseek, | ||
461 | .read = si470x_fops_read, | ||
462 | .poll = si470x_fops_poll, | ||
463 | .ioctl = video_ioctl2, | ||
464 | .compat_ioctl = v4l_compat_ioctl32, | ||
465 | .open = si470x_fops_open, | ||
466 | .release = si470x_fops_release, | ||
467 | }; | ||
468 | |||
469 | /* Video device functions */ | ||
470 | static int si470x_vidioc_querycap(struct file *file, void *priv, | ||
471 | struct v4l2_capability *capability); | ||
472 | static int si470x_vidioc_g_input(struct file *file, void *priv, | ||
473 | unsigned int *i); | ||
474 | static int si470x_vidioc_s_input(struct file *file, void *priv, | ||
475 | unsigned int i); | ||
476 | static int si470x_vidioc_queryctrl(struct file *file, void *priv, | ||
477 | struct v4l2_queryctrl *qc); | ||
478 | static int si470x_vidioc_g_ctrl(struct file *file, void *priv, | ||
479 | struct v4l2_control *ctrl); | ||
480 | static int si470x_vidioc_s_ctrl(struct file *file, void *priv, | ||
481 | struct v4l2_control *ctrl); | ||
482 | static int si470x_vidioc_g_audio(struct file *file, void *priv, | ||
483 | struct v4l2_audio *audio); | ||
484 | static int si470x_vidioc_s_audio(struct file *file, void *priv, | ||
485 | struct v4l2_audio *audio); | ||
486 | static int si470x_vidioc_g_tuner(struct file *file, void *priv, | ||
487 | struct v4l2_tuner *tuner); | ||
488 | static int si470x_vidioc_s_tuner(struct file *file, void *priv, | ||
489 | struct v4l2_tuner *tuner); | ||
490 | static int si470x_vidioc_g_frequency(struct file *file, void *priv, | ||
491 | struct v4l2_frequency *freq); | ||
492 | static int si470x_vidioc_s_frequency(struct file *file, void *priv, | ||
493 | struct v4l2_frequency *freq); | ||
494 | |||
495 | /* Video device interface */ | ||
496 | static struct video_device si470x_viddev_template = { | ||
497 | .fops = &si470x_fops, | ||
498 | .name = DRIVER_NAME, | ||
499 | .type = VID_TYPE_TUNER, | ||
500 | .release = video_device_release, | ||
501 | .vidioc_querycap = si470x_vidioc_querycap, | ||
502 | .vidioc_g_input = si470x_vidioc_g_input, | ||
503 | .vidioc_s_input = si470x_vidioc_s_input, | ||
504 | .vidioc_queryctrl = si470x_vidioc_queryctrl, | ||
505 | .vidioc_g_ctrl = si470x_vidioc_g_ctrl, | ||
506 | .vidioc_s_ctrl = si470x_vidioc_s_ctrl, | ||
507 | .vidioc_g_audio = si470x_vidioc_g_audio, | ||
508 | .vidioc_s_audio = si470x_vidioc_s_audio, | ||
509 | .vidioc_g_tuner = si470x_vidioc_g_tuner, | ||
510 | .vidioc_s_tuner = si470x_vidioc_s_tuner, | ||
511 | .vidioc_g_frequency = si470x_vidioc_g_frequency, | ||
512 | .vidioc_s_frequency = si470x_vidioc_s_frequency, | ||
513 | .owner = THIS_MODULE, | ||
514 | }; | ||
515 | |||
516 | /* Query control */ | ||
517 | static struct v4l2_queryctrl radio_queryctrl[] = { | ||
518 | /* HINT: the disabled controls are only here to satify kradio and such apps */ | ||
519 | { | ||
520 | .id = V4L2_CID_AUDIO_VOLUME, | ||
521 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
522 | .name = "Volume", | ||
523 | .minimum = 0, | ||
524 | .maximum = 15, | ||
525 | .step = 1, | ||
526 | .default_value = 15, | ||
527 | }, | ||
528 | { | ||
529 | .id = V4L2_CID_AUDIO_BALANCE, | ||
530 | .flags = V4L2_CTRL_FLAG_DISABLED, | ||
531 | }, | ||
532 | { | ||
533 | .id = V4L2_CID_AUDIO_BASS, | ||
534 | .flags = V4L2_CTRL_FLAG_DISABLED, | ||
535 | }, | ||
536 | { | ||
537 | .id = V4L2_CID_AUDIO_TREBLE, | ||
538 | .flags = V4L2_CTRL_FLAG_DISABLED, | ||
539 | }, | ||
540 | { | ||
541 | .id = V4L2_CID_AUDIO_MUTE, | ||
542 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
543 | .name = "Mute", | ||
544 | .minimum = 0, | ||
545 | .maximum = 1, | ||
546 | .step = 1, | ||
547 | .default_value = 1, | ||
548 | }, | ||
549 | { | ||
550 | .id = V4L2_CID_AUDIO_LOUDNESS, | ||
551 | .flags = V4L2_CTRL_FLAG_DISABLED, | ||
552 | }, | ||
553 | }; | ||
554 | |||
555 | |||
556 | |||
557 | /************************************************************************** | ||
558 | * Driver private functions | ||
559 | **************************************************************************/ | ||
560 | |||
561 | /* | ||
562 | * si470x_get_report - receive a HID report | ||
563 | */ | ||
564 | static int si470x_get_report(struct si470x_device *radio, int size) | ||
565 | { | ||
566 | return usb_control_msg(radio->usbdev, | ||
567 | usb_rcvctrlpipe(radio->usbdev, 0), | ||
568 | HID_REQ_GET_REPORT, | ||
569 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
570 | radio->buf[0], 2, | ||
571 | radio->buf, size, usb_timeout); | ||
572 | } | ||
573 | |||
574 | |||
575 | /* | ||
576 | * si470x_set_report - send a HID report | ||
577 | */ | ||
578 | static int si470x_set_report(struct si470x_device *radio, int size) | ||
579 | { | ||
580 | return usb_control_msg(radio->usbdev, | ||
581 | usb_sndctrlpipe(radio->usbdev, 0), | ||
582 | HID_REQ_SET_REPORT, | ||
583 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | ||
584 | radio->buf[0], 2, | ||
585 | radio->buf, size, usb_timeout); | ||
586 | } | ||
587 | |||
588 | |||
589 | /* | ||
590 | * si470x_get_register - read register | ||
591 | */ | ||
592 | static int si470x_get_register(struct si470x_device *radio, int regnr) | ||
593 | { | ||
594 | int retval; | ||
595 | |||
596 | radio->buf[0] = REGISTER_REPORT(regnr); | ||
597 | |||
598 | retval = si470x_get_report(radio, REGISTER_REPORT_SIZE); | ||
599 | if (retval >= 0) | ||
600 | radio->registers[regnr] = (radio->buf[1] << 8) | radio->buf[2]; | ||
601 | |||
602 | return (retval < 0) ? -EINVAL : 0; | ||
603 | } | ||
604 | |||
605 | |||
606 | /* | ||
607 | * si470x_set_register - write register | ||
608 | */ | ||
609 | static int si470x_set_register(struct si470x_device *radio, int regnr) | ||
610 | { | ||
611 | int retval; | ||
612 | |||
613 | radio->buf[0] = REGISTER_REPORT(regnr); | ||
614 | radio->buf[1] = (radio->registers[regnr] & 0xff00) >> 8; | ||
615 | radio->buf[2] = (radio->registers[regnr] & 0x00ff); | ||
616 | |||
617 | retval = si470x_set_report(radio, REGISTER_REPORT_SIZE); | ||
618 | |||
619 | return (retval < 0) ? -EINVAL : 0; | ||
620 | } | ||
621 | |||
622 | |||
623 | /* | ||
624 | * si470x_get_all_registers - read entire registers | ||
625 | */ | ||
626 | static int si470x_get_all_registers(struct si470x_device *radio) | ||
627 | { | ||
628 | int retval; | ||
629 | int regnr; | ||
630 | |||
631 | radio->buf[0] = ENTIRE_REPORT; | ||
632 | |||
633 | retval = si470x_get_report(radio, ENTIRE_REPORT_SIZE); | ||
634 | |||
635 | if (retval >= 0) | ||
636 | for (regnr = 0; regnr < FMRADIO_REGISTER_NUM; regnr++) | ||
637 | radio->registers[regnr] = | ||
638 | (radio->buf[regnr * FMRADIO_REGISTER_SIZE + 1] << 8) | | ||
639 | radio->buf[regnr * FMRADIO_REGISTER_SIZE + 2]; | ||
640 | |||
641 | return (retval < 0) ? -EINVAL : 0; | ||
642 | } | ||
643 | |||
644 | |||
645 | /* | ||
646 | * si470x_get_rds_registers - read rds registers | ||
647 | */ | ||
648 | static int si470x_get_rds_registers(struct si470x_device *radio) | ||
649 | { | ||
650 | int retval; | ||
651 | int regnr; | ||
652 | int size; | ||
653 | |||
654 | radio->buf[0] = RDS_REPORT; | ||
655 | |||
656 | retval = usb_interrupt_msg(radio->usbdev, | ||
657 | usb_rcvctrlpipe(radio->usbdev, 1), | ||
658 | radio->buf, RDS_REPORT_SIZE, &size, usb_timeout); | ||
659 | |||
660 | if (retval >= 0) { | ||
661 | for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) { | ||
662 | radio->registers[STATUSRSSI + regnr] = | ||
663 | (radio->buf[regnr * FMRADIO_REGISTER_SIZE + 1] << 8) | | ||
664 | radio->buf[regnr * FMRADIO_REGISTER_SIZE + 2]; | ||
665 | } | ||
666 | } | ||
667 | |||
668 | return (retval < 0) ? - EINVAL : 0; | ||
669 | } | ||
670 | |||
671 | |||
672 | /* | ||
673 | * si470x_start - switch on radio | ||
674 | */ | ||
675 | static int si470x_start(struct si470x_device *radio) | ||
676 | { | ||
677 | int retval; | ||
678 | |||
679 | /* powercfg */ | ||
680 | radio->registers[POWERCFG] = | ||
681 | POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM; | ||
682 | retval = si470x_set_register(radio, POWERCFG); | ||
683 | if (retval < 0) | ||
684 | return retval; | ||
685 | |||
686 | /* sysconfig 1 */ | ||
687 | radio->registers[SYSCONFIG1] = | ||
688 | SYSCONFIG1_DE | SYSCONFIG1_RDS; | ||
689 | retval = si470x_set_register(radio, SYSCONFIG1); | ||
690 | if (retval < 0) | ||
691 | return retval; | ||
692 | |||
693 | /* sysconfig 2 */ | ||
694 | radio->registers[SYSCONFIG2] = | ||
695 | (0x3f << 8) | /* SEEKTH */ | ||
696 | (band << 6) | /* BAND */ | ||
697 | (space << 4) | /* SPACE */ | ||
698 | 15; /* VOLUME (max) */ | ||
699 | retval = si470x_set_register(radio, SYSCONFIG2); | ||
700 | if (retval < 0) | ||
701 | return retval; | ||
702 | |||
703 | /* reset last channel */ | ||
704 | return si470x_set_chan(radio, | ||
705 | radio->registers[CHANNEL] & CHANNEL_CHAN); | ||
706 | } | ||
707 | |||
708 | |||
709 | /* | ||
710 | * si470x_stop - switch off radio | ||
711 | */ | ||
712 | static int si470x_stop(struct si470x_device *radio) | ||
713 | { | ||
714 | /* powercfg */ | ||
715 | radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; | ||
716 | /* POWERCFG_ENABLE has to automatically go low */ | ||
717 | radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE; | ||
718 | return si470x_set_register(radio, POWERCFG); | ||
719 | } | ||
720 | |||
721 | |||
722 | /* | ||
723 | * si470x_set_chan - set the channel | ||
724 | */ | ||
725 | static int si470x_set_chan(struct si470x_device *radio, int chan) | ||
726 | { | ||
727 | int retval, i; | ||
728 | |||
729 | /* start tuning */ | ||
730 | radio->registers[CHANNEL] &= ~CHANNEL_CHAN; | ||
731 | radio->registers[CHANNEL] |= CHANNEL_TUNE | chan; | ||
732 | retval = si470x_set_register(radio, CHANNEL); | ||
733 | if (retval < 0) | ||
734 | return retval; | ||
735 | |||
736 | /* wait till seek operation has completed */ | ||
737 | i = 0; | ||
738 | do { | ||
739 | retval = si470x_get_register(radio, STATUSRSSI); | ||
740 | if (retval < 0) | ||
741 | return retval; | ||
742 | } while ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) && | ||
743 | (++i < seek_retries)); | ||
744 | if (i >= seek_retries) | ||
745 | printk(KERN_WARNING DRIVER_NAME | ||
746 | ": seek does not finish after %d tries\n", i); | ||
747 | |||
748 | /* stop tuning */ | ||
749 | radio->registers[CHANNEL] &= ~CHANNEL_TUNE; | ||
750 | return si470x_set_register(radio, CHANNEL); | ||
751 | } | ||
752 | |||
753 | |||
754 | /* | ||
755 | * si470x_get_freq - get the frequency | ||
756 | */ | ||
757 | static int si470x_get_freq(struct si470x_device *radio) | ||
758 | { | ||
759 | int spacing, band_bottom, chan, freq; | ||
760 | int retval; | ||
761 | |||
762 | /* Spacing (kHz) */ | ||
763 | switch (space) { | ||
764 | /* 0: 200 kHz (USA, Australia) */ | ||
765 | default: spacing = 0.200 * FREQ_MUL; break; | ||
766 | /* 1: 100 kHz (Europe, Japan) */ | ||
767 | case 1 : spacing = 0.100 * FREQ_MUL; break; | ||
768 | /* 2: 50 kHz */ | ||
769 | case 2 : spacing = 0.050 * FREQ_MUL; break; | ||
770 | }; | ||
771 | |||
772 | /* Bottom of Band (MHz) */ | ||
773 | switch (band) { | ||
774 | /* 0: 87.5 - 108 MHz (USA, Europe) */ | ||
775 | default: band_bottom = 87.5 * FREQ_MUL; break; | ||
776 | /* 1: 76 - 108 MHz (Japan wide band) */ | ||
777 | case 1 : band_bottom = 76 * FREQ_MUL; break; | ||
778 | /* 2: 76 - 90 MHz (Japan) */ | ||
779 | case 2 : band_bottom = 76 * FREQ_MUL; break; | ||
780 | }; | ||
781 | |||
782 | /* read channel */ | ||
783 | retval = si470x_get_register(radio, READCHAN); | ||
784 | if (retval < 0) | ||
785 | return retval; | ||
786 | chan = radio->registers[READCHAN] & READCHAN_READCHAN; | ||
787 | |||
788 | /* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */ | ||
789 | freq = chan * spacing + band_bottom; | ||
790 | |||
791 | return freq; | ||
792 | } | ||
793 | |||
794 | |||
795 | /* | ||
796 | * si470x_set_freq - set the frequency | ||
797 | */ | ||
798 | static int si470x_set_freq(struct si470x_device *radio, int freq) | ||
799 | { | ||
800 | int spacing, band_bottom, chan; | ||
801 | |||
802 | /* Spacing (kHz) */ | ||
803 | switch (space) { | ||
804 | /* 0: 200 kHz (USA, Australia) */ | ||
805 | default: spacing = 0.200 * FREQ_MUL; break; | ||
806 | /* 1: 100 kHz (Europe, Japan) */ | ||
807 | case 1 : spacing = 0.100 * FREQ_MUL; break; | ||
808 | /* 2: 50 kHz */ | ||
809 | case 2 : spacing = 0.050 * FREQ_MUL; break; | ||
810 | }; | ||
811 | |||
812 | /* Bottom of Band (MHz) */ | ||
813 | switch (band) { | ||
814 | /* 0: 87.5 - 108 MHz (USA, Europe) */ | ||
815 | default: band_bottom = 87.5 * FREQ_MUL; break; | ||
816 | /* 1: 76 - 108 MHz (Japan wide band) */ | ||
817 | case 1 : band_bottom = 76 * FREQ_MUL; break; | ||
818 | /* 2: 76 - 90 MHz (Japan) */ | ||
819 | case 2 : band_bottom = 76 * FREQ_MUL; break; | ||
820 | }; | ||
821 | |||
822 | /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */ | ||
823 | chan = (freq - band_bottom) / spacing; | ||
824 | |||
825 | return si470x_set_chan(radio, chan); | ||
826 | } | ||
827 | |||
828 | |||
829 | |||
830 | /* | ||
831 | * si470x_rds - rds processing function | ||
832 | */ | ||
833 | static void si470x_rds(struct si470x_device *radio) | ||
834 | { | ||
835 | unsigned long flags; | ||
836 | unsigned char tmpbuf[3]; | ||
837 | unsigned char blocknum; | ||
838 | unsigned char bler; /* RDS block errors */ | ||
839 | unsigned short rds; | ||
840 | unsigned int i; | ||
841 | |||
842 | if (radio->users == 0) | ||
843 | return; | ||
844 | if (si470x_get_rds_registers(radio) < 0) | ||
845 | return; | ||
846 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { | ||
847 | /* No RDS group ready */ | ||
848 | return; | ||
849 | } | ||
850 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) { | ||
851 | /* RDS decoder not synchronized */ | ||
852 | return; | ||
853 | } | ||
854 | |||
855 | for (blocknum = 0; blocknum < 4; blocknum++) { | ||
856 | switch (blocknum) { | ||
857 | default: | ||
858 | bler = (radio->registers[STATUSRSSI] & | ||
859 | STATUSRSSI_BLERA) >> 9; | ||
860 | rds = radio->registers[RDSA]; | ||
861 | break; | ||
862 | case 1: | ||
863 | bler = (radio->registers[READCHAN] & | ||
864 | READCHAN_BLERB) >> 14; | ||
865 | rds = radio->registers[RDSB]; | ||
866 | break; | ||
867 | case 2: | ||
868 | bler = (radio->registers[READCHAN] & | ||
869 | READCHAN_BLERC) >> 12; | ||
870 | rds = radio->registers[RDSC]; | ||
871 | break; | ||
872 | case 3: | ||
873 | bler = (radio->registers[READCHAN] & | ||
874 | READCHAN_BLERD) >> 10; | ||
875 | rds = radio->registers[RDSD]; | ||
876 | break; | ||
877 | }; | ||
878 | |||
879 | /* Fill the V4L2 RDS buffer */ | ||
880 | tmpbuf[0] = rds & 0x00ff; /* LSB */ | ||
881 | tmpbuf[1] = (rds & 0xff00) >> 8;/* MSB */ | ||
882 | tmpbuf[2] = blocknum; /* offset name */ | ||
883 | tmpbuf[2] |= blocknum << 3; /* received offset */ | ||
884 | if (bler > max_rds_errors) | ||
885 | tmpbuf[2] |= 0x80; /* uncorrectable errors */ | ||
886 | else if (bler > 0) | ||
887 | tmpbuf[2] |= 0x40; /* corrected error(s) */ | ||
888 | |||
889 | spin_lock_irqsave(&radio->lock, flags); | ||
890 | |||
891 | /* copy RDS block to internal buffer */ | ||
892 | for (i = 0; i < 3; i++) { | ||
893 | radio->buffer[radio->wr_index] = tmpbuf[i]; | ||
894 | radio->wr_index++; | ||
895 | } | ||
896 | |||
897 | if (radio->wr_index >= radio->buf_size) | ||
898 | radio->wr_index = 0; | ||
899 | |||
900 | if (radio->wr_index == radio->rd_index) { | ||
901 | radio->rd_index += 3; | ||
902 | if (radio->rd_index >= radio->buf_size) | ||
903 | radio->rd_index = 0; | ||
904 | } else | ||
905 | radio->block_count++; | ||
906 | |||
907 | spin_unlock_irqrestore(&radio->lock, flags); | ||
908 | } | ||
909 | |||
910 | radio->data_available_for_read = 1; | ||
911 | wake_up_interruptible(&radio->read_queue); | ||
912 | } | ||
913 | |||
914 | |||
915 | /* | ||
916 | * si470x_timer - rds timer function | ||
917 | */ | ||
918 | static void si470x_timer(unsigned long data) | ||
919 | { | ||
920 | struct si470x_device *radio = (struct si470x_device *) data; | ||
921 | |||
922 | schedule_work(&radio->work); | ||
923 | } | ||
924 | |||
925 | |||
926 | /* | ||
927 | * si470x_timer - rds work function | ||
928 | */ | ||
929 | static void si470x_work(struct work_struct *work) | ||
930 | { | ||
931 | struct si470x_device *radio = container_of(work, struct si470x_device, | ||
932 | work); | ||
933 | |||
934 | if (radio->users == 0) | ||
935 | return; | ||
936 | |||
937 | si470x_rds(radio); | ||
938 | mod_timer(&radio->timer, jiffies + msecs_to_jiffies(rds_poll_time)); | ||
939 | } | ||
940 | |||
941 | |||
942 | |||
943 | /************************************************************************** | ||
944 | * USB interface functions | ||
945 | **************************************************************************/ | ||
946 | |||
947 | /* | ||
948 | * si470x_usb_driver_probe - probe for the device | ||
949 | */ | ||
950 | static int si470x_usb_driver_probe(struct usb_interface *intf, | ||
951 | const struct usb_device_id *id) | ||
952 | { | ||
953 | struct si470x_device *radio; | ||
954 | |||
955 | if (!(radio = kmalloc(sizeof(struct si470x_device), GFP_KERNEL))) | ||
956 | return -ENOMEM; | ||
957 | if (!(radio->videodev = video_device_alloc())) { | ||
958 | kfree(radio); | ||
959 | return -ENOMEM; | ||
960 | } | ||
961 | memcpy(radio->videodev, &si470x_viddev_template, | ||
962 | sizeof(si470x_viddev_template)); | ||
963 | radio->users = 0; | ||
964 | radio->usbdev = interface_to_usbdev(intf); | ||
965 | video_set_drvdata(radio->videodev, radio); | ||
966 | if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) { | ||
967 | printk(KERN_WARNING DRIVER_NAME | ||
968 | ": Could not register video device\n"); | ||
969 | video_device_release(radio->videodev); | ||
970 | kfree(radio); | ||
971 | return -EIO; | ||
972 | } | ||
973 | usb_set_intfdata(intf, radio); | ||
974 | |||
975 | /* show some infos about the specific device */ | ||
976 | if (si470x_get_all_registers(radio) < 0) { | ||
977 | video_device_release(radio->videodev); | ||
978 | kfree(radio); | ||
979 | return -EIO; | ||
980 | } | ||
981 | printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4x ChipID=0x%4.4x\n", | ||
982 | radio->registers[DEVICEID], radio->registers[CHIPID]); | ||
983 | |||
984 | /* check if firmware is current */ | ||
985 | if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) | ||
986 | < FMRADIO_SW_VERSION_CURRENT) { | ||
987 | printk(KERN_WARNING DRIVER_NAME | ||
988 | ": This driver is known to work with chip version %d, " | ||
989 | "but the device has firmware %d. If you have some " | ||
990 | "trouble using this driver, please report to V4L ML " | ||
991 | "at video4linux-list@redhat.com\n", | ||
992 | radio->registers[CHIPID] & CHIPID_FIRMWARE, | ||
993 | FMRADIO_SW_VERSION_CURRENT); | ||
994 | } | ||
995 | |||
996 | /* set initial frequency */ | ||
997 | si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ | ||
998 | |||
999 | /* rds initialization */ | ||
1000 | radio->buf_size = rds_buf * 3; | ||
1001 | if (NULL == (radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL))) { | ||
1002 | video_device_release(radio->videodev); | ||
1003 | kfree(radio); | ||
1004 | return -ENOMEM; | ||
1005 | } | ||
1006 | radio->block_count = 0; | ||
1007 | radio->wr_index = 0; | ||
1008 | radio->rd_index = 0; | ||
1009 | radio->last_blocknum = 0xff; | ||
1010 | init_waitqueue_head(&radio->read_queue); | ||
1011 | radio->data_available_for_read = 0; | ||
1012 | |||
1013 | /* prepare polling via eventd */ | ||
1014 | INIT_WORK(&radio->work, si470x_work); | ||
1015 | init_timer(&radio->timer); | ||
1016 | radio->timer.function = si470x_timer; | ||
1017 | radio->timer.data = (unsigned long) radio; | ||
1018 | |||
1019 | return 0; | ||
1020 | } | ||
1021 | |||
1022 | |||
1023 | /* | ||
1024 | * si470x_usb_driver_disconnect - disconnect the device | ||
1025 | */ | ||
1026 | static void si470x_usb_driver_disconnect(struct usb_interface *intf) | ||
1027 | { | ||
1028 | struct si470x_device *radio = usb_get_intfdata(intf); | ||
1029 | |||
1030 | del_timer_sync(&radio->timer); | ||
1031 | flush_scheduled_work(); | ||
1032 | |||
1033 | usb_set_intfdata(intf, NULL); | ||
1034 | if (radio) { | ||
1035 | video_unregister_device(radio->videodev); | ||
1036 | kfree(radio->buffer); | ||
1037 | kfree(radio); | ||
1038 | } | ||
1039 | } | ||
1040 | |||
1041 | |||
1042 | |||
1043 | /************************************************************************** | ||
1044 | * Video4Linux interface functions | ||
1045 | **************************************************************************/ | ||
1046 | |||
1047 | /* | ||
1048 | * si470x_fops_read - read RDS data | ||
1049 | */ | ||
1050 | static ssize_t si470x_fops_read(struct file *file, char __user *buf, | ||
1051 | size_t count, loff_t *ppos) | ||
1052 | { | ||
1053 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1054 | struct rds_command cmd; | ||
1055 | unsigned long flags; | ||
1056 | unsigned int i; | ||
1057 | unsigned int rd_blocks; | ||
1058 | |||
1059 | cmd.block_count = count / 3; /* each RDS block needs 3 bytes */ | ||
1060 | cmd.result = 0; | ||
1061 | cmd.buffer = buf; | ||
1062 | cmd.instance = file; | ||
1063 | |||
1064 | /* copy RDS block out of internal buffer */ | ||
1065 | while (!radio->data_available_for_read) { | ||
1066 | if (wait_event_interruptible(radio->read_queue, | ||
1067 | radio->data_available_for_read) < 0) | ||
1068 | return -EINTR; | ||
1069 | } | ||
1070 | |||
1071 | spin_lock_irqsave(&radio->lock, flags); | ||
1072 | rd_blocks = cmd.block_count; | ||
1073 | if (rd_blocks > radio->block_count) | ||
1074 | rd_blocks = radio->block_count; | ||
1075 | |||
1076 | if (!rd_blocks) { | ||
1077 | spin_unlock_irqrestore(&radio->lock, flags); | ||
1078 | return cmd.result; | ||
1079 | } | ||
1080 | |||
1081 | for (i = 0; i < rd_blocks; i++) { | ||
1082 | /* copy RDS block to user buffer */ | ||
1083 | if (radio->rd_index == radio->wr_index) | ||
1084 | break; | ||
1085 | |||
1086 | if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3)) | ||
1087 | break; | ||
1088 | |||
1089 | radio->rd_index += 3; | ||
1090 | if (radio->rd_index >= radio->buf_size) | ||
1091 | radio->rd_index = 0; | ||
1092 | radio->block_count--; | ||
1093 | |||
1094 | buf += 3; | ||
1095 | cmd.result += 3; | ||
1096 | } | ||
1097 | radio->data_available_for_read = (radio->block_count > 0); | ||
1098 | spin_unlock_irqrestore(&radio->lock, flags); | ||
1099 | |||
1100 | return cmd.result; | ||
1101 | } | ||
1102 | |||
1103 | |||
1104 | /* | ||
1105 | * si470x_fops_poll - poll RDS data | ||
1106 | */ | ||
1107 | static unsigned int si470x_fops_poll(struct file *file, | ||
1108 | struct poll_table_struct *pts) | ||
1109 | { | ||
1110 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1111 | int retval; | ||
1112 | |||
1113 | retval = 0; | ||
1114 | if (radio->data_available_for_read) | ||
1115 | retval = POLLIN | POLLRDNORM; | ||
1116 | poll_wait(file, &radio->read_queue, pts); | ||
1117 | |||
1118 | return retval; | ||
1119 | } | ||
1120 | |||
1121 | |||
1122 | /* | ||
1123 | * si470x_fops_open - file open | ||
1124 | */ | ||
1125 | static int si470x_fops_open(struct inode *inode, struct file *file) | ||
1126 | { | ||
1127 | int retval; | ||
1128 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1129 | |||
1130 | radio->users++; | ||
1131 | if (radio->users == 1) { | ||
1132 | retval = si470x_start(radio); | ||
1133 | if (retval < 0) | ||
1134 | return retval; | ||
1135 | |||
1136 | schedule_work(&radio->work); | ||
1137 | } | ||
1138 | |||
1139 | return 0; | ||
1140 | } | ||
1141 | |||
1142 | |||
1143 | /* | ||
1144 | * si470x_fops_release - file release | ||
1145 | */ | ||
1146 | static int si470x_fops_release(struct inode *inode, struct file *file) | ||
1147 | { | ||
1148 | int retval; | ||
1149 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1150 | |||
1151 | if (!radio) | ||
1152 | return -ENODEV; | ||
1153 | |||
1154 | radio->users--; | ||
1155 | if (radio->users == 0) { | ||
1156 | radio->data_available_for_read = 1; /* ? */ | ||
1157 | wake_up_interruptible(&radio->read_queue); /* ? */ | ||
1158 | |||
1159 | retval = si470x_stop(radio); | ||
1160 | if (retval < 0) | ||
1161 | return retval; | ||
1162 | } | ||
1163 | |||
1164 | return 0; | ||
1165 | } | ||
1166 | |||
1167 | |||
1168 | /* | ||
1169 | * si470x_vidioc_querycap - query device capabilities | ||
1170 | */ | ||
1171 | static int si470x_vidioc_querycap(struct file *file, void *priv, | ||
1172 | struct v4l2_capability *capability) | ||
1173 | { | ||
1174 | strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); | ||
1175 | strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); | ||
1176 | sprintf(capability->bus_info, "USB"); | ||
1177 | capability->version = DRIVER_VERSION; | ||
1178 | capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | ||
1179 | |||
1180 | return 0; | ||
1181 | } | ||
1182 | |||
1183 | |||
1184 | /* | ||
1185 | * si470x_vidioc_g_input - get input | ||
1186 | */ | ||
1187 | static int si470x_vidioc_g_input(struct file *filp, void *priv, | ||
1188 | unsigned int *i) | ||
1189 | { | ||
1190 | *i = 0; | ||
1191 | |||
1192 | return 0; | ||
1193 | } | ||
1194 | |||
1195 | |||
1196 | /* | ||
1197 | * si470x_vidioc_s_input - set input | ||
1198 | */ | ||
1199 | static int si470x_vidioc_s_input(struct file *filp, void *priv, unsigned int i) | ||
1200 | { | ||
1201 | if (i != 0) | ||
1202 | return -EINVAL; | ||
1203 | |||
1204 | return 0; | ||
1205 | } | ||
1206 | |||
1207 | |||
1208 | /* | ||
1209 | * si470x_vidioc_queryctrl - enumerate control items | ||
1210 | */ | ||
1211 | static int si470x_vidioc_queryctrl(struct file *file, void *priv, | ||
1212 | struct v4l2_queryctrl *qc) | ||
1213 | { | ||
1214 | int i; | ||
1215 | |||
1216 | for (i = 0; i < ARRAY_SIZE(radio_queryctrl); i++) { | ||
1217 | if (qc->id && qc->id == radio_queryctrl[i].id) { | ||
1218 | memcpy(qc, &(radio_queryctrl[i]), sizeof(*qc)); | ||
1219 | return 0; | ||
1220 | } | ||
1221 | } | ||
1222 | |||
1223 | return -EINVAL; | ||
1224 | } | ||
1225 | |||
1226 | |||
1227 | /* | ||
1228 | * si470x_vidioc_g_ctrl - get the value of a control | ||
1229 | */ | ||
1230 | static int si470x_vidioc_g_ctrl(struct file *file, void *priv, | ||
1231 | struct v4l2_control *ctrl) | ||
1232 | { | ||
1233 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1234 | |||
1235 | switch (ctrl->id) { | ||
1236 | case V4L2_CID_AUDIO_VOLUME: | ||
1237 | ctrl->value = radio->registers[SYSCONFIG2] & | ||
1238 | SYSCONFIG2_VOLUME; | ||
1239 | break; | ||
1240 | case V4L2_CID_AUDIO_MUTE: | ||
1241 | ctrl->value = ((radio->registers[POWERCFG] & | ||
1242 | POWERCFG_DMUTE) == 0) ? 1 : 0; | ||
1243 | break; | ||
1244 | } | ||
1245 | |||
1246 | return 0; | ||
1247 | } | ||
1248 | |||
1249 | |||
1250 | /* | ||
1251 | * si470x_vidioc_s_ctrl - set the value of a control | ||
1252 | */ | ||
1253 | static int si470x_vidioc_s_ctrl(struct file *file, void *priv, | ||
1254 | struct v4l2_control *ctrl) | ||
1255 | { | ||
1256 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1257 | |||
1258 | switch (ctrl->id) { | ||
1259 | case V4L2_CID_AUDIO_VOLUME: | ||
1260 | radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; | ||
1261 | radio->registers[SYSCONFIG2] |= ctrl->value; | ||
1262 | return si470x_set_register(radio, SYSCONFIG2); | ||
1263 | case V4L2_CID_AUDIO_MUTE: | ||
1264 | if (ctrl->value == 1) | ||
1265 | radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; | ||
1266 | else | ||
1267 | radio->registers[POWERCFG] |= POWERCFG_DMUTE; | ||
1268 | return si470x_set_register(radio, POWERCFG); | ||
1269 | } | ||
1270 | |||
1271 | return -EINVAL; | ||
1272 | } | ||
1273 | |||
1274 | |||
1275 | /* | ||
1276 | * si470x_vidioc_g_audio - get audio attributes | ||
1277 | */ | ||
1278 | static int si470x_vidioc_g_audio(struct file *file, void *priv, | ||
1279 | struct v4l2_audio *audio) | ||
1280 | { | ||
1281 | if (audio->index > 1) | ||
1282 | return -EINVAL; | ||
1283 | |||
1284 | strcpy(audio->name, "Radio"); | ||
1285 | audio->capability = V4L2_AUDCAP_STEREO; | ||
1286 | |||
1287 | return 0; | ||
1288 | } | ||
1289 | |||
1290 | |||
1291 | /* | ||
1292 | * si470x_vidioc_s_audio - set audio attributes | ||
1293 | */ | ||
1294 | static int si470x_vidioc_s_audio(struct file *file, void *priv, | ||
1295 | struct v4l2_audio *audio) | ||
1296 | { | ||
1297 | if (audio->index != 0) | ||
1298 | return -EINVAL; | ||
1299 | |||
1300 | return 0; | ||
1301 | } | ||
1302 | |||
1303 | |||
1304 | /* | ||
1305 | * si470x_vidioc_g_tuner - get tuner attributes | ||
1306 | */ | ||
1307 | static int si470x_vidioc_g_tuner(struct file *file, void *priv, | ||
1308 | struct v4l2_tuner *tuner) | ||
1309 | { | ||
1310 | int retval; | ||
1311 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1312 | |||
1313 | if (tuner->index > 0) | ||
1314 | return -EINVAL; | ||
1315 | |||
1316 | /* read status rssi */ | ||
1317 | retval = si470x_get_register(radio, STATUSRSSI); | ||
1318 | if (retval < 0) | ||
1319 | return retval; | ||
1320 | |||
1321 | strcpy(tuner->name, "FM"); | ||
1322 | tuner->type = V4L2_TUNER_RADIO; | ||
1323 | switch(band) { | ||
1324 | /* 0: 87.5 - 108 MHz (USA, Europe, default) */ | ||
1325 | default: | ||
1326 | tuner->rangelow = 87.5 * FREQ_MUL; | ||
1327 | tuner->rangehigh = 108 * FREQ_MUL; | ||
1328 | break; | ||
1329 | /* 1: 76 - 108 MHz (Japan wide band) */ | ||
1330 | case 1 : | ||
1331 | tuner->rangelow = 76 * FREQ_MUL; | ||
1332 | tuner->rangehigh = 108 * FREQ_MUL; | ||
1333 | break; | ||
1334 | /* 2: 76 - 90 MHz (Japan) */ | ||
1335 | case 2 : | ||
1336 | tuner->rangelow = 76 * FREQ_MUL; | ||
1337 | tuner->rangehigh = 90 * FREQ_MUL; | ||
1338 | break; | ||
1339 | }; | ||
1340 | tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; | ||
1341 | tuner->capability = V4L2_TUNER_CAP_LOW; | ||
1342 | |||
1343 | /* Stereo indicator == Stereo (instead of Mono) */ | ||
1344 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 1) | ||
1345 | tuner->audmode = V4L2_TUNER_MODE_STEREO; | ||
1346 | else | ||
1347 | tuner->audmode = V4L2_TUNER_MODE_MONO; | ||
1348 | |||
1349 | /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ | ||
1350 | tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI) | ||
1351 | * 0x0101; | ||
1352 | |||
1353 | /* automatic frequency control: -1: freq to low, 1 freq to high */ | ||
1354 | tuner->afc = 0; | ||
1355 | |||
1356 | return 0; | ||
1357 | } | ||
1358 | |||
1359 | |||
1360 | /* | ||
1361 | * si470x_vidioc_s_tuner - set tuner attributes | ||
1362 | */ | ||
1363 | static int si470x_vidioc_s_tuner(struct file *file, void *priv, | ||
1364 | struct v4l2_tuner *tuner) | ||
1365 | { | ||
1366 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1367 | |||
1368 | if (tuner->index > 0) | ||
1369 | return -EINVAL; | ||
1370 | |||
1371 | if (tuner->audmode == V4L2_TUNER_MODE_MONO) | ||
1372 | radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ | ||
1373 | else | ||
1374 | radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ | ||
1375 | |||
1376 | return si470x_set_register(radio, POWERCFG); | ||
1377 | } | ||
1378 | |||
1379 | |||
1380 | /* | ||
1381 | * si470x_vidioc_g_frequency - get tuner or modulator radio frequency | ||
1382 | */ | ||
1383 | static int si470x_vidioc_g_frequency(struct file *file, void *priv, | ||
1384 | struct v4l2_frequency *freq) | ||
1385 | { | ||
1386 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1387 | |||
1388 | freq->type = V4L2_TUNER_RADIO; | ||
1389 | freq->frequency = si470x_get_freq(radio); | ||
1390 | |||
1391 | return 0; | ||
1392 | } | ||
1393 | |||
1394 | |||
1395 | /* | ||
1396 | * si470x_vidioc_s_frequency - set tuner or modulator radio frequency | ||
1397 | */ | ||
1398 | static int si470x_vidioc_s_frequency(struct file *file, void *priv, | ||
1399 | struct v4l2_frequency *freq) | ||
1400 | { | ||
1401 | struct si470x_device *radio = video_get_drvdata(video_devdata(file)); | ||
1402 | |||
1403 | if (freq->type != V4L2_TUNER_RADIO) | ||
1404 | return -EINVAL; | ||
1405 | |||
1406 | return si470x_set_freq(radio, freq->frequency); | ||
1407 | } | ||
1408 | |||
1409 | |||
1410 | |||
1411 | /************************************************************************** | ||
1412 | * Module interface definitions and functions | ||
1413 | **************************************************************************/ | ||
1414 | |||
1415 | /* | ||
1416 | * si470x_module_init - module init | ||
1417 | */ | ||
1418 | static int __init si470x_module_init(void) | ||
1419 | { | ||
1420 | printk(KERN_INFO DRIVER_DESC "\n"); | ||
1421 | return usb_register(&si470x_usb_driver); | ||
1422 | } | ||
1423 | |||
1424 | |||
1425 | /* | ||
1426 | * si470x_module_exit - module exit | ||
1427 | */ | ||
1428 | static void __exit si470x_module_exit(void) | ||
1429 | { | ||
1430 | usb_deregister(&si470x_usb_driver); | ||
1431 | } | ||
1432 | |||
1433 | |||
1434 | module_init(si470x_module_init); | ||
1435 | module_exit(si470x_module_exit); | ||
1436 | |||
1437 | MODULE_LICENSE("GPL"); | ||
1438 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
1439 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
1440 | MODULE_VERSION("1.0.2"); | ||