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