Merge with http://kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
[linux-2.6] / drivers / usb / media / dsbr100.c
1 /* 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
36  Version 0.40:
37   Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing
38
39  Version 0.30:
40         Markus: Updates for 2.5.x kernel and more ISO compliant source
41
42  Version 0.25:
43         PSL and Markus: Cleanup, radio now doesn't stop on device close
44
45  Version 0.24:
46         Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally
47         right.  Some minor cleanup, improved standalone compilation
48
49  Version 0.23:
50         Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
51
52  Version 0.22:
53         Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, 
54         thanks to Mike Cox for pointing the problem out.
55
56  Version 0.21:
57         Markus: Minor cleanup, warnings if something goes wrong, lame attempt
58         to adhere to Documentation/CodingStyle
59
60  Version 0.2: 
61         Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
62         Markus: Copyright clarification
63
64  Version 0.01: Markus: initial release
65
66 */
67
68
69 #include <linux/kernel.h>
70 #include <linux/module.h>
71 #include <linux/init.h>
72 #include <linux/slab.h>
73 #include <linux/input.h>
74 #include <linux/videodev.h>
75 #include <linux/usb.h>
76 #include <linux/smp_lock.h>
77
78 /*
79  * Version Information
80  */
81 #define DRIVER_VERSION "v0.40"
82 #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
83 #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
84
85 #define DSB100_VENDOR 0x04b4
86 #define DSB100_PRODUCT 0x1002
87
88 /* Commands the device appears to understand */
89 #define DSB100_TUNE 1
90 #define DSB100_ONOFF 2
91
92 #define TB_LEN 16
93
94 /* Frequency limits in MHz -- these are European values.  For Japanese
95 devices, that would be 76 and 91.  */
96 #define FREQ_MIN  87.5
97 #define FREQ_MAX 108.0
98 #define FREQ_MUL 16000
99
100
101 static int usb_dsbr100_probe(struct usb_interface *intf,
102                              const struct usb_device_id *id);
103 static void usb_dsbr100_disconnect(struct usb_interface *intf);
104 static int usb_dsbr100_ioctl(struct inode *inode, struct file *file,
105                              unsigned int cmd, unsigned long arg);
106 static int usb_dsbr100_open(struct inode *inode, struct file *file);
107 static int usb_dsbr100_close(struct inode *inode, struct file *file);
108
109 static int radio_nr = -1;
110 module_param(radio_nr, int, 0);
111
112 /* Data for one (physical) device */
113 typedef struct {
114         struct usb_device *usbdev;
115         struct video_device *videodev;
116         unsigned char transfer_buffer[TB_LEN];
117         int curfreq;
118         int stereo;
119         int users;
120         int removed;
121 } dsbr100_device;
122
123
124 /* File system interface */
125 static struct file_operations usb_dsbr100_fops = {
126         .owner =        THIS_MODULE,
127         .open =         usb_dsbr100_open,
128         .release =      usb_dsbr100_close,
129         .ioctl =        usb_dsbr100_ioctl,
130         .llseek =       no_llseek,
131 };
132
133 /* V4L interface */
134 static struct video_device dsbr100_videodev_template=
135 {
136         .owner =        THIS_MODULE,
137         .name =         "D-Link DSB-R 100",
138         .type =         VID_TYPE_TUNER,
139         .hardware =     VID_HARDWARE_AZTECH,
140         .fops =         &usb_dsbr100_fops,
141         .release = video_device_release,
142 };
143
144 static struct usb_device_id usb_dsbr100_device_table [] = {
145         { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
146         { }                                             /* Terminating entry */
147 };
148
149 MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);
150
151 /* USB subsystem interface */
152 static struct usb_driver usb_dsbr100_driver = {
153         .name =         "dsbr100",
154         .probe =        usb_dsbr100_probe,
155         .disconnect =   usb_dsbr100_disconnect,
156         .id_table =     usb_dsbr100_device_table,
157 };
158
159 /* Low-level device interface begins here */
160
161 /* switch on radio */
162 static int dsbr100_start(dsbr100_device *radio)
163 {
164         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
165                         USB_REQ_GET_STATUS, 
166                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
167                         0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
168         usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
169                         DSB100_ONOFF, 
170                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
171                         0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
172                 return -1;
173         return (radio->transfer_buffer)[0];
174 }
175
176
177 /* switch off radio */
178 static int dsbr100_stop(dsbr100_device *radio)
179 {
180         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
181                         USB_REQ_GET_STATUS, 
182                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
183                         0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
184         usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
185                         DSB100_ONOFF, 
186                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
187                         0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
188                 return -1;
189         return (radio->transfer_buffer)[0];
190 }
191
192 /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
193 static int dsbr100_setfreq(dsbr100_device *radio, int freq)
194 {
195         freq = (freq/16*80)/1000+856;
196         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
197                         DSB100_TUNE, 
198                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
199                         (freq>>8)&0x00ff, freq&0xff, 
200                         radio->transfer_buffer, 8, 300)<0 ||
201            usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
202                         USB_REQ_GET_STATUS, 
203                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
204                         0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
205         usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
206                         USB_REQ_GET_STATUS, 
207                         USB_TYPE_VENDOR | USB_RECIP_DEVICE |  USB_DIR_IN,
208                         0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
209                 radio->stereo = -1;
210                 return -1;
211         }
212         radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
213         return (radio->transfer_buffer)[0];
214 }
215
216 /* return the device status.  This is, in effect, just whether it
217 sees a stereo signal or not.  Pity. */
218 static void dsbr100_getstat(dsbr100_device *radio)
219 {
220         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
221                 USB_REQ_GET_STATUS, 
222                 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
223                 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
224                 radio->stereo = -1;
225         else
226                 radio->stereo = ! (radio->transfer_buffer[0]&0x01);
227 }
228
229
230 /* USB subsystem interface begins here */
231
232 /* check if the device is present and register with v4l and
233 usb if it is */
234 static int usb_dsbr100_probe(struct usb_interface *intf, 
235                          const struct usb_device_id *id)
236 {
237         dsbr100_device *radio;
238
239         if (!(radio = kmalloc(sizeof(dsbr100_device), GFP_KERNEL)))
240                 return -ENOMEM;
241         if (!(radio->videodev = video_device_alloc())) {
242                 kfree(radio);
243                 return -ENOMEM;
244         }
245         memcpy(radio->videodev, &dsbr100_videodev_template, 
246                 sizeof(dsbr100_videodev_template));
247         radio->removed = 0;
248         radio->users = 0;
249         radio->usbdev = interface_to_usbdev(intf);
250         radio->curfreq = FREQ_MIN*FREQ_MUL;
251         video_set_drvdata(radio->videodev, radio);
252         if (video_register_device(radio->videodev, VFL_TYPE_RADIO,
253                 radio_nr)) {
254                 warn("Could not register video device");
255                 video_device_release(radio->videodev);
256                 kfree(radio);
257                 return -EIO;
258         }
259         usb_set_intfdata(intf, radio);
260         return 0;
261 }
262
263 /* handle unplugging of the device, release data structures
264 if nothing keeps us from doing it.  If something is still
265 keeping us busy, the release callback of v4l will take care
266 of releasing it.  stv680.c does not relase its private
267 data, so I don't do this here either.  Checking out the
268 code I'd expect I better did that, but if there's a memory
269 leak here it's tiny (~50 bytes per disconnect) */
270 static void usb_dsbr100_disconnect(struct usb_interface *intf)
271 {
272         dsbr100_device *radio = usb_get_intfdata(intf);
273
274         usb_set_intfdata (intf, NULL);
275         if (radio) {
276                 video_unregister_device(radio->videodev);
277                 radio->videodev = NULL;
278                 if (radio->users) {
279                         kfree(radio);
280                 } else {
281                         radio->removed = 1;
282                 }
283         }
284 }
285
286
287 /* Video for Linux interface */
288
289 static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file,
290                                 unsigned int cmd, void *arg)
291 {
292         dsbr100_device *radio=video_get_drvdata(video_devdata(file));
293
294         if (!radio)
295                 return -EIO;
296
297         switch(cmd) {
298                 case VIDIOCGCAP: {
299                         struct video_capability *v = arg;
300
301                         memset(v, 0, sizeof(*v));
302                         v->type = VID_TYPE_TUNER;
303                         v->channels = 1;
304                         v->audios = 1;
305                         strcpy(v->name, "D-Link R-100 USB FM Radio");
306                         return 0;
307                 }
308                 case VIDIOCGTUNER: {
309                         struct video_tuner *v = arg;
310
311                         dsbr100_getstat(radio);
312                         if(v->tuner)    /* Only 1 tuner */ 
313                                 return -EINVAL;
314                         v->rangelow = FREQ_MIN*FREQ_MUL;
315                         v->rangehigh = FREQ_MAX*FREQ_MUL;
316                         v->flags = VIDEO_TUNER_LOW;
317                         v->mode = VIDEO_MODE_AUTO;
318                         v->signal = radio->stereo*0x7000;
319                                 /* Don't know how to get signal strength */
320                         v->flags |= VIDEO_TUNER_STEREO_ON*radio->stereo;
321                         strcpy(v->name, "DSB R-100");
322                         return 0;
323                 }
324                 case VIDIOCSTUNER: {
325                         struct video_tuner *v = arg;
326
327                         if(v->tuner!=0)
328                                 return -EINVAL;
329                         /* Only 1 tuner so no setting needed ! */
330                         return 0;
331                 }
332                 case VIDIOCGFREQ: {
333                         int *freq = arg;
334
335                         if (radio->curfreq==-1)
336                                 return -EINVAL;
337                         *freq = radio->curfreq;
338                         return 0;
339                 }
340                 case VIDIOCSFREQ: {
341                         int *freq = arg;
342
343                         radio->curfreq = *freq;
344                         if (dsbr100_setfreq(radio, radio->curfreq)==-1)
345                                 warn("Set frequency failed");
346                         return 0;
347                 }
348                 case VIDIOCGAUDIO: {
349                         struct video_audio *v = arg;
350
351                         memset(v, 0, sizeof(*v));
352                         v->flags |= VIDEO_AUDIO_MUTABLE;
353                         v->mode = VIDEO_SOUND_STEREO;
354                         v->volume = 1;
355                         v->step = 1;
356                         strcpy(v->name, "Radio");
357                         return 0;                       
358                 }
359                 case VIDIOCSAUDIO: {
360                         struct video_audio *v = arg;
361
362                         if (v->audio) 
363                                 return -EINVAL;
364                         if (v->flags&VIDEO_AUDIO_MUTE) {
365                                 if (dsbr100_stop(radio)==-1)
366                                         warn("Radio did not respond properly");
367                         }
368                         else
369                                 if (dsbr100_start(radio)==-1)
370                                         warn("Radio did not respond properly");
371                         return 0;
372                 }
373                 default:
374                         return -ENOIOCTLCMD;
375         }
376 }
377
378 static int usb_dsbr100_ioctl(struct inode *inode, struct file *file,
379                              unsigned int cmd, unsigned long arg)
380 {
381         return video_usercopy(inode, file, cmd, arg, usb_dsbr100_do_ioctl);
382 }
383
384 static int usb_dsbr100_open(struct inode *inode, struct file *file)
385 {
386         dsbr100_device *radio=video_get_drvdata(video_devdata(file));
387
388         radio->users = 1;
389         if (dsbr100_start(radio)<0) {
390                 warn("Radio did not start up properly");
391                 radio->users = 0;
392                 return -EIO;
393         }
394         dsbr100_setfreq(radio, radio->curfreq);
395         return 0;
396 }
397
398 static int usb_dsbr100_close(struct inode *inode, struct file *file)
399 {
400         dsbr100_device *radio=video_get_drvdata(video_devdata(file));
401
402         if (!radio)
403                 return -ENODEV;
404         radio->users = 0;
405         if (radio->removed) {
406                 kfree(radio);
407         }
408         return 0;
409 }
410
411 static int __init dsbr100_init(void)
412 {
413         int retval = usb_register(&usb_dsbr100_driver);
414         info(DRIVER_VERSION ":" DRIVER_DESC);
415         return retval;
416 }
417
418 static void __exit dsbr100_exit(void)
419 {
420         usb_deregister(&usb_dsbr100_driver);
421 }
422
423 module_init (dsbr100_init);
424 module_exit (dsbr100_exit);
425
426 MODULE_AUTHOR( DRIVER_AUTHOR );
427 MODULE_DESCRIPTION( DRIVER_DESC );
428 MODULE_LICENSE("GPL");