aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio/radio-shark2.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2012-06-23 03:39:58 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-07-30 17:46:17 -0400
commit4faba767c6243b43ad975406fe027be7394e4591 (patch)
tree2c2d6848b37080ed12b554214b655b55d3e83241 /drivers/media/radio/radio-shark2.c
parent7a3ed2d95e9ef3032700c2e56f3369d8652a6e8b (diff)
[media] shark2: New driver for the Griffin radioSHARK v2 USB radio receiver
This driver consists of 2 parts, a generic tea5777 driver and a driver for the Griffin radioSHARK v2 USB radio receiver, which is the only driver using the generic tea5777 for now. This first version only implements FM support, once the the new VIDIOC_ENUM_FREQ_BANDS API is upstream I'll also add AM support. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio/radio-shark2.c')
-rw-r--r--drivers/media/radio/radio-shark2.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c
new file mode 100644
index 000000000000..b9575de3e7e8
--- /dev/null
+++ b/drivers/media/radio/radio-shark2.c
@@ -0,0 +1,348 @@
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 * 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 "radio-tea5777.h"
37
38MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
39MODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver");
40MODULE_LICENSE("GPL");
41
42static int debug;
43module_param(debug, int, 0);
44MODULE_PARM_DESC(debug, "Debug level (0-1)");
45
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
55enum { BLUE_LED, RED_LED, NO_LEDS };
56
57static void shark_led_set_blue(struct led_classdev *led_cdev,
58 enum led_brightness value);
59static void shark_led_set_red(struct led_classdev *led_cdev,
60 enum led_brightness value);
61
62static const struct led_classdev shark_led_templates[NO_LEDS] = {
63 [BLUE_LED] = {
64 .name = "%s:blue:",
65 .brightness = LED_OFF,
66 .max_brightness = 127,
67 .brightness_set = shark_led_set_blue,
68 },
69 [RED_LED] = {
70 .name = "%s:red:",
71 .brightness = LED_OFF,
72 .max_brightness = 1,
73 .brightness_set = shark_led_set_red,
74 },
75};
76
77struct shark_device {
78 struct usb_device *usbdev;
79 struct v4l2_device v4l2_dev;
80 struct radio_tea5777 tea;
81
82 struct work_struct led_work;
83 struct led_classdev leds[NO_LEDS];
84 char led_names[NO_LEDS][32];
85 atomic_t brightness[NO_LEDS];
86 unsigned long brightness_new;
87
88 u8 *transfer_buffer;
89};
90
91static atomic_t shark_instance = ATOMIC_INIT(0);
92
93static int shark_write_reg(struct radio_tea5777 *tea, u64 reg)
94{
95 struct shark_device *shark = tea->private_data;
96 int i, res, actual_len;
97
98 memset(shark->transfer_buffer, 0, TB_LEN);
99 shark->transfer_buffer[0] = 0x81; /* Write register command */
100 for (i = 0; i < 6; i++)
101 shark->transfer_buffer[i + 1] = (reg >> (40 - i * 8)) & 0xff;
102
103 v4l2_dbg(1, debug, tea->v4l2_dev,
104 "shark2-write: %02x %02x %02x %02x %02x %02x %02x\n",
105 shark->transfer_buffer[0], shark->transfer_buffer[1],
106 shark->transfer_buffer[2], shark->transfer_buffer[3],
107 shark->transfer_buffer[4], shark->transfer_buffer[5],
108 shark->transfer_buffer[6]);
109
110 res = usb_interrupt_msg(shark->usbdev,
111 usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
112 shark->transfer_buffer, TB_LEN,
113 &actual_len, 1000);
114 if (res < 0) {
115 v4l2_err(tea->v4l2_dev, "write error: %d\n", res);
116 return res;
117 }
118
119 return 0;
120}
121
122static int shark_read_reg(struct radio_tea5777 *tea, u32 *reg_ret)
123{
124 struct shark_device *shark = tea->private_data;
125 int i, res, actual_len;
126 u32 reg = 0;
127
128 memset(shark->transfer_buffer, 0, TB_LEN);
129 shark->transfer_buffer[0] = 0x82;
130 res = usb_interrupt_msg(shark->usbdev,
131 usb_sndintpipe(shark->usbdev, SHARK_OUT_EP),
132 shark->transfer_buffer, TB_LEN,
133 &actual_len, 1000);
134 if (res < 0) {
135 v4l2_err(tea->v4l2_dev, "request-read error: %d\n", res);
136 return res;
137 }
138
139 res = usb_interrupt_msg(shark->usbdev,
140 usb_rcvintpipe(shark->usbdev, SHARK_IN_EP),
141 shark->transfer_buffer, TB_LEN,
142 &actual_len, 1000);
143 if (res < 0) {
144 v4l2_err(tea->v4l2_dev, "read error: %d\n", res);
145 return res;
146 }
147
148 for (i = 0; i < 3; i++)
149 reg |= shark->transfer_buffer[i] << (16 - i * 8);
150
151 v4l2_dbg(1, debug, tea->v4l2_dev, "shark2-read: %02x %02x %02x\n",
152 shark->transfer_buffer[0], shark->transfer_buffer[1],
153 shark->transfer_buffer[2]);
154
155 *reg_ret = reg;
156 return 0;
157}
158
159static struct radio_tea5777_ops shark_tea_ops = {
160 .write_reg = shark_write_reg,
161 .read_reg = shark_read_reg,
162};
163
164static void shark_led_work(struct work_struct *work)
165{
166 struct shark_device *shark =
167 container_of(work, struct shark_device, led_work);
168 int i, res, brightness, actual_len;
169 /*
170 * We use the v4l2_dev lock and registered bit to ensure the device
171 * does not get unplugged and unreffed while we're running.
172 */
173 mutex_lock(&shark->tea.mutex);
174 if (!video_is_registered(&shark->tea.vd))
175 goto leave;
176
177 for (i = 0; i < 2; i++) {
178 if (!test_and_clear_bit(i, &shark->brightness_new))
179 continue;
180
181 brightness = atomic_read(&shark->brightness[i]);
182 memset(shark->transfer_buffer, 0, TB_LEN);
183 shark->transfer_buffer[0] = 0x83 + i;
184 shark->transfer_buffer[1] = brightness;
185 res = usb_interrupt_msg(shark->usbdev,
186 usb_sndintpipe(shark->usbdev,
187 SHARK_OUT_EP),
188 shark->transfer_buffer, TB_LEN,
189 &actual_len, 1000);
190 if (res < 0)
191 v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
192 shark->led_names[i], res);
193 }
194leave:
195 mutex_unlock(&shark->tea.mutex);
196}
197
198static void shark_led_set_blue(struct led_classdev *led_cdev,
199 enum led_brightness value)
200{
201 struct shark_device *shark =
202 container_of(led_cdev, struct shark_device, leds[BLUE_LED]);
203
204 atomic_set(&shark->brightness[BLUE_LED], value);
205 set_bit(BLUE_LED, &shark->brightness_new);
206 schedule_work(&shark->led_work);
207}
208
209static void shark_led_set_red(struct led_classdev *led_cdev,
210 enum led_brightness value)
211{
212 struct shark_device *shark =
213 container_of(led_cdev, struct shark_device, leds[RED_LED]);
214
215 atomic_set(&shark->brightness[RED_LED], value);
216 set_bit(RED_LED, &shark->brightness_new);
217 schedule_work(&shark->led_work);
218}
219
220static void usb_shark_disconnect(struct usb_interface *intf)
221{
222 struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
223 struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
224 int i;
225
226 mutex_lock(&shark->tea.mutex);
227 v4l2_device_disconnect(&shark->v4l2_dev);
228 radio_tea5777_exit(&shark->tea);
229 mutex_unlock(&shark->tea.mutex);
230
231 for (i = 0; i < NO_LEDS; i++)
232 led_classdev_unregister(&shark->leds[i]);
233
234 v4l2_device_put(&shark->v4l2_dev);
235}
236
237static void usb_shark_release(struct v4l2_device *v4l2_dev)
238{
239 struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
240
241 cancel_work_sync(&shark->led_work);
242 v4l2_device_unregister(&shark->v4l2_dev);
243 kfree(shark->transfer_buffer);
244 kfree(shark);
245}
246
247static int usb_shark_probe(struct usb_interface *intf,
248 const struct usb_device_id *id)
249{
250 struct shark_device *shark;
251 int i, retval = -ENOMEM;
252
253 shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
254 if (!shark)
255 return retval;
256
257 shark->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL);
258 if (!shark->transfer_buffer)
259 goto err_alloc_buffer;
260
261 /*
262 * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
263 * pointer in intfdata causing v4l2-device.c to not set it. Which
264 * results in usb_shark_disconnect() referencing the dangling pointer
265 *
266 * REMOVE (as soon as the above bug is fixed, patch submitted)
267 */
268 usb_set_intfdata(intf, NULL);
269
270 shark->v4l2_dev.release = usb_shark_release;
271 v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
272 retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
273 if (retval) {
274 v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
275 goto err_reg_dev;
276 }
277
278 shark->usbdev = interface_to_usbdev(intf);
279 shark->tea.v4l2_dev = &shark->v4l2_dev;
280 shark->tea.private_data = shark;
281 shark->tea.ops = &shark_tea_ops;
282 shark->tea.has_am = true;
283 shark->tea.write_before_read = true;
284 strlcpy(shark->tea.card, "Griffin radioSHARK2",
285 sizeof(shark->tea.card));
286 usb_make_path(shark->usbdev, shark->tea.bus_info,
287 sizeof(shark->tea.bus_info));
288
289 retval = radio_tea5777_init(&shark->tea, THIS_MODULE);
290 if (retval) {
291 v4l2_err(&shark->v4l2_dev, "couldn't init tea5777\n");
292 goto err_init_tea;
293 }
294
295 INIT_WORK(&shark->led_work, shark_led_work);
296 for (i = 0; i < NO_LEDS; i++) {
297 shark->leds[i] = shark_led_templates[i];
298 snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
299 shark->leds[i].name, shark->v4l2_dev.name);
300 shark->leds[i].name = shark->led_names[i];
301 /*
302 * We don't fail the probe if we fail to register the leds,
303 * because once we've called radio_tea5777_init, the /dev/radio0
304 * node may be opened from userspace holding a reference to us!
305 *
306 * Note we cannot register the leds first instead as
307 * shark_led_work depends on the v4l2 mutex and registered bit.
308 */
309 retval = led_classdev_register(&intf->dev, &shark->leds[i]);
310 if (retval)
311 v4l2_err(&shark->v4l2_dev,
312 "couldn't register led: %s\n",
313 shark->led_names[i]);
314 }
315
316 return 0;
317
318err_init_tea:
319 v4l2_device_unregister(&shark->v4l2_dev);
320err_reg_dev:
321 kfree(shark->transfer_buffer);
322err_alloc_buffer:
323 kfree(shark);
324
325 return retval;
326}
327
328/* Specify the bcdDevice value, as the radioSHARK and radioSHARK2 share ids */
329static struct usb_device_id usb_shark_device_table[] = {
330 { .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
331 USB_DEVICE_ID_MATCH_INT_CLASS,
332 .idVendor = 0x077d,
333 .idProduct = 0x627a,
334 .bcdDevice_lo = 0x0010,
335 .bcdDevice_hi = 0x0010,
336 .bInterfaceClass = 3,
337 },
338 { }
339};
340MODULE_DEVICE_TABLE(usb, usb_shark_device_table);
341
342static struct usb_driver usb_shark_driver = {
343 .name = DRV_NAME,
344 .probe = usb_shark_probe,
345 .disconnect = usb_shark_disconnect,
346 .id_table = usb_shark_device_table,
347};
348module_usb_driver(usb_shark_driver);