Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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.43:
 37         Oliver Neukum: avoided DMA coherency issue
 38 
 39  Version 0.42:
 40         Converted dsbr100 to use video_ioctl2
 41         by Douglas Landgraf <dougsland@gmail.com>
 42 
 43  Version 0.41-ac1:
 44         Alan Cox: Some cleanups and fixes
 45 
 46  Version 0.41:
 47         Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
 48 
 49  Version 0.40:
 50         Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing
 51 
 52  Version 0.30:
 53         Markus: Updates for 2.5.x kernel and more ISO compliant source
 54 
 55  Version 0.25:
 56         PSL and Markus: Cleanup, radio now doesn't stop on device close
 57 
 58  Version 0.24:
 59         Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally
 60         right.  Some minor cleanup, improved standalone compilation
 61 
 62  Version 0.23:
 63         Markus: Sign extension bug fixed by declaring transfer_buffer unsigned
 64 
 65  Version 0.22:
 66         Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns,
 67         thanks to Mike Cox for pointing the problem out.
 68 
 69  Version 0.21:
 70         Markus: Minor cleanup, warnings if something goes wrong, lame attempt
 71         to adhere to Documentation/CodingStyle
 72 
 73  Version 0.2:
 74         Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
 75         Markus: Copyright clarification
 76 
 77  Version 0.01: Markus: initial release
 78 
 79 */
 80 
 81 #include <linux/kernel.h>
 82 #include <linux/module.h>
 83 #include <linux/init.h>
 84 #include <linux/slab.h>
 85 #include <linux/input.h>
 86 #include <linux/videodev2.h>
 87 #include <media/v4l2-common.h>
 88 #include <linux/usb.h>
 89 
 90 /*
 91  * Version Information
 92  */
 93 #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
 94 
 95 #define DRIVER_VERSION "v0.41"
 96 #define RADIO_VERSION KERNEL_VERSION(0,4,1)
 97 
 98 static struct v4l2_queryctrl radio_qctrl[] = {
 99         {
100                 .id            = V4L2_CID_AUDIO_MUTE,
101                 .name          = "Mute",
102                 .minimum       = 0,
103                 .maximum       = 1,
104                 .default_value = 1,
105                 .type          = V4L2_CTRL_TYPE_BOOLEAN,
106         }
107 };
108 
109 #define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
110 #define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
111 
112 #define DSB100_VENDOR 0x04b4
113 #define DSB100_PRODUCT 0x1002
114 
115 /* Commands the device appears to understand */
116 #define DSB100_TUNE 1
117 #define DSB100_ONOFF 2
118 
119 #define TB_LEN 16
120 
121 /* Frequency limits in MHz -- these are European values.  For Japanese
122 devices, that would be 76 and 91.  */
123 #define FREQ_MIN  87.5
124 #define FREQ_MAX 108.0
125 #define FREQ_MUL 16000
126 
127 
128 static int usb_dsbr100_probe(struct usb_interface *intf,
129                              const struct usb_device_id *id);
130 static void usb_dsbr100_disconnect(struct usb_interface *intf);
131 static int usb_dsbr100_open(struct inode *inode, struct file *file);
132 static int usb_dsbr100_close(struct inode *inode, struct file *file);
133 
134 static int radio_nr = -1;
135 module_param(radio_nr, int, 0);
136 
137 /* Data for one (physical) device */
138 struct dsbr100_device {
139         struct usb_device *usbdev;
140         struct video_device *videodev;
141         u8 *transfer_buffer;
142         int curfreq;
143         int stereo;
144         int users;
145         int removed;
146         int muted;
147 };
148 
149 
150 static struct usb_device_id usb_dsbr100_device_table [] = {
151         { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) },
152         { }                                             /* Terminating entry */
153 };
154 
155 MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table);
156 
157 /* USB subsystem interface */
158 static struct usb_driver usb_dsbr100_driver = {
159         .name =         "dsbr100",
160         .probe =        usb_dsbr100_probe,
161         .disconnect =   usb_dsbr100_disconnect,
162         .id_table =     usb_dsbr100_device_table,
163 };
164 
165 /* Low-level device interface begins here */
166 
167 /* switch on radio */
168 static int dsbr100_start(struct dsbr100_device *radio)
169 {
170         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
171                         USB_REQ_GET_STATUS,
172                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
173                         0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
174         usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
175                         DSB100_ONOFF,
176                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
177                         0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
178                 return -1;
179         radio->muted=0;
180         return (radio->transfer_buffer)[0];
181 }
182 
183 
184 /* switch off radio */
185 static int dsbr100_stop(struct dsbr100_device *radio)
186 {
187         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
188                         USB_REQ_GET_STATUS,
189                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
190                         0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
191         usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
192                         DSB100_ONOFF,
193                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
194                         0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
195                 return -1;
196         radio->muted=1;
197         return (radio->transfer_buffer)[0];
198 }
199 
200 /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
201 static int dsbr100_setfreq(struct dsbr100_device *radio, int freq)
202 {
203         freq = (freq/16*80)/1000+856;
204         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
205                         DSB100_TUNE,
206                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
207                         (freq>>8)&0x00ff, freq&0xff,
208                         radio->transfer_buffer, 8, 300)<0 ||
209            usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
210                         USB_REQ_GET_STATUS,
211                         USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
212                         0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
213         usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
214                         USB_REQ_GET_STATUS,
215                         USB_TYPE_VENDOR | USB_RECIP_DEVICE |  USB_DIR_IN,
216                         0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
217                 radio->stereo = -1;
218                 return -1;
219         }
220         radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
221         return (radio->transfer_buffer)[0];
222 }
223 
224 /* return the device status.  This is, in effect, just whether it
225 sees a stereo signal or not.  Pity. */
226 static void dsbr100_getstat(struct dsbr100_device *radio)
227 {
228         if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
229                 USB_REQ_GET_STATUS,
230                 USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
231                 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
232                 radio->stereo = -1;
233         else
234                 radio->stereo = ! (radio->transfer_buffer[0]&0x01);
235 }
236 
237 
238 /* USB subsystem interface begins here */
239 
240 /* handle unplugging of the device, release data structures
241 if nothing keeps us from doing it.  If something is still
242 keeping us busy, the release callback of v4l will take care
243 of releasing it. */
244 static void usb_dsbr100_disconnect(struct usb_interface *intf)
245 {
246         struct dsbr100_device *radio = usb_get_intfdata(intf);
247 
248         usb_set_intfdata (intf, NULL);
249         if (radio) {
250                 video_unregister_device(radio->videodev);
251                 radio->videodev = NULL;
252                 if (radio->users) {
253                         kfree(radio->transfer_buffer);
254                         kfree(radio);
255                 } else {
256                         radio->removed = 1;
257                 }
258         }
259 }
260 
261 
262 static int vidioc_querycap(struct file *file, void *priv,
263                                         struct v4l2_capability *v)
264 {
265         strlcpy(v->driver, "dsbr100", sizeof(v->driver));
266         strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card));
267         sprintf(v->bus_info, "ISA");
268         v->version = RADIO_VERSION;
269         v->capabilities = V4L2_CAP_TUNER;
270         return 0;
271 }
272 
273 static int vidioc_g_tuner(struct file *file, void *priv,
274                                 struct v4l2_tuner *v)
275 {
276         struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
277 
278         if (v->index > 0)
279                 return -EINVAL;
280 
281         dsbr100_getstat(radio);
282         strcpy(v->name, "FM");
283         v->type = V4L2_TUNER_RADIO;
284         v->rangelow = FREQ_MIN*FREQ_MUL;
285         v->rangehigh = FREQ_MAX*FREQ_MUL;
286         v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
287         v->capability = V4L2_TUNER_CAP_LOW;
288         if(radio->stereo)
289                 v->audmode = V4L2_TUNER_MODE_STEREO;
290         else
291                 v->audmode = V4L2_TUNER_MODE_MONO;
292         v->signal = 0xffff;     /* We can't get the signal strength */
293         return 0;
294 }
295 
296 static int vidioc_s_tuner(struct file *file, void *priv,
297                                 struct v4l2_tuner *v)
298 {
299         if (v->index > 0)
300                 return -EINVAL;
301 
302         return 0;
303 }
304 
305 static int vidioc_s_frequency(struct file *file, void *priv,
306                                 struct v4l2_frequency *f)
307 {
308         struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
309 
310         radio->curfreq = f->frequency;
311         if (dsbr100_setfreq(radio, radio->curfreq)==-1)
312                 warn("Set frequency failed");
313         return 0;
314 }
315 
316 static int vidioc_g_frequency(struct file *file, void *priv,
317                                 struct v4l2_frequency *f)
318 {
319         struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
320 
321         f->type = V4L2_TUNER_RADIO;
322         f->frequency = radio->curfreq;
323         return 0;
324 }
325 
326 static int vidioc_queryctrl(struct file *file, void *priv,
327                                 struct v4l2_queryctrl *qc)
328 {
329         int i;
330 
331         for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
332                 if (qc->id && qc->id == radio_qctrl[i].id) {
333                         memcpy(qc, &(radio_qctrl[i]),
334                                                 sizeof(*qc));
335                         return 0;
336                 }
337         }
338         return -EINVAL;
339 }
340 
341 static int vidioc_g_ctrl(struct file *file, void *priv,
342                                 struct v4l2_control *ctrl)
343 {
344         struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
345 
346         switch (ctrl->id) {
347         case V4L2_CID_AUDIO_MUTE:
348                 ctrl->value = radio->muted;
349                 return 0;
350         }
351         return -EINVAL;
352 }
353 
354 static int vidioc_s_ctrl(struct file *file, void *priv,
355                                 struct v4l2_control *ctrl)
356 {
357         struct dsbr100_device *radio = video_get_drvdata(video_devdata(file));
358 
359         switch (ctrl->id) {
360         case V4L2_CID_AUDIO_MUTE:
361                 if (ctrl->value) {
362                         if (dsbr100_stop(radio)==-1)
363                                 warn("Radio did not respond properly");
364                 } else {
365                         if (dsbr100_start(radio)==-1)
366                                 warn("Radio did not respond properly");
367                 }
368                 return 0;
369         }
370         return -EINVAL;
371 }
372 
373 static int vidioc_g_audio(struct file *file, void *priv,
374                                 struct v4l2_audio *a)
375 {
376         if (a->index > 1)
377                 return -EINVAL;
378 
379         strcpy(a->name, "Radio");
380         a->capability = V4L2_AUDCAP_STEREO;
381         return 0;
382 }
383 
384 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
385 {
386         *i = 0;
387         return 0;
388 }
389 
390 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
391 {
392         if (i != 0)
393                 return -EINVAL;
394         return 0;
395 }
396 
397 static int vidioc_s_audio(struct file *file, void *priv,
398                                         struct v4l2_audio *a)
399 {
400         if (a->index != 0)
401                 return -EINVAL;
402         return 0;
403 }
404 
405 static int usb_dsbr100_open(struct inode *inode, struct file *file)
406 {
407         struct dsbr100_device *radio=video_get_drvdata(video_devdata(file));
408 
409         radio->users = 1;
410         radio->muted = 1;
411 
412         if (dsbr100_start(radio)<0) {
413                 warn("Radio did not start up properly");
414                 radio->users = 0;
415                 return -EIO;
416         }
417         dsbr100_setfreq(radio, radio->curfreq);
418         return 0;
419 }
420 
421 static int usb_dsbr100_close(struct inode *inode, struct file *file)
422 {
423         struct dsbr100_device *radio=video_get_drvdata(video_devdata(file));
424 
425         if (!radio)
426                 return -ENODEV;
427         radio->users = 0;
428         if (radio->removed) {
429                 kfree(radio->transfer_buffer);
430                 kfree(radio);
431         }
432         return 0;
433 }
434 
435 /* File system interface */
436 static const struct file_operations usb_dsbr100_fops = {
437         .owner          = THIS_MODULE,
438         .open           = usb_dsbr100_open,
439         .release        = usb_dsbr100_close,
440         .ioctl          = video_ioctl2,
441         .compat_ioctl   = v4l_compat_ioctl32,
442         .llseek         = no_llseek,
443 };
444 
445 /* V4L2 interface */
446 static struct video_device dsbr100_videodev_template =
447 {
448         .owner          = THIS_MODULE,
449         .name           = "D-Link DSB-R 100",
450         .type           = VID_TYPE_TUNER,
451         .fops           = &usb_dsbr100_fops,
452         .release        = video_device_release,
453         .vidioc_querycap    = vidioc_querycap,
454         .vidioc_g_tuner     = vidioc_g_tuner,
455         .vidioc_s_tuner     = vidioc_s_tuner,
456         .vidioc_g_frequency = vidioc_g_frequency,
457         .vidioc_s_frequency = vidioc_s_frequency,
458         .vidioc_queryctrl   = vidioc_queryctrl,
459         .vidioc_g_ctrl      = vidioc_g_ctrl,
460         .vidioc_s_ctrl      = vidioc_s_ctrl,
461         .vidioc_g_audio     = vidioc_g_audio,
462         .vidioc_s_audio     = vidioc_s_audio,
463         .vidioc_g_input     = vidioc_g_input,
464         .vidioc_s_input     = vidioc_s_input,
465 };
466 
467 /* check if the device is present and register with v4l and
468 usb if it is */
469 static int usb_dsbr100_probe(struct usb_interface *intf,
470                                 const struct usb_device_id *id)
471 {
472         struct dsbr100_device *radio;
473 
474         if (!(radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL)))
475                 return -ENOMEM;
476         if (!(radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL))) {
477                 kfree(radio);
478                 return -ENOMEM;
479         }
480         if (!(radio->videodev = video_device_alloc())) {
481                 kfree(radio->transfer_buffer);
482                 kfree(radio);
483                 return -ENOMEM;
484         }
485         memcpy(radio->videodev, &dsbr100_videodev_template,
486                 sizeof(dsbr100_videodev_template));
487         radio->removed = 0;
488         radio->users = 0;
489         radio->usbdev = interface_to_usbdev(intf);
490         radio->curfreq = FREQ_MIN*FREQ_MUL;
491         video_set_drvdata(radio->videodev, radio);
492         if (video_register_device(radio->videodev, VFL_TYPE_RADIO,radio_nr)) {
493                 warn("Could not register video device");
494                 video_device_release(radio->videodev);
495                 kfree(radio->transfer_buffer);
496                 kfree(radio);
497                 return -EIO;
498         }
499         usb_set_intfdata(intf, radio);
500         return 0;
501 }
502 
503 static int __init dsbr100_init(void)
504 {
505         int retval = usb_register(&usb_dsbr100_driver);
506         info(DRIVER_VERSION ":" DRIVER_DESC);
507         return retval;
508 }
509 
510 static void __exit dsbr100_exit(void)
511 {
512         usb_deregister(&usb_dsbr100_driver);
513 }
514 
515 module_init (dsbr100_init);
516 module_exit (dsbr100_exit);
517 
518 MODULE_AUTHOR( DRIVER_AUTHOR );
519 MODULE_DESCRIPTION( DRIVER_DESC );
520 MODULE_LICENSE("GPL");
521 
  This page was automatically generated by the LXR engine.