18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Linux V4L2 radio driver for the Griffin radioSHARK2 USB radio receiver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Note the radioSHARK2 offers the audio through a regular USB audio device, 58c2ecf20Sopenharmony_ci * this driver only handles the tuning. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * The info necessary to drive the shark2 was taken from the small userspace 88c2ecf20Sopenharmony_ci * shark2.c program by Hisaaki Shibata, which he kindly placed in the Public 98c2ecf20Sopenharmony_ci * Domain. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 148c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 158c2ecf20Sopenharmony_ci * the Free Software Foundation; either version 2 of the License, or 168c2ecf20Sopenharmony_ci * (at your option) any later version. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 198c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 208c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 218c2ecf20Sopenharmony_ci * GNU General Public License for more details. 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/init.h> 258c2ecf20Sopenharmony_ci#include <linux/kernel.h> 268c2ecf20Sopenharmony_ci#include <linux/leds.h> 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/usb.h> 308c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 318c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 328c2ecf20Sopenharmony_ci#include "radio-tea5777.h" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#if defined(CONFIG_LEDS_CLASS) || \ 358c2ecf20Sopenharmony_ci (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK2_MODULE)) 368c2ecf20Sopenharmony_ci#define SHARK_USE_LEDS 1 378c2ecf20Sopenharmony_ci#endif 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver"); 418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int debug; 448c2ecf20Sopenharmony_cimodule_param(debug, int, 0); 458c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level (0-1)"); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define SHARK_IN_EP 0x83 488c2ecf20Sopenharmony_ci#define SHARK_OUT_EP 0x05 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define TB_LEN 7 518c2ecf20Sopenharmony_ci#define DRV_NAME "radioshark2" 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cienum { BLUE_LED, RED_LED, NO_LEDS }; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct shark_device { 588c2ecf20Sopenharmony_ci struct usb_device *usbdev; 598c2ecf20Sopenharmony_ci struct v4l2_device v4l2_dev; 608c2ecf20Sopenharmony_ci struct radio_tea5777 tea; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#ifdef SHARK_USE_LEDS 638c2ecf20Sopenharmony_ci struct work_struct led_work; 648c2ecf20Sopenharmony_ci struct led_classdev leds[NO_LEDS]; 658c2ecf20Sopenharmony_ci char led_names[NO_LEDS][32]; 668c2ecf20Sopenharmony_ci atomic_t brightness[NO_LEDS]; 678c2ecf20Sopenharmony_ci unsigned long brightness_new; 688c2ecf20Sopenharmony_ci#endif 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci u8 *transfer_buffer; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic atomic_t shark_instance = ATOMIC_INIT(0); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int shark_write_reg(struct radio_tea5777 *tea, u64 reg) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct shark_device *shark = tea->private_data; 788c2ecf20Sopenharmony_ci int i, res, actual_len; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci memset(shark->transfer_buffer, 0, TB_LEN); 818c2ecf20Sopenharmony_ci shark->transfer_buffer[0] = 0x81; /* Write register command */ 828c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) 838c2ecf20Sopenharmony_ci shark->transfer_buffer[i + 1] = (reg >> (40 - i * 8)) & 0xff; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-write: %*ph\n", 868c2ecf20Sopenharmony_ci 7, shark->transfer_buffer); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci res = usb_interrupt_msg(shark->usbdev, 898c2ecf20Sopenharmony_ci usb_sndintpipe(shark->usbdev, SHARK_OUT_EP), 908c2ecf20Sopenharmony_ci shark->transfer_buffer, TB_LEN, 918c2ecf20Sopenharmony_ci &actual_len, 1000); 928c2ecf20Sopenharmony_ci if (res < 0) { 938c2ecf20Sopenharmony_ci v4l2_err(tea->v4l2_dev, "write error: %d\n", res); 948c2ecf20Sopenharmony_ci return res; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int shark_read_reg(struct radio_tea5777 *tea, u32 *reg_ret) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct shark_device *shark = tea->private_data; 1038c2ecf20Sopenharmony_ci int i, res, actual_len; 1048c2ecf20Sopenharmony_ci u32 reg = 0; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci memset(shark->transfer_buffer, 0, TB_LEN); 1078c2ecf20Sopenharmony_ci shark->transfer_buffer[0] = 0x82; 1088c2ecf20Sopenharmony_ci res = usb_interrupt_msg(shark->usbdev, 1098c2ecf20Sopenharmony_ci usb_sndintpipe(shark->usbdev, SHARK_OUT_EP), 1108c2ecf20Sopenharmony_ci shark->transfer_buffer, TB_LEN, 1118c2ecf20Sopenharmony_ci &actual_len, 1000); 1128c2ecf20Sopenharmony_ci if (res < 0) { 1138c2ecf20Sopenharmony_ci v4l2_err(tea->v4l2_dev, "request-read error: %d\n", res); 1148c2ecf20Sopenharmony_ci return res; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci res = usb_interrupt_msg(shark->usbdev, 1188c2ecf20Sopenharmony_ci usb_rcvintpipe(shark->usbdev, SHARK_IN_EP), 1198c2ecf20Sopenharmony_ci shark->transfer_buffer, TB_LEN, 1208c2ecf20Sopenharmony_ci &actual_len, 1000); 1218c2ecf20Sopenharmony_ci if (res < 0) { 1228c2ecf20Sopenharmony_ci v4l2_err(tea->v4l2_dev, "read error: %d\n", res); 1238c2ecf20Sopenharmony_ci return res; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) 1278c2ecf20Sopenharmony_ci reg |= shark->transfer_buffer[i] << (16 - i * 8); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-read: %*ph\n", 1308c2ecf20Sopenharmony_ci 3, shark->transfer_buffer); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci *reg_ret = reg; 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic const struct radio_tea5777_ops shark_tea_ops = { 1378c2ecf20Sopenharmony_ci .write_reg = shark_write_reg, 1388c2ecf20Sopenharmony_ci .read_reg = shark_read_reg, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci#ifdef SHARK_USE_LEDS 1428c2ecf20Sopenharmony_cistatic void shark_led_work(struct work_struct *work) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct shark_device *shark = 1458c2ecf20Sopenharmony_ci container_of(work, struct shark_device, led_work); 1468c2ecf20Sopenharmony_ci int i, res, brightness, actual_len; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < 2; i++) { 1498c2ecf20Sopenharmony_ci if (!test_and_clear_bit(i, &shark->brightness_new)) 1508c2ecf20Sopenharmony_ci continue; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci brightness = atomic_read(&shark->brightness[i]); 1538c2ecf20Sopenharmony_ci memset(shark->transfer_buffer, 0, TB_LEN); 1548c2ecf20Sopenharmony_ci shark->transfer_buffer[0] = 0x83 + i; 1558c2ecf20Sopenharmony_ci shark->transfer_buffer[1] = brightness; 1568c2ecf20Sopenharmony_ci res = usb_interrupt_msg(shark->usbdev, 1578c2ecf20Sopenharmony_ci usb_sndintpipe(shark->usbdev, 1588c2ecf20Sopenharmony_ci SHARK_OUT_EP), 1598c2ecf20Sopenharmony_ci shark->transfer_buffer, TB_LEN, 1608c2ecf20Sopenharmony_ci &actual_len, 1000); 1618c2ecf20Sopenharmony_ci if (res < 0) 1628c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n", 1638c2ecf20Sopenharmony_ci shark->led_names[i], res); 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic void shark_led_set_blue(struct led_classdev *led_cdev, 1688c2ecf20Sopenharmony_ci enum led_brightness value) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct shark_device *shark = 1718c2ecf20Sopenharmony_ci container_of(led_cdev, struct shark_device, leds[BLUE_LED]); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci atomic_set(&shark->brightness[BLUE_LED], value); 1748c2ecf20Sopenharmony_ci set_bit(BLUE_LED, &shark->brightness_new); 1758c2ecf20Sopenharmony_ci schedule_work(&shark->led_work); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void shark_led_set_red(struct led_classdev *led_cdev, 1798c2ecf20Sopenharmony_ci enum led_brightness value) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct shark_device *shark = 1828c2ecf20Sopenharmony_ci container_of(led_cdev, struct shark_device, leds[RED_LED]); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci atomic_set(&shark->brightness[RED_LED], value); 1858c2ecf20Sopenharmony_ci set_bit(RED_LED, &shark->brightness_new); 1868c2ecf20Sopenharmony_ci schedule_work(&shark->led_work); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic const struct led_classdev shark_led_templates[NO_LEDS] = { 1908c2ecf20Sopenharmony_ci [BLUE_LED] = { 1918c2ecf20Sopenharmony_ci .name = "%s:blue:", 1928c2ecf20Sopenharmony_ci .brightness = LED_OFF, 1938c2ecf20Sopenharmony_ci .max_brightness = 127, 1948c2ecf20Sopenharmony_ci .brightness_set = shark_led_set_blue, 1958c2ecf20Sopenharmony_ci }, 1968c2ecf20Sopenharmony_ci [RED_LED] = { 1978c2ecf20Sopenharmony_ci .name = "%s:red:", 1988c2ecf20Sopenharmony_ci .brightness = LED_OFF, 1998c2ecf20Sopenharmony_ci .max_brightness = 1, 2008c2ecf20Sopenharmony_ci .brightness_set = shark_led_set_red, 2018c2ecf20Sopenharmony_ci }, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int shark_register_leds(struct shark_device *shark, struct device *dev) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci int i, retval; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci atomic_set(&shark->brightness[BLUE_LED], 127); 2098c2ecf20Sopenharmony_ci INIT_WORK(&shark->led_work, shark_led_work); 2108c2ecf20Sopenharmony_ci for (i = 0; i < NO_LEDS; i++) { 2118c2ecf20Sopenharmony_ci shark->leds[i] = shark_led_templates[i]; 2128c2ecf20Sopenharmony_ci snprintf(shark->led_names[i], sizeof(shark->led_names[0]), 2138c2ecf20Sopenharmony_ci shark->leds[i].name, shark->v4l2_dev.name); 2148c2ecf20Sopenharmony_ci shark->leds[i].name = shark->led_names[i]; 2158c2ecf20Sopenharmony_ci retval = led_classdev_register(dev, &shark->leds[i]); 2168c2ecf20Sopenharmony_ci if (retval) { 2178c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, 2188c2ecf20Sopenharmony_ci "couldn't register led: %s\n", 2198c2ecf20Sopenharmony_ci shark->led_names[i]); 2208c2ecf20Sopenharmony_ci return retval; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic void shark_unregister_leds(struct shark_device *shark) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int i; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci for (i = 0; i < NO_LEDS; i++) 2318c2ecf20Sopenharmony_ci led_classdev_unregister(&shark->leds[i]); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci cancel_work_sync(&shark->led_work); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic inline void shark_resume_leds(struct shark_device *shark) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci int i; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci for (i = 0; i < NO_LEDS; i++) 2418c2ecf20Sopenharmony_ci set_bit(i, &shark->brightness_new); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci schedule_work(&shark->led_work); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci#else 2468c2ecf20Sopenharmony_cistatic int shark_register_leds(struct shark_device *shark, struct device *dev) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci v4l2_warn(&shark->v4l2_dev, 2498c2ecf20Sopenharmony_ci "CONFIG_LEDS_CLASS not enabled, LED support disabled\n"); 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_cistatic inline void shark_unregister_leds(struct shark_device *shark) { } 2538c2ecf20Sopenharmony_cistatic inline void shark_resume_leds(struct shark_device *shark) { } 2548c2ecf20Sopenharmony_ci#endif 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic void usb_shark_disconnect(struct usb_interface *intf) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); 2598c2ecf20Sopenharmony_ci struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mutex_lock(&shark->tea.mutex); 2628c2ecf20Sopenharmony_ci v4l2_device_disconnect(&shark->v4l2_dev); 2638c2ecf20Sopenharmony_ci radio_tea5777_exit(&shark->tea); 2648c2ecf20Sopenharmony_ci mutex_unlock(&shark->tea.mutex); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci shark_unregister_leds(shark); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci v4l2_device_put(&shark->v4l2_dev); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void usb_shark_release(struct v4l2_device *v4l2_dev) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci v4l2_device_unregister(&shark->v4l2_dev); 2768c2ecf20Sopenharmony_ci kfree(shark->transfer_buffer); 2778c2ecf20Sopenharmony_ci kfree(shark); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int usb_shark_probe(struct usb_interface *intf, 2818c2ecf20Sopenharmony_ci const struct usb_device_id *id) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct shark_device *shark; 2848c2ecf20Sopenharmony_ci int retval = -ENOMEM; 2858c2ecf20Sopenharmony_ci static const u8 ep_addresses[] = { 2868c2ecf20Sopenharmony_ci SHARK_IN_EP | USB_DIR_IN, 2878c2ecf20Sopenharmony_ci SHARK_OUT_EP | USB_DIR_OUT, 2888c2ecf20Sopenharmony_ci 0}; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* Are the expected endpoints present? */ 2918c2ecf20Sopenharmony_ci if (!usb_check_int_endpoints(intf, ep_addresses)) { 2928c2ecf20Sopenharmony_ci dev_err(&intf->dev, "Invalid radioSHARK2 device\n"); 2938c2ecf20Sopenharmony_ci return -EINVAL; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL); 2978c2ecf20Sopenharmony_ci if (!shark) 2988c2ecf20Sopenharmony_ci return retval; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL); 3018c2ecf20Sopenharmony_ci if (!shark->transfer_buffer) 3028c2ecf20Sopenharmony_ci goto err_alloc_buffer; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci retval = shark_register_leds(shark, &intf->dev); 3078c2ecf20Sopenharmony_ci if (retval) 3088c2ecf20Sopenharmony_ci goto err_reg_leds; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci shark->v4l2_dev.release = usb_shark_release; 3118c2ecf20Sopenharmony_ci retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev); 3128c2ecf20Sopenharmony_ci if (retval) { 3138c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n"); 3148c2ecf20Sopenharmony_ci goto err_reg_dev; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci shark->usbdev = interface_to_usbdev(intf); 3188c2ecf20Sopenharmony_ci shark->tea.v4l2_dev = &shark->v4l2_dev; 3198c2ecf20Sopenharmony_ci shark->tea.private_data = shark; 3208c2ecf20Sopenharmony_ci shark->tea.ops = &shark_tea_ops; 3218c2ecf20Sopenharmony_ci shark->tea.has_am = true; 3228c2ecf20Sopenharmony_ci shark->tea.write_before_read = true; 3238c2ecf20Sopenharmony_ci strscpy(shark->tea.card, "Griffin radioSHARK2", 3248c2ecf20Sopenharmony_ci sizeof(shark->tea.card)); 3258c2ecf20Sopenharmony_ci usb_make_path(shark->usbdev, shark->tea.bus_info, 3268c2ecf20Sopenharmony_ci sizeof(shark->tea.bus_info)); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci retval = radio_tea5777_init(&shark->tea, THIS_MODULE); 3298c2ecf20Sopenharmony_ci if (retval) { 3308c2ecf20Sopenharmony_ci v4l2_err(&shark->v4l2_dev, "couldn't init tea5777\n"); 3318c2ecf20Sopenharmony_ci goto err_init_tea; 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cierr_init_tea: 3378c2ecf20Sopenharmony_ci v4l2_device_unregister(&shark->v4l2_dev); 3388c2ecf20Sopenharmony_cierr_reg_dev: 3398c2ecf20Sopenharmony_ci shark_unregister_leds(shark); 3408c2ecf20Sopenharmony_cierr_reg_leds: 3418c2ecf20Sopenharmony_ci kfree(shark->transfer_buffer); 3428c2ecf20Sopenharmony_cierr_alloc_buffer: 3438c2ecf20Sopenharmony_ci kfree(shark); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return retval; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3498c2ecf20Sopenharmony_cistatic int usb_shark_suspend(struct usb_interface *intf, pm_message_t message) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci return 0; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int usb_shark_resume(struct usb_interface *intf) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); 3578c2ecf20Sopenharmony_ci struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); 3588c2ecf20Sopenharmony_ci int ret; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci mutex_lock(&shark->tea.mutex); 3618c2ecf20Sopenharmony_ci ret = radio_tea5777_set_freq(&shark->tea); 3628c2ecf20Sopenharmony_ci mutex_unlock(&shark->tea.mutex); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci shark_resume_leds(shark); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return ret; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci#endif 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */ 3718c2ecf20Sopenharmony_cistatic const struct usb_device_id usb_shark_device_table[] = { 3728c2ecf20Sopenharmony_ci { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | 3738c2ecf20Sopenharmony_ci USB_DEVICE_ID_MATCH_INT_CLASS, 3748c2ecf20Sopenharmony_ci .idVendor = 0x077d, 3758c2ecf20Sopenharmony_ci .idProduct = 0x627a, 3768c2ecf20Sopenharmony_ci .bcdDevice_lo = 0x0010, 3778c2ecf20Sopenharmony_ci .bcdDevice_hi = 0x0010, 3788c2ecf20Sopenharmony_ci .bInterfaceClass = 3, 3798c2ecf20Sopenharmony_ci }, 3808c2ecf20Sopenharmony_ci { } 3818c2ecf20Sopenharmony_ci}; 3828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usb_shark_device_table); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic struct usb_driver usb_shark_driver = { 3858c2ecf20Sopenharmony_ci .name = DRV_NAME, 3868c2ecf20Sopenharmony_ci .probe = usb_shark_probe, 3878c2ecf20Sopenharmony_ci .disconnect = usb_shark_disconnect, 3888c2ecf20Sopenharmony_ci .id_table = usb_shark_device_table, 3898c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 3908c2ecf20Sopenharmony_ci .suspend = usb_shark_suspend, 3918c2ecf20Sopenharmony_ci .resume = usb_shark_resume, 3928c2ecf20Sopenharmony_ci .reset_resume = usb_shark_resume, 3938c2ecf20Sopenharmony_ci#endif 3948c2ecf20Sopenharmony_ci}; 3958c2ecf20Sopenharmony_cimodule_usb_driver(usb_shark_driver); 396