diff options
Diffstat (limited to 'drivers/media/radio/si470x/radio-si470x-usb.c')
-rw-r--r-- | drivers/media/radio/si470x/radio-si470x-usb.c | 988 |
1 files changed, 988 insertions, 0 deletions
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c new file mode 100644 index 000000000000..f2d0e1ddb301 --- /dev/null +++ b/drivers/media/radio/si470x/radio-si470x-usb.c | |||
@@ -0,0 +1,988 @@ | |||
1 | /* | ||
2 | * drivers/media/radio/si470x/radio-si470x-usb.c | ||
3 | * | ||
4 | * USB driver for radios with Silicon Labs Si470x FM Radio Receivers | ||
5 | * | ||
6 | * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | |||
24 | /* | ||
25 | * ToDo: | ||
26 | * - add firmware download/update support | ||
27 | */ | ||
28 | |||
29 | |||
30 | /* driver definitions */ | ||
31 | #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" | ||
32 | #define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 10) | ||
33 | #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver" | ||
34 | #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" | ||
35 | #define DRIVER_VERSION "1.0.10" | ||
36 | |||
37 | /* kernel includes */ | ||
38 | #include <linux/usb.h> | ||
39 | #include <linux/hid.h> | ||
40 | |||
41 | #include "radio-si470x.h" | ||
42 | |||
43 | |||
44 | /* USB Device ID List */ | ||
45 | static struct usb_device_id si470x_usb_driver_id_table[] = { | ||
46 | /* Silicon Labs USB FM Radio Reference Design */ | ||
47 | { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, | ||
48 | /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */ | ||
49 | { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) }, | ||
50 | /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */ | ||
51 | { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) }, | ||
52 | /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */ | ||
53 | { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) }, | ||
54 | /* Terminating entry */ | ||
55 | { } | ||
56 | }; | ||
57 | MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table); | ||
58 | |||
59 | |||
60 | |||
61 | /************************************************************************** | ||
62 | * Module Parameters | ||
63 | **************************************************************************/ | ||
64 | |||
65 | /* Radio Nr */ | ||
66 | static int radio_nr = -1; | ||
67 | module_param(radio_nr, int, 0444); | ||
68 | MODULE_PARM_DESC(radio_nr, "Radio Nr"); | ||
69 | |||
70 | /* USB timeout */ | ||
71 | static unsigned int usb_timeout = 500; | ||
72 | module_param(usb_timeout, uint, 0644); | ||
73 | MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*"); | ||
74 | |||
75 | /* RDS buffer blocks */ | ||
76 | static unsigned int rds_buf = 100; | ||
77 | module_param(rds_buf, uint, 0444); | ||
78 | MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*"); | ||
79 | |||
80 | /* RDS maximum block errors */ | ||
81 | static unsigned short max_rds_errors = 1; | ||
82 | /* 0 means 0 errors requiring correction */ | ||
83 | /* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */ | ||
84 | /* 2 means 3-5 errors requiring correction */ | ||
85 | /* 3 means 6+ errors or errors in checkword, correction not possible */ | ||
86 | module_param(max_rds_errors, ushort, 0644); | ||
87 | MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); | ||
88 | |||
89 | |||
90 | |||
91 | /************************************************************************** | ||
92 | * USB HID Reports | ||
93 | **************************************************************************/ | ||
94 | |||
95 | /* Reports 1-16 give direct read/write access to the 16 Si470x registers */ | ||
96 | /* with the (REPORT_ID - 1) corresponding to the register address across USB */ | ||
97 | /* endpoint 0 using GET_REPORT and SET_REPORT */ | ||
98 | #define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1) | ||
99 | #define REGISTER_REPORT(reg) ((reg) + 1) | ||
100 | |||
101 | /* Report 17 gives direct read/write access to the entire Si470x register */ | ||
102 | /* map across endpoint 0 using GET_REPORT and SET_REPORT */ | ||
103 | #define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) | ||
104 | #define ENTIRE_REPORT 17 | ||
105 | |||
106 | /* Report 18 is used to send the lowest 6 Si470x registers up the HID */ | ||
107 | /* interrupt endpoint 1 to Windows every 20 milliseconds for status */ | ||
108 | #define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) | ||
109 | #define RDS_REPORT 18 | ||
110 | |||
111 | /* Report 19: LED state */ | ||
112 | #define LED_REPORT_SIZE 3 | ||
113 | #define LED_REPORT 19 | ||
114 | |||
115 | /* Report 19: stream */ | ||
116 | #define STREAM_REPORT_SIZE 3 | ||
117 | #define STREAM_REPORT 19 | ||
118 | |||
119 | /* Report 20: scratch */ | ||
120 | #define SCRATCH_PAGE_SIZE 63 | ||
121 | #define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1) | ||
122 | #define SCRATCH_REPORT 20 | ||
123 | |||
124 | /* Reports 19-22: flash upgrade of the C8051F321 */ | ||
125 | #define WRITE_REPORT_SIZE 4 | ||
126 | #define WRITE_REPORT 19 | ||
127 | #define FLASH_REPORT_SIZE 64 | ||
128 | #define FLASH_REPORT 20 | ||
129 | #define CRC_REPORT_SIZE 3 | ||
130 | #define CRC_REPORT 21 | ||
131 | #define RESPONSE_REPORT_SIZE 2 | ||
132 | #define RESPONSE_REPORT 22 | ||
133 | |||
134 | /* Report 23: currently unused, but can accept 60 byte reports on the HID */ | ||
135 | /* interrupt out endpoint 2 every 1 millisecond */ | ||
136 | #define UNUSED_REPORT 23 | ||
137 | |||
138 | |||
139 | |||
140 | /************************************************************************** | ||
141 | * Software/Hardware Versions from Scratch Page | ||
142 | **************************************************************************/ | ||
143 | #define RADIO_SW_VERSION_NOT_BOOTLOADABLE 6 | ||
144 | #define RADIO_SW_VERSION 7 | ||
145 | #define RADIO_HW_VERSION 1 | ||
146 | |||
147 | |||
148 | |||
149 | /************************************************************************** | ||
150 | * LED State Definitions | ||
151 | **************************************************************************/ | ||
152 | #define LED_COMMAND 0x35 | ||
153 | |||
154 | #define NO_CHANGE_LED 0x00 | ||
155 | #define ALL_COLOR_LED 0x01 /* streaming state */ | ||
156 | #define BLINK_GREEN_LED 0x02 /* connect state */ | ||
157 | #define BLINK_RED_LED 0x04 | ||
158 | #define BLINK_ORANGE_LED 0x10 /* disconnect state */ | ||
159 | #define SOLID_GREEN_LED 0x20 /* tuning/seeking state */ | ||
160 | #define SOLID_RED_LED 0x40 /* bootload state */ | ||
161 | #define SOLID_ORANGE_LED 0x80 | ||
162 | |||
163 | |||
164 | |||
165 | /************************************************************************** | ||
166 | * Stream State Definitions | ||
167 | **************************************************************************/ | ||
168 | #define STREAM_COMMAND 0x36 | ||
169 | #define STREAM_VIDPID 0x00 | ||
170 | #define STREAM_AUDIO 0xff | ||
171 | |||
172 | |||
173 | |||
174 | /************************************************************************** | ||
175 | * Bootloader / Flash Commands | ||
176 | **************************************************************************/ | ||
177 | |||
178 | /* unique id sent to bootloader and required to put into a bootload state */ | ||
179 | #define UNIQUE_BL_ID 0x34 | ||
180 | |||
181 | /* mask for the flash data */ | ||
182 | #define FLASH_DATA_MASK 0x55 | ||
183 | |||
184 | /* bootloader commands */ | ||
185 | #define GET_SW_VERSION_COMMAND 0x00 | ||
186 | #define SET_PAGE_COMMAND 0x01 | ||
187 | #define ERASE_PAGE_COMMAND 0x02 | ||
188 | #define WRITE_PAGE_COMMAND 0x03 | ||
189 | #define CRC_ON_PAGE_COMMAND 0x04 | ||
190 | #define READ_FLASH_BYTE_COMMAND 0x05 | ||
191 | #define RESET_DEVICE_COMMAND 0x06 | ||
192 | #define GET_HW_VERSION_COMMAND 0x07 | ||
193 | #define BLANK 0xff | ||
194 | |||
195 | /* bootloader command responses */ | ||
196 | #define COMMAND_OK 0x01 | ||
197 | #define COMMAND_FAILED 0x02 | ||
198 | #define COMMAND_PENDING 0x03 | ||
199 | |||
200 | |||
201 | |||
202 | /************************************************************************** | ||
203 | * General Driver Functions - REGISTER_REPORTs | ||
204 | **************************************************************************/ | ||
205 | |||
206 | /* | ||
207 | * si470x_get_report - receive a HID report | ||
208 | */ | ||
209 | static int si470x_get_report(struct si470x_device *radio, void *buf, int size) | ||
210 | { | ||
211 | unsigned char *report = (unsigned char *) buf; | ||
212 | int retval; | ||
213 | |||
214 | retval = usb_control_msg(radio->usbdev, | ||
215 | usb_rcvctrlpipe(radio->usbdev, 0), | ||
216 | HID_REQ_GET_REPORT, | ||
217 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
218 | report[0], 2, | ||
219 | buf, size, usb_timeout); | ||
220 | |||
221 | if (retval < 0) | ||
222 | dev_warn(&radio->intf->dev, | ||
223 | "si470x_get_report: usb_control_msg returned %d\n", | ||
224 | retval); | ||
225 | return retval; | ||
226 | } | ||
227 | |||
228 | |||
229 | /* | ||
230 | * si470x_set_report - send a HID report | ||
231 | */ | ||
232 | static int si470x_set_report(struct si470x_device *radio, void *buf, int size) | ||
233 | { | ||
234 | unsigned char *report = (unsigned char *) buf; | ||
235 | int retval; | ||
236 | |||
237 | retval = usb_control_msg(radio->usbdev, | ||
238 | usb_sndctrlpipe(radio->usbdev, 0), | ||
239 | HID_REQ_SET_REPORT, | ||
240 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | ||
241 | report[0], 2, | ||
242 | buf, size, usb_timeout); | ||
243 | |||
244 | if (retval < 0) | ||
245 | dev_warn(&radio->intf->dev, | ||
246 | "si470x_set_report: usb_control_msg returned %d\n", | ||
247 | retval); | ||
248 | return retval; | ||
249 | } | ||
250 | |||
251 | |||
252 | /* | ||
253 | * si470x_get_register - read register | ||
254 | */ | ||
255 | int si470x_get_register(struct si470x_device *radio, int regnr) | ||
256 | { | ||
257 | unsigned char buf[REGISTER_REPORT_SIZE]; | ||
258 | int retval; | ||
259 | |||
260 | buf[0] = REGISTER_REPORT(regnr); | ||
261 | |||
262 | retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); | ||
263 | |||
264 | if (retval >= 0) | ||
265 | radio->registers[regnr] = get_unaligned_be16(&buf[1]); | ||
266 | |||
267 | return (retval < 0) ? -EINVAL : 0; | ||
268 | } | ||
269 | |||
270 | |||
271 | /* | ||
272 | * si470x_set_register - write register | ||
273 | */ | ||
274 | int si470x_set_register(struct si470x_device *radio, int regnr) | ||
275 | { | ||
276 | unsigned char buf[REGISTER_REPORT_SIZE]; | ||
277 | int retval; | ||
278 | |||
279 | buf[0] = REGISTER_REPORT(regnr); | ||
280 | put_unaligned_be16(radio->registers[regnr], &buf[1]); | ||
281 | |||
282 | retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); | ||
283 | |||
284 | return (retval < 0) ? -EINVAL : 0; | ||
285 | } | ||
286 | |||
287 | |||
288 | |||
289 | /************************************************************************** | ||
290 | * General Driver Functions - ENTIRE_REPORT | ||
291 | **************************************************************************/ | ||
292 | |||
293 | /* | ||
294 | * si470x_get_all_registers - read entire registers | ||
295 | */ | ||
296 | static int si470x_get_all_registers(struct si470x_device *radio) | ||
297 | { | ||
298 | unsigned char buf[ENTIRE_REPORT_SIZE]; | ||
299 | int retval; | ||
300 | unsigned char regnr; | ||
301 | |||
302 | buf[0] = ENTIRE_REPORT; | ||
303 | |||
304 | retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); | ||
305 | |||
306 | if (retval >= 0) | ||
307 | for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) | ||
308 | radio->registers[regnr] = get_unaligned_be16( | ||
309 | &buf[regnr * RADIO_REGISTER_SIZE + 1]); | ||
310 | |||
311 | return (retval < 0) ? -EINVAL : 0; | ||
312 | } | ||
313 | |||
314 | |||
315 | |||
316 | /************************************************************************** | ||
317 | * General Driver Functions - LED_REPORT | ||
318 | **************************************************************************/ | ||
319 | |||
320 | /* | ||
321 | * si470x_set_led_state - sets the led state | ||
322 | */ | ||
323 | static int si470x_set_led_state(struct si470x_device *radio, | ||
324 | unsigned char led_state) | ||
325 | { | ||
326 | unsigned char buf[LED_REPORT_SIZE]; | ||
327 | int retval; | ||
328 | |||
329 | buf[0] = LED_REPORT; | ||
330 | buf[1] = LED_COMMAND; | ||
331 | buf[2] = led_state; | ||
332 | |||
333 | retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); | ||
334 | |||
335 | return (retval < 0) ? -EINVAL : 0; | ||
336 | } | ||
337 | |||
338 | |||
339 | |||
340 | /************************************************************************** | ||
341 | * General Driver Functions - SCRATCH_REPORT | ||
342 | **************************************************************************/ | ||
343 | |||
344 | /* | ||
345 | * si470x_get_scratch_versions - gets the scratch page and version infos | ||
346 | */ | ||
347 | static int si470x_get_scratch_page_versions(struct si470x_device *radio) | ||
348 | { | ||
349 | unsigned char buf[SCRATCH_REPORT_SIZE]; | ||
350 | int retval; | ||
351 | |||
352 | buf[0] = SCRATCH_REPORT; | ||
353 | |||
354 | retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); | ||
355 | |||
356 | if (retval < 0) | ||
357 | dev_warn(&radio->intf->dev, "si470x_get_scratch: " | ||
358 | "si470x_get_report returned %d\n", retval); | ||
359 | else { | ||
360 | radio->software_version = buf[1]; | ||
361 | radio->hardware_version = buf[2]; | ||
362 | } | ||
363 | |||
364 | return (retval < 0) ? -EINVAL : 0; | ||
365 | } | ||
366 | |||
367 | |||
368 | |||
369 | /************************************************************************** | ||
370 | * General Driver Functions - DISCONNECT_CHECK | ||
371 | **************************************************************************/ | ||
372 | |||
373 | /* | ||
374 | * si470x_disconnect_check - check whether radio disconnects | ||
375 | */ | ||
376 | int si470x_disconnect_check(struct si470x_device *radio) | ||
377 | { | ||
378 | if (radio->disconnected) | ||
379 | return -EIO; | ||
380 | else | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | |||
385 | |||
386 | /************************************************************************** | ||
387 | * RDS Driver Functions | ||
388 | **************************************************************************/ | ||
389 | |||
390 | /* | ||
391 | * si470x_int_in_callback - rds callback and processing function | ||
392 | * | ||
393 | * TODO: do we need to use mutex locks in some sections? | ||
394 | */ | ||
395 | static void si470x_int_in_callback(struct urb *urb) | ||
396 | { | ||
397 | struct si470x_device *radio = urb->context; | ||
398 | unsigned char buf[RDS_REPORT_SIZE]; | ||
399 | int retval; | ||
400 | unsigned char regnr; | ||
401 | unsigned char blocknum; | ||
402 | unsigned short bler; /* rds block errors */ | ||
403 | unsigned short rds; | ||
404 | unsigned char tmpbuf[3]; | ||
405 | |||
406 | if (urb->status) { | ||
407 | if (urb->status == -ENOENT || | ||
408 | urb->status == -ECONNRESET || | ||
409 | urb->status == -ESHUTDOWN) { | ||
410 | return; | ||
411 | } else { | ||
412 | dev_warn(&radio->intf->dev, | ||
413 | "non-zero urb status (%d)\n", urb->status); | ||
414 | goto resubmit; /* Maybe we can recover. */ | ||
415 | } | ||
416 | } | ||
417 | |||
418 | /* safety checks */ | ||
419 | if (radio->disconnected) | ||
420 | return; | ||
421 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) | ||
422 | goto resubmit; | ||
423 | |||
424 | if (urb->actual_length > 0) { | ||
425 | /* Update RDS registers with URB data */ | ||
426 | buf[0] = RDS_REPORT; | ||
427 | for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++) | ||
428 | radio->registers[STATUSRSSI + regnr] = | ||
429 | get_unaligned_be16(&radio->int_in_buffer[ | ||
430 | regnr * RADIO_REGISTER_SIZE + 1]); | ||
431 | /* get rds blocks */ | ||
432 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { | ||
433 | /* No RDS group ready, better luck next time */ | ||
434 | goto resubmit; | ||
435 | } | ||
436 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) { | ||
437 | /* RDS decoder not synchronized */ | ||
438 | goto resubmit; | ||
439 | } | ||
440 | for (blocknum = 0; blocknum < 4; blocknum++) { | ||
441 | switch (blocknum) { | ||
442 | default: | ||
443 | bler = (radio->registers[STATUSRSSI] & | ||
444 | STATUSRSSI_BLERA) >> 9; | ||
445 | rds = radio->registers[RDSA]; | ||
446 | break; | ||
447 | case 1: | ||
448 | bler = (radio->registers[READCHAN] & | ||
449 | READCHAN_BLERB) >> 14; | ||
450 | rds = radio->registers[RDSB]; | ||
451 | break; | ||
452 | case 2: | ||
453 | bler = (radio->registers[READCHAN] & | ||
454 | READCHAN_BLERC) >> 12; | ||
455 | rds = radio->registers[RDSC]; | ||
456 | break; | ||
457 | case 3: | ||
458 | bler = (radio->registers[READCHAN] & | ||
459 | READCHAN_BLERD) >> 10; | ||
460 | rds = radio->registers[RDSD]; | ||
461 | break; | ||
462 | }; | ||
463 | |||
464 | /* Fill the V4L2 RDS buffer */ | ||
465 | put_unaligned_le16(rds, &tmpbuf); | ||
466 | tmpbuf[2] = blocknum; /* offset name */ | ||
467 | tmpbuf[2] |= blocknum << 3; /* received offset */ | ||
468 | if (bler > max_rds_errors) | ||
469 | tmpbuf[2] |= 0x80; /* uncorrectable errors */ | ||
470 | else if (bler > 0) | ||
471 | tmpbuf[2] |= 0x40; /* corrected error(s) */ | ||
472 | |||
473 | /* copy RDS block to internal buffer */ | ||
474 | memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); | ||
475 | radio->wr_index += 3; | ||
476 | |||
477 | /* wrap write pointer */ | ||
478 | if (radio->wr_index >= radio->buf_size) | ||
479 | radio->wr_index = 0; | ||
480 | |||
481 | /* check for overflow */ | ||
482 | if (radio->wr_index == radio->rd_index) { | ||
483 | /* increment and wrap read pointer */ | ||
484 | radio->rd_index += 3; | ||
485 | if (radio->rd_index >= radio->buf_size) | ||
486 | radio->rd_index = 0; | ||
487 | } | ||
488 | } | ||
489 | if (radio->wr_index != radio->rd_index) | ||
490 | wake_up_interruptible(&radio->read_queue); | ||
491 | } | ||
492 | |||
493 | resubmit: | ||
494 | /* Resubmit if we're still running. */ | ||
495 | if (radio->int_in_running && radio->usbdev) { | ||
496 | retval = usb_submit_urb(radio->int_in_urb, GFP_ATOMIC); | ||
497 | if (retval) { | ||
498 | dev_warn(&radio->intf->dev, | ||
499 | "resubmitting urb failed (%d)", retval); | ||
500 | radio->int_in_running = 0; | ||
501 | } | ||
502 | } | ||
503 | } | ||
504 | |||
505 | |||
506 | |||
507 | /************************************************************************** | ||
508 | * File Operations Interface | ||
509 | **************************************************************************/ | ||
510 | |||
511 | /* | ||
512 | * si470x_fops_read - read RDS data | ||
513 | */ | ||
514 | static ssize_t si470x_fops_read(struct file *file, char __user *buf, | ||
515 | size_t count, loff_t *ppos) | ||
516 | { | ||
517 | struct si470x_device *radio = video_drvdata(file); | ||
518 | int retval = 0; | ||
519 | unsigned int block_count = 0; | ||
520 | |||
521 | /* switch on rds reception */ | ||
522 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) | ||
523 | si470x_rds_on(radio); | ||
524 | |||
525 | /* block if no new data available */ | ||
526 | while (radio->wr_index == radio->rd_index) { | ||
527 | if (file->f_flags & O_NONBLOCK) { | ||
528 | retval = -EWOULDBLOCK; | ||
529 | goto done; | ||
530 | } | ||
531 | if (wait_event_interruptible(radio->read_queue, | ||
532 | radio->wr_index != radio->rd_index) < 0) { | ||
533 | retval = -EINTR; | ||
534 | goto done; | ||
535 | } | ||
536 | } | ||
537 | |||
538 | /* calculate block count from byte count */ | ||
539 | count /= 3; | ||
540 | |||
541 | /* copy RDS block out of internal buffer and to user buffer */ | ||
542 | mutex_lock(&radio->lock); | ||
543 | while (block_count < count) { | ||
544 | if (radio->rd_index == radio->wr_index) | ||
545 | break; | ||
546 | |||
547 | /* always transfer rds complete blocks */ | ||
548 | if (copy_to_user(buf, &radio->buffer[radio->rd_index], 3)) | ||
549 | /* retval = -EFAULT; */ | ||
550 | break; | ||
551 | |||
552 | /* increment and wrap read pointer */ | ||
553 | radio->rd_index += 3; | ||
554 | if (radio->rd_index >= radio->buf_size) | ||
555 | radio->rd_index = 0; | ||
556 | |||
557 | /* increment counters */ | ||
558 | block_count++; | ||
559 | buf += 3; | ||
560 | retval += 3; | ||
561 | } | ||
562 | mutex_unlock(&radio->lock); | ||
563 | |||
564 | done: | ||
565 | return retval; | ||
566 | } | ||
567 | |||
568 | |||
569 | /* | ||
570 | * si470x_fops_poll - poll RDS data | ||
571 | */ | ||
572 | static unsigned int si470x_fops_poll(struct file *file, | ||
573 | struct poll_table_struct *pts) | ||
574 | { | ||
575 | struct si470x_device *radio = video_drvdata(file); | ||
576 | int retval = 0; | ||
577 | |||
578 | /* switch on rds reception */ | ||
579 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) | ||
580 | si470x_rds_on(radio); | ||
581 | |||
582 | poll_wait(file, &radio->read_queue, pts); | ||
583 | |||
584 | if (radio->rd_index != radio->wr_index) | ||
585 | retval = POLLIN | POLLRDNORM; | ||
586 | |||
587 | return retval; | ||
588 | } | ||
589 | |||
590 | |||
591 | /* | ||
592 | * si470x_fops_open - file open | ||
593 | */ | ||
594 | static int si470x_fops_open(struct file *file) | ||
595 | { | ||
596 | struct si470x_device *radio = video_drvdata(file); | ||
597 | int retval; | ||
598 | |||
599 | lock_kernel(); | ||
600 | radio->users++; | ||
601 | |||
602 | retval = usb_autopm_get_interface(radio->intf); | ||
603 | if (retval < 0) { | ||
604 | radio->users--; | ||
605 | retval = -EIO; | ||
606 | goto done; | ||
607 | } | ||
608 | |||
609 | if (radio->users == 1) { | ||
610 | /* start radio */ | ||
611 | retval = si470x_start(radio); | ||
612 | if (retval < 0) { | ||
613 | usb_autopm_put_interface(radio->intf); | ||
614 | goto done; | ||
615 | } | ||
616 | |||
617 | /* initialize interrupt urb */ | ||
618 | usb_fill_int_urb(radio->int_in_urb, radio->usbdev, | ||
619 | usb_rcvintpipe(radio->usbdev, | ||
620 | radio->int_in_endpoint->bEndpointAddress), | ||
621 | radio->int_in_buffer, | ||
622 | le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), | ||
623 | si470x_int_in_callback, | ||
624 | radio, | ||
625 | radio->int_in_endpoint->bInterval); | ||
626 | |||
627 | radio->int_in_running = 1; | ||
628 | mb(); | ||
629 | |||
630 | retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); | ||
631 | if (retval) { | ||
632 | dev_info(&radio->intf->dev, | ||
633 | "submitting int urb failed (%d)\n", retval); | ||
634 | radio->int_in_running = 0; | ||
635 | usb_autopm_put_interface(radio->intf); | ||
636 | } | ||
637 | } | ||
638 | |||
639 | done: | ||
640 | unlock_kernel(); | ||
641 | return retval; | ||
642 | } | ||
643 | |||
644 | |||
645 | /* | ||
646 | * si470x_fops_release - file release | ||
647 | */ | ||
648 | static int si470x_fops_release(struct file *file) | ||
649 | { | ||
650 | struct si470x_device *radio = video_drvdata(file); | ||
651 | int retval = 0; | ||
652 | |||
653 | /* safety check */ | ||
654 | if (!radio) { | ||
655 | retval = -ENODEV; | ||
656 | goto done; | ||
657 | } | ||
658 | |||
659 | mutex_lock(&radio->disconnect_lock); | ||
660 | radio->users--; | ||
661 | if (radio->users == 0) { | ||
662 | /* shutdown interrupt handler */ | ||
663 | if (radio->int_in_running) { | ||
664 | radio->int_in_running = 0; | ||
665 | if (radio->int_in_urb) | ||
666 | usb_kill_urb(radio->int_in_urb); | ||
667 | } | ||
668 | |||
669 | if (radio->disconnected) { | ||
670 | video_unregister_device(radio->videodev); | ||
671 | kfree(radio->int_in_buffer); | ||
672 | kfree(radio->buffer); | ||
673 | kfree(radio); | ||
674 | goto unlock; | ||
675 | } | ||
676 | |||
677 | /* cancel read processes */ | ||
678 | wake_up_interruptible(&radio->read_queue); | ||
679 | |||
680 | /* stop radio */ | ||
681 | retval = si470x_stop(radio); | ||
682 | usb_autopm_put_interface(radio->intf); | ||
683 | } | ||
684 | unlock: | ||
685 | mutex_unlock(&radio->disconnect_lock); | ||
686 | done: | ||
687 | return retval; | ||
688 | } | ||
689 | |||
690 | |||
691 | /* | ||
692 | * si470x_fops - file operations interface | ||
693 | */ | ||
694 | const struct v4l2_file_operations si470x_fops = { | ||
695 | .owner = THIS_MODULE, | ||
696 | .read = si470x_fops_read, | ||
697 | .poll = si470x_fops_poll, | ||
698 | .ioctl = video_ioctl2, | ||
699 | .open = si470x_fops_open, | ||
700 | .release = si470x_fops_release, | ||
701 | }; | ||
702 | |||
703 | |||
704 | |||
705 | /************************************************************************** | ||
706 | * Video4Linux Interface | ||
707 | **************************************************************************/ | ||
708 | |||
709 | /* | ||
710 | * si470x_vidioc_querycap - query device capabilities | ||
711 | */ | ||
712 | int si470x_vidioc_querycap(struct file *file, void *priv, | ||
713 | struct v4l2_capability *capability) | ||
714 | { | ||
715 | struct si470x_device *radio = video_drvdata(file); | ||
716 | |||
717 | strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); | ||
718 | strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); | ||
719 | usb_make_path(radio->usbdev, capability->bus_info, | ||
720 | sizeof(capability->bus_info)); | ||
721 | capability->version = DRIVER_KERNEL_VERSION; | ||
722 | capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | | ||
723 | V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; | ||
724 | |||
725 | return 0; | ||
726 | } | ||
727 | |||
728 | |||
729 | |||
730 | /************************************************************************** | ||
731 | * USB Interface | ||
732 | **************************************************************************/ | ||
733 | |||
734 | /* | ||
735 | * si470x_usb_driver_probe - probe for the device | ||
736 | */ | ||
737 | static int si470x_usb_driver_probe(struct usb_interface *intf, | ||
738 | const struct usb_device_id *id) | ||
739 | { | ||
740 | struct si470x_device *radio; | ||
741 | struct usb_host_interface *iface_desc; | ||
742 | struct usb_endpoint_descriptor *endpoint; | ||
743 | int i, int_end_size, retval = 0; | ||
744 | unsigned char version_warning = 0; | ||
745 | |||
746 | /* private data allocation and initialization */ | ||
747 | radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL); | ||
748 | if (!radio) { | ||
749 | retval = -ENOMEM; | ||
750 | goto err_initial; | ||
751 | } | ||
752 | radio->users = 0; | ||
753 | radio->disconnected = 0; | ||
754 | radio->usbdev = interface_to_usbdev(intf); | ||
755 | radio->intf = intf; | ||
756 | mutex_init(&radio->disconnect_lock); | ||
757 | mutex_init(&radio->lock); | ||
758 | |||
759 | iface_desc = intf->cur_altsetting; | ||
760 | |||
761 | /* Set up interrupt endpoint information. */ | ||
762 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { | ||
763 | endpoint = &iface_desc->endpoint[i].desc; | ||
764 | if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == | ||
765 | USB_DIR_IN) && ((endpoint->bmAttributes & | ||
766 | USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) | ||
767 | radio->int_in_endpoint = endpoint; | ||
768 | } | ||
769 | if (!radio->int_in_endpoint) { | ||
770 | dev_info(&intf->dev, "could not find interrupt in endpoint\n"); | ||
771 | retval = -EIO; | ||
772 | goto err_radio; | ||
773 | } | ||
774 | |||
775 | int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize); | ||
776 | |||
777 | radio->int_in_buffer = kmalloc(int_end_size, GFP_KERNEL); | ||
778 | if (!radio->int_in_buffer) { | ||
779 | dev_info(&intf->dev, "could not allocate int_in_buffer"); | ||
780 | retval = -ENOMEM; | ||
781 | goto err_radio; | ||
782 | } | ||
783 | |||
784 | radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); | ||
785 | if (!radio->int_in_urb) { | ||
786 | dev_info(&intf->dev, "could not allocate int_in_urb"); | ||
787 | retval = -ENOMEM; | ||
788 | goto err_intbuffer; | ||
789 | } | ||
790 | |||
791 | /* video device allocation and initialization */ | ||
792 | radio->videodev = video_device_alloc(); | ||
793 | if (!radio->videodev) { | ||
794 | retval = -ENOMEM; | ||
795 | goto err_intbuffer; | ||
796 | } | ||
797 | memcpy(radio->videodev, &si470x_viddev_template, | ||
798 | sizeof(si470x_viddev_template)); | ||
799 | video_set_drvdata(radio->videodev, radio); | ||
800 | |||
801 | /* get device and chip versions */ | ||
802 | if (si470x_get_all_registers(radio) < 0) { | ||
803 | retval = -EIO; | ||
804 | goto err_video; | ||
805 | } | ||
806 | dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", | ||
807 | radio->registers[DEVICEID], radio->registers[CHIPID]); | ||
808 | if ((radio->registers[CHIPID] & CHIPID_FIRMWARE) < RADIO_FW_VERSION) { | ||
809 | dev_warn(&intf->dev, | ||
810 | "This driver is known to work with " | ||
811 | "firmware version %hu,\n", RADIO_FW_VERSION); | ||
812 | dev_warn(&intf->dev, | ||
813 | "but the device has firmware version %hu.\n", | ||
814 | radio->registers[CHIPID] & CHIPID_FIRMWARE); | ||
815 | version_warning = 1; | ||
816 | } | ||
817 | |||
818 | /* get software and hardware versions */ | ||
819 | if (si470x_get_scratch_page_versions(radio) < 0) { | ||
820 | retval = -EIO; | ||
821 | goto err_video; | ||
822 | } | ||
823 | dev_info(&intf->dev, "software version %d, hardware version %d\n", | ||
824 | radio->software_version, radio->hardware_version); | ||
825 | if (radio->software_version < RADIO_SW_VERSION) { | ||
826 | dev_warn(&intf->dev, | ||
827 | "This driver is known to work with " | ||
828 | "software version %hu,\n", RADIO_SW_VERSION); | ||
829 | dev_warn(&intf->dev, | ||
830 | "but the device has software version %hu.\n", | ||
831 | radio->software_version); | ||
832 | version_warning = 1; | ||
833 | } | ||
834 | if (radio->hardware_version < RADIO_HW_VERSION) { | ||
835 | dev_warn(&intf->dev, | ||
836 | "This driver is known to work with " | ||
837 | "hardware version %hu,\n", RADIO_HW_VERSION); | ||
838 | dev_warn(&intf->dev, | ||
839 | "but the device has hardware version %hu.\n", | ||
840 | radio->hardware_version); | ||
841 | version_warning = 1; | ||
842 | } | ||
843 | |||
844 | /* give out version warning */ | ||
845 | if (version_warning == 1) { | ||
846 | dev_warn(&intf->dev, | ||
847 | "If you have some trouble using this driver,\n"); | ||
848 | dev_warn(&intf->dev, | ||
849 | "please report to V4L ML at " | ||
850 | "linux-media@vger.kernel.org\n"); | ||
851 | } | ||
852 | |||
853 | /* set initial frequency */ | ||
854 | si470x_set_freq(radio, 87.5 * FREQ_MUL); /* available in all regions */ | ||
855 | |||
856 | /* set led to connect state */ | ||
857 | si470x_set_led_state(radio, BLINK_GREEN_LED); | ||
858 | |||
859 | /* rds buffer allocation */ | ||
860 | radio->buf_size = rds_buf * 3; | ||
861 | radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); | ||
862 | if (!radio->buffer) { | ||
863 | retval = -EIO; | ||
864 | goto err_video; | ||
865 | } | ||
866 | |||
867 | /* rds buffer configuration */ | ||
868 | radio->wr_index = 0; | ||
869 | radio->rd_index = 0; | ||
870 | init_waitqueue_head(&radio->read_queue); | ||
871 | |||
872 | /* register video device */ | ||
873 | retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, | ||
874 | radio_nr); | ||
875 | if (retval) { | ||
876 | dev_warn(&intf->dev, "Could not register video device\n"); | ||
877 | goto err_all; | ||
878 | } | ||
879 | usb_set_intfdata(intf, radio); | ||
880 | |||
881 | return 0; | ||
882 | err_all: | ||
883 | kfree(radio->buffer); | ||
884 | err_video: | ||
885 | video_device_release(radio->videodev); | ||
886 | err_intbuffer: | ||
887 | kfree(radio->int_in_buffer); | ||
888 | err_radio: | ||
889 | kfree(radio); | ||
890 | err_initial: | ||
891 | return retval; | ||
892 | } | ||
893 | |||
894 | |||
895 | /* | ||
896 | * si470x_usb_driver_suspend - suspend the device | ||
897 | */ | ||
898 | static int si470x_usb_driver_suspend(struct usb_interface *intf, | ||
899 | pm_message_t message) | ||
900 | { | ||
901 | dev_info(&intf->dev, "suspending now...\n"); | ||
902 | |||
903 | return 0; | ||
904 | } | ||
905 | |||
906 | |||
907 | /* | ||
908 | * si470x_usb_driver_resume - resume the device | ||
909 | */ | ||
910 | static int si470x_usb_driver_resume(struct usb_interface *intf) | ||
911 | { | ||
912 | dev_info(&intf->dev, "resuming now...\n"); | ||
913 | |||
914 | return 0; | ||
915 | } | ||
916 | |||
917 | |||
918 | /* | ||
919 | * si470x_usb_driver_disconnect - disconnect the device | ||
920 | */ | ||
921 | static void si470x_usb_driver_disconnect(struct usb_interface *intf) | ||
922 | { | ||
923 | struct si470x_device *radio = usb_get_intfdata(intf); | ||
924 | |||
925 | mutex_lock(&radio->disconnect_lock); | ||
926 | radio->disconnected = 1; | ||
927 | usb_set_intfdata(intf, NULL); | ||
928 | if (radio->users == 0) { | ||
929 | /* set led to disconnect state */ | ||
930 | si470x_set_led_state(radio, BLINK_ORANGE_LED); | ||
931 | |||
932 | /* Free data structures. */ | ||
933 | usb_free_urb(radio->int_in_urb); | ||
934 | |||
935 | kfree(radio->int_in_buffer); | ||
936 | video_unregister_device(radio->videodev); | ||
937 | kfree(radio->buffer); | ||
938 | kfree(radio); | ||
939 | } | ||
940 | mutex_unlock(&radio->disconnect_lock); | ||
941 | } | ||
942 | |||
943 | |||
944 | /* | ||
945 | * si470x_usb_driver - usb driver interface | ||
946 | */ | ||
947 | static struct usb_driver si470x_usb_driver = { | ||
948 | .name = DRIVER_NAME, | ||
949 | .probe = si470x_usb_driver_probe, | ||
950 | .disconnect = si470x_usb_driver_disconnect, | ||
951 | .suspend = si470x_usb_driver_suspend, | ||
952 | .resume = si470x_usb_driver_resume, | ||
953 | .id_table = si470x_usb_driver_id_table, | ||
954 | .supports_autosuspend = 1, | ||
955 | }; | ||
956 | |||
957 | |||
958 | |||
959 | /************************************************************************** | ||
960 | * Module Interface | ||
961 | **************************************************************************/ | ||
962 | |||
963 | /* | ||
964 | * si470x_module_init - module init | ||
965 | */ | ||
966 | static int __init si470x_module_init(void) | ||
967 | { | ||
968 | printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n"); | ||
969 | return usb_register(&si470x_usb_driver); | ||
970 | } | ||
971 | |||
972 | |||
973 | /* | ||
974 | * si470x_module_exit - module exit | ||
975 | */ | ||
976 | static void __exit si470x_module_exit(void) | ||
977 | { | ||
978 | usb_deregister(&si470x_usb_driver); | ||
979 | } | ||
980 | |||
981 | |||
982 | module_init(si470x_module_init); | ||
983 | module_exit(si470x_module_exit); | ||
984 | |||
985 | MODULE_LICENSE("GPL"); | ||
986 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
987 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
988 | MODULE_VERSION(DRIVER_VERSION); | ||