diff options
Diffstat (limited to 'drivers/media/radio/si470x/radio-si470x-usb.c')
-rw-r--r-- | drivers/media/radio/si470x/radio-si470x-usb.c | 81 |
1 files changed, 55 insertions, 26 deletions
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index d6d4d60261d5..07ef40595efd 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c | |||
@@ -137,6 +137,8 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); | |||
137 | /* interrupt out endpoint 2 every 1 millisecond */ | 137 | /* interrupt out endpoint 2 every 1 millisecond */ |
138 | #define UNUSED_REPORT 23 | 138 | #define UNUSED_REPORT 23 |
139 | 139 | ||
140 | #define MAX_REPORT_SIZE 64 | ||
141 | |||
140 | 142 | ||
141 | 143 | ||
142 | /************************************************************************** | 144 | /************************************************************************** |
@@ -208,7 +210,7 @@ MODULE_PARM_DESC(max_rds_errors, "RDS maximum block errors: *1*"); | |||
208 | */ | 210 | */ |
209 | static int si470x_get_report(struct si470x_device *radio, void *buf, int size) | 211 | static int si470x_get_report(struct si470x_device *radio, void *buf, int size) |
210 | { | 212 | { |
211 | unsigned char *report = (unsigned char *) buf; | 213 | unsigned char *report = buf; |
212 | int retval; | 214 | int retval; |
213 | 215 | ||
214 | retval = usb_control_msg(radio->usbdev, | 216 | retval = usb_control_msg(radio->usbdev, |
@@ -231,7 +233,7 @@ static int si470x_get_report(struct si470x_device *radio, void *buf, int size) | |||
231 | */ | 233 | */ |
232 | static int si470x_set_report(struct si470x_device *radio, void *buf, int size) | 234 | static int si470x_set_report(struct si470x_device *radio, void *buf, int size) |
233 | { | 235 | { |
234 | unsigned char *report = (unsigned char *) buf; | 236 | unsigned char *report = buf; |
235 | int retval; | 237 | int retval; |
236 | 238 | ||
237 | retval = usb_control_msg(radio->usbdev, | 239 | retval = usb_control_msg(radio->usbdev, |
@@ -254,15 +256,14 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size) | |||
254 | */ | 256 | */ |
255 | int si470x_get_register(struct si470x_device *radio, int regnr) | 257 | int si470x_get_register(struct si470x_device *radio, int regnr) |
256 | { | 258 | { |
257 | unsigned char buf[REGISTER_REPORT_SIZE]; | ||
258 | int retval; | 259 | int retval; |
259 | 260 | ||
260 | buf[0] = REGISTER_REPORT(regnr); | 261 | radio->usb_buf[0] = REGISTER_REPORT(regnr); |
261 | 262 | ||
262 | retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); | 263 | retval = si470x_get_report(radio, radio->usb_buf, REGISTER_REPORT_SIZE); |
263 | 264 | ||
264 | if (retval >= 0) | 265 | if (retval >= 0) |
265 | radio->registers[regnr] = get_unaligned_be16(&buf[1]); | 266 | radio->registers[regnr] = get_unaligned_be16(&radio->usb_buf[1]); |
266 | 267 | ||
267 | return (retval < 0) ? -EINVAL : 0; | 268 | return (retval < 0) ? -EINVAL : 0; |
268 | } | 269 | } |
@@ -273,13 +274,12 @@ int si470x_get_register(struct si470x_device *radio, int regnr) | |||
273 | */ | 274 | */ |
274 | int si470x_set_register(struct si470x_device *radio, int regnr) | 275 | int si470x_set_register(struct si470x_device *radio, int regnr) |
275 | { | 276 | { |
276 | unsigned char buf[REGISTER_REPORT_SIZE]; | ||
277 | int retval; | 277 | int retval; |
278 | 278 | ||
279 | buf[0] = REGISTER_REPORT(regnr); | 279 | radio->usb_buf[0] = REGISTER_REPORT(regnr); |
280 | put_unaligned_be16(radio->registers[regnr], &buf[1]); | 280 | put_unaligned_be16(radio->registers[regnr], &radio->usb_buf[1]); |
281 | 281 | ||
282 | retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); | 282 | retval = si470x_set_report(radio, radio->usb_buf, REGISTER_REPORT_SIZE); |
283 | 283 | ||
284 | return (retval < 0) ? -EINVAL : 0; | 284 | return (retval < 0) ? -EINVAL : 0; |
285 | } | 285 | } |
@@ -295,18 +295,17 @@ int si470x_set_register(struct si470x_device *radio, int regnr) | |||
295 | */ | 295 | */ |
296 | static int si470x_get_all_registers(struct si470x_device *radio) | 296 | static int si470x_get_all_registers(struct si470x_device *radio) |
297 | { | 297 | { |
298 | unsigned char buf[ENTIRE_REPORT_SIZE]; | ||
299 | int retval; | 298 | int retval; |
300 | unsigned char regnr; | 299 | unsigned char regnr; |
301 | 300 | ||
302 | buf[0] = ENTIRE_REPORT; | 301 | radio->usb_buf[0] = ENTIRE_REPORT; |
303 | 302 | ||
304 | retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); | 303 | retval = si470x_get_report(radio, radio->usb_buf, ENTIRE_REPORT_SIZE); |
305 | 304 | ||
306 | if (retval >= 0) | 305 | if (retval >= 0) |
307 | for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) | 306 | for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++) |
308 | radio->registers[regnr] = get_unaligned_be16( | 307 | radio->registers[regnr] = get_unaligned_be16( |
309 | &buf[regnr * RADIO_REGISTER_SIZE + 1]); | 308 | &radio->usb_buf[regnr * RADIO_REGISTER_SIZE + 1]); |
310 | 309 | ||
311 | return (retval < 0) ? -EINVAL : 0; | 310 | return (retval < 0) ? -EINVAL : 0; |
312 | } | 311 | } |
@@ -323,14 +322,13 @@ static int si470x_get_all_registers(struct si470x_device *radio) | |||
323 | static int si470x_set_led_state(struct si470x_device *radio, | 322 | static int si470x_set_led_state(struct si470x_device *radio, |
324 | unsigned char led_state) | 323 | unsigned char led_state) |
325 | { | 324 | { |
326 | unsigned char buf[LED_REPORT_SIZE]; | ||
327 | int retval; | 325 | int retval; |
328 | 326 | ||
329 | buf[0] = LED_REPORT; | 327 | radio->usb_buf[0] = LED_REPORT; |
330 | buf[1] = LED_COMMAND; | 328 | radio->usb_buf[1] = LED_COMMAND; |
331 | buf[2] = led_state; | 329 | radio->usb_buf[2] = led_state; |
332 | 330 | ||
333 | retval = si470x_set_report(radio, (void *) &buf, sizeof(buf)); | 331 | retval = si470x_set_report(radio, radio->usb_buf, LED_REPORT_SIZE); |
334 | 332 | ||
335 | return (retval < 0) ? -EINVAL : 0; | 333 | return (retval < 0) ? -EINVAL : 0; |
336 | } | 334 | } |
@@ -346,19 +344,18 @@ static int si470x_set_led_state(struct si470x_device *radio, | |||
346 | */ | 344 | */ |
347 | static int si470x_get_scratch_page_versions(struct si470x_device *radio) | 345 | static int si470x_get_scratch_page_versions(struct si470x_device *radio) |
348 | { | 346 | { |
349 | unsigned char buf[SCRATCH_REPORT_SIZE]; | ||
350 | int retval; | 347 | int retval; |
351 | 348 | ||
352 | buf[0] = SCRATCH_REPORT; | 349 | radio->usb_buf[0] = SCRATCH_REPORT; |
353 | 350 | ||
354 | retval = si470x_get_report(radio, (void *) &buf, sizeof(buf)); | 351 | retval = si470x_get_report(radio, radio->usb_buf, SCRATCH_REPORT_SIZE); |
355 | 352 | ||
356 | if (retval < 0) | 353 | if (retval < 0) |
357 | dev_warn(&radio->intf->dev, "si470x_get_scratch: " | 354 | dev_warn(&radio->intf->dev, "si470x_get_scratch: " |
358 | "si470x_get_report returned %d\n", retval); | 355 | "si470x_get_report returned %d\n", retval); |
359 | else { | 356 | else { |
360 | radio->software_version = buf[1]; | 357 | radio->software_version = radio->usb_buf[1]; |
361 | radio->hardware_version = buf[2]; | 358 | radio->hardware_version = radio->usb_buf[2]; |
362 | } | 359 | } |
363 | 360 | ||
364 | return (retval < 0) ? -EINVAL : 0; | 361 | return (retval < 0) ? -EINVAL : 0; |
@@ -509,6 +506,7 @@ static void si470x_usb_release(struct v4l2_device *v4l2_dev) | |||
509 | v4l2_device_unregister(&radio->v4l2_dev); | 506 | v4l2_device_unregister(&radio->v4l2_dev); |
510 | kfree(radio->int_in_buffer); | 507 | kfree(radio->int_in_buffer); |
511 | kfree(radio->buffer); | 508 | kfree(radio->buffer); |
509 | kfree(radio->usb_buf); | ||
512 | kfree(radio); | 510 | kfree(radio); |
513 | } | 511 | } |
514 | 512 | ||
@@ -593,6 +591,11 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
593 | retval = -ENOMEM; | 591 | retval = -ENOMEM; |
594 | goto err_initial; | 592 | goto err_initial; |
595 | } | 593 | } |
594 | radio->usb_buf = kmalloc(MAX_REPORT_SIZE, GFP_KERNEL); | ||
595 | if (radio->usb_buf == NULL) { | ||
596 | retval = -ENOMEM; | ||
597 | goto err_radio; | ||
598 | } | ||
596 | radio->usbdev = interface_to_usbdev(intf); | 599 | radio->usbdev = interface_to_usbdev(intf); |
597 | radio->intf = intf; | 600 | radio->intf = intf; |
598 | radio->band = 1; /* Default to 76 - 108 MHz */ | 601 | radio->band = 1; /* Default to 76 - 108 MHz */ |
@@ -612,7 +615,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
612 | if (!radio->int_in_endpoint) { | 615 | if (!radio->int_in_endpoint) { |
613 | dev_info(&intf->dev, "could not find interrupt in endpoint\n"); | 616 | dev_info(&intf->dev, "could not find interrupt in endpoint\n"); |
614 | retval = -EIO; | 617 | retval = -EIO; |
615 | goto err_radio; | 618 | goto err_usbbuf; |
616 | } | 619 | } |
617 | 620 | ||
618 | int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize); | 621 | int_end_size = le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize); |
@@ -621,7 +624,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
621 | if (!radio->int_in_buffer) { | 624 | if (!radio->int_in_buffer) { |
622 | dev_info(&intf->dev, "could not allocate int_in_buffer"); | 625 | dev_info(&intf->dev, "could not allocate int_in_buffer"); |
623 | retval = -ENOMEM; | 626 | retval = -ENOMEM; |
624 | goto err_radio; | 627 | goto err_usbbuf; |
625 | } | 628 | } |
626 | 629 | ||
627 | radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); | 630 | radio->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); |
@@ -632,6 +635,30 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, | |||
632 | } | 635 | } |
633 | 636 | ||
634 | radio->v4l2_dev.release = si470x_usb_release; | 637 | radio->v4l2_dev.release = si470x_usb_release; |
638 | |||
639 | /* | ||
640 | * The si470x SiLabs reference design uses the same USB IDs as | ||
641 | * 'Thanko's Raremono' si4734 based receiver. So check here which we | ||
642 | * have: attempt to read the device ID from the si470x: the lower 12 | ||
643 | * bits should be 0x0242 for the si470x. | ||
644 | * | ||
645 | * We use this check to determine which device we are dealing with. | ||
646 | */ | ||
647 | if (id->idVendor == 0x10c4 && id->idProduct == 0x818a) { | ||
648 | retval = usb_control_msg(radio->usbdev, | ||
649 | usb_rcvctrlpipe(radio->usbdev, 0), | ||
650 | HID_REQ_GET_REPORT, | ||
651 | USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | ||
652 | 1, 2, | ||
653 | radio->usb_buf, 3, 500); | ||
654 | if (retval != 3 || | ||
655 | (get_unaligned_be16(&radio->usb_buf[1]) & 0xfff) != 0x0242) { | ||
656 | dev_info(&intf->dev, "this is not a si470x device.\n"); | ||
657 | retval = -ENODEV; | ||
658 | goto err_urb; | ||
659 | } | ||
660 | } | ||
661 | |||
635 | retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); | 662 | retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); |
636 | if (retval < 0) { | 663 | if (retval < 0) { |
637 | dev_err(&intf->dev, "couldn't register v4l2_device\n"); | 664 | dev_err(&intf->dev, "couldn't register v4l2_device\n"); |
@@ -743,6 +770,8 @@ err_urb: | |||
743 | usb_free_urb(radio->int_in_urb); | 770 | usb_free_urb(radio->int_in_urb); |
744 | err_intbuffer: | 771 | err_intbuffer: |
745 | kfree(radio->int_in_buffer); | 772 | kfree(radio->int_in_buffer); |
773 | err_usbbuf: | ||
774 | kfree(radio->usb_buf); | ||
746 | err_radio: | 775 | err_radio: |
747 | kfree(radio); | 776 | kfree(radio); |
748 | err_initial: | 777 | err_initial: |