Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card
0003  *
0004  * by Fred Gleason <fredg@wava.com>
0005  * Version 0.3.3
0006  *
0007  * (Loosely) based on code for the Aztech radio card by
0008  *
0009  * Russell Kroll    (rkroll@exploits.org)
0010  * Quay Ly
0011  * Donald Song
0012  * Jason Lewis      (jlewis@twilight.vtc.vsc.edu)
0013  * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
0014  * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
0015  *
0016  * History:
0017  * 2000-04-29   Russell Kroll <rkroll@exploits.org>
0018  *      Added ISAPnP detection for Linux 2.3/2.4
0019  *
0020  * 2001-01-10   Russell Kroll <rkroll@exploits.org>
0021  *      Removed dead CONFIG_RADIO_CADET_PORT code
0022  *      PnP detection on load is now default (no args necessary)
0023  *
0024  * 2002-01-17   Adam Belay <ambx1@neo.rr.com>
0025  *      Updated to latest pnp code
0026  *
0027  * 2003-01-31   Alan Cox <alan@lxorguk.ukuu.org.uk>
0028  *      Cleaned up locking, delay code, general odds and ends
0029  *
0030  * 2006-07-30   Hans J. Koch <koch@hjk-az.de>
0031  *      Changed API to V4L2
0032  */
0033 
0034 #include <linux/module.h>   /* Modules          */
0035 #include <linux/init.h>     /* Initdata         */
0036 #include <linux/ioport.h>   /* request_region       */
0037 #include <linux/delay.h>    /* udelay           */
0038 #include <linux/videodev2.h>    /* V4L2 API defs        */
0039 #include <linux/param.h>
0040 #include <linux/pnp.h>
0041 #include <linux/sched.h>
0042 #include <linux/io.h>       /* outb, outb_p         */
0043 #include <media/v4l2-device.h>
0044 #include <media/v4l2-ioctl.h>
0045 #include <media/v4l2-ctrls.h>
0046 #include <media/v4l2-fh.h>
0047 #include <media/v4l2-event.h>
0048 
0049 MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
0050 MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
0051 MODULE_LICENSE("GPL");
0052 MODULE_VERSION("0.3.4");
0053 
0054 static int io = -1;     /* default to isapnp activation */
0055 static int radio_nr = -1;
0056 
0057 module_param(io, int, 0);
0058 MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
0059 module_param(radio_nr, int, 0);
0060 
0061 #define RDS_BUFFER 256
0062 #define RDS_RX_FLAG 1
0063 #define MBS_RX_FLAG 2
0064 
0065 struct cadet {
0066     struct v4l2_device v4l2_dev;
0067     struct video_device vdev;
0068     struct v4l2_ctrl_handler ctrl_handler;
0069     int io;
0070     bool is_fm_band;
0071     u32 curfreq;
0072     int tunestat;
0073     int sigstrength;
0074     wait_queue_head_t read_queue;
0075     struct timer_list readtimer;
0076     u8 rdsin, rdsout, rdsstat;
0077     unsigned char rdsbuf[RDS_BUFFER];
0078     struct mutex lock;
0079     int reading;
0080 };
0081 
0082 static struct cadet cadet_card;
0083 
0084 /*
0085  * Signal Strength Threshold Values
0086  * The V4L API spec does not define any particular unit for the signal
0087  * strength value.  These values are in microvolts of RF at the tuner's input.
0088  */
0089 static u16 sigtable[2][4] = {
0090     { 1835, 2621,  4128, 65535 },
0091     { 2185, 4369, 13107, 65535 },
0092 };
0093 
0094 static const struct v4l2_frequency_band bands[] = {
0095     {
0096         .index = 0,
0097         .type = V4L2_TUNER_RADIO,
0098         .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
0099         .rangelow = 8320,      /* 520 kHz */
0100         .rangehigh = 26400,    /* 1650 kHz */
0101         .modulation = V4L2_BAND_MODULATION_AM,
0102     }, {
0103         .index = 1,
0104         .type = V4L2_TUNER_RADIO,
0105         .capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
0106             V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW |
0107             V4L2_TUNER_CAP_FREQ_BANDS,
0108         .rangelow = 1400000,   /* 87.5 MHz */
0109         .rangehigh = 1728000,  /* 108.0 MHz */
0110         .modulation = V4L2_BAND_MODULATION_FM,
0111     },
0112 };
0113 
0114 
0115 static int cadet_getstereo(struct cadet *dev)
0116 {
0117     int ret = V4L2_TUNER_SUB_MONO;
0118 
0119     if (!dev->is_fm_band)   /* Only FM has stereo capability! */
0120         return V4L2_TUNER_SUB_MONO;
0121 
0122     outb(7, dev->io);          /* Select tuner control */
0123     if ((inb(dev->io + 1) & 0x40) == 0)
0124         ret = V4L2_TUNER_SUB_STEREO;
0125     return ret;
0126 }
0127 
0128 static unsigned cadet_gettune(struct cadet *dev)
0129 {
0130     int curvol, i;
0131     unsigned fifo = 0;
0132 
0133     /*
0134      * Prepare for read
0135      */
0136 
0137     outb(7, dev->io);       /* Select tuner control */
0138     curvol = inb(dev->io + 1); /* Save current volume/mute setting */
0139     outb(0x00, dev->io + 1);  /* Ensure WRITE-ENABLE is LOW */
0140     dev->tunestat = 0xffff;
0141 
0142     /*
0143      * Read the shift register
0144      */
0145     for (i = 0; i < 25; i++) {
0146         fifo = (fifo << 1) | ((inb(dev->io + 1) >> 7) & 0x01);
0147         if (i < 24) {
0148             outb(0x01, dev->io + 1);
0149             dev->tunestat &= inb(dev->io + 1);
0150             outb(0x00, dev->io + 1);
0151         }
0152     }
0153 
0154     /*
0155      * Restore volume/mute setting
0156      */
0157     outb(curvol, dev->io + 1);
0158     return fifo;
0159 }
0160 
0161 static unsigned cadet_getfreq(struct cadet *dev)
0162 {
0163     int i;
0164     unsigned freq = 0, test, fifo = 0;
0165 
0166     /*
0167      * Read current tuning
0168      */
0169     fifo = cadet_gettune(dev);
0170 
0171     /*
0172      * Convert to actual frequency
0173      */
0174     if (!dev->is_fm_band)    /* AM */
0175         return ((fifo & 0x7fff) - 450) * 16;
0176 
0177     test = 12500;
0178     for (i = 0; i < 14; i++) {
0179         if ((fifo & 0x01) != 0)
0180             freq += test;
0181         test = test << 1;
0182         fifo = fifo >> 1;
0183     }
0184     freq -= 10700000;           /* IF frequency is 10.7 MHz */
0185     freq = (freq * 16) / 1000;   /* Make it 1/16 kHz */
0186     return freq;
0187 }
0188 
0189 static void cadet_settune(struct cadet *dev, unsigned fifo)
0190 {
0191     int i;
0192     unsigned test;
0193 
0194     outb(7, dev->io);                /* Select tuner control */
0195     /*
0196      * Write the shift register
0197      */
0198     test = 0;
0199     test = (fifo >> 23) & 0x02;      /* Align data for SDO */
0200     test |= 0x1c;                /* SDM=1, SWE=1, SEN=1, SCK=0 */
0201     outb(7, dev->io);                /* Select tuner control */
0202     outb(test, dev->io + 1);           /* Initialize for write */
0203     for (i = 0; i < 25; i++) {
0204         test |= 0x01;              /* Toggle SCK High */
0205         outb(test, dev->io + 1);
0206         test &= 0xfe;              /* Toggle SCK Low */
0207         outb(test, dev->io + 1);
0208         fifo = fifo << 1;            /* Prepare the next bit */
0209         test = 0x1c | ((fifo >> 23) & 0x02);
0210         outb(test, dev->io + 1);
0211     }
0212 }
0213 
0214 static void cadet_setfreq(struct cadet *dev, unsigned freq)
0215 {
0216     unsigned fifo;
0217     int i, j, test;
0218     int curvol;
0219 
0220     freq = clamp(freq, bands[dev->is_fm_band].rangelow,
0221                bands[dev->is_fm_band].rangehigh);
0222     dev->curfreq = freq;
0223     /*
0224      * Formulate a fifo command
0225      */
0226     fifo = 0;
0227     if (dev->is_fm_band) {    /* FM */
0228         test = 102400;
0229         freq = freq / 16;       /* Make it kHz */
0230         freq += 10700;               /* IF is 10700 kHz */
0231         for (i = 0; i < 14; i++) {
0232             fifo = fifo << 1;
0233             if (freq >= test) {
0234                 fifo |= 0x01;
0235                 freq -= test;
0236             }
0237             test = test >> 1;
0238         }
0239     } else {    /* AM */
0240         fifo = (freq / 16) + 450;   /* Make it kHz */
0241         fifo |= 0x100000;       /* Select AM Band */
0242     }
0243 
0244     /*
0245      * Save current volume/mute setting
0246      */
0247 
0248     outb(7, dev->io);                /* Select tuner control */
0249     curvol = inb(dev->io + 1);
0250 
0251     /*
0252      * Tune the card
0253      */
0254     for (j = 3; j > -1; j--) {
0255         cadet_settune(dev, fifo | (j << 16));
0256 
0257         outb(7, dev->io);         /* Select tuner control */
0258         outb(curvol, dev->io + 1);
0259 
0260         msleep(100);
0261 
0262         cadet_gettune(dev);
0263         if ((dev->tunestat & 0x40) == 0) {   /* Tuned */
0264             dev->sigstrength = sigtable[dev->is_fm_band][j];
0265             goto reset_rds;
0266         }
0267     }
0268     dev->sigstrength = 0;
0269 reset_rds:
0270     outb(3, dev->io);
0271     outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
0272 }
0273 
0274 static bool cadet_has_rds_data(struct cadet *dev)
0275 {
0276     bool result;
0277 
0278     mutex_lock(&dev->lock);
0279     result = dev->rdsin != dev->rdsout;
0280     mutex_unlock(&dev->lock);
0281     return result;
0282 }
0283 
0284 
0285 static void cadet_handler(struct timer_list *t)
0286 {
0287     struct cadet *dev = from_timer(dev, t, readtimer);
0288 
0289     /* Service the RDS fifo */
0290     if (mutex_trylock(&dev->lock)) {
0291         outb(0x3, dev->io);       /* Select RDS Decoder Control */
0292         if ((inb(dev->io + 1) & 0x20) != 0)
0293             pr_err("cadet: RDS fifo overflow\n");
0294         outb(0x80, dev->io);      /* Select RDS fifo */
0295 
0296         while ((inb(dev->io) & 0x80) != 0) {
0297             dev->rdsbuf[dev->rdsin] = inb(dev->io + 1);
0298             if (dev->rdsin + 1 != dev->rdsout)
0299                 dev->rdsin++;
0300         }
0301         mutex_unlock(&dev->lock);
0302     }
0303 
0304     /*
0305      * Service pending read
0306      */
0307     if (cadet_has_rds_data(dev))
0308         wake_up_interruptible(&dev->read_queue);
0309 
0310     /*
0311      * Clean up and exit
0312      */
0313     dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
0314     add_timer(&dev->readtimer);
0315 }
0316 
0317 static void cadet_start_rds(struct cadet *dev)
0318 {
0319     dev->rdsstat = 1;
0320     outb(0x80, dev->io);        /* Select RDS fifo */
0321     timer_setup(&dev->readtimer, cadet_handler, 0);
0322     dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
0323     add_timer(&dev->readtimer);
0324 }
0325 
0326 static ssize_t cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
0327 {
0328     struct cadet *dev = video_drvdata(file);
0329     unsigned char readbuf[RDS_BUFFER];
0330     int i = 0;
0331 
0332     mutex_lock(&dev->lock);
0333     if (dev->rdsstat == 0)
0334         cadet_start_rds(dev);
0335     mutex_unlock(&dev->lock);
0336 
0337     if (!cadet_has_rds_data(dev) && (file->f_flags & O_NONBLOCK))
0338         return -EWOULDBLOCK;
0339     i = wait_event_interruptible(dev->read_queue, cadet_has_rds_data(dev));
0340     if (i)
0341         return i;
0342 
0343     mutex_lock(&dev->lock);
0344     while (i < count && dev->rdsin != dev->rdsout)
0345         readbuf[i++] = dev->rdsbuf[dev->rdsout++];
0346     mutex_unlock(&dev->lock);
0347 
0348     if (i && copy_to_user(data, readbuf, i))
0349         return -EFAULT;
0350     return i;
0351 }
0352 
0353 
0354 static int vidioc_querycap(struct file *file, void *priv,
0355                 struct v4l2_capability *v)
0356 {
0357     strscpy(v->driver, "ADS Cadet", sizeof(v->driver));
0358     strscpy(v->card, "ADS Cadet", sizeof(v->card));
0359     strscpy(v->bus_info, "ISA:radio-cadet", sizeof(v->bus_info));
0360     return 0;
0361 }
0362 
0363 static int vidioc_g_tuner(struct file *file, void *priv,
0364                 struct v4l2_tuner *v)
0365 {
0366     struct cadet *dev = video_drvdata(file);
0367 
0368     if (v->index)
0369         return -EINVAL;
0370     v->type = V4L2_TUNER_RADIO;
0371     strscpy(v->name, "Radio", sizeof(v->name));
0372     v->capability = bands[0].capability | bands[1].capability;
0373     v->rangelow = bands[0].rangelow;       /* 520 kHz (start of AM band) */
0374     v->rangehigh = bands[1].rangehigh;    /* 108.0 MHz (end of FM band) */
0375     if (dev->is_fm_band) {
0376         v->rxsubchans = cadet_getstereo(dev);
0377         outb(3, dev->io);
0378         outb(inb(dev->io + 1) & 0x7f, dev->io + 1);
0379         mdelay(100);
0380         outb(3, dev->io);
0381         if (inb(dev->io + 1) & 0x80)
0382             v->rxsubchans |= V4L2_TUNER_SUB_RDS;
0383     } else {
0384         v->rangelow = 8320;      /* 520 kHz */
0385         v->rangehigh = 26400;    /* 1650 kHz */
0386         v->rxsubchans = V4L2_TUNER_SUB_MONO;
0387     }
0388     v->audmode = V4L2_TUNER_MODE_STEREO;
0389     v->signal = dev->sigstrength; /* We might need to modify scaling of this */
0390     return 0;
0391 }
0392 
0393 static int vidioc_s_tuner(struct file *file, void *priv,
0394                 const struct v4l2_tuner *v)
0395 {
0396     return v->index ? -EINVAL : 0;
0397 }
0398 
0399 static int vidioc_enum_freq_bands(struct file *file, void *priv,
0400                 struct v4l2_frequency_band *band)
0401 {
0402     if (band->tuner)
0403         return -EINVAL;
0404     if (band->index >= ARRAY_SIZE(bands))
0405         return -EINVAL;
0406     *band = bands[band->index];
0407     return 0;
0408 }
0409 
0410 static int vidioc_g_frequency(struct file *file, void *priv,
0411                 struct v4l2_frequency *f)
0412 {
0413     struct cadet *dev = video_drvdata(file);
0414 
0415     if (f->tuner)
0416         return -EINVAL;
0417     f->type = V4L2_TUNER_RADIO;
0418     f->frequency = dev->curfreq;
0419     return 0;
0420 }
0421 
0422 
0423 static int vidioc_s_frequency(struct file *file, void *priv,
0424                 const struct v4l2_frequency *f)
0425 {
0426     struct cadet *dev = video_drvdata(file);
0427 
0428     if (f->tuner)
0429         return -EINVAL;
0430     dev->is_fm_band =
0431         f->frequency >= (bands[0].rangehigh + bands[1].rangelow) / 2;
0432     cadet_setfreq(dev, f->frequency);
0433     return 0;
0434 }
0435 
0436 static int cadet_s_ctrl(struct v4l2_ctrl *ctrl)
0437 {
0438     struct cadet *dev = container_of(ctrl->handler, struct cadet, ctrl_handler);
0439 
0440     switch (ctrl->id) {
0441     case V4L2_CID_AUDIO_MUTE:
0442         outb(7, dev->io);                /* Select tuner control */
0443         if (ctrl->val)
0444             outb(0x00, dev->io + 1);
0445         else
0446             outb(0x20, dev->io + 1);
0447         return 0;
0448     }
0449     return -EINVAL;
0450 }
0451 
0452 static int cadet_open(struct file *file)
0453 {
0454     struct cadet *dev = video_drvdata(file);
0455     int err;
0456 
0457     mutex_lock(&dev->lock);
0458     err = v4l2_fh_open(file);
0459     if (err)
0460         goto fail;
0461     if (v4l2_fh_is_singular_file(file))
0462         init_waitqueue_head(&dev->read_queue);
0463 fail:
0464     mutex_unlock(&dev->lock);
0465     return err;
0466 }
0467 
0468 static int cadet_release(struct file *file)
0469 {
0470     struct cadet *dev = video_drvdata(file);
0471 
0472     mutex_lock(&dev->lock);
0473     if (v4l2_fh_is_singular_file(file) && dev->rdsstat) {
0474         del_timer_sync(&dev->readtimer);
0475         dev->rdsstat = 0;
0476     }
0477     v4l2_fh_release(file);
0478     mutex_unlock(&dev->lock);
0479     return 0;
0480 }
0481 
0482 static __poll_t cadet_poll(struct file *file, struct poll_table_struct *wait)
0483 {
0484     struct cadet *dev = video_drvdata(file);
0485     __poll_t req_events = poll_requested_events(wait);
0486     __poll_t res = v4l2_ctrl_poll(file, wait);
0487 
0488     poll_wait(file, &dev->read_queue, wait);
0489     if (dev->rdsstat == 0 && (req_events & (EPOLLIN | EPOLLRDNORM))) {
0490         mutex_lock(&dev->lock);
0491         if (dev->rdsstat == 0)
0492             cadet_start_rds(dev);
0493         mutex_unlock(&dev->lock);
0494     }
0495     if (cadet_has_rds_data(dev))
0496         res |= EPOLLIN | EPOLLRDNORM;
0497     return res;
0498 }
0499 
0500 
0501 static const struct v4l2_file_operations cadet_fops = {
0502     .owner      = THIS_MODULE,
0503     .open       = cadet_open,
0504     .release    = cadet_release,
0505     .read       = cadet_read,
0506     .unlocked_ioctl = video_ioctl2,
0507     .poll       = cadet_poll,
0508 };
0509 
0510 static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
0511     .vidioc_querycap    = vidioc_querycap,
0512     .vidioc_g_tuner     = vidioc_g_tuner,
0513     .vidioc_s_tuner     = vidioc_s_tuner,
0514     .vidioc_g_frequency = vidioc_g_frequency,
0515     .vidioc_s_frequency = vidioc_s_frequency,
0516     .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
0517     .vidioc_log_status  = v4l2_ctrl_log_status,
0518     .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
0519     .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
0520 };
0521 
0522 static const struct v4l2_ctrl_ops cadet_ctrl_ops = {
0523     .s_ctrl = cadet_s_ctrl,
0524 };
0525 
0526 #ifdef CONFIG_PNP
0527 
0528 static const struct pnp_device_id cadet_pnp_devices[] = {
0529     /* ADS Cadet AM/FM Radio Card */
0530     {.id = "MSM0c24", .driver_data = 0},
0531     {.id = ""}
0532 };
0533 
0534 MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices);
0535 
0536 static int cadet_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
0537 {
0538     if (!dev)
0539         return -ENODEV;
0540     /* only support one device */
0541     if (io > 0)
0542         return -EBUSY;
0543 
0544     if (!pnp_port_valid(dev, 0))
0545         return -ENODEV;
0546 
0547     io = pnp_port_start(dev, 0);
0548 
0549     printk(KERN_INFO "radio-cadet: PnP reports device at %#x\n", io);
0550 
0551     return io;
0552 }
0553 
0554 static struct pnp_driver cadet_pnp_driver = {
0555     .name       = "radio-cadet",
0556     .id_table   = cadet_pnp_devices,
0557     .probe      = cadet_pnp_probe,
0558     .remove     = NULL,
0559 };
0560 
0561 #else
0562 static struct pnp_driver cadet_pnp_driver;
0563 #endif
0564 
0565 static void cadet_probe(struct cadet *dev)
0566 {
0567     static int iovals[8] = { 0x330, 0x332, 0x334, 0x336, 0x338, 0x33a, 0x33c, 0x33e };
0568     int i;
0569 
0570     for (i = 0; i < 8; i++) {
0571         dev->io = iovals[i];
0572         if (request_region(dev->io, 2, "cadet-probe")) {
0573             cadet_setfreq(dev, bands[1].rangelow);
0574             if (cadet_getfreq(dev) == bands[1].rangelow) {
0575                 release_region(dev->io, 2);
0576                 return;
0577             }
0578             release_region(dev->io, 2);
0579         }
0580     }
0581     dev->io = -1;
0582 }
0583 
0584 /*
0585  * io should only be set if the user has used something like
0586  * isapnp (the userspace program) to initialize this card for us
0587  */
0588 
0589 static int __init cadet_init(void)
0590 {
0591     struct cadet *dev = &cadet_card;
0592     struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
0593     struct v4l2_ctrl_handler *hdl;
0594     int res = -ENODEV;
0595 
0596     strscpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name));
0597     mutex_init(&dev->lock);
0598 
0599     /* If a probe was requested then probe ISAPnP first (safest) */
0600     if (io < 0)
0601         pnp_register_driver(&cadet_pnp_driver);
0602     dev->io = io;
0603 
0604     /* If that fails then probe unsafely if probe is requested */
0605     if (dev->io < 0)
0606         cadet_probe(dev);
0607 
0608     /* Else we bail out */
0609     if (dev->io < 0) {
0610 #ifdef MODULE
0611         v4l2_err(v4l2_dev, "you must set an I/O address with io=0x330, 0x332, 0x334,\n");
0612         v4l2_err(v4l2_dev, "0x336, 0x338, 0x33a, 0x33c or 0x33e\n");
0613 #endif
0614         goto fail;
0615     }
0616     if (!request_region(dev->io, 2, "cadet"))
0617         goto fail;
0618 
0619     res = v4l2_device_register(NULL, v4l2_dev);
0620     if (res < 0) {
0621         release_region(dev->io, 2);
0622         v4l2_err(v4l2_dev, "could not register v4l2_device\n");
0623         goto fail;
0624     }
0625 
0626     hdl = &dev->ctrl_handler;
0627     v4l2_ctrl_handler_init(hdl, 2);
0628     v4l2_ctrl_new_std(hdl, &cadet_ctrl_ops,
0629             V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
0630     v4l2_dev->ctrl_handler = hdl;
0631     if (hdl->error) {
0632         res = hdl->error;
0633         v4l2_err(v4l2_dev, "Could not register controls\n");
0634         goto err_hdl;
0635     }
0636 
0637     dev->is_fm_band = true;
0638     dev->curfreq = bands[dev->is_fm_band].rangelow;
0639     cadet_setfreq(dev, dev->curfreq);
0640     strscpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
0641     dev->vdev.v4l2_dev = v4l2_dev;
0642     dev->vdev.fops = &cadet_fops;
0643     dev->vdev.ioctl_ops = &cadet_ioctl_ops;
0644     dev->vdev.release = video_device_release_empty;
0645     dev->vdev.lock = &dev->lock;
0646     dev->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
0647                 V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
0648     video_set_drvdata(&dev->vdev, dev);
0649 
0650     res = video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr);
0651     if (res < 0)
0652         goto err_hdl;
0653     v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x\n", dev->io);
0654     return 0;
0655 err_hdl:
0656     v4l2_ctrl_handler_free(hdl);
0657     v4l2_device_unregister(v4l2_dev);
0658     release_region(dev->io, 2);
0659 fail:
0660     pnp_unregister_driver(&cadet_pnp_driver);
0661     return res;
0662 }
0663 
0664 static void __exit cadet_exit(void)
0665 {
0666     struct cadet *dev = &cadet_card;
0667 
0668     video_unregister_device(&dev->vdev);
0669     v4l2_ctrl_handler_free(&dev->ctrl_handler);
0670     v4l2_device_unregister(&dev->v4l2_dev);
0671     outb(7, dev->io);   /* Mute */
0672     outb(0x00, dev->io + 1);
0673     release_region(dev->io, 2);
0674     pnp_unregister_driver(&cadet_pnp_driver);
0675 }
0676 
0677 module_init(cadet_init);
0678 module_exit(cadet_exit);
0679