patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / 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_PARM(radio_nr, "i");
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         .owner =        THIS_MODULE,
154         .name =         "dsbr100",
155         .probe =        usb_dsbr100_probe,
156         .disconnect =   usb_dsbr100_disconnect,
157         .id_table =     usb_dsbr100_device_table,
158 };
159
160 /* Low-level device interface begins here */
161
162 /* switch on radio */
163 static int dsbr100_start(dsbr100_device *radio)
164 {
165         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
166                         USB_REQ_GET_STATUS, 
167                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
168                         0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
169         usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
170                         DSB100_ONOFF, 
171                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
172                         0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
173                 return -1;
174         return (radio->transfer_buffer)[0];
175 }
176
177
178 /* switch off radio */
179 static int dsbr100_stop(dsbr100_device *radio)
180 {
181         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
182                         USB_REQ_GET_STATUS, 
183                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
184                         0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
185         usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
186                         DSB100_ONOFF, 
187                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
188                         0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
189                 return -1;
190         return (radio->transfer_buffer)[0];
191 }
192
193 /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
194 static int dsbr100_setfreq(dsbr100_device *radio, int freq)
195 {
196         freq = (freq/16*80)/1000+856;
197         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
198                         DSB100_TUNE, 
199                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
200                         (freq>>8)&0x00ff, freq&0xff, 
201                         radio->transfer_buffer, 8, 300)<0 ||
202            usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
203                         USB_REQ_GET_STATUS, 
204                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
205                         0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
206         usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
207                         USB_REQ_GET_STATUS, 
208                         USB_TYPE_VENDOR | USB_RECIP_DEVICE |  USB_DIR_IN,
209                         0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
210                 radio->stereo = -1;
211                 return -1;
212         }
213         radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
214         return (radio->transfer_buffer)[0];
215 }
216
217 /* return the device status.  This is, in effect, just whether it
218 sees a stereo signal or not.  Pity. */
219 static void dsbr100_getstat(dsbr100_device *radio)
220 {
221         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
222                 USB_REQ_GET_STATUS, 
223                 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
224                 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
225                 radio->stereo = -1;
226         else
227                 radio->stereo = ! (radio->transfer_buffer[0]&0x01);
228 }
229
230
231 /* USB subsystem interface begins here */
232
233 /* check if the device is present and register with v4l and
234 usb if it is */
235 static int usb_dsbr100_probe(struct usb_interface *intf, 
236                          const struct usb_device_id *id)
237 {
238         dsbr100_device *radio;
239
240         if (!(radio = kmalloc(sizeof(dsbr100_device), GFP_KERNEL)))
241                 return -ENOMEM;
242         if (!(radio->videodev = video_device_alloc())) {
243                 kfree(radio);
244                 return -ENOMEM;
245         }
246         memcpy(radio->videodev, &dsbr100_videodev_template, 
247                 sizeof(dsbr100_videodev_template));
248         radio->removed = 0;
249         radio->users = 0;
250         radio->usbdev = interface_to_usbdev(intf);
251         radio->curfreq = FREQ_MIN*FREQ_MUL;
252         video_set_drvdata(radio->videodev, radio);
253         if (video_register_device(radio->videodev, VFL_TYPE_RADIO,
254                 radio_nr)) {
255                 warn("Could not register video device");
256                 video_device_release(radio->videodev);
257                 kfree(radio);
258                 return -EIO;
259         }
260         usb_set_intfdata(intf, radio);
261         return 0;
262 }
263
264 /* handle unplugging of the device, release data structures
265 if nothing keeps us from doing it.  If something is still
266 keeping us busy, the release callback of v4l will take care
267 of releasing it.  stv680.c does not relase its private
268 data, so I don't do this here either.  Checking out the
269 code I'd expect I better did that, but if there's a memory
270 leak here it's tiny (~50 bytes per disconnect) */
271 static void usb_dsbr100_disconnect(struct usb_interface *intf)
272 {
273         dsbr100_device *radio = usb_get_intfdata(intf);
274
275         usb_set_intfdata (intf, NULL);
276         if (radio) {
277                 video_unregister_device(radio->videodev);
278                 radio->videodev = NULL;
279                 if (radio->users) {
280                         kfree(radio);
281                 } else {
282                         radio->removed = 1;
283                 }
284         }
285 }
286
287
288 /* Video for Linux interface */
289
290 static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file,
291                                 unsigned int cmd, void *arg)
292 {
293         dsbr100_device *radio=video_get_drvdata(video_devdata(file));
294
295         if (!radio)
296                 return -EIO;
297
298         switch(cmd) {
299                 case VIDIOCGCAP: {
300                         struct video_capability *v = arg;
301
302                         memset(v, 0, sizeof(*v));
303                         v->type = VID_TYPE_TUNER;
304                         v->channels = 1;
305                         v->audios = 1;
306                         strcpy(v->name, "D-Link R-100 USB FM Radio");
307                         return 0;
308                 }
309                 case VIDIOCGTUNER: {
310                         struct video_tuner *v = arg;
311
312                         dsbr100_getstat(radio);
313                         if(v->tuner)    /* Only 1 tuner */ 
314                                 return -EINVAL;
315                         v->rangelow = FREQ_MIN*FREQ_MUL;
316                         v->rangehigh = FREQ_MAX*FREQ_MUL;
317                         v->flags = VIDEO_TUNER_LOW;
318                         v->mode = VIDEO_MODE_AUTO;
319                         v->signal = radio->stereo*0x7000;
320                                 /* Don't know how to get signal strength */
321                         v->flags |= VIDEO_TUNER_STEREO_ON*radio->stereo;
322                         strcpy(v->name, "DSB R-100");
323                         return 0;
324                 }
325                 case VIDIOCSTUNER: {
326                         struct video_tuner *v = arg;
327
328                         if(v->tuner!=0)
329                                 return -EINVAL;
330                         /* Only 1 tuner so no setting needed ! */
331                         return 0;
332                 }
333                 case VIDIOCGFREQ: {
334                         int *freq = arg;
335
336                         if (radio->curfreq==-1)
337                                 return -EINVAL;
338                         *freq = radio->curfreq;
339                         return 0;
340                 }
341                 case VIDIOCSFREQ: {
342                         int *freq = arg;
343
344                         radio->curfreq = *freq;
345                         if (dsbr100_setfreq(radio, radio->curfreq)==-1)
346                                 warn("Set frequency failed");
347                         return 0;
348                 }
349                 case VIDIOCGAUDIO: {
350                         struct video_audio *v = arg;
351
352                         memset(v, 0, sizeof(*v));
353                         v->flags |= VIDEO_AUDIO_MUTABLE;
354                         v->mode = VIDEO_SOUND_STEREO;
355                         v->volume = 1;
356                         v->step = 1;
357                         strcpy(v->name, "Radio");
358                         return 0;                       
359                 }
360                 case VIDIOCSAUDIO: {
361                         struct video_audio *v = arg;
362
363                         if (v->audio) 
364                                 return -EINVAL;
365                         if (v->flags&VIDEO_AUDIO_MUTE) {
366                                 if (dsbr100_stop(radio)==-1)
367                                         warn("Radio did not respond properly");
368                         }
369                         else
370                                 if (dsbr100_start(radio)==-1)
371                                         warn("Radio did not respond properly");
372                         return 0;
373                 }
374                 default:
375                         return -ENOIOCTLCMD;
376         }
377 }
378
379 static int usb_dsbr100_ioctl(struct inode *inode, struct file *file,
380                              unsigned int cmd, unsigned long arg)
381 {
382         return video_usercopy(inode, file, cmd, arg, usb_dsbr100_do_ioctl);
383 }
384
385 static int usb_dsbr100_open(struct inode *inode, struct file *file)
386 {
387         dsbr100_device *radio=video_get_drvdata(video_devdata(file));
388
389         radio->users = 1;
390         if (dsbr100_start(radio)<0) {
391                 warn("Radio did not start up properly");
392                 radio->users = 0;
393                 return -EIO;
394         }
395         dsbr100_setfreq(radio, radio->curfreq);
396         return 0;
397 }
398
399 static int usb_dsbr100_close(struct inode *inode, struct file *file)
400 {
401         dsbr100_device *radio=video_get_drvdata(video_devdata(file));
402
403         if (!radio)
404                 return -ENODEV;
405         radio->users = 0;
406         if (radio->removed) {
407                 kfree(radio);
408         }
409         return 0;
410 }
411
412 static int __init dsbr100_init(void)
413 {
414         int retval = usb_register(&usb_dsbr100_driver);
415         info(DRIVER_VERSION ":" DRIVER_DESC);
416         return retval;
417 }
418
419 static void __exit dsbr100_exit(void)
420 {
421         usb_deregister(&usb_dsbr100_driver);
422 }
423
424 module_init (dsbr100_init);
425 module_exit (dsbr100_exit);
426
427 MODULE_AUTHOR( DRIVER_AUTHOR );
428 MODULE_DESCRIPTION( DRIVER_DESC );
429 MODULE_LICENSE("GPL");