blob: 3bd07f7e377489198ca539deee8e3401ffa5056a [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs
2 into both the USB and an analog audio input, so this thing
3 only deals with initialisation and frequency setting, the
4 audio data has to be handled by a sound driver.
5
6 Major issue: I can't find out where the device reports the signal
7 strength, and indeed the windows software appearantly just looks
8 at the stereo indicator as well. So, scanning will only find
9 stereo stations. Sad, but I can't help it.
10
11 Also, the windows program sends oodles of messages over to the
12 device, and I couldn't figure out their meaning. My suspicion
13 is that they don't have any:-)
14
15 You might find some interesting stuff about this module at
16 http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
17
18 Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.de>
19
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation; either version 2 of the License, or
23 (at your option) any later version.
24
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
29
30 You should have received a copy of the GNU General Public License
31 along with this program; if not, write to the Free Software
32 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
33
34 History:
35
Douglas Landgraf7002a4f2007-05-07 16:43:01 -030036 Version 0.42:
37 Converted dsbr100 to use video_ioctl2
38 by Douglas Landgraf <dougsland@gmail.com>
39
Alan Cox5aff3082006-08-08 15:47:50 -030040 Version 0.41-ac1:
41 Alan Cox: Some cleanups and fixes
42
43 Version 0.41:
44 Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
45
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 Version 0.40:
Alan Cox5aff3082006-08-08 15:47:50 -030047 Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
49 Version 0.30:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030050 Markus: Updates for 2.5.x kernel and more ISO compliant source
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52 Version 0.25:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030053 PSL and Markus: Cleanup, radio now doesn't stop on device close
Linus Torvalds1da177e2005-04-16 15:20:36 -070054
55 Version 0.24:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030056 Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally
Linus Torvalds1da177e2005-04-16 15:20:36 -070057 right. Some minor cleanup, improved standalone compilation
58
59 Version 0.23:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030060 Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
62 Version 0.22:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030063 Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns,
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 thanks to Mike Cox for pointing the problem out.
65
66 Version 0.21:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030067 Markus: Minor cleanup, warnings if something goes wrong, lame attempt
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 to adhere to Documentation/CodingStyle
69
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -030070 Version 0.2:
71 Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
Linus Torvalds1da177e2005-04-16 15:20:36 -070072 Markus: Copyright clarification
73
74 Version 0.01: Markus: initial release
75
76*/
77
Linus Torvalds1da177e2005-04-16 15:20:36 -070078#include <linux/kernel.h>
79#include <linux/module.h>
80#include <linux/init.h>
81#include <linux/slab.h>
82#include <linux/input.h>
Alan Cox5aff3082006-08-08 15:47:50 -030083#include <linux/videodev2.h>
Mauro Carvalho Chehab5e87efa2006-06-05 10:26:32 -030084#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070085#include <linux/usb.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
87/*
88 * Version Information
89 */
Alan Cox5aff3082006-08-08 15:47:50 -030090#include <linux/version.h> /* for KERNEL_VERSION MACRO */
91
92#define DRIVER_VERSION "v0.41"
93#define RADIO_VERSION KERNEL_VERSION(0,4,1)
94
95static struct v4l2_queryctrl radio_qctrl[] = {
96 {
97 .id = V4L2_CID_AUDIO_MUTE,
98 .name = "Mute",
99 .minimum = 0,
100 .maximum = 1,
101 .default_value = 1,
102 .type = V4L2_CTRL_TYPE_BOOLEAN,
103 }
104};
105
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106#define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
107#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
108
109#define DSB100_VENDOR 0x04b4
110#define DSB100_PRODUCT 0x1002
111
112/* Commands the device appears to understand */
113#define DSB100_TUNE 1
114#define DSB100_ONOFF 2
115
116#define TB_LEN 16
117
118/* Frequency limits in MHz -- these are European values. For Japanese
119devices, that would be 76 and 91. */
120#define FREQ_MIN 87.5
121#define FREQ_MAX 108.0
122#define FREQ_MUL 16000
123
124
125static int usb_dsbr100_probe(struct usb_interface *intf,
126 const struct usb_device_id *id);
127static void usb_dsbr100_disconnect(struct usb_interface *intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128static int usb_dsbr100_open(struct inode *inode, struct file *file);
129static int usb_dsbr100_close(struct inode *inode, struct file *file);
130
131static int radio_nr = -1;
132module_param(radio_nr, int, 0);
133
134/* Data for one (physical) device */
Alan Cox5aff3082006-08-08 15:47:50 -0300135struct dsbr100_device {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 struct usb_device *usbdev;
137 struct video_device *videodev;
138 unsigned char transfer_buffer[TB_LEN];
139 int curfreq;
140 int stereo;
141 int users;
142 int removed;
Alan Cox5aff3082006-08-08 15:47:50 -0300143 int muted;
144};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147static struct usb_device_id usb_dsbr100_device_table [] = {
148 { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
149 { } /* Terminating entry */
150};
151
152MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);
153
154/* USB subsystem interface */
155static struct usb_driver usb_dsbr100_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 .name = "dsbr100",
157 .probe = usb_dsbr100_probe,
158 .disconnect = usb_dsbr100_disconnect,
159 .id_table = usb_dsbr100_device_table,
160};
161
162/* Low-level device interface begins here */
163
164/* switch on radio */
Alan Cox5aff3082006-08-08 15:47:50 -0300165static int dsbr100_start(struct dsbr100_device *radio)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166{
167 if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300168 USB_REQ_GET_STATUS,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
170 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
171 usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300172 DSB100_ONOFF,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
174 0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
175 return -1;
Alan Cox5aff3082006-08-08 15:47:50 -0300176 radio->muted=0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 return (radio->transfer_buffer)[0];
178}
179
180
181/* switch off radio */
Alan Cox5aff3082006-08-08 15:47:50 -0300182static int dsbr100_stop(struct dsbr100_device *radio)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183{
184 if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300185 USB_REQ_GET_STATUS,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
187 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
188 usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300189 DSB100_ONOFF,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
191 0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
192 return -1;
Alan Cox5aff3082006-08-08 15:47:50 -0300193 radio->muted=1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 return (radio->transfer_buffer)[0];
195}
196
197/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
Alan Cox5aff3082006-08-08 15:47:50 -0300198static int dsbr100_setfreq(struct dsbr100_device *radio, int freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199{
200 freq = (freq/16*80)/1000+856;
201 if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300202 DSB100_TUNE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300204 (freq>>8)&0x00ff, freq&0xff,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 radio->transfer_buffer, 8, 300)<0 ||
206 usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300207 USB_REQ_GET_STATUS,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
209 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
210 usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300211 USB_REQ_GET_STATUS,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
213 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
214 radio->stereo = -1;
215 return -1;
216 }
217 radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
218 return (radio->transfer_buffer)[0];
219}
220
221/* return the device status. This is, in effect, just whether it
222sees a stereo signal or not. Pity. */
Alan Cox5aff3082006-08-08 15:47:50 -0300223static void dsbr100_getstat(struct dsbr100_device *radio)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224{
225 if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300226 USB_REQ_GET_STATUS,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
228 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
229 radio->stereo = -1;
230 else
231 radio->stereo = ! (radio->transfer_buffer[0]&0x01);
232}
233
234
235/* USB subsystem interface begins here */
236
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237/* handle unplugging of the device, release data structures
238if nothing keeps us from doing it. If something is still
239keeping us busy, the release callback of v4l will take care
240of releasing it. stv680.c does not relase its private
241data, so I don't do this here either. Checking out the
242code I'd expect I better did that, but if there's a memory
243leak here it's tiny (~50 bytes per disconnect) */
244static void usb_dsbr100_disconnect(struct usb_interface *intf)
245{
Alan Cox5aff3082006-08-08 15:47:50 -0300246 struct dsbr100_device *radio = usb_get_intfdata(intf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
248 usb_set_intfdata (intf, NULL);
249 if (radio) {
250 video_unregister_device(radio->videodev);
251 radio->videodev = NULL;
252 if (radio->users) {
253 kfree(radio);
254 } else {
255 radio->removed = 1;
256 }
257 }
258}
259
260
Douglas Landgraf7002a4f2007-05-07 16:43:01 -0300261static int vidioc_querycap(struct file *file, void *priv,
262 struct v4l2_capability *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263{
Douglas Landgraf7002a4f2007-05-07 16:43:01 -0300264 strlcpy(v->driver, "dsbr100", sizeof(v->driver));
265 strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card));
266 sprintf(v->bus_info, "ISA");
267 v->version = RADIO_VERSION;
268 v->capabilities = V4L2_CAP_TUNER;
269 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270}
271
Douglas Landgraf7002a4f2007-05-07 16:43:01 -0300272static int vidioc_g_tuner(struct file *file, void *priv,
273 struct v4l2_tuner *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
Douglas Landgraf7002a4f2007-05-07 16:43:01 -0300275 struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
276
277 if (v->index > 0)
278 return -EINVAL;
279
280 dsbr100_getstat(radio);
281 strcpy(v->name, "FM");
282 v->type = V4L2_TUNER_RADIO;
283 v->rangelow = FREQ_MIN*FREQ_MUL;
284 v->rangehigh = FREQ_MAX*FREQ_MUL;
285 v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
286 v->capability = V4L2_TUNER_CAP_LOW;
287 if(radio->stereo)
288 v->audmode = V4L2_TUNER_MODE_STEREO;
289 else
290 v->audmode = V4L2_TUNER_MODE_MONO;
291 v->signal = 0xffff; /* We can't get the signal strength */
292 return 0;
293}
294
295static int vidioc_s_tuner(struct file *file, void *priv,
296 struct v4l2_tuner *v)
297{
298 if (v->index > 0)
299 return -EINVAL;
300
301 return 0;
302}
303
304static int vidioc_s_frequency(struct file *file, void *priv,
305 struct v4l2_frequency *f)
306{
307 struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
308
309 radio->curfreq = f->frequency;
310 if (dsbr100_setfreq(radio, radio->curfreq)==-1)
311 warn("Set frequency failed");
312 return 0;
313}
314
315static int vidioc_g_frequency(struct file *file, void *priv,
316 struct v4l2_frequency *f)
317{
318 struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
319
320 f->type = V4L2_TUNER_RADIO;
321 f->frequency = radio->curfreq;
322 return 0;
323}
324
325static int vidioc_queryctrl(struct file *file, void *priv,
326 struct v4l2_queryctrl *qc)
327{
328 int i;
329
330 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
331 if (qc->id && qc->id == radio_qctrl[i].id) {
332 memcpy(qc, &(radio_qctrl[i]),
333 sizeof(*qc));
334 return 0;
335 }
336 }
337 return -EINVAL;
338}
339
340static int vidioc_g_ctrl(struct file *file, void *priv,
341 struct v4l2_control *ctrl)
342{
343 struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
344
345 switch (ctrl->id) {
346 case V4L2_CID_AUDIO_MUTE:
347 ctrl->value = radio->muted;
348 return 0;
349 }
350 return -EINVAL;
351}
352
353static int vidioc_s_ctrl(struct file *file, void *priv,
354 struct v4l2_control *ctrl)
355{
356 struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
357
358 switch (ctrl->id) {
359 case V4L2_CID_AUDIO_MUTE:
360 if (ctrl->value) {
361 if (dsbr100_stop(radio)==-1)
362 warn("Radio did not respond properly");
363 } else {
364 if (dsbr100_start(radio)==-1)
365 warn("Radio did not respond properly");
366 }
367 return 0;
368 }
369 return -EINVAL;
370}
371
372static int vidioc_g_audio(struct file *file, void *priv,
373 struct v4l2_audio *a)
374{
375 if (a->index > 1)
376 return -EINVAL;
377
378 strcpy(a->name, "Radio");
379 a->capability = V4L2_AUDCAP_STEREO;
380 return 0;
381}
382
383static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
384{
385 *i = 0;
386 return 0;
387}
388
389static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
390{
391 if (i != 0)
392 return -EINVAL;
393 return 0;
394}
395
396static int vidioc_s_audio(struct file *file, void *priv,
397 struct v4l2_audio *a)
398{
399 if (a->index != 0)
400 return -EINVAL;
401 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402}
403
404static int usb_dsbr100_open(struct inode *inode, struct file *file)
405{
Alan Cox5aff3082006-08-08 15:47:50 -0300406 struct dsbr100_device *radio=video_get_drvdata(video_devdata(file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
408 radio->users = 1;
Alan Cox5aff3082006-08-08 15:47:50 -0300409 radio->muted = 1;
410
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 if (dsbr100_start(radio)<0) {
412 warn("Radio did not start up properly");
413 radio->users = 0;
414 return -EIO;
415 }
416 dsbr100_setfreq(radio, radio->curfreq);
417 return 0;
418}
419
420static int usb_dsbr100_close(struct inode *inode, struct file *file)
421{
Alan Cox5aff3082006-08-08 15:47:50 -0300422 struct dsbr100_device *radio=video_get_drvdata(video_devdata(file));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423
424 if (!radio)
425 return -ENODEV;
426 radio->users = 0;
427 if (radio->removed) {
428 kfree(radio);
429 }
430 return 0;
431}
432
Douglas Landgraf7002a4f2007-05-07 16:43:01 -0300433/* File system interface */
434static const struct file_operations usb_dsbr100_fops = {
435 .owner = THIS_MODULE,
436 .open = usb_dsbr100_open,
437 .release = usb_dsbr100_close,
438 .ioctl = video_ioctl2,
439 .compat_ioctl = v4l_compat_ioctl32,
440 .llseek = no_llseek,
441};
442
443/* V4L2 interface */
444static struct video_device dsbr100_videodev_template =
445{
446 .owner = THIS_MODULE,
447 .name = "D-Link DSB-R 100",
448 .type = VID_TYPE_TUNER,
449 .fops = &usb_dsbr100_fops,
450 .release = video_device_release,
451 .vidioc_querycap = vidioc_querycap,
452 .vidioc_g_tuner = vidioc_g_tuner,
453 .vidioc_s_tuner = vidioc_s_tuner,
454 .vidioc_g_frequency = vidioc_g_frequency,
455 .vidioc_s_frequency = vidioc_s_frequency,
456 .vidioc_queryctrl = vidioc_queryctrl,
457 .vidioc_g_ctrl = vidioc_g_ctrl,
458 .vidioc_s_ctrl = vidioc_s_ctrl,
459 .vidioc_g_audio = vidioc_g_audio,
460 .vidioc_s_audio = vidioc_s_audio,
461 .vidioc_g_input = vidioc_g_input,
462 .vidioc_s_input = vidioc_s_input,
463};
464
465/* check if the device is present and register with v4l and
466usb if it is */
467static int usb_dsbr100_probe(struct usb_interface *intf,
468 const struct usb_device_id *id)
469{
470 struct dsbr100_device *radio;
471
472 if (!(radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL)))
473 return -ENOMEM;
474 if (!(radio->videodev = video_device_alloc())) {
475 kfree(radio);
476 return -ENOMEM;
477 }
478 memcpy(radio->videodev, &dsbr100_videodev_template,
479 sizeof(dsbr100_videodev_template));
480 radio->removed = 0;
481 radio->users = 0;
482 radio->usbdev = interface_to_usbdev(intf);
483 radio->curfreq = FREQ_MIN*FREQ_MUL;
484 video_set_drvdata(radio->videodev, radio);
485 if (video_register_device(radio->videodev, VFL_TYPE_RADIO,radio_nr)) {
486 warn("Could not register video device");
487 video_device_release(radio->videodev);
488 kfree(radio);
489 return -EIO;
490 }
491 usb_set_intfdata(intf, radio);
492 return 0;
493}
494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495static int __init dsbr100_init(void)
496{
497 int retval = usb_register(&usb_dsbr100_driver);
498 info(DRIVER_VERSION ":" DRIVER_DESC);
499 return retval;
500}
501
502static void __exit dsbr100_exit(void)
503{
504 usb_deregister(&usb_dsbr100_driver);
505}
506
507module_init (dsbr100_init);
508module_exit (dsbr100_exit);
509
510MODULE_AUTHOR( DRIVER_AUTHOR );
511MODULE_DESCRIPTION( DRIVER_DESC );
512MODULE_LICENSE("GPL");