1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * drivers/media/radio/si470x/radio-si470x-usb.c |
4 | * |
5 | * USB driver for radios with Silicon Labs Si470x FM Radio Receivers |
6 | * |
7 | * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net> |
8 | */ |
9 | |
10 | |
11 | /* |
12 | * ToDo: |
13 | * - add firmware download/update support |
14 | */ |
15 | |
16 | |
17 | /* driver definitions */ |
18 | #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>" |
19 | #define DRIVER_CARD "Silicon Labs Si470x FM Radio" |
20 | #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers" |
21 | #define DRIVER_VERSION "1.0.10" |
22 | |
23 | /* kernel includes */ |
24 | #include <linux/usb.h> |
25 | #include <linux/hid.h> |
26 | #include <linux/slab.h> |
27 | |
28 | #include "radio-si470x.h" |
29 | |
30 | |
31 | /* USB Device ID List */ |
32 | static const struct usb_device_id si470x_usb_driver_id_table[] = { |
33 | /* Silicon Labs USB FM Radio Reference Design */ |
34 | { USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) }, |
35 | /* ADS/Tech FM Radio Receiver (formerly Instant FM Music) */ |
36 | { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) }, |
37 | /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */ |
38 | { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) }, |
39 | /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */ |
40 | { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) }, |
41 | /* Axentia ALERT FM USB Receiver */ |
42 | { USB_DEVICE_AND_INTERFACE_INFO(0x12cf, 0x7111, USB_CLASS_HID, 0, 0) }, |
43 | /* Terminating entry */ |
44 | { } |
45 | }; |
46 | MODULE_DEVICE_TABLE(usb, si470x_usb_driver_id_table); |
47 | |
48 | |
49 | |
50 | /************************************************************************** |
51 | * Module Parameters |
52 | **************************************************************************/ |
53 | |
54 | /* Radio Nr */ |
55 | static int radio_nr = -1; |
56 | module_param(radio_nr, int, 0444); |
57 | MODULE_PARM_DESC(radio_nr, "Radio Nr" ); |
58 | |
59 | /* USB timeout */ |
60 | static unsigned int usb_timeout = 500; |
61 | module_param(usb_timeout, uint, 0644); |
62 | MODULE_PARM_DESC(usb_timeout, "USB timeout (ms): *500*" ); |
63 | |
64 | /* RDS buffer blocks */ |
65 | static unsigned int rds_buf = 100; |
66 | module_param(rds_buf, uint, 0444); |
67 | MODULE_PARM_DESC(rds_buf, "RDS buffer entries: *100*" ); |
68 | |
69 | /* RDS maximum block errors */ |
70 | static unsigned short max_rds_errors = 1; |
71 | /* 0 means 0 errors requiring correction */ |
72 | /* 1 means 1-2 errors requiring correction (used by original USBRadio.exe) */ |
73 | /* 2 means 3-5 errors requiring correction */ |
74 | /* 3 means 6+ errors or errors in checkword, correction not possible */ |
75 | module_param(max_rds_errors, ushort, 0644); |
76 | MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*" ); |
77 | |
78 | |
79 | |
80 | /************************************************************************** |
81 | * USB HID Reports |
82 | **************************************************************************/ |
83 | |
84 | /* Reports 1-16 give direct read/write access to the 16 Si470x registers */ |
85 | /* with the (REPORT_ID - 1) corresponding to the register address across USB */ |
86 | /* endpoint 0 using GET_REPORT and SET_REPORT */ |
87 | #define REGISTER_REPORT_SIZE (RADIO_REGISTER_SIZE + 1) |
88 | #define REGISTER_REPORT(reg) ((reg) + 1) |
89 | |
90 | /* Report 17 gives direct read/write access to the entire Si470x register */ |
91 | /* map across endpoint 0 using GET_REPORT and SET_REPORT */ |
92 | #define ENTIRE_REPORT_SIZE (RADIO_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) |
93 | #define ENTIRE_REPORT 17 |
94 | |
95 | /* Report 18 is used to send the lowest 6 Si470x registers up the HID */ |
96 | /* interrupt endpoint 1 to Windows every 20 milliseconds for status */ |
97 | #define RDS_REPORT_SIZE (RDS_REGISTER_NUM * RADIO_REGISTER_SIZE + 1) |
98 | #define RDS_REPORT 18 |
99 | |
100 | /* Report 19: LED state */ |
101 | #define LED_REPORT_SIZE 3 |
102 | #define LED_REPORT 19 |
103 | |
104 | /* Report 19: stream */ |
105 | #define STREAM_REPORT_SIZE 3 |
106 | #define STREAM_REPORT 19 |
107 | |
108 | /* Report 20: scratch */ |
109 | #define SCRATCH_PAGE_SIZE 63 |
110 | #define SCRATCH_REPORT_SIZE (SCRATCH_PAGE_SIZE + 1) |
111 | #define SCRATCH_REPORT 20 |
112 | |
113 | /* Reports 19-22: flash upgrade of the C8051F321 */ |
114 | #define WRITE_REPORT_SIZE 4 |
115 | #define WRITE_REPORT 19 |
116 | #define FLASH_REPORT_SIZE 64 |
117 | #define FLASH_REPORT 20 |
118 | #define CRC_REPORT_SIZE 3 |
119 | #define CRC_REPORT 21 |
120 | #define RESPONSE_REPORT_SIZE 2 |
121 | #define RESPONSE_REPORT 22 |
122 | |
123 | /* Report 23: currently unused, but can accept 60 byte reports on the HID */ |
124 | /* interrupt out endpoint 2 every 1 millisecond */ |
125 | #define UNUSED_REPORT 23 |
126 | |
127 | #define MAX_REPORT_SIZE 64 |
128 | |
129 | |
130 | |
131 | /************************************************************************** |
132 | * Software/Hardware Versions from Scratch Page |
133 | **************************************************************************/ |
134 | #define RADIO_HW_VERSION 1 |
135 | |
136 | |
137 | |
138 | /************************************************************************** |
139 | * LED State Definitions |
140 | **************************************************************************/ |
141 | #define LED_COMMAND 0x35 |
142 | |
143 | #define NO_CHANGE_LED 0x00 |
144 | #define ALL_COLOR_LED 0x01 /* streaming state */ |
145 | #define BLINK_GREEN_LED 0x02 /* connect state */ |
146 | #define BLINK_RED_LED 0x04 |
147 | #define BLINK_ORANGE_LED 0x10 /* disconnect state */ |
148 | #define SOLID_GREEN_LED 0x20 /* tuning/seeking state */ |
149 | #define SOLID_RED_LED 0x40 /* bootload state */ |
150 | #define SOLID_ORANGE_LED 0x80 |
151 | |
152 | |
153 | |
154 | /************************************************************************** |
155 | * Stream State Definitions |
156 | **************************************************************************/ |
157 | #define STREAM_COMMAND 0x36 |
158 | #define STREAM_VIDPID 0x00 |
159 | #define STREAM_AUDIO 0xff |
160 | |
161 | |
162 | |
163 | /************************************************************************** |
164 | * Bootloader / Flash Commands |
165 | **************************************************************************/ |
166 | |
167 | /* unique id sent to bootloader and required to put into a bootload state */ |
168 | #define UNIQUE_BL_ID 0x34 |
169 | |
170 | /* mask for the flash data */ |
171 | #define FLASH_DATA_MASK 0x55 |
172 | |
173 | /* bootloader commands */ |
174 | #define GET_SW_VERSION_COMMAND 0x00 |
175 | #define SET_PAGE_COMMAND 0x01 |
176 | #define ERASE_PAGE_COMMAND 0x02 |
177 | #define WRITE_PAGE_COMMAND 0x03 |
178 | #define CRC_ON_PAGE_COMMAND 0x04 |
179 | #define READ_FLASH_BYTE_COMMAND 0x05 |
180 | #define RESET_DEVICE_COMMAND 0x06 |
181 | #define GET_HW_VERSION_COMMAND 0x07 |
182 | #define BLANK 0xff |
183 | |
184 | /* bootloader command responses */ |
185 | #define COMMAND_OK 0x01 |
186 | #define COMMAND_FAILED 0x02 |
187 | #define COMMAND_PENDING 0x03 |
188 | |
189 | |
190 | |
191 | /************************************************************************** |
192 | * General Driver Functions - REGISTER_REPORTs |
193 | **************************************************************************/ |
194 | |
195 | /* |
196 | * si470x_get_report - receive a HID report |
197 | */ |
198 | static int si470x_get_report(struct si470x_device *radio, void *buf, int size) |
199 | { |
200 | unsigned char *report = buf; |
201 | int retval; |
202 | |
203 | retval = usb_control_msg(dev: radio->usbdev, |
204 | usb_rcvctrlpipe(radio->usbdev, 0), |
205 | request: HID_REQ_GET_REPORT, |
206 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, |
207 | value: report[0], index: 2, |
208 | data: buf, size, timeout: usb_timeout); |
209 | |
210 | if (retval < 0) |
211 | dev_warn(&radio->intf->dev, |
212 | "si470x_get_report: usb_control_msg returned %d\n" , |
213 | retval); |
214 | return retval; |
215 | } |
216 | |
217 | |
218 | /* |
219 | * si470x_set_report - send a HID report |
220 | */ |
221 | static int si470x_set_report(struct si470x_device *radio, void *buf, int size) |
222 | { |
223 | unsigned char *report = buf; |
224 | int retval; |
225 | |
226 | retval = usb_control_msg(dev: radio->usbdev, |
227 | usb_sndctrlpipe(radio->usbdev, 0), |
228 | request: HID_REQ_SET_REPORT, |
229 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, |
230 | value: report[0], index: 2, |
231 | data: buf, size, timeout: usb_timeout); |
232 | |
233 | if (retval < 0) |
234 | dev_warn(&radio->intf->dev, |
235 | "si470x_set_report: usb_control_msg returned %d\n" , |
236 | retval); |
237 | return retval; |
238 | } |
239 | |
240 | |
241 | /* |
242 | * si470x_get_register - read register |
243 | */ |
244 | static int si470x_get_register(struct si470x_device *radio, int regnr) |
245 | { |
246 | int retval; |
247 | |
248 | radio->usb_buf[0] = REGISTER_REPORT(regnr); |
249 | |
250 | retval = si470x_get_report(radio, buf: radio->usb_buf, REGISTER_REPORT_SIZE); |
251 | |
252 | if (retval >= 0) |
253 | radio->registers[regnr] = get_unaligned_be16(p: &radio->usb_buf[1]); |
254 | |
255 | return (retval < 0) ? -EINVAL : 0; |
256 | } |
257 | |
258 | |
259 | /* |
260 | * si470x_set_register - write register |
261 | */ |
262 | static int si470x_set_register(struct si470x_device *radio, int regnr) |
263 | { |
264 | int retval; |
265 | |
266 | radio->usb_buf[0] = REGISTER_REPORT(regnr); |
267 | put_unaligned_be16(val: radio->registers[regnr], p: &radio->usb_buf[1]); |
268 | |
269 | retval = si470x_set_report(radio, buf: radio->usb_buf, REGISTER_REPORT_SIZE); |
270 | |
271 | return (retval < 0) ? -EINVAL : 0; |
272 | } |
273 | |
274 | |
275 | |
276 | /************************************************************************** |
277 | * General Driver Functions - ENTIRE_REPORT |
278 | **************************************************************************/ |
279 | |
280 | /* |
281 | * si470x_get_all_registers - read entire registers |
282 | */ |
283 | static int si470x_get_all_registers(struct si470x_device *radio) |
284 | { |
285 | int retval; |
286 | unsigned char regnr; |
287 | |
288 | radio->usb_buf[0] = ENTIRE_REPORT; |
289 | |
290 | retval = si470x_get_report(radio, buf: radio->usb_buf, ENTIRE_REPORT_SIZE); |
291 | |
292 | if (retval >= 0) |
293 | for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) |
294 | radio->registers[regnr] = get_unaligned_be16( |
295 | p: &radio->usb_buf[regnr * RADIO_REGISTER_SIZE + 1]); |
296 | |
297 | return (retval < 0) ? -EINVAL : 0; |
298 | } |
299 | |
300 | |
301 | |
302 | /************************************************************************** |
303 | * General Driver Functions - LED_REPORT |
304 | **************************************************************************/ |
305 | |
306 | /* |
307 | * si470x_set_led_state - sets the led state |
308 | */ |
309 | static int si470x_set_led_state(struct si470x_device *radio, |
310 | unsigned char led_state) |
311 | { |
312 | int retval; |
313 | |
314 | radio->usb_buf[0] = LED_REPORT; |
315 | radio->usb_buf[1] = LED_COMMAND; |
316 | radio->usb_buf[2] = led_state; |
317 | |
318 | retval = si470x_set_report(radio, buf: radio->usb_buf, LED_REPORT_SIZE); |
319 | |
320 | return (retval < 0) ? -EINVAL : 0; |
321 | } |
322 | |
323 | |
324 | |
325 | /************************************************************************** |
326 | * General Driver Functions - SCRATCH_REPORT |
327 | **************************************************************************/ |
328 | |
329 | /* |
330 | * si470x_get_scratch_versions - gets the scratch page and version infos |
331 | */ |
332 | static int si470x_get_scratch_page_versions(struct si470x_device *radio) |
333 | { |
334 | int retval; |
335 | |
336 | radio->usb_buf[0] = SCRATCH_REPORT; |
337 | |
338 | retval = si470x_get_report(radio, buf: radio->usb_buf, SCRATCH_REPORT_SIZE); |
339 | |
340 | if (retval < 0) |
341 | dev_warn(&radio->intf->dev, "si470x_get_scratch: si470x_get_report returned %d\n" , |
342 | retval); |
343 | else { |
344 | radio->software_version = radio->usb_buf[1]; |
345 | radio->hardware_version = radio->usb_buf[2]; |
346 | } |
347 | |
348 | return (retval < 0) ? -EINVAL : 0; |
349 | } |
350 | |
351 | |
352 | |
353 | /************************************************************************** |
354 | * RDS Driver Functions |
355 | **************************************************************************/ |
356 | |
357 | /* |
358 | * si470x_int_in_callback - rds callback and processing function |
359 | * |
360 | * TODO: do we need to use mutex locks in some sections? |
361 | */ |
362 | static void si470x_int_in_callback(struct urb *urb) |
363 | { |
364 | struct si470x_device *radio = urb->context; |
365 | int retval; |
366 | unsigned char regnr; |
367 | unsigned char blocknum; |
368 | unsigned short bler; /* rds block errors */ |
369 | unsigned short rds; |
370 | unsigned char tmpbuf[3]; |
371 | |
372 | if (urb->status) { |
373 | if (urb->status == -ENOENT || |
374 | urb->status == -ECONNRESET || |
375 | urb->status == -ESHUTDOWN) { |
376 | return; |
377 | } else { |
378 | dev_warn(&radio->intf->dev, |
379 | "non-zero urb status (%d)\n" , urb->status); |
380 | goto resubmit; /* Maybe we can recover. */ |
381 | } |
382 | } |
383 | |
384 | /* Sometimes the device returns len 0 packets */ |
385 | if (urb->actual_length != RDS_REPORT_SIZE) |
386 | goto resubmit; |
387 | |
388 | radio->registers[STATUSRSSI] = |
389 | get_unaligned_be16(p: &radio->int_in_buffer[1]); |
390 | |
391 | if (radio->registers[STATUSRSSI] & STATUSRSSI_STC) |
392 | complete(&radio->completion); |
393 | |
394 | if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS)) { |
395 | /* Update RDS registers with URB data */ |
396 | for (regnr = 1; regnr < RDS_REGISTER_NUM; regnr++) |
397 | radio->registers[STATUSRSSI + regnr] = |
398 | get_unaligned_be16(p: &radio->int_in_buffer[ |
399 | regnr * RADIO_REGISTER_SIZE + 1]); |
400 | /* get rds blocks */ |
401 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSR) == 0) { |
402 | /* No RDS group ready, better luck next time */ |
403 | goto resubmit; |
404 | } |
405 | if ((radio->registers[STATUSRSSI] & STATUSRSSI_RDSS) == 0) { |
406 | /* RDS decoder not synchronized */ |
407 | goto resubmit; |
408 | } |
409 | for (blocknum = 0; blocknum < 4; blocknum++) { |
410 | switch (blocknum) { |
411 | default: |
412 | bler = (radio->registers[STATUSRSSI] & |
413 | STATUSRSSI_BLERA) >> 9; |
414 | rds = radio->registers[RDSA]; |
415 | break; |
416 | case 1: |
417 | bler = (radio->registers[READCHAN] & |
418 | READCHAN_BLERB) >> 14; |
419 | rds = radio->registers[RDSB]; |
420 | break; |
421 | case 2: |
422 | bler = (radio->registers[READCHAN] & |
423 | READCHAN_BLERC) >> 12; |
424 | rds = radio->registers[RDSC]; |
425 | break; |
426 | case 3: |
427 | bler = (radio->registers[READCHAN] & |
428 | READCHAN_BLERD) >> 10; |
429 | rds = radio->registers[RDSD]; |
430 | break; |
431 | } |
432 | |
433 | /* Fill the V4L2 RDS buffer */ |
434 | put_unaligned_le16(val: rds, p: &tmpbuf); |
435 | tmpbuf[2] = blocknum; /* offset name */ |
436 | tmpbuf[2] |= blocknum << 3; /* received offset */ |
437 | if (bler > max_rds_errors) |
438 | tmpbuf[2] |= 0x80; /* uncorrectable errors */ |
439 | else if (bler > 0) |
440 | tmpbuf[2] |= 0x40; /* corrected error(s) */ |
441 | |
442 | /* copy RDS block to internal buffer */ |
443 | memcpy(&radio->buffer[radio->wr_index], &tmpbuf, 3); |
444 | radio->wr_index += 3; |
445 | |
446 | /* wrap write pointer */ |
447 | if (radio->wr_index >= radio->buf_size) |
448 | radio->wr_index = 0; |
449 | |
450 | /* check for overflow */ |
451 | if (radio->wr_index == radio->rd_index) { |
452 | /* increment and wrap read pointer */ |
453 | radio->rd_index += 3; |
454 | if (radio->rd_index >= radio->buf_size) |
455 | radio->rd_index = 0; |
456 | } |
457 | } |
458 | if (radio->wr_index != radio->rd_index) |
459 | wake_up_interruptible(&radio->read_queue); |
460 | } |
461 | |
462 | resubmit: |
463 | /* Resubmit if we're still running. */ |
464 | if (radio->int_in_running && radio->usbdev) { |
465 | retval = usb_submit_urb(urb: radio->int_in_urb, GFP_ATOMIC); |
466 | if (retval) { |
467 | dev_warn(&radio->intf->dev, |
468 | "resubmitting urb failed (%d)" , retval); |
469 | radio->int_in_running = 0; |
470 | } |
471 | } |
472 | radio->status_rssi_auto_update = radio->int_in_running; |
473 | } |
474 | |
475 | |
476 | static int si470x_fops_open(struct file *file) |
477 | { |
478 | return v4l2_fh_open(filp: file); |
479 | } |
480 | |
481 | static int si470x_fops_release(struct file *file) |
482 | { |
483 | return v4l2_fh_release(filp: file); |
484 | } |
485 | |
486 | static void si470x_usb_release(struct v4l2_device *v4l2_dev) |
487 | { |
488 | struct si470x_device *radio = |
489 | container_of(v4l2_dev, struct si470x_device, v4l2_dev); |
490 | |
491 | usb_free_urb(urb: radio->int_in_urb); |
492 | v4l2_ctrl_handler_free(hdl: &radio->hdl); |
493 | v4l2_device_unregister(v4l2_dev: &radio->v4l2_dev); |
494 | kfree(objp: radio->int_in_buffer); |
495 | kfree(objp: radio->buffer); |
496 | kfree(objp: radio->usb_buf); |
497 | kfree(objp: radio); |
498 | } |
499 | |
500 | |
501 | /************************************************************************** |
502 | * Video4Linux Interface |
503 | **************************************************************************/ |
504 | |
505 | /* |
506 | * si470x_vidioc_querycap - query device capabilities |
507 | */ |
508 | static int si470x_vidioc_querycap(struct file *file, void *priv, |
509 | struct v4l2_capability *capability) |
510 | { |
511 | struct si470x_device *radio = video_drvdata(file); |
512 | |
513 | strscpy(capability->driver, DRIVER_NAME, sizeof(capability->driver)); |
514 | strscpy(capability->card, DRIVER_CARD, sizeof(capability->card)); |
515 | usb_make_path(dev: radio->usbdev, buf: capability->bus_info, |
516 | size: sizeof(capability->bus_info)); |
517 | return 0; |
518 | } |
519 | |
520 | |
521 | static int si470x_start_usb(struct si470x_device *radio) |
522 | { |
523 | int retval; |
524 | |
525 | /* initialize interrupt urb */ |
526 | usb_fill_int_urb(urb: radio->int_in_urb, dev: radio->usbdev, |
527 | usb_rcvintpipe(radio->usbdev, |
528 | radio->int_in_endpoint->bEndpointAddress), |
529 | transfer_buffer: radio->int_in_buffer, |
530 | le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), |
531 | complete_fn: si470x_int_in_callback, |
532 | context: radio, |
533 | interval: radio->int_in_endpoint->bInterval); |
534 | |
535 | radio->int_in_running = 1; |
536 | mb(); |
537 | |
538 | retval = usb_submit_urb(urb: radio->int_in_urb, GFP_KERNEL); |
539 | if (retval) { |
540 | dev_info(&radio->intf->dev, |
541 | "submitting int urb failed (%d)\n" , retval); |
542 | radio->int_in_running = 0; |
543 | } |
544 | radio->status_rssi_auto_update = radio->int_in_running; |
545 | |
546 | /* start radio */ |
547 | retval = si470x_start(radio); |
548 | if (retval < 0) |
549 | return retval; |
550 | |
551 | v4l2_ctrl_handler_setup(hdl: &radio->hdl); |
552 | |
553 | return retval; |
554 | } |
555 | |
556 | /************************************************************************** |
557 | * USB Interface |
558 | **************************************************************************/ |
559 | |
560 | /* |
561 | * si470x_usb_driver_probe - probe for the device |
562 | */ |
563 | static int si470x_usb_driver_probe(struct usb_interface *intf, |
564 | const struct usb_device_id *id) |
565 | { |
566 | struct si470x_device *radio; |
567 | struct usb_host_interface *iface_desc; |
568 | struct usb_endpoint_descriptor *endpoint; |
569 | int i, int_end_size, retval; |
570 | unsigned char version_warning = 0; |
571 | |
572 | /* private data allocation and initialization */ |
573 | radio = kzalloc(size: sizeof(struct si470x_device), GFP_KERNEL); |
574 | if (!radio) { |
575 | retval = -ENOMEM; |
576 | goto err_initial; |
577 | } |
578 | radio->usb_buf = kmalloc(MAX_REPORT_SIZE, GFP_KERNEL); |
579 | if (radio->usb_buf == NULL) { |
580 | retval = -ENOMEM; |
581 | goto err_radio; |
582 | } |
583 | radio->usbdev = interface_to_usbdev(intf); |
584 | radio->intf = intf; |
585 | radio->band = 1; /* Default to 76 - 108 MHz */ |
586 | mutex_init(&radio->lock); |
587 | init_completion(x: &radio->completion); |
588 | |
589 | radio->get_register = si470x_get_register; |
590 | radio->set_register = si470x_set_register; |
591 | radio->fops_open = si470x_fops_open; |
592 | radio->fops_release = si470x_fops_release; |
593 | radio->vidioc_querycap = si470x_vidioc_querycap; |
594 | |
595 | iface_desc = intf->cur_altsetting; |
596 | |
597 | /* Set up interrupt endpoint information. */ |
598 | for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { |
599 | endpoint = &iface_desc->endpoint[i].desc; |
600 | if (usb_endpoint_is_int_in(epd: endpoint)) |
601 | radio->int_in_endpoint = endpoint; |
602 | } |
603 | if (!radio->int_in_endpoint) { |
604 | dev_info(&intf->dev, "could not find interrupt in endpoint\n" ); |
605 | retval = -EIO; |
606 | goto err_usbbuf; |
607 | } |
608 | |
609 | int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize); |
610 | |
611 | radio->int_in_buffer = kmalloc(size: int_end_size, GFP_KERNEL); |
612 | if (!radio->int_in_buffer) { |
613 | dev_info(&intf->dev, "could not allocate int_in_buffer" ); |
614 | retval = -ENOMEM; |
615 | goto err_usbbuf; |
616 | } |
617 | |
618 | radio->int_in_urb = usb_alloc_urb(iso_packets: 0, GFP_KERNEL); |
619 | if (!radio->int_in_urb) { |
620 | retval = -ENOMEM; |
621 | goto err_intbuffer; |
622 | } |
623 | |
624 | radio->v4l2_dev.release = si470x_usb_release; |
625 | |
626 | /* |
627 | * The si470x SiLabs reference design uses the same USB IDs as |
628 | * 'Thanko's Raremono' si4734 based receiver. So check here which we |
629 | * have: attempt to read the device ID from the si470x: the lower 12 |
630 | * bits should be 0x0242 for the si470x. |
631 | * |
632 | * We use this check to determine which device we are dealing with. |
633 | */ |
634 | if (id->idVendor == 0x10c4 && id->idProduct == 0x818a) { |
635 | retval = usb_control_msg(dev: radio->usbdev, |
636 | usb_rcvctrlpipe(radio->usbdev, 0), |
637 | request: HID_REQ_GET_REPORT, |
638 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, |
639 | value: 1, index: 2, |
640 | data: radio->usb_buf, size: 3, timeout: 500); |
641 | if (retval != 3 || |
642 | (get_unaligned_be16(p: &radio->usb_buf[1]) & 0xfff) != 0x0242) { |
643 | dev_info(&intf->dev, "this is not a si470x device.\n" ); |
644 | retval = -ENODEV; |
645 | goto err_urb; |
646 | } |
647 | } |
648 | |
649 | retval = v4l2_device_register(dev: &intf->dev, v4l2_dev: &radio->v4l2_dev); |
650 | if (retval < 0) { |
651 | dev_err(&intf->dev, "couldn't register v4l2_device\n" ); |
652 | goto err_urb; |
653 | } |
654 | |
655 | v4l2_ctrl_handler_init(&radio->hdl, 2); |
656 | v4l2_ctrl_new_std(hdl: &radio->hdl, ops: &si470x_ctrl_ops, |
657 | V4L2_CID_AUDIO_MUTE, min: 0, max: 1, step: 1, def: 1); |
658 | v4l2_ctrl_new_std(hdl: &radio->hdl, ops: &si470x_ctrl_ops, |
659 | V4L2_CID_AUDIO_VOLUME, min: 0, max: 15, step: 1, def: 15); |
660 | if (radio->hdl.error) { |
661 | retval = radio->hdl.error; |
662 | dev_err(&intf->dev, "couldn't register control\n" ); |
663 | goto err_dev; |
664 | } |
665 | radio->videodev = si470x_viddev_template; |
666 | radio->videodev.ctrl_handler = &radio->hdl; |
667 | radio->videodev.lock = &radio->lock; |
668 | radio->videodev.v4l2_dev = &radio->v4l2_dev; |
669 | radio->videodev.release = video_device_release_empty; |
670 | radio->videodev.device_caps = |
671 | V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE | V4L2_CAP_TUNER | |
672 | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; |
673 | video_set_drvdata(vdev: &radio->videodev, data: radio); |
674 | |
675 | /* get device and chip versions */ |
676 | if (si470x_get_all_registers(radio) < 0) { |
677 | retval = -EIO; |
678 | goto err_ctrl; |
679 | } |
680 | dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n" , |
681 | radio->registers[DEVICEID], radio->registers[SI_CHIPID]); |
682 | if ((radio->registers[SI_CHIPID] & SI_CHIPID_FIRMWARE) < RADIO_FW_VERSION) { |
683 | dev_warn(&intf->dev, |
684 | "This driver is known to work with firmware version %u, but the device has firmware version %u.\n" , |
685 | RADIO_FW_VERSION, |
686 | radio->registers[SI_CHIPID] & SI_CHIPID_FIRMWARE); |
687 | version_warning = 1; |
688 | } |
689 | |
690 | /* get software and hardware versions */ |
691 | if (si470x_get_scratch_page_versions(radio) < 0) { |
692 | retval = -EIO; |
693 | goto err_ctrl; |
694 | } |
695 | dev_info(&intf->dev, "software version %d, hardware version %d\n" , |
696 | radio->software_version, radio->hardware_version); |
697 | if (radio->hardware_version < RADIO_HW_VERSION) { |
698 | dev_warn(&intf->dev, |
699 | "This driver is known to work with hardware version %u, but the device has hardware version %u.\n" , |
700 | RADIO_HW_VERSION, |
701 | radio->hardware_version); |
702 | version_warning = 1; |
703 | } |
704 | |
705 | /* give out version warning */ |
706 | if (version_warning == 1) { |
707 | dev_warn(&intf->dev, |
708 | "If you have some trouble using this driver, please report to V4L ML at linux-media@vger.kernel.org\n" ); |
709 | } |
710 | |
711 | /* set led to connect state */ |
712 | si470x_set_led_state(radio, BLINK_GREEN_LED); |
713 | |
714 | /* rds buffer allocation */ |
715 | radio->buf_size = rds_buf * 3; |
716 | radio->buffer = kmalloc(size: radio->buf_size, GFP_KERNEL); |
717 | if (!radio->buffer) { |
718 | retval = -EIO; |
719 | goto err_ctrl; |
720 | } |
721 | |
722 | /* rds buffer configuration */ |
723 | radio->wr_index = 0; |
724 | radio->rd_index = 0; |
725 | init_waitqueue_head(&radio->read_queue); |
726 | usb_set_intfdata(intf, data: radio); |
727 | |
728 | /* start radio */ |
729 | retval = si470x_start_usb(radio); |
730 | if (retval < 0 && !radio->int_in_running) |
731 | goto err_buf; |
732 | else if (retval < 0) /* in case of radio->int_in_running == 1 */ |
733 | goto err_all; |
734 | |
735 | /* set initial frequency */ |
736 | si470x_set_freq(radio, freq: 87.5 * FREQ_MUL); /* available in all regions */ |
737 | |
738 | /* register video device */ |
739 | retval = video_register_device(vdev: &radio->videodev, type: VFL_TYPE_RADIO, |
740 | nr: radio_nr); |
741 | if (retval) { |
742 | dev_err(&intf->dev, "Could not register video device\n" ); |
743 | goto err_all; |
744 | } |
745 | |
746 | return 0; |
747 | err_all: |
748 | usb_kill_urb(urb: radio->int_in_urb); |
749 | err_buf: |
750 | kfree(objp: radio->buffer); |
751 | err_ctrl: |
752 | v4l2_ctrl_handler_free(hdl: &radio->hdl); |
753 | err_dev: |
754 | v4l2_device_unregister(v4l2_dev: &radio->v4l2_dev); |
755 | err_urb: |
756 | usb_free_urb(urb: radio->int_in_urb); |
757 | err_intbuffer: |
758 | kfree(objp: radio->int_in_buffer); |
759 | err_usbbuf: |
760 | kfree(objp: radio->usb_buf); |
761 | err_radio: |
762 | kfree(objp: radio); |
763 | err_initial: |
764 | return retval; |
765 | } |
766 | |
767 | |
768 | /* |
769 | * si470x_usb_driver_suspend - suspend the device |
770 | */ |
771 | static int si470x_usb_driver_suspend(struct usb_interface *intf, |
772 | pm_message_t message) |
773 | { |
774 | struct si470x_device *radio = usb_get_intfdata(intf); |
775 | |
776 | dev_info(&intf->dev, "suspending now...\n" ); |
777 | |
778 | /* shutdown interrupt handler */ |
779 | if (radio->int_in_running) { |
780 | radio->int_in_running = 0; |
781 | if (radio->int_in_urb) |
782 | usb_kill_urb(urb: radio->int_in_urb); |
783 | } |
784 | |
785 | /* cancel read processes */ |
786 | wake_up_interruptible(&radio->read_queue); |
787 | |
788 | /* stop radio */ |
789 | si470x_stop(radio); |
790 | return 0; |
791 | } |
792 | |
793 | |
794 | /* |
795 | * si470x_usb_driver_resume - resume the device |
796 | */ |
797 | static int si470x_usb_driver_resume(struct usb_interface *intf) |
798 | { |
799 | struct si470x_device *radio = usb_get_intfdata(intf); |
800 | int ret; |
801 | |
802 | dev_info(&intf->dev, "resuming now...\n" ); |
803 | |
804 | /* start radio */ |
805 | ret = si470x_start_usb(radio); |
806 | if (ret == 0) |
807 | v4l2_ctrl_handler_setup(hdl: &radio->hdl); |
808 | |
809 | return ret; |
810 | } |
811 | |
812 | |
813 | /* |
814 | * si470x_usb_driver_disconnect - disconnect the device |
815 | */ |
816 | static void si470x_usb_driver_disconnect(struct usb_interface *intf) |
817 | { |
818 | struct si470x_device *radio = usb_get_intfdata(intf); |
819 | |
820 | mutex_lock(&radio->lock); |
821 | v4l2_device_disconnect(v4l2_dev: &radio->v4l2_dev); |
822 | video_unregister_device(vdev: &radio->videodev); |
823 | usb_kill_urb(urb: radio->int_in_urb); |
824 | usb_set_intfdata(intf, NULL); |
825 | mutex_unlock(lock: &radio->lock); |
826 | v4l2_device_put(v4l2_dev: &radio->v4l2_dev); |
827 | } |
828 | |
829 | |
830 | /* |
831 | * si470x_usb_driver - usb driver interface |
832 | * |
833 | * A note on suspend/resume: this driver had only empty suspend/resume |
834 | * functions, and when I tried to test suspend/resume it always disconnected |
835 | * instead of resuming (using my ADS InstantFM stick). So I've decided to |
836 | * remove these callbacks until someone else with better hardware can |
837 | * implement and test this. |
838 | */ |
839 | static struct usb_driver si470x_usb_driver = { |
840 | .name = DRIVER_NAME, |
841 | .probe = si470x_usb_driver_probe, |
842 | .disconnect = si470x_usb_driver_disconnect, |
843 | .suspend = si470x_usb_driver_suspend, |
844 | .resume = si470x_usb_driver_resume, |
845 | .reset_resume = si470x_usb_driver_resume, |
846 | .id_table = si470x_usb_driver_id_table, |
847 | }; |
848 | |
849 | module_usb_driver(si470x_usb_driver); |
850 | |
851 | MODULE_LICENSE("GPL" ); |
852 | MODULE_AUTHOR(DRIVER_AUTHOR); |
853 | MODULE_DESCRIPTION(DRIVER_DESC); |
854 | MODULE_VERSION(DRIVER_VERSION); |
855 | |