linux-old/drivers/usb/dsbr100.c
<<
>>
Prefs
   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
  85devices, that would be 76 and 91 */
  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/* 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
 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        { }                                             /* Terminating entry */
 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)     /* 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
 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.