~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/drivers/usb/dsbr100.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.