aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio/radio-cadet.c
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2012-07-02 08:36:39 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-07-30 19:23:08 -0400
commitb54c97db7f51c47c361533956db18c8b191033b5 (patch)
tree60f48b61dabf0ac0c40777b6aa88a17d8fd3587b /drivers/media/radio/radio-cadet.c
parent50121317c2035c017d62c8ec24f84b91ef8d4de2 (diff)
[media] radio-cadet: upgrade to latest frameworks
- add control framework - use core locking - use V4L2_TUNER_CAP_LOW - remove volume support: there is no hardware volume control Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio/radio-cadet.c')
-rw-r--r--drivers/media/radio/radio-cadet.c243
1 files changed, 83 insertions, 160 deletions
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index 16a089fad909..93536b7e75c7 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -41,6 +41,9 @@
41#include <linux/io.h> /* outb, outb_p */ 41#include <linux/io.h> /* outb, outb_p */
42#include <media/v4l2-device.h> 42#include <media/v4l2-device.h>
43#include <media/v4l2-ioctl.h> 43#include <media/v4l2-ioctl.h>
44#include <media/v4l2-ctrls.h>
45#include <media/v4l2-fh.h>
46#include <media/v4l2-event.h>
44 47
45MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); 48MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
46MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card."); 49MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
@@ -61,8 +64,8 @@ module_param(radio_nr, int, 0);
61struct cadet { 64struct cadet {
62 struct v4l2_device v4l2_dev; 65 struct v4l2_device v4l2_dev;
63 struct video_device vdev; 66 struct video_device vdev;
67 struct v4l2_ctrl_handler ctrl_handler;
64 int io; 68 int io;
65 int users;
66 int curtuner; 69 int curtuner;
67 int tunestat; 70 int tunestat;
68 int sigstrength; 71 int sigstrength;
@@ -94,11 +97,9 @@ static int cadet_getstereo(struct cadet *dev)
94 if (dev->curtuner != 0) /* Only FM has stereo capability! */ 97 if (dev->curtuner != 0) /* Only FM has stereo capability! */
95 return V4L2_TUNER_SUB_MONO; 98 return V4L2_TUNER_SUB_MONO;
96 99
97 mutex_lock(&dev->lock);
98 outb(7, dev->io); /* Select tuner control */ 100 outb(7, dev->io); /* Select tuner control */
99 if ((inb(dev->io + 1) & 0x40) == 0) 101 if ((inb(dev->io + 1) & 0x40) == 0)
100 ret = V4L2_TUNER_SUB_STEREO; 102 ret = V4L2_TUNER_SUB_STEREO;
101 mutex_unlock(&dev->lock);
102 return ret; 103 return ret;
103} 104}
104 105
@@ -111,8 +112,6 @@ static unsigned cadet_gettune(struct cadet *dev)
111 * Prepare for read 112 * Prepare for read
112 */ 113 */
113 114
114 mutex_lock(&dev->lock);
115
116 outb(7, dev->io); /* Select tuner control */ 115 outb(7, dev->io); /* Select tuner control */
117 curvol = inb(dev->io + 1); /* Save current volume/mute setting */ 116 curvol = inb(dev->io + 1); /* Save current volume/mute setting */
118 outb(0x00, dev->io + 1); /* Ensure WRITE-ENABLE is LOW */ 117 outb(0x00, dev->io + 1); /* Ensure WRITE-ENABLE is LOW */
@@ -134,8 +133,6 @@ static unsigned cadet_gettune(struct cadet *dev)
134 * Restore volume/mute setting 133 * Restore volume/mute setting
135 */ 134 */
136 outb(curvol, dev->io + 1); 135 outb(curvol, dev->io + 1);
137 mutex_unlock(&dev->lock);
138
139 return fifo; 136 return fifo;
140} 137}
141 138
@@ -161,7 +158,7 @@ static unsigned cadet_getfreq(struct cadet *dev)
161 fifo = fifo >> 1; 158 fifo = fifo >> 1;
162 } 159 }
163 freq -= 10700000; /* IF frequency is 10.7 MHz */ 160 freq -= 10700000; /* IF frequency is 10.7 MHz */
164 freq = (freq * 16) / 1000000; /* Make it 1/16 MHz */ 161 freq = (freq * 16) / 1000; /* Make it 1/16 kHz */
165 } 162 }
166 if (dev->curtuner == 1) /* AM */ 163 if (dev->curtuner == 1) /* AM */
167 freq = ((fifo & 0x7fff) - 2010) * 16; 164 freq = ((fifo & 0x7fff) - 2010) * 16;
@@ -174,8 +171,6 @@ static void cadet_settune(struct cadet *dev, unsigned fifo)
174 int i; 171 int i;
175 unsigned test; 172 unsigned test;
176 173
177 mutex_lock(&dev->lock);
178
179 outb(7, dev->io); /* Select tuner control */ 174 outb(7, dev->io); /* Select tuner control */
180 /* 175 /*
181 * Write the shift register 176 * Write the shift register
@@ -194,7 +189,6 @@ static void cadet_settune(struct cadet *dev, unsigned fifo)
194 test = 0x1c | ((fifo >> 23) & 0x02); 189 test = 0x1c | ((fifo >> 23) & 0x02);
195 outb(test, dev->io + 1); 190 outb(test, dev->io + 1);
196 } 191 }
197 mutex_unlock(&dev->lock);
198} 192}
199 193
200static void cadet_setfreq(struct cadet *dev, unsigned freq) 194static void cadet_setfreq(struct cadet *dev, unsigned freq)
@@ -209,7 +203,7 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
209 fifo = 0; 203 fifo = 0;
210 if (dev->curtuner == 0) { /* FM */ 204 if (dev->curtuner == 0) { /* FM */
211 test = 102400; 205 test = 102400;
212 freq = (freq * 1000) / 16; /* Make it kHz */ 206 freq = freq / 16; /* Make it kHz */
213 freq += 10700; /* IF is 10700 kHz */ 207 freq += 10700; /* IF is 10700 kHz */
214 for (i = 0; i < 14; i++) { 208 for (i = 0; i < 14; i++) {
215 fifo = fifo << 1; 209 fifo = fifo << 1;
@@ -229,10 +223,8 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
229 * Save current volume/mute setting 223 * Save current volume/mute setting
230 */ 224 */
231 225
232 mutex_lock(&dev->lock);
233 outb(7, dev->io); /* Select tuner control */ 226 outb(7, dev->io); /* Select tuner control */
234 curvol = inb(dev->io + 1); 227 curvol = inb(dev->io + 1);
235 mutex_unlock(&dev->lock);
236 228
237 /* 229 /*
238 * Tune the card 230 * Tune the card
@@ -240,10 +232,8 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
240 for (j = 3; j > -1; j--) { 232 for (j = 3; j > -1; j--) {
241 cadet_settune(dev, fifo | (j << 16)); 233 cadet_settune(dev, fifo | (j << 16));
242 234
243 mutex_lock(&dev->lock);
244 outb(7, dev->io); /* Select tuner control */ 235 outb(7, dev->io); /* Select tuner control */
245 outb(curvol, dev->io + 1); 236 outb(curvol, dev->io + 1);
246 mutex_unlock(&dev->lock);
247 237
248 msleep(100); 238 msleep(100);
249 239
@@ -257,32 +247,6 @@ static void cadet_setfreq(struct cadet *dev, unsigned freq)
257} 247}
258 248
259 249
260static int cadet_getvol(struct cadet *dev)
261{
262 int ret = 0;
263
264 mutex_lock(&dev->lock);
265
266 outb(7, dev->io); /* Select tuner control */
267 if ((inb(dev->io + 1) & 0x20) != 0)
268 ret = 0xffff;
269
270 mutex_unlock(&dev->lock);
271 return ret;
272}
273
274
275static void cadet_setvol(struct cadet *dev, int vol)
276{
277 mutex_lock(&dev->lock);
278 outb(7, dev->io); /* Select tuner control */
279 if (vol > 0)
280 outb(0x20, dev->io + 1);
281 else
282 outb(0x00, dev->io + 1);
283 mutex_unlock(&dev->lock);
284}
285
286static void cadet_handler(unsigned long data) 250static void cadet_handler(unsigned long data)
287{ 251{
288 struct cadet *dev = (void *)data; 252 struct cadet *dev = (void *)data;
@@ -337,18 +301,19 @@ static ssize_t cadet_read(struct file *file, char __user *data, size_t count, lo
337 add_timer(&dev->readtimer); 301 add_timer(&dev->readtimer);
338 } 302 }
339 if (dev->rdsin == dev->rdsout) { 303 if (dev->rdsin == dev->rdsout) {
340 mutex_unlock(&dev->lock); 304 if (file->f_flags & O_NONBLOCK) {
341 if (file->f_flags & O_NONBLOCK) 305 i = -EWOULDBLOCK;
342 return -EWOULDBLOCK; 306 goto unlock;
307 }
343 interruptible_sleep_on(&dev->read_queue); 308 interruptible_sleep_on(&dev->read_queue);
344 mutex_lock(&dev->lock);
345 } 309 }
346 while (i < count && dev->rdsin != dev->rdsout) 310 while (i < count && dev->rdsin != dev->rdsout)
347 readbuf[i++] = dev->rdsbuf[dev->rdsout++]; 311 readbuf[i++] = dev->rdsbuf[dev->rdsout++];
348 mutex_unlock(&dev->lock);
349 312
350 if (copy_to_user(data, readbuf, i)) 313 if (copy_to_user(data, readbuf, i))
351 return -EFAULT; 314 i = -EFAULT;
315unlock:
316 mutex_unlock(&dev->lock);
352 return i; 317 return i;
353} 318}
354 319
@@ -359,8 +324,9 @@ static int vidioc_querycap(struct file *file, void *priv,
359 strlcpy(v->driver, "ADS Cadet", sizeof(v->driver)); 324 strlcpy(v->driver, "ADS Cadet", sizeof(v->driver));
360 strlcpy(v->card, "ADS Cadet", sizeof(v->card)); 325 strlcpy(v->card, "ADS Cadet", sizeof(v->card));
361 strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); 326 strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
362 v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO | 327 v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
363 V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE; 328 V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE;
329 v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
364 return 0; 330 return 0;
365} 331}
366 332
@@ -374,20 +340,11 @@ static int vidioc_g_tuner(struct file *file, void *priv,
374 case 0: 340 case 0:
375 strlcpy(v->name, "FM", sizeof(v->name)); 341 strlcpy(v->name, "FM", sizeof(v->name));
376 v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS | 342 v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_RDS |
377 V4L2_TUNER_CAP_RDS_BLOCK_IO; 343 V4L2_TUNER_CAP_RDS_BLOCK_IO | V4L2_TUNER_CAP_LOW;
378 v->rangelow = 1400; /* 87.5 MHz */ 344 v->rangelow = 1400000; /* 87.5 MHz */
379 v->rangehigh = 1728; /* 108.0 MHz */ 345 v->rangehigh = 1728000; /* 108.0 MHz */
380 v->rxsubchans = cadet_getstereo(dev); 346 v->rxsubchans = cadet_getstereo(dev);
381 switch (v->rxsubchans) { 347 v->audmode = V4L2_TUNER_MODE_STEREO;
382 case V4L2_TUNER_SUB_MONO:
383 v->audmode = V4L2_TUNER_MODE_MONO;
384 break;
385 case V4L2_TUNER_SUB_STEREO:
386 v->audmode = V4L2_TUNER_MODE_STEREO;
387 break;
388 default:
389 break;
390 }
391 v->rxsubchans |= V4L2_TUNER_SUB_RDS; 348 v->rxsubchans |= V4L2_TUNER_SUB_RDS;
392 break; 349 break;
393 case 1: 350 case 1:
@@ -408,11 +365,8 @@ static int vidioc_g_tuner(struct file *file, void *priv,
408static int vidioc_s_tuner(struct file *file, void *priv, 365static int vidioc_s_tuner(struct file *file, void *priv,
409 struct v4l2_tuner *v) 366 struct v4l2_tuner *v)
410{ 367{
411 struct cadet *dev = video_drvdata(file);
412
413 if (v->index != 0 && v->index != 1) 368 if (v->index != 0 && v->index != 1)
414 return -EINVAL; 369 return -EINVAL;
415 dev->curtuner = v->index;
416 return 0; 370 return 0;
417} 371}
418 372
@@ -421,7 +375,8 @@ static int vidioc_g_frequency(struct file *file, void *priv,
421{ 375{
422 struct cadet *dev = video_drvdata(file); 376 struct cadet *dev = video_drvdata(file);
423 377
424 f->tuner = dev->curtuner; 378 if (f->tuner > 1)
379 return -EINVAL;
425 f->type = V4L2_TUNER_RADIO; 380 f->type = V4L2_TUNER_RADIO;
426 f->frequency = cadet_getfreq(dev); 381 f->frequency = cadet_getfreq(dev);
427 return 0; 382 return 0;
@@ -435,101 +390,52 @@ static int vidioc_s_frequency(struct file *file, void *priv,
435 390
436 if (f->type != V4L2_TUNER_RADIO) 391 if (f->type != V4L2_TUNER_RADIO)
437 return -EINVAL; 392 return -EINVAL;
438 if (dev->curtuner == 0 && (f->frequency < 1400 || f->frequency > 1728)) 393 if (f->tuner == 0) {
439 return -EINVAL; 394 if (f->frequency < 1400000)
440 if (dev->curtuner == 1 && (f->frequency < 8320 || f->frequency > 26400)) 395 f->frequency = 1400000;
396 else if (f->frequency > 1728000)
397 f->frequency = 1728000;
398 } else if (f->tuner == 1) {
399 if (f->frequency < 8320)
400 f->frequency = 8320;
401 else if (f->frequency > 26400)
402 f->frequency = 26400;
403 } else
441 return -EINVAL; 404 return -EINVAL;
442 cadet_setfreq(dev, f->frequency); 405 cadet_setfreq(dev, f->frequency);
443 return 0; 406 return 0;
444} 407}
445 408
446static int vidioc_queryctrl(struct file *file, void *priv, 409static int cadet_s_ctrl(struct v4l2_ctrl *ctrl)
447 struct v4l2_queryctrl *qc)
448{ 410{
449 switch (qc->id) { 411 struct cadet *dev = container_of(ctrl->handler, struct cadet, ctrl_handler);
450 case V4L2_CID_AUDIO_MUTE:
451 return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
452 case V4L2_CID_AUDIO_VOLUME:
453 return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
454 }
455 return -EINVAL;
456}
457
458static int vidioc_g_ctrl(struct file *file, void *priv,
459 struct v4l2_control *ctrl)
460{
461 struct cadet *dev = video_drvdata(file);
462 412
463 switch (ctrl->id) { 413 switch (ctrl->id) {
464 case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */ 414 case V4L2_CID_AUDIO_MUTE:
465 ctrl->value = (cadet_getvol(dev) == 0); 415 outb(7, dev->io); /* Select tuner control */
466 break; 416 if (ctrl->val)
467 case V4L2_CID_AUDIO_VOLUME: 417 outb(0x00, dev->io + 1);
468 ctrl->value = cadet_getvol(dev);
469 break;
470 default:
471 return -EINVAL;
472 }
473 return 0;
474}
475
476static int vidioc_s_ctrl(struct file *file, void *priv,
477 struct v4l2_control *ctrl)
478{
479 struct cadet *dev = video_drvdata(file);
480
481 switch (ctrl->id){
482 case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
483 if (ctrl->value)
484 cadet_setvol(dev, 0);
485 else 418 else
486 cadet_setvol(dev, 0xffff); 419 outb(0x20, dev->io + 1);
487 break; 420 return 0;
488 case V4L2_CID_AUDIO_VOLUME:
489 cadet_setvol(dev, ctrl->value);
490 break;
491 default:
492 return -EINVAL;
493 } 421 }
494 return 0; 422 return -EINVAL;
495}
496
497static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
498{
499 *i = 0;
500 return 0;
501}
502
503static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
504{
505 return i ? -EINVAL : 0;
506}
507
508static int vidioc_g_audio(struct file *file, void *priv,
509 struct v4l2_audio *a)
510{
511 a->index = 0;
512 strlcpy(a->name, "Radio", sizeof(a->name));
513 a->capability = V4L2_AUDCAP_STEREO;
514 return 0;
515}
516
517static int vidioc_s_audio(struct file *file, void *priv,
518 struct v4l2_audio *a)
519{
520 return a->index ? -EINVAL : 0;
521} 423}
522 424
523static int cadet_open(struct file *file) 425static int cadet_open(struct file *file)
524{ 426{
525 struct cadet *dev = video_drvdata(file); 427 struct cadet *dev = video_drvdata(file);
428 int err;
526 429
527 mutex_lock(&dev->lock); 430 mutex_lock(&dev->lock);
528 dev->users++; 431 err = v4l2_fh_open(file);
529 if (1 == dev->users) 432 if (err)
433 goto fail;
434 if (v4l2_fh_is_singular_file(file))
530 init_waitqueue_head(&dev->read_queue); 435 init_waitqueue_head(&dev->read_queue);
436fail:
531 mutex_unlock(&dev->lock); 437 mutex_unlock(&dev->lock);
532 return 0; 438 return err;
533} 439}
534 440
535static int cadet_release(struct file *file) 441static int cadet_release(struct file *file)
@@ -537,11 +443,11 @@ static int cadet_release(struct file *file)
537 struct cadet *dev = video_drvdata(file); 443 struct cadet *dev = video_drvdata(file);
538 444
539 mutex_lock(&dev->lock); 445 mutex_lock(&dev->lock);
540 dev->users--; 446 if (v4l2_fh_is_singular_file(file) && dev->rdsstat) {
541 if (0 == dev->users) {
542 del_timer_sync(&dev->readtimer); 447 del_timer_sync(&dev->readtimer);
543 dev->rdsstat = 0; 448 dev->rdsstat = 0;
544 } 449 }
450 v4l2_fh_release(file);
545 mutex_unlock(&dev->lock); 451 mutex_unlock(&dev->lock);
546 return 0; 452 return 0;
547} 453}
@@ -549,11 +455,12 @@ static int cadet_release(struct file *file)
549static unsigned int cadet_poll(struct file *file, struct poll_table_struct *wait) 455static unsigned int cadet_poll(struct file *file, struct poll_table_struct *wait)
550{ 456{
551 struct cadet *dev = video_drvdata(file); 457 struct cadet *dev = video_drvdata(file);
458 unsigned int res = v4l2_ctrl_poll(file, wait);
552 459
553 poll_wait(file, &dev->read_queue, wait); 460 poll_wait(file, &dev->read_queue, wait);
554 if (dev->rdsin != dev->rdsout) 461 if (dev->rdsin != dev->rdsout)
555 return POLLIN | POLLRDNORM; 462 res |= POLLIN | POLLRDNORM;
556 return 0; 463 return res;
557} 464}
558 465
559 466
@@ -572,13 +479,13 @@ static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
572 .vidioc_s_tuner = vidioc_s_tuner, 479 .vidioc_s_tuner = vidioc_s_tuner,
573 .vidioc_g_frequency = vidioc_g_frequency, 480 .vidioc_g_frequency = vidioc_g_frequency,
574 .vidioc_s_frequency = vidioc_s_frequency, 481 .vidioc_s_frequency = vidioc_s_frequency,
575 .vidioc_queryctrl = vidioc_queryctrl, 482 .vidioc_log_status = v4l2_ctrl_log_status,
576 .vidioc_g_ctrl = vidioc_g_ctrl, 483 .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
577 .vidioc_s_ctrl = vidioc_s_ctrl, 484 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
578 .vidioc_g_audio = vidioc_g_audio, 485};
579 .vidioc_s_audio = vidioc_s_audio, 486
580 .vidioc_g_input = vidioc_g_input, 487static const struct v4l2_ctrl_ops cadet_ctrl_ops = {
581 .vidioc_s_input = vidioc_s_input, 488 .s_ctrl = cadet_s_ctrl,
582}; 489};
583 490
584#ifdef CONFIG_PNP 491#ifdef CONFIG_PNP
@@ -648,7 +555,8 @@ static int __init cadet_init(void)
648{ 555{
649 struct cadet *dev = &cadet_card; 556 struct cadet *dev = &cadet_card;
650 struct v4l2_device *v4l2_dev = &dev->v4l2_dev; 557 struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
651 int res; 558 struct v4l2_ctrl_handler *hdl;
559 int res = -ENODEV;
652 560
653 strlcpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name)); 561 strlcpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name));
654 mutex_init(&dev->lock); 562 mutex_init(&dev->lock);
@@ -680,23 +588,37 @@ static int __init cadet_init(void)
680 goto fail; 588 goto fail;
681 } 589 }
682 590
591 hdl = &dev->ctrl_handler;
592 v4l2_ctrl_handler_init(hdl, 2);
593 v4l2_ctrl_new_std(hdl, &cadet_ctrl_ops,
594 V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
595 v4l2_dev->ctrl_handler = hdl;
596 if (hdl->error) {
597 res = hdl->error;
598 v4l2_err(v4l2_dev, "Could not register controls\n");
599 goto err_hdl;
600 }
601
683 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); 602 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
684 dev->vdev.v4l2_dev = v4l2_dev; 603 dev->vdev.v4l2_dev = v4l2_dev;
685 dev->vdev.fops = &cadet_fops; 604 dev->vdev.fops = &cadet_fops;
686 dev->vdev.ioctl_ops = &cadet_ioctl_ops; 605 dev->vdev.ioctl_ops = &cadet_ioctl_ops;
687 dev->vdev.release = video_device_release_empty; 606 dev->vdev.release = video_device_release_empty;
607 dev->vdev.lock = &dev->lock;
608 set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags);
688 video_set_drvdata(&dev->vdev, dev); 609 video_set_drvdata(&dev->vdev, dev);
689 610
690 if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { 611 if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
691 v4l2_device_unregister(v4l2_dev); 612 goto err_hdl;
692 release_region(dev->io, 2);
693 goto fail;
694 }
695 v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x\n", dev->io); 613 v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x\n", dev->io);
696 return 0; 614 return 0;
615err_hdl:
616 v4l2_ctrl_handler_free(hdl);
617 v4l2_device_unregister(v4l2_dev);
618 release_region(dev->io, 2);
697fail: 619fail:
698 pnp_unregister_driver(&cadet_pnp_driver); 620 pnp_unregister_driver(&cadet_pnp_driver);
699 return -ENODEV; 621 return res;
700} 622}
701 623
702static void __exit cadet_exit(void) 624static void __exit cadet_exit(void)
@@ -704,6 +626,7 @@ static void __exit cadet_exit(void)
704 struct cadet *dev = &cadet_card; 626 struct cadet *dev = &cadet_card;
705 627
706 video_unregister_device(&dev->vdev); 628 video_unregister_device(&dev->vdev);
629 v4l2_ctrl_handler_free(&dev->ctrl_handler);
707 v4l2_device_unregister(&dev->v4l2_dev); 630 v4l2_device_unregister(&dev->v4l2_dev);
708 release_region(dev->io, 2); 631 release_region(dev->io, 2);
709 pnp_unregister_driver(&cadet_pnp_driver); 632 pnp_unregister_driver(&cadet_pnp_driver);