1 | /* |
2 | * Linux V4L2 radio driver for the Griffin radioSHARK2 USB radio receiver |
3 | * |
4 | * Note the radioSHARK2 offers the audio through a regular USB audio device, |
5 | * this driver only handles the tuning. |
6 | * |
7 | * The info necessary to drive the shark2 was taken from the small userspace |
8 | * shark2.c program by Hisaaki Shibata, which he kindly placed in the Public |
9 | * Domain. |
10 | * |
11 | * Copyright (c) 2012 Hans de Goede <hdegoede@redhat.com> |
12 | * |
13 | * This program is free software; you can redistribute it and/or modify |
14 | * it under the terms of the GNU General Public License as published by |
15 | * the Free Software Foundation; either version 2 of the License, or |
16 | * (at your option) any later version. |
17 | * |
18 | * This program is distributed in the hope that it will be useful, |
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
21 | * GNU General Public License for more details. |
22 | */ |
23 | |
24 | #include <linux/init.h> |
25 | #include <linux/kernel.h> |
26 | #include <linux/leds.h> |
27 | #include <linux/module.h> |
28 | #include <linux/slab.h> |
29 | #include <linux/usb.h> |
30 | #include <linux/workqueue.h> |
31 | #include <media/v4l2-device.h> |
32 | #include "radio-tea5777.h" |
33 | |
34 | #if defined(CONFIG_LEDS_CLASS) || \ |
35 | (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK2_MODULE)) |
36 | #define SHARK_USE_LEDS 1 |
37 | #endif |
38 | |
39 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>" ); |
40 | MODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver" ); |
41 | MODULE_LICENSE("GPL" ); |
42 | |
43 | static int debug; |
44 | module_param(debug, int, 0); |
45 | MODULE_PARM_DESC(debug, "Debug level (0-1)" ); |
46 | |
47 | #define SHARK_IN_EP 0x83 |
48 | #define SHARK_OUT_EP 0x05 |
49 | |
50 | #define TB_LEN 7 |
51 | #define DRV_NAME "radioshark2" |
52 | |
53 | #define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev) |
54 | |
55 | enum { BLUE_LED, RED_LED, NO_LEDS }; |
56 | |
57 | struct shark_device { |
58 | struct usb_device *usbdev; |
59 | struct v4l2_device v4l2_dev; |
60 | struct radio_tea5777 tea; |
61 | |
62 | #ifdef SHARK_USE_LEDS |
63 | struct work_struct led_work; |
64 | struct led_classdev leds[NO_LEDS]; |
65 | char led_names[NO_LEDS][32]; |
66 | atomic_t brightness[NO_LEDS]; |
67 | unsigned long brightness_new; |
68 | #endif |
69 | |
70 | u8 *transfer_buffer; |
71 | }; |
72 | |
73 | static atomic_t shark_instance = ATOMIC_INIT(0); |
74 | |
75 | static int shark_write_reg(struct radio_tea5777 *tea, u64 reg) |
76 | { |
77 | struct shark_device *shark = tea->private_data; |
78 | int i, res, actual_len; |
79 | |
80 | memset(shark->transfer_buffer, 0, TB_LEN); |
81 | shark->transfer_buffer[0] = 0x81; /* Write register command */ |
82 | for (i = 0; i < 6; i++) |
83 | shark->transfer_buffer[i + 1] = (reg >> (40 - i * 8)) & 0xff; |
84 | |
85 | v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-write: %*ph\n" , |
86 | 7, shark->transfer_buffer); |
87 | |
88 | res = usb_interrupt_msg(usb_dev: shark->usbdev, |
89 | usb_sndintpipe(shark->usbdev, SHARK_OUT_EP), |
90 | data: shark->transfer_buffer, TB_LEN, |
91 | actual_length: &actual_len, timeout: 1000); |
92 | if (res < 0) { |
93 | v4l2_err(tea->v4l2_dev, "write error: %d\n" , res); |
94 | return res; |
95 | } |
96 | |
97 | return 0; |
98 | } |
99 | |
100 | static int shark_read_reg(struct radio_tea5777 *tea, u32 *reg_ret) |
101 | { |
102 | struct shark_device *shark = tea->private_data; |
103 | int i, res, actual_len; |
104 | u32 reg = 0; |
105 | |
106 | memset(shark->transfer_buffer, 0, TB_LEN); |
107 | shark->transfer_buffer[0] = 0x82; |
108 | res = usb_interrupt_msg(usb_dev: shark->usbdev, |
109 | usb_sndintpipe(shark->usbdev, SHARK_OUT_EP), |
110 | data: shark->transfer_buffer, TB_LEN, |
111 | actual_length: &actual_len, timeout: 1000); |
112 | if (res < 0) { |
113 | v4l2_err(tea->v4l2_dev, "request-read error: %d\n" , res); |
114 | return res; |
115 | } |
116 | |
117 | res = usb_interrupt_msg(usb_dev: shark->usbdev, |
118 | usb_rcvintpipe(shark->usbdev, SHARK_IN_EP), |
119 | data: shark->transfer_buffer, TB_LEN, |
120 | actual_length: &actual_len, timeout: 1000); |
121 | if (res < 0) { |
122 | v4l2_err(tea->v4l2_dev, "read error: %d\n" , res); |
123 | return res; |
124 | } |
125 | |
126 | for (i = 0; i < 3; i++) |
127 | reg |= shark->transfer_buffer[i] << (16 - i * 8); |
128 | |
129 | v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-read: %*ph\n" , |
130 | 3, shark->transfer_buffer); |
131 | |
132 | *reg_ret = reg; |
133 | return 0; |
134 | } |
135 | |
136 | static const struct radio_tea5777_ops shark_tea_ops = { |
137 | .write_reg = shark_write_reg, |
138 | .read_reg = shark_read_reg, |
139 | }; |
140 | |
141 | #ifdef SHARK_USE_LEDS |
142 | static void shark_led_work(struct work_struct *work) |
143 | { |
144 | struct shark_device *shark = |
145 | container_of(work, struct shark_device, led_work); |
146 | int i, res, brightness, actual_len; |
147 | |
148 | for (i = 0; i < 2; i++) { |
149 | if (!test_and_clear_bit(nr: i, addr: &shark->brightness_new)) |
150 | continue; |
151 | |
152 | brightness = atomic_read(v: &shark->brightness[i]); |
153 | memset(shark->transfer_buffer, 0, TB_LEN); |
154 | shark->transfer_buffer[0] = 0x83 + i; |
155 | shark->transfer_buffer[1] = brightness; |
156 | res = usb_interrupt_msg(usb_dev: shark->usbdev, |
157 | usb_sndintpipe(shark->usbdev, |
158 | SHARK_OUT_EP), |
159 | data: shark->transfer_buffer, TB_LEN, |
160 | actual_length: &actual_len, timeout: 1000); |
161 | if (res < 0) |
162 | v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n" , |
163 | shark->led_names[i], res); |
164 | } |
165 | } |
166 | |
167 | static void shark_led_set_blue(struct led_classdev *led_cdev, |
168 | enum led_brightness value) |
169 | { |
170 | struct shark_device *shark = |
171 | container_of(led_cdev, struct shark_device, leds[BLUE_LED]); |
172 | |
173 | atomic_set(v: &shark->brightness[BLUE_LED], i: value); |
174 | set_bit(nr: BLUE_LED, addr: &shark->brightness_new); |
175 | schedule_work(work: &shark->led_work); |
176 | } |
177 | |
178 | static void shark_led_set_red(struct led_classdev *led_cdev, |
179 | enum led_brightness value) |
180 | { |
181 | struct shark_device *shark = |
182 | container_of(led_cdev, struct shark_device, leds[RED_LED]); |
183 | |
184 | atomic_set(v: &shark->brightness[RED_LED], i: value); |
185 | set_bit(nr: RED_LED, addr: &shark->brightness_new); |
186 | schedule_work(work: &shark->led_work); |
187 | } |
188 | |
189 | static const struct led_classdev shark_led_templates[NO_LEDS] = { |
190 | [BLUE_LED] = { |
191 | .name = "%s:blue:" , |
192 | .brightness = LED_OFF, |
193 | .max_brightness = 127, |
194 | .brightness_set = shark_led_set_blue, |
195 | }, |
196 | [RED_LED] = { |
197 | .name = "%s:red:" , |
198 | .brightness = LED_OFF, |
199 | .max_brightness = 1, |
200 | .brightness_set = shark_led_set_red, |
201 | }, |
202 | }; |
203 | |
204 | static int shark_register_leds(struct shark_device *shark, struct device *dev) |
205 | { |
206 | int i, retval; |
207 | |
208 | atomic_set(v: &shark->brightness[BLUE_LED], i: 127); |
209 | INIT_WORK(&shark->led_work, shark_led_work); |
210 | for (i = 0; i < NO_LEDS; i++) { |
211 | shark->leds[i] = shark_led_templates[i]; |
212 | snprintf(buf: shark->led_names[i], size: sizeof(shark->led_names[0]), |
213 | fmt: shark->leds[i].name, shark->v4l2_dev.name); |
214 | shark->leds[i].name = shark->led_names[i]; |
215 | retval = led_classdev_register(parent: dev, led_cdev: &shark->leds[i]); |
216 | if (retval) { |
217 | v4l2_err(&shark->v4l2_dev, |
218 | "couldn't register led: %s\n" , |
219 | shark->led_names[i]); |
220 | return retval; |
221 | } |
222 | } |
223 | return 0; |
224 | } |
225 | |
226 | static void shark_unregister_leds(struct shark_device *shark) |
227 | { |
228 | int i; |
229 | |
230 | for (i = 0; i < NO_LEDS; i++) |
231 | led_classdev_unregister(led_cdev: &shark->leds[i]); |
232 | |
233 | cancel_work_sync(work: &shark->led_work); |
234 | } |
235 | |
236 | static inline void shark_resume_leds(struct shark_device *shark) |
237 | { |
238 | int i; |
239 | |
240 | for (i = 0; i < NO_LEDS; i++) |
241 | set_bit(nr: i, addr: &shark->brightness_new); |
242 | |
243 | schedule_work(work: &shark->led_work); |
244 | } |
245 | #else |
246 | static int shark_register_leds(struct shark_device *shark, struct device *dev) |
247 | { |
248 | v4l2_warn(&shark->v4l2_dev, |
249 | "CONFIG_LEDS_CLASS not enabled, LED support disabled\n" ); |
250 | return 0; |
251 | } |
252 | static inline void shark_unregister_leds(struct shark_device *shark) { } |
253 | static inline void shark_resume_leds(struct shark_device *shark) { } |
254 | #endif |
255 | |
256 | static void usb_shark_disconnect(struct usb_interface *intf) |
257 | { |
258 | struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); |
259 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); |
260 | |
261 | mutex_lock(&shark->tea.mutex); |
262 | v4l2_device_disconnect(v4l2_dev: &shark->v4l2_dev); |
263 | radio_tea5777_exit(tea: &shark->tea); |
264 | mutex_unlock(lock: &shark->tea.mutex); |
265 | |
266 | shark_unregister_leds(shark); |
267 | |
268 | v4l2_device_put(v4l2_dev: &shark->v4l2_dev); |
269 | } |
270 | |
271 | static void usb_shark_release(struct v4l2_device *v4l2_dev) |
272 | { |
273 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); |
274 | |
275 | v4l2_device_unregister(v4l2_dev: &shark->v4l2_dev); |
276 | kfree(objp: shark->transfer_buffer); |
277 | kfree(objp: shark); |
278 | } |
279 | |
280 | static int usb_shark_probe(struct usb_interface *intf, |
281 | const struct usb_device_id *id) |
282 | { |
283 | struct shark_device *shark; |
284 | int retval = -ENOMEM; |
285 | static const u8 ep_addresses[] = { |
286 | SHARK_IN_EP | USB_DIR_IN, |
287 | SHARK_OUT_EP | USB_DIR_OUT, |
288 | 0}; |
289 | |
290 | /* Are the expected endpoints present? */ |
291 | if (!usb_check_int_endpoints(intf, ep_addrs: ep_addresses)) { |
292 | dev_err(&intf->dev, "Invalid radioSHARK2 device\n" ); |
293 | return -EINVAL; |
294 | } |
295 | |
296 | shark = kzalloc(size: sizeof(struct shark_device), GFP_KERNEL); |
297 | if (!shark) |
298 | return retval; |
299 | |
300 | shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL); |
301 | if (!shark->transfer_buffer) |
302 | goto err_alloc_buffer; |
303 | |
304 | v4l2_device_set_name(v4l2_dev: &shark->v4l2_dev, DRV_NAME, instance: &shark_instance); |
305 | |
306 | retval = shark_register_leds(shark, dev: &intf->dev); |
307 | if (retval) |
308 | goto err_reg_leds; |
309 | |
310 | shark->v4l2_dev.release = usb_shark_release; |
311 | retval = v4l2_device_register(dev: &intf->dev, v4l2_dev: &shark->v4l2_dev); |
312 | if (retval) { |
313 | v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n" ); |
314 | goto err_reg_dev; |
315 | } |
316 | |
317 | shark->usbdev = interface_to_usbdev(intf); |
318 | shark->tea.v4l2_dev = &shark->v4l2_dev; |
319 | shark->tea.private_data = shark; |
320 | shark->tea.ops = &shark_tea_ops; |
321 | shark->tea.has_am = true; |
322 | shark->tea.write_before_read = true; |
323 | strscpy(shark->tea.card, "Griffin radioSHARK2" , |
324 | sizeof(shark->tea.card)); |
325 | usb_make_path(dev: shark->usbdev, buf: shark->tea.bus_info, |
326 | size: sizeof(shark->tea.bus_info)); |
327 | |
328 | retval = radio_tea5777_init(tea: &shark->tea, THIS_MODULE); |
329 | if (retval) { |
330 | v4l2_err(&shark->v4l2_dev, "couldn't init tea5777\n" ); |
331 | goto err_init_tea; |
332 | } |
333 | |
334 | return 0; |
335 | |
336 | err_init_tea: |
337 | v4l2_device_unregister(v4l2_dev: &shark->v4l2_dev); |
338 | err_reg_dev: |
339 | shark_unregister_leds(shark); |
340 | err_reg_leds: |
341 | kfree(objp: shark->transfer_buffer); |
342 | err_alloc_buffer: |
343 | kfree(objp: shark); |
344 | |
345 | return retval; |
346 | } |
347 | |
348 | #ifdef CONFIG_PM |
349 | static int usb_shark_suspend(struct usb_interface *intf, pm_message_t message) |
350 | { |
351 | return 0; |
352 | } |
353 | |
354 | static int usb_shark_resume(struct usb_interface *intf) |
355 | { |
356 | struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); |
357 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); |
358 | int ret; |
359 | |
360 | mutex_lock(&shark->tea.mutex); |
361 | ret = radio_tea5777_set_freq(tea: &shark->tea); |
362 | mutex_unlock(lock: &shark->tea.mutex); |
363 | |
364 | shark_resume_leds(shark); |
365 | |
366 | return ret; |
367 | } |
368 | #endif |
369 | |
370 | /* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */ |
371 | static const struct usb_device_id usb_shark_device_table[] = { |
372 | { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | |
373 | USB_DEVICE_ID_MATCH_INT_CLASS, |
374 | .idVendor = 0x077d, |
375 | .idProduct = 0x627a, |
376 | .bcdDevice_lo = 0x0010, |
377 | .bcdDevice_hi = 0x0010, |
378 | .bInterfaceClass = 3, |
379 | }, |
380 | { } |
381 | }; |
382 | MODULE_DEVICE_TABLE(usb, usb_shark_device_table); |
383 | |
384 | static struct usb_driver usb_shark_driver = { |
385 | .name = DRV_NAME, |
386 | .probe = usb_shark_probe, |
387 | .disconnect = usb_shark_disconnect, |
388 | .id_table = usb_shark_device_table, |
389 | #ifdef CONFIG_PM |
390 | .suspend = usb_shark_suspend, |
391 | .resume = usb_shark_resume, |
392 | .reset_resume = usb_shark_resume, |
393 | #endif |
394 | }; |
395 | module_usb_driver(usb_shark_driver); |
396 | |