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