1 | /* |
2 | * Linux V4L2 radio driver for the Griffin radioSHARK USB radio receiver |
3 | * |
4 | * Note the radioSHARK offers the audio through a regular USB audio device, |
5 | * this driver only handles the tuning. |
6 | * |
7 | * The info necessary to drive the shark was taken from the small userspace |
8 | * shark.c program by Michael Rolig, 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 <media/drv-intf/tea575x.h> |
33 | |
34 | #if defined(CONFIG_LEDS_CLASS) || \ |
35 | (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK_MODULE)) |
36 | #define SHARK_USE_LEDS 1 |
37 | #endif |
38 | |
39 | /* |
40 | * Version Information |
41 | */ |
42 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>" ); |
43 | MODULE_DESCRIPTION("Griffin radioSHARK, USB radio receiver driver" ); |
44 | MODULE_LICENSE("GPL" ); |
45 | |
46 | #define SHARK_IN_EP 0x83 |
47 | #define SHARK_OUT_EP 0x05 |
48 | |
49 | #define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */ |
50 | #define TEA575X_BIT_BAND_MASK (3<<20) |
51 | #define TEA575X_BIT_BAND_FM (0<<20) |
52 | |
53 | #define TB_LEN 6 |
54 | #define DRV_NAME "radioshark" |
55 | |
56 | #define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev) |
57 | |
58 | /* Note BLUE_IS_PULSE comes after NO_LEDS as it is a status bit, not a LED */ |
59 | enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS, BLUE_IS_PULSE }; |
60 | |
61 | struct shark_device { |
62 | struct usb_device *usbdev; |
63 | struct v4l2_device v4l2_dev; |
64 | struct snd_tea575x tea; |
65 | |
66 | #ifdef SHARK_USE_LEDS |
67 | struct work_struct led_work; |
68 | struct led_classdev leds[NO_LEDS]; |
69 | char led_names[NO_LEDS][32]; |
70 | atomic_t brightness[NO_LEDS]; |
71 | unsigned long brightness_new; |
72 | #endif |
73 | |
74 | u8 *transfer_buffer; |
75 | u32 last_val; |
76 | }; |
77 | |
78 | static atomic_t shark_instance = ATOMIC_INIT(0); |
79 | |
80 | static void shark_write_val(struct snd_tea575x *tea, u32 val) |
81 | { |
82 | struct shark_device *shark = tea->private_data; |
83 | int i, res, actual_len; |
84 | |
85 | /* Avoid unnecessary (slow) USB transfers */ |
86 | if (shark->last_val == val) |
87 | return; |
88 | |
89 | memset(shark->transfer_buffer, 0, TB_LEN); |
90 | shark->transfer_buffer[0] = 0xc0; /* Write shift register command */ |
91 | for (i = 0; i < 4; i++) |
92 | shark->transfer_buffer[i] |= (val >> (24 - i * 8)) & 0xff; |
93 | |
94 | res = usb_interrupt_msg(usb_dev: shark->usbdev, |
95 | usb_sndintpipe(shark->usbdev, SHARK_OUT_EP), |
96 | data: shark->transfer_buffer, TB_LEN, |
97 | actual_length: &actual_len, timeout: 1000); |
98 | if (res >= 0) |
99 | shark->last_val = val; |
100 | else |
101 | v4l2_err(&shark->v4l2_dev, "set-freq error: %d\n" , res); |
102 | } |
103 | |
104 | static u32 shark_read_val(struct snd_tea575x *tea) |
105 | { |
106 | struct shark_device *shark = tea->private_data; |
107 | int i, res, actual_len; |
108 | u32 val = 0; |
109 | |
110 | memset(shark->transfer_buffer, 0, TB_LEN); |
111 | shark->transfer_buffer[0] = 0x80; |
112 | res = usb_interrupt_msg(usb_dev: shark->usbdev, |
113 | usb_sndintpipe(shark->usbdev, SHARK_OUT_EP), |
114 | data: shark->transfer_buffer, TB_LEN, |
115 | actual_length: &actual_len, timeout: 1000); |
116 | if (res < 0) { |
117 | v4l2_err(&shark->v4l2_dev, "request-status error: %d\n" , res); |
118 | return shark->last_val; |
119 | } |
120 | |
121 | res = usb_interrupt_msg(usb_dev: shark->usbdev, |
122 | usb_rcvintpipe(shark->usbdev, SHARK_IN_EP), |
123 | data: shark->transfer_buffer, TB_LEN, |
124 | actual_length: &actual_len, timeout: 1000); |
125 | if (res < 0) { |
126 | v4l2_err(&shark->v4l2_dev, "get-status error: %d\n" , res); |
127 | return shark->last_val; |
128 | } |
129 | |
130 | for (i = 0; i < 4; i++) |
131 | val |= shark->transfer_buffer[i] << (24 - i * 8); |
132 | |
133 | shark->last_val = val; |
134 | |
135 | /* |
136 | * The shark does not allow actually reading the stereo / mono pin :( |
137 | * So assume that when we're tuned to an FM station and mono has not |
138 | * been requested, that we're receiving stereo. |
139 | */ |
140 | if (((val & TEA575X_BIT_BAND_MASK) == TEA575X_BIT_BAND_FM) && |
141 | !(val & TEA575X_BIT_MONO)) |
142 | shark->tea.stereo = true; |
143 | else |
144 | shark->tea.stereo = false; |
145 | |
146 | return val; |
147 | } |
148 | |
149 | static const struct snd_tea575x_ops shark_tea_ops = { |
150 | .write_val = shark_write_val, |
151 | .read_val = shark_read_val, |
152 | }; |
153 | |
154 | #ifdef SHARK_USE_LEDS |
155 | static void shark_led_work(struct work_struct *work) |
156 | { |
157 | struct shark_device *shark = |
158 | container_of(work, struct shark_device, led_work); |
159 | int i, res, brightness, actual_len; |
160 | |
161 | for (i = 0; i < 3; i++) { |
162 | if (!test_and_clear_bit(nr: i, addr: &shark->brightness_new)) |
163 | continue; |
164 | |
165 | brightness = atomic_read(v: &shark->brightness[i]); |
166 | memset(shark->transfer_buffer, 0, TB_LEN); |
167 | if (i != RED_LED) { |
168 | shark->transfer_buffer[0] = 0xA0 + i; |
169 | shark->transfer_buffer[1] = brightness; |
170 | } else |
171 | shark->transfer_buffer[0] = brightness ? 0xA9 : 0xA8; |
172 | res = usb_interrupt_msg(usb_dev: shark->usbdev, |
173 | usb_sndintpipe(shark->usbdev, 0x05), |
174 | data: shark->transfer_buffer, TB_LEN, |
175 | actual_length: &actual_len, timeout: 1000); |
176 | if (res < 0) |
177 | v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n" , |
178 | shark->led_names[i], res); |
179 | } |
180 | } |
181 | |
182 | static void shark_led_set_blue(struct led_classdev *led_cdev, |
183 | enum led_brightness value) |
184 | { |
185 | struct shark_device *shark = |
186 | container_of(led_cdev, struct shark_device, leds[BLUE_LED]); |
187 | |
188 | atomic_set(v: &shark->brightness[BLUE_LED], i: value); |
189 | set_bit(nr: BLUE_LED, addr: &shark->brightness_new); |
190 | clear_bit(nr: BLUE_IS_PULSE, addr: &shark->brightness_new); |
191 | schedule_work(work: &shark->led_work); |
192 | } |
193 | |
194 | static void shark_led_set_blue_pulse(struct led_classdev *led_cdev, |
195 | enum led_brightness value) |
196 | { |
197 | struct shark_device *shark = container_of(led_cdev, |
198 | struct shark_device, leds[BLUE_PULSE_LED]); |
199 | |
200 | atomic_set(v: &shark->brightness[BLUE_PULSE_LED], i: 256 - value); |
201 | set_bit(nr: BLUE_PULSE_LED, addr: &shark->brightness_new); |
202 | set_bit(nr: BLUE_IS_PULSE, addr: &shark->brightness_new); |
203 | schedule_work(work: &shark->led_work); |
204 | } |
205 | |
206 | static void shark_led_set_red(struct led_classdev *led_cdev, |
207 | enum led_brightness value) |
208 | { |
209 | struct shark_device *shark = |
210 | container_of(led_cdev, struct shark_device, leds[RED_LED]); |
211 | |
212 | atomic_set(v: &shark->brightness[RED_LED], i: value); |
213 | set_bit(nr: RED_LED, addr: &shark->brightness_new); |
214 | schedule_work(work: &shark->led_work); |
215 | } |
216 | |
217 | static const struct led_classdev shark_led_templates[NO_LEDS] = { |
218 | [BLUE_LED] = { |
219 | .name = "%s:blue:" , |
220 | .brightness = LED_OFF, |
221 | .max_brightness = 127, |
222 | .brightness_set = shark_led_set_blue, |
223 | }, |
224 | [BLUE_PULSE_LED] = { |
225 | .name = "%s:blue-pulse:" , |
226 | .brightness = LED_OFF, |
227 | .max_brightness = 255, |
228 | .brightness_set = shark_led_set_blue_pulse, |
229 | }, |
230 | [RED_LED] = { |
231 | .name = "%s:red:" , |
232 | .brightness = LED_OFF, |
233 | .max_brightness = 1, |
234 | .brightness_set = shark_led_set_red, |
235 | }, |
236 | }; |
237 | |
238 | static int shark_register_leds(struct shark_device *shark, struct device *dev) |
239 | { |
240 | int i, retval; |
241 | |
242 | atomic_set(v: &shark->brightness[BLUE_LED], i: 127); |
243 | INIT_WORK(&shark->led_work, shark_led_work); |
244 | for (i = 0; i < NO_LEDS; i++) { |
245 | shark->leds[i] = shark_led_templates[i]; |
246 | snprintf(buf: shark->led_names[i], size: sizeof(shark->led_names[0]), |
247 | fmt: shark->leds[i].name, shark->v4l2_dev.name); |
248 | shark->leds[i].name = shark->led_names[i]; |
249 | retval = led_classdev_register(parent: dev, led_cdev: &shark->leds[i]); |
250 | if (retval) { |
251 | v4l2_err(&shark->v4l2_dev, |
252 | "couldn't register led: %s\n" , |
253 | shark->led_names[i]); |
254 | return retval; |
255 | } |
256 | } |
257 | return 0; |
258 | } |
259 | |
260 | static void shark_unregister_leds(struct shark_device *shark) |
261 | { |
262 | int i; |
263 | |
264 | for (i = 0; i < NO_LEDS; i++) |
265 | led_classdev_unregister(led_cdev: &shark->leds[i]); |
266 | |
267 | cancel_work_sync(work: &shark->led_work); |
268 | } |
269 | |
270 | static inline void shark_resume_leds(struct shark_device *shark) |
271 | { |
272 | if (test_bit(BLUE_IS_PULSE, &shark->brightness_new)) |
273 | set_bit(nr: BLUE_PULSE_LED, addr: &shark->brightness_new); |
274 | else |
275 | set_bit(nr: BLUE_LED, addr: &shark->brightness_new); |
276 | set_bit(nr: RED_LED, addr: &shark->brightness_new); |
277 | schedule_work(work: &shark->led_work); |
278 | } |
279 | #else |
280 | static int shark_register_leds(struct shark_device *shark, struct device *dev) |
281 | { |
282 | v4l2_warn(&shark->v4l2_dev, |
283 | "CONFIG_LEDS_CLASS not enabled, LED support disabled\n" ); |
284 | return 0; |
285 | } |
286 | static inline void shark_unregister_leds(struct shark_device *shark) { } |
287 | static inline void shark_resume_leds(struct shark_device *shark) { } |
288 | #endif |
289 | |
290 | static void usb_shark_disconnect(struct usb_interface *intf) |
291 | { |
292 | struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); |
293 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); |
294 | |
295 | mutex_lock(&shark->tea.mutex); |
296 | v4l2_device_disconnect(v4l2_dev: &shark->v4l2_dev); |
297 | snd_tea575x_exit(tea: &shark->tea); |
298 | mutex_unlock(lock: &shark->tea.mutex); |
299 | |
300 | shark_unregister_leds(shark); |
301 | |
302 | v4l2_device_put(v4l2_dev: &shark->v4l2_dev); |
303 | } |
304 | |
305 | static void usb_shark_release(struct v4l2_device *v4l2_dev) |
306 | { |
307 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); |
308 | |
309 | v4l2_device_unregister(v4l2_dev: &shark->v4l2_dev); |
310 | kfree(objp: shark->transfer_buffer); |
311 | kfree(objp: shark); |
312 | } |
313 | |
314 | static int usb_shark_probe(struct usb_interface *intf, |
315 | const struct usb_device_id *id) |
316 | { |
317 | struct shark_device *shark; |
318 | int retval = -ENOMEM; |
319 | static const u8 ep_addresses[] = { |
320 | SHARK_IN_EP | USB_DIR_IN, |
321 | SHARK_OUT_EP | USB_DIR_OUT, |
322 | 0}; |
323 | |
324 | /* Are the expected endpoints present? */ |
325 | if (!usb_check_int_endpoints(intf, ep_addrs: ep_addresses)) { |
326 | dev_err(&intf->dev, "Invalid radioSHARK device\n" ); |
327 | return -EINVAL; |
328 | } |
329 | |
330 | shark = kzalloc(size: sizeof(struct shark_device), GFP_KERNEL); |
331 | if (!shark) |
332 | return retval; |
333 | |
334 | shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL); |
335 | if (!shark->transfer_buffer) |
336 | goto err_alloc_buffer; |
337 | |
338 | v4l2_device_set_name(v4l2_dev: &shark->v4l2_dev, DRV_NAME, instance: &shark_instance); |
339 | |
340 | retval = shark_register_leds(shark, dev: &intf->dev); |
341 | if (retval) |
342 | goto err_reg_leds; |
343 | |
344 | shark->v4l2_dev.release = usb_shark_release; |
345 | retval = v4l2_device_register(dev: &intf->dev, v4l2_dev: &shark->v4l2_dev); |
346 | if (retval) { |
347 | v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n" ); |
348 | goto err_reg_dev; |
349 | } |
350 | |
351 | shark->usbdev = interface_to_usbdev(intf); |
352 | shark->tea.v4l2_dev = &shark->v4l2_dev; |
353 | shark->tea.private_data = shark; |
354 | shark->tea.radio_nr = -1; |
355 | shark->tea.ops = &shark_tea_ops; |
356 | shark->tea.cannot_mute = true; |
357 | shark->tea.has_am = true; |
358 | strscpy(shark->tea.card, "Griffin radioSHARK" , |
359 | sizeof(shark->tea.card)); |
360 | usb_make_path(dev: shark->usbdev, buf: shark->tea.bus_info, |
361 | size: sizeof(shark->tea.bus_info)); |
362 | |
363 | retval = snd_tea575x_init(tea: &shark->tea, THIS_MODULE); |
364 | if (retval) { |
365 | v4l2_err(&shark->v4l2_dev, "couldn't init tea5757\n" ); |
366 | goto err_init_tea; |
367 | } |
368 | |
369 | return 0; |
370 | |
371 | err_init_tea: |
372 | v4l2_device_unregister(v4l2_dev: &shark->v4l2_dev); |
373 | err_reg_dev: |
374 | shark_unregister_leds(shark); |
375 | err_reg_leds: |
376 | kfree(objp: shark->transfer_buffer); |
377 | err_alloc_buffer: |
378 | kfree(objp: shark); |
379 | |
380 | return retval; |
381 | } |
382 | |
383 | #ifdef CONFIG_PM |
384 | static int usb_shark_suspend(struct usb_interface *intf, pm_message_t message) |
385 | { |
386 | return 0; |
387 | } |
388 | |
389 | static int usb_shark_resume(struct usb_interface *intf) |
390 | { |
391 | struct v4l2_device *v4l2_dev = usb_get_intfdata(intf); |
392 | struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev); |
393 | |
394 | mutex_lock(&shark->tea.mutex); |
395 | snd_tea575x_set_freq(tea: &shark->tea); |
396 | mutex_unlock(lock: &shark->tea.mutex); |
397 | |
398 | shark_resume_leds(shark); |
399 | |
400 | return 0; |
401 | } |
402 | #endif |
403 | |
404 | /* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */ |
405 | static const struct usb_device_id usb_shark_device_table[] = { |
406 | { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | |
407 | USB_DEVICE_ID_MATCH_INT_CLASS, |
408 | .idVendor = 0x077d, |
409 | .idProduct = 0x627a, |
410 | .bcdDevice_lo = 0x0001, |
411 | .bcdDevice_hi = 0x0001, |
412 | .bInterfaceClass = 3, |
413 | }, |
414 | { } |
415 | }; |
416 | MODULE_DEVICE_TABLE(usb, usb_shark_device_table); |
417 | |
418 | static struct usb_driver usb_shark_driver = { |
419 | .name = DRV_NAME, |
420 | .probe = usb_shark_probe, |
421 | .disconnect = usb_shark_disconnect, |
422 | .id_table = usb_shark_device_table, |
423 | #ifdef CONFIG_PM |
424 | .suspend = usb_shark_suspend, |
425 | .resume = usb_shark_resume, |
426 | .reset_resume = usb_shark_resume, |
427 | #endif |
428 | }; |
429 | module_usb_driver(usb_shark_driver); |
430 | |