1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
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
85
86#define FREQ_MIN 87.5
87#define FREQ_MAX 108.0
88#define FREQ_MUL 16000
89
90static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum,
91 const struct usb_device_id *id);
92static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr);
93static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd,
94 void *arg);
95static int usb_dsbr100_open(struct video_device *dev, int flags);
96static void usb_dsbr100_close(struct video_device *dev);
97
98static int radio_nr = -1;
99MODULE_PARM(radio_nr, "i");
100
101typedef 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
111
112
113
114static 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
124static int users = 0;
125
126static struct usb_device_id usb_dsbr100_table [] = {
127 { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
128 { }
129};
130
131MODULE_DEVICE_TABLE (usb, usb_dsbr100_table);
132
133static 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
143static 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
154static 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
165static 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
182static 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
192static 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
206static 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
220static 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)
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
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
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
322static 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
343static 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
353static 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
366static 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
376module_init (dsbr100_init);
377module_exit (dsbr100_exit);
378
379MODULE_AUTHOR( DRIVER_AUTHOR );
380MODULE_DESCRIPTION( DRIVER_DESC );
381MODULE_LICENSE("GPL");
382