File: /usr/src/linux/drivers/usb/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@tucana.harvard.edu>
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.24:
37      	Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally
38     	right.  Some minor cleanup, improved standalone compilation
39     
40      Version 0.23:
41      	Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
42     
43      Version 0.22:
44      	Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, 
45     	thanks to Mike Cox for pointing the problem out.
46     
47      Version 0.21:
48      	Markus: Minor cleanup, warnings if something goes wrong, lame attempt
49     	to adhere to Documentation/CodingStyle
50     
51      Version 0.2: 
52      	Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
53     	Markus: Copyright clarification
54     
55      Version 0.01: Markus: initial release
56     
57     */
58     
59     
60     #include <linux/kernel.h>
61     #include <linux/module.h>
62     #include <linux/init.h>
63     #include <linux/slab.h>
64     #include <linux/input.h>
65     #include <linux/videodev.h>
66     #include <linux/usb.h>
67     
68     /*
69      * Version Information
70      */
71     #define DRIVER_VERSION "v0.24"
72     #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
73     #define DRIVER_DESC "D-Link DSB-R100 USB radio driver"
74     
75     #define DSB100_VENDOR 0x04b4
76     #define DSB100_PRODUCT 0x1002
77     
78     #define TB_LEN 16
79     
80     static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum,
81     			 const struct usb_device_id *id);
82     static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr);
83     static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, 
84     	void *arg);
85     static int usb_dsbr100_open(struct video_device *dev, int flags);
86     static void usb_dsbr100_close(struct video_device *dev);
87     
88     static int radio_nr = -1;
89     MODULE_PARM(radio_nr, "i");
90     
91     typedef struct
92     {	struct urb readurb,writeurb;
93     	struct usb_device *dev;
94     	unsigned char transfer_buffer[TB_LEN];
95     	int curfreq;
96     	int stereo;
97     	int ifnum;
98     } usb_dsbr100;
99     
100     
101     static struct video_device usb_dsbr100_radio=
102     {
103     	name:		"D-Link DSB R-100 USB radio",
104     	type:		VID_TYPE_TUNER,
105     	hardware:	VID_HARDWARE_AZTECH,
106     	open:		usb_dsbr100_open,
107     	close:		usb_dsbr100_close,
108     	ioctl:		usb_dsbr100_ioctl,
109     };
110     
111     static int users = 0;
112     
113     static struct usb_device_id usb_dsbr100_table [] = {
114     	{ USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
115     	{ }						/* Terminating entry */
116     };
117     
118     MODULE_DEVICE_TABLE (usb, usb_dsbr100_table);
119     
120     static struct usb_driver usb_dsbr100_driver = {
121     	name:		"dsbr100",
122     	probe:		usb_dsbr100_probe,
123     	disconnect:	usb_dsbr100_disconnect,
124     	fops:		NULL,
125     	minor:		0,
126     	id_table:	usb_dsbr100_table,
127     };
128     
129     
130     static int dsbr100_start(usb_dsbr100 *radio)
131     {
132     	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
133     		0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
134     	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
135     		0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
136     		return -1;
137     	return (radio->transfer_buffer)[0];
138     }
139     
140     
141     static int dsbr100_stop(usb_dsbr100 *radio)
142     {
143     	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
144     		0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
145     	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
146     		0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
147     		return -1;
148     	return (radio->transfer_buffer)[0];
149     }
150     
151     
152     static int dsbr100_setfreq(usb_dsbr100 *radio, int freq)
153     {
154     	freq = (freq/16*80)/1000+856;
155     	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
156     		0x01, 0xC0, (freq>>8)&0x00ff, freq&0xff, 
157     		radio->transfer_buffer, 8, 300)<0 ||
158     	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
159     		0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
160     	    usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
161     		0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
162     		radio->stereo = -1;
163     		return -1;
164     	}
165     	radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
166     	return (radio->transfer_buffer)[0];
167     }
168     
169     static void dsbr100_getstat(usb_dsbr100 *radio)
170     {
171     	if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
172     		0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
173     		radio->stereo = -1;
174     	else
175     		radio->stereo = ! (radio->transfer_buffer[0]&0x01);
176     }
177     
178     
179     static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum,
180     			 const struct usb_device_id *id)
181     {
182     	usb_dsbr100 *radio;
183     
184     	if (!(radio = kmalloc(sizeof(usb_dsbr100),GFP_KERNEL)))
185     		return NULL;
186     	usb_dsbr100_radio.priv = radio;
187     	radio->dev = dev;
188     	radio->ifnum = ifnum;
189     	radio->curfreq = 1454000;
190     	return (void*)radio;
191     }
192     
193     static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr)
194     {
195     	usb_dsbr100 *radio=ptr;
196     
197     	if (users)
198     		return;
199     	kfree(radio);
200     	usb_dsbr100_radio.priv = NULL;
201     }
202     
203     static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, 
204     	void *arg)
205     {
206     	usb_dsbr100 *radio=dev->priv;
207     
208     	if (!radio)
209     		return -EINVAL;
210     
211     	switch(cmd)
212     	{
213     		case VIDIOCGCAP: {
214     			struct video_capability v;
215     			v.type=VID_TYPE_TUNER;
216     			v.channels=1;
217     			v.audios=1;
218     			/* No we don't do pictures */
219     			v.maxwidth=0;
220     			v.maxheight=0;
221     			v.minwidth=0;
222     			v.minheight=0;
223     			strcpy(v.name, "D-Link R-100 USB Radio");
224     			if(copy_to_user(arg,&v,sizeof(v)))
225     				return -EFAULT;
226     			return 0;
227     		}
228     		case VIDIOCGTUNER: {
229     			struct video_tuner v;
230     			dsbr100_getstat(radio);
231     			if(copy_from_user(&v, arg,sizeof(v))!=0) 
232     				return -EFAULT;
233     			if(v.tuner)	/* Only 1 tuner */ 
234     				return -EINVAL;
235     			v.rangelow = 87*16000;
236     			v.rangehigh = 108*16000;
237     			v.flags = VIDEO_TUNER_LOW;
238     			v.mode = VIDEO_MODE_AUTO;
239     			v.signal = radio->stereo*0x7000;
240     				/* Don't know how to get signal strength */
241     			v.flags |= VIDEO_TUNER_STEREO_ON*radio->stereo;
242     			strcpy(v.name, "DSB R-100");
243     			if(copy_to_user(arg,&v, sizeof(v)))
244     				return -EFAULT;
245     			return 0;
246     		}
247     		case VIDIOCSTUNER: {
248     			struct video_tuner v;
249     			if(copy_from_user(&v, arg, sizeof(v)))
250     				return -EFAULT;
251     			if(v.tuner!=0)
252     				return -EINVAL;
253     			/* Only 1 tuner so no setting needed ! */
254     			return 0;
255     		}
256     		case VIDIOCGFREQ: 
257     			if (radio->curfreq==-1)
258     				return -EINVAL;
259     			if(copy_to_user(arg, &(radio->curfreq), 
260     				sizeof(radio->curfreq)))
261     				return -EFAULT;
262     			return 0;
263     
264     		case VIDIOCSFREQ:
265     			if(copy_from_user(&(radio->curfreq), arg,
266     				sizeof(radio->curfreq)))
267     				return -EFAULT;
268     			if (dsbr100_setfreq(radio, radio->curfreq)==-1)
269     				warn("set frequency failed");
270     			return 0;
271     
272     		case VIDIOCGAUDIO: {
273     			struct video_audio v;
274     			memset(&v,0, sizeof(v));
275     			v.flags|=VIDEO_AUDIO_MUTABLE;
276     			v.mode=VIDEO_SOUND_STEREO;
277     			v.volume=1;
278     			v.step=1;
279     			strcpy(v.name, "Radio");
280     			if(copy_to_user(arg,&v, sizeof(v)))
281     				return -EFAULT;
282     			return 0;			
283     		}
284     		case VIDIOCSAUDIO: {
285     			struct video_audio v;
286     			if(copy_from_user(&v, arg, sizeof(v))) 
287     				return -EFAULT;	
288     			if(v.audio) 
289     				return -EINVAL;
290     
291     			if(v.flags&VIDEO_AUDIO_MUTE) {
292     				if (dsbr100_stop(radio)==-1)
293     					warn("radio did not respond properly");
294     			}
295     			else
296     				if (dsbr100_start(radio)==-1)
297     					warn("radio did not respond properly");
298     			return 0;
299     		}
300     		default:
301     			return -ENOIOCTLCMD;
302     	}
303     }
304     
305     
306     static int usb_dsbr100_open(struct video_device *dev, int flags)
307     {
308     	usb_dsbr100 *radio=dev->priv;
309     
310     	if (! radio) {
311     		warn("radio not initialised");
312     		return -EAGAIN;
313     	}
314     	if(users)
315     	{
316     		warn("radio in use");
317     		return -EBUSY;
318     	}
319     	users++;
320     	MOD_INC_USE_COUNT;
321     	if (dsbr100_start(radio)<0)
322     		warn("radio did not start up properly");
323     	dsbr100_setfreq(radio,radio->curfreq);
324     	return 0;
325     }
326     
327     static void usb_dsbr100_close(struct video_device *dev)
328     {
329     	usb_dsbr100 *radio=dev->priv;
330     
331     	if (!radio)
332     		return;
333     	users--;
334     	dsbr100_stop(radio);
335     	MOD_DEC_USE_COUNT;
336     }
337     
338     static int __init dsbr100_init(void)
339     {
340     	usb_dsbr100_radio.priv = NULL;
341     	usb_register(&usb_dsbr100_driver);
342     	if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO,radio_nr)==-1) {	
343     		warn("couldn't register video device");
344     		return -EINVAL;
345     	}
346     	info(DRIVER_VERSION ":" DRIVER_DESC);
347     	return 0;
348     }
349     
350     static void __exit dsbr100_exit(void)
351     {
352     	usb_dsbr100 *radio=usb_dsbr100_radio.priv;
353     
354     	if (radio)
355     		dsbr100_stop(radio);
356     	video_unregister_device(&usb_dsbr100_radio);
357     	usb_deregister(&usb_dsbr100_driver);
358     }
359     
360     module_init (dsbr100_init);
361     module_exit (dsbr100_exit);
362     
363     MODULE_AUTHOR( DRIVER_AUTHOR );
364     MODULE_DESCRIPTION( DRIVER_DESC );
365     
366     /*
367     vi: ts=8
368     Sigh.  Of course, I am one of the ts=2 heretics, but Linus' wish is
369     my command.
370     */
371