aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-05-21 14:24:50 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-07-30 17:00:32 -0400
commit8e2ce73e932b629c3e12546e5fffac7ee54d0093 (patch)
tree2973bcedd055bd61d63513f57f3a99937d2f13f1 /drivers/media/radio
parent3d0fe51cfa3d07751224c034f8226136a7dd05f2 (diff)
[media] radio-shark: New driver for the Griffin radioSHARK USB radio receiver
Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r--drivers/media/radio/Kconfig16
-rw-r--r--drivers/media/radio/Makefile1
-rw-r--r--drivers/media/radio/radio-shark.c376
3 files changed, 393 insertions, 0 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 24ce5a47f955..e3c117122351 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -57,6 +57,22 @@ config RADIO_MAXIRADIO
57 To compile this driver as a module, choose M here: the 57 To compile this driver as a module, choose M here: the
58 module will be called radio-maxiradio. 58 module will be called radio-maxiradio.
59 59
60config RADIO_SHARK
61 tristate "Griffin radioSHARK USB radio receiver"
62 depends on USB && SND
63 ---help---
64 Choose Y here if you have this radio receiver.
65
66 There are 2 versions of this device, this driver is for version 1,
67 which is white.
68
69 In order to control your radio card, you will need to use programs
70 that are compatible with the Video For Linux API. Information on
71 this API and pointers to "v4l" programs may be found at
72 <file:Documentation/video4linux/API.html>.
73
74 To compile this driver as a module, choose M here: the
75 module will be called radio-shark.
60 76
61config I2C_SI4713 77config I2C_SI4713
62 tristate "I2C driver for Silicon Labs Si4713 device" 78 tristate "I2C driver for Silicon Labs Si4713 device"
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index ca8c7d134b95..e03b258e18d4 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_RADIO_CADET) += radio-cadet.o
11obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o 11obj-$(CONFIG_RADIO_TYPHOON) += radio-typhoon.o
12obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o 12obj-$(CONFIG_RADIO_TERRATEC) += radio-terratec.o
13obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o 13obj-$(CONFIG_RADIO_MAXIRADIO) += radio-maxiradio.o
14obj-$(CONFIG_RADIO_SHARK) += radio-shark.o
14obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o 15obj-$(CONFIG_RADIO_RTRACK) += radio-aimslab.o
15obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o 16obj-$(CONFIG_RADIO_ZOLTRIX) += radio-zoltrix.o
16obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o 17obj-$(CONFIG_RADIO_GEMTEK) += radio-gemtek.o
diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c
new file mode 100644
index 000000000000..d0b6bb507634
--- /dev/null
+++ b/drivers/media/radio/radio-shark.c
@@ -0,0 +1,376 @@
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 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26*/
27
28#include <linux/init.h>
29#include <linux/kernel.h>
30#include <linux/leds.h>
31#include <linux/module.h>
32#include <linux/slab.h>
33#include <linux/usb.h>
34#include <linux/workqueue.h>
35#include <media/v4l2-device.h>
36#include <sound/tea575x-tuner.h>
37
38/*
39 * Version Information
40 */
41MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
42MODULE_DESCRIPTION("Griffin radioSHARK, USB radio receiver driver");
43MODULE_LICENSE("GPL");
44
45#define SHARK_IN_EP 0x83
46#define SHARK_OUT_EP 0x05
47
48#define TEA575X_BIT_MONO (1<<22) /* 0 = stereo, 1 = mono */
49#define TEA575X_BIT_BAND_MASK (3<<20)
50#define TEA575X_BIT_BAND_FM (0<<20)
51
52#define TB_LEN 6
53#define DRV_NAME "radioshark"
54
55#define v4l2_dev_to_shark(d) container_of(d, struct shark_device, v4l2_dev)
56
57enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS };
58
59static void shark_led_set_blue(struct led_classdev *led_cdev,
60 enum led_brightness value);
61static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
62 enum led_brightness value);
63static void shark_led_set_red(struct led_classdev *led_cdev,
64 enum led_brightness value);
65
66static const struct led_classdev shark_led_templates[NO_LEDS] = {
67 [BLUE_LED] = {
68 .name = "%s:blue:",
69 .brightness = LED_OFF,
70 .max_brightness = 127,
71 .brightness_set = shark_led_set_blue,
72 },
73 [BLUE_PULSE_LED] = {
74 .name = "%s:blue-pulse:",
75 .brightness = LED_OFF,
76 .max_brightness = 255,
77 .brightness_set = shark_led_set_blue_pulse,
78 },
79 [RED_LED] = {
80 .name = "%s:red:",
81 .brightness = LED_OFF,
82 .max_brightness = 1,
83 .brightness_set = shark_led_set_red,
84 },
85};
86
87struct shark_device {
88 struct usb_device *usbdev;
89 struct v4l2_device v4l2_dev;
90 struct snd_tea575x tea;
91
92 struct work_struct led_work;
93 struct led_classdev leds[NO_LEDS];
94 char led_names[NO_LEDS][32];
95 atomic_t brightness[NO_LEDS];
96 unsigned long brightness_new;
97
98 u8 *transfer_buffer;
99 u32 last_val;
100};
101
102static atomic_t shark_instance = ATOMIC_INIT(0);
103
104static void shark_write_val(struct snd_tea575x *tea, u32 val)
105{
106 struct shark_device *shark = tea->private_data;
107 int i, res, actual_len;
108
109 /* Avoid unnecessary (slow) USB transfers */
110 if (shark->last_val == val)
111 return;
112
113 memset(shark->transfer_buffer, 0, TB_LEN);
114 shark->transfer_buffer[0] = 0xc0; /* Write shift register command */
115 for (i = 0; i < 4; i++)
116 shark->transfer_buffer[i] |= (val >> (24 - i * 8)) & 0xff;
117
118 res = usb_interrupt_msg(shark->usbdev,
119 usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
120 shark->transfer_buffer, TB_LEN,
121 &actual_len, 1000);
122 if (res >= 0)
123 shark->last_val = val;
124 else
125 v4l2_err(&shark->v4l2_dev, "set-freq error: %d\n", res);
126}
127
128static u32 shark_read_val(struct snd_tea575x *tea)
129{
130 struct shark_device *shark = tea->private_data;
131 int i, res, actual_len;
132 u32 val = 0;
133
134 memset(shark->transfer_buffer, 0, TB_LEN);
135 shark->transfer_buffer[0] = 0x80;
136 res = usb_interrupt_msg(shark->usbdev,
137 usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
138 shark->transfer_buffer, TB_LEN,
139 &actual_len, 1000);
140 if (res < 0) {
141 v4l2_err(&shark->v4l2_dev, "request-status error: %d\n", res);
142 return shark->last_val;
143 }
144
145 res = usb_interrupt_msg(shark->usbdev,
146 usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
147 shark->transfer_buffer, TB_LEN,
148 &actual_len, 1000);
149 if (res < 0) {
150 v4l2_err(&shark->v4l2_dev, "get-status error: %d\n", res);
151 return shark->last_val;
152 }
153
154 for (i = 0; i < 4; i++)
155 val |= shark->transfer_buffer[i] << (24 - i * 8);
156
157 shark->last_val = val;
158
159 /*
160 * The shark does not allow actually reading the stereo / mono pin :(
161 * So assume that when we're tuned to an FM station and mono has not
162 * been requested, that we're receiving stereo.
163 */
164 if (((val & TEA575X_BIT_BAND_MASK) == TEA575X_BIT_BAND_FM) &&
165 !(val & TEA575X_BIT_MONO))
166 shark->tea.stereo = true;
167 else
168 shark->tea.stereo = false;
169
170 return val;
171}
172
173static struct snd_tea575x_ops shark_tea_ops = {
174 .write_val = shark_write_val,
175 .read_val = shark_read_val,
176};
177
178static void shark_led_work(struct work_struct *work)
179{
180 struct shark_device *shark =
181 container_of(work, struct shark_device, led_work);
182 int i, res, brightness, actual_len;
183
184 /*
185 * We use the v4l2_dev lock and registered bit to ensure the device
186 * does not get unplugged and unreffed while we're running.
187 */
188 mutex_lock(&shark->tea.mutex);
189 if (!video_is_registered(&shark->tea.vd))
190 goto leave;
191
192 for (i = 0; i < 3; i++) {
193 if (!test_and_clear_bit(i, &shark->brightness_new))
194 continue;
195
196 brightness = atomic_read(&shark->brightness[i]);
197 memset(shark->transfer_buffer, 0, TB_LEN);
198 if (i != RED_LED) {
199 shark->transfer_buffer[0] = 0xA0 + i;
200 shark->transfer_buffer[1] = brightness;
201 } else
202 shark->transfer_buffer[0] = brightness ? 0xA9 : 0xA8;
203 res = usb_interrupt_msg(shark->usbdev,
204 usb_sndintpipe(shark->usbdev, 0x05),
205 shark->transfer_buffer, TB_LEN,
206 &actual_len, 1000);
207 if (res < 0)
208 v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
209 shark->led_names[i], res);
210 }
211leave:
212 mutex_unlock(&shark->tea.mutex);
213}
214
215static void shark_led_set_blue(struct led_classdev *led_cdev,
216 enum led_brightness value)
217{
218 struct shark_device *shark =
219 container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
220
221 atomic_set(&shark->brightness[BLUE_LED], value);
222 set_bit(BLUE_LED, &shark->brightness_new);
223 schedule_work(&shark->led_work);
224}
225
226static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
227 enum led_brightness value)
228{
229 struct shark_device *shark = container_of(led_cdev,
230 struct shark_device, leds[BLUE_PULSE_LED]);
231
232 atomic_set(&shark->brightness[BLUE_PULSE_LED], 256 - value);
233 set_bit(BLUE_PULSE_LED, &shark->brightness_new);
234 schedule_work(&shark->led_work);
235}
236
237static void shark_led_set_red(struct led_classdev *led_cdev,
238 enum led_brightness value)
239{
240 struct shark_device *shark =
241 container_of(led_cdev, struct shark_device, leds[RED_LED]);
242
243 atomic_set(&shark->brightness[RED_LED], value);
244 set_bit(RED_LED, &shark->brightness_new);
245 schedule_work(&shark->led_work);
246}
247
248static void usb_shark_disconnect(struct usb_interface *intf)
249{
250 struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
251 struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
252 int i;
253
254 mutex_lock(&shark->tea.mutex);
255 v4l2_device_disconnect(&shark->v4l2_dev);
256 snd_tea575x_exit(&shark->tea);
257 mutex_unlock(&shark->tea.mutex);
258
259 for (i = 0; i < NO_LEDS; i++)
260 led_classdev_unregister(&shark->leds[i]);
261
262 v4l2_device_put(&shark->v4l2_dev);
263}
264
265static void usb_shark_release(struct v4l2_device *v4l2_dev)
266{
267 struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
268
269 cancel_work_sync(&shark->led_work);
270 v4l2_device_unregister(&shark->v4l2_dev);
271 kfree(shark->transfer_buffer);
272 kfree(shark);
273}
274
275static int usb_shark_probe(struct usb_interface *intf,
276 const struct usb_device_id *id)
277{
278 struct shark_device *shark;
279 int i, retval = -ENOMEM;
280
281 shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
282 if (!shark)
283 return retval;
284
285 shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
286 if (!shark->transfer_buffer)
287 goto err_alloc_buffer;
288
289 /*
290 * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
291 * pointer in intfdata causing v4l2-device.c to not set it. Which
292 * results in usb_shark_disconnect() referencing the dangling pointer
293 *
294 * REMOVE (as soon as the above bug is fixed, patch submitted)
295 */
296 usb_set_intfdata(intf, NULL);
297
298 shark->v4l2_dev.release = usb_shark_release;
299 v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
300 retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
301 if (retval) {
302 v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
303 goto err_reg_dev;
304 }
305
306 shark->usbdev = interface_to_usbdev(intf);
307 shark->tea.v4l2_dev = &shark->v4l2_dev;
308 shark->tea.private_data = shark;
309 shark->tea.radio_nr = -1;
310 shark->tea.ops = &shark_tea_ops;
311 shark->tea.cannot_mute = true;
312 strlcpy(shark->tea.card, "Griffin radioSHARK",
313 sizeof(shark->tea.card));
314 usb_make_path(shark->usbdev, shark->tea.bus_info,
315 sizeof(shark->tea.bus_info));
316
317 retval = snd_tea575x_init(&shark->tea, THIS_MODULE);
318 if (retval) {
319 v4l2_err(&shark->v4l2_dev, "couldn't init tea5757\n");
320 goto err_init_tea;
321 }
322
323 INIT_WORK(&shark->led_work, shark_led_work);
324 for (i = 0; i < NO_LEDS; i++) {
325 shark->leds[i] = shark_led_templates[i];
326 snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
327 shark->leds[i].name, shark->v4l2_dev.name);
328 shark->leds[i].name = shark->led_names[i];
329 /*
330 * We don't fail the probe if we fail to register the leds,
331 * because once we've called snd_tea575x_init, the /dev/radio0
332 * node may be opened from userspace holding a reference to us!
333 *
334 * Note we cannot register the leds first instead as
335 * shark_led_work depends on the v4l2 mutex and registered bit.
336 */
337 retval = led_classdev_register(&intf->dev, &shark->leds[i]);
338 if (retval)
339 v4l2_err(&shark->v4l2_dev,
340 "couldn't register led: %s\n",
341 shark->led_names[i]);
342 }
343
344 return 0;
345
346err_init_tea:
347 v4l2_device_unregister(&shark->v4l2_dev);
348err_reg_dev:
349 kfree(shark->transfer_buffer);
350err_alloc_buffer:
351 kfree(shark);
352
353 return retval;
354}
355
356/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
357static struct usb_device_id usb_shark_device_table[] = {
358 { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
359 USB_DEVICE_ID_MATCH_INT_CLASS,
360 .idVendor = 0x077d,
361 .idProduct = 0x627a,
362 .bcdDevice_lo = 0x0001,
363 .bcdDevice_hi = 0x0001,
364 .bInterfaceClass = 3,
365 },
366 { }
367};
368MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
369
370static struct usb_driver usb_shark_driver = {
371 .name = DRV_NAME,
372 .probe = usb_shark_probe,
373 .disconnect = usb_shark_disconnect,
374 .id_table = usb_shark_device_table,
375};
376module_usb_driver(usb_shark_driver);