Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

V4L/DVB (4406): Convert radio-cadet to V4L2 API

This is a card with RDS capabilities.
RDS specifications didn't change from V4L1 to V4L2, so that part should be OK.
This patch changed the following stuff:
* The device can be opened multiple times. That's necessary because there are
at least a radio application and an RDS application (rdsd) that want to
open() the device.
* Added a poll() function. Every character device should have that, and rdsd
expects it as it uses select() on that file descriptor.
* Converted the ioctls to V4L2. MUTE is not implemented correctly as the
card doesn't seem to have a special bit for that. Probably there are a few
more ioctls that should at least return 0 or an error.
As I do not own such a card, I couldn't test anything. If there is anybody out
there who owns such an ancient card, please test and report.
I just checked that the code compiles.

Signed-off-by: Hans J. Koch <koch@hjk-az.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>

authored by

Hans J. Koch and committed by
Mauro Carvalho Chehab
c0c7fa09 176ac9da

+146 -125
+1 -1
drivers/media/radio/Kconfig
··· 7 7 8 8 config RADIO_CADET 9 9 tristate "ADS Cadet AM/FM Tuner" 10 - depends on ISA && VIDEO_V4L1 10 + depends on ISA && VIDEO_V4L2 11 11 ---help--- 12 12 Choose Y here if you have one of these AM/FM radio cards, and then 13 13 fill in the port address below.
+145 -124
drivers/media/radio/radio-cadet.c
··· 25 25 * 26 26 * 2003-01-31 Alan Cox <alan@redhat.com> 27 27 * Cleaned up locking, delay code, general odds and ends 28 + * 29 + * 2006-07-30 Hans J. Koch <koch@hjk-az.de> 30 + * Changed API to V4L2 28 31 */ 29 32 30 33 #include <linux/module.h> /* Modules */ ··· 36 33 #include <linux/delay.h> /* udelay */ 37 34 #include <asm/io.h> /* outb, outb_p */ 38 35 #include <asm/uaccess.h> /* copy to/from user */ 39 - #include <linux/videodev.h> /* kernel radio structs */ 36 + #include <linux/videodev2.h> /* V4L2 API defs */ 40 37 #include <media/v4l2-common.h> 41 38 #include <linux/param.h> 42 39 #include <linux/pnp.h> 43 40 44 41 #define RDS_BUFFER 256 42 + #define RDS_RX_FLAG 1 43 + #define MBS_RX_FLAG 2 44 + 45 + #define CADET_VERSION KERNEL_VERSION(0,3,3) 45 46 46 47 static int io=-1; /* default to isapnp activation */ 47 48 static int radio_nr = -1; ··· 68 61 */ 69 62 static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}}; 70 63 71 - static int cadet_getrds(void) 64 + 65 + static int 66 + cadet_getstereo(void) 72 67 { 73 - int rdsstat=0; 74 - 75 - spin_lock(&cadet_io_lock); 76 - outb(3,io); /* Select Decoder Control/Status */ 77 - outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */ 78 - spin_unlock(&cadet_io_lock); 79 - 80 - msleep(100); 81 - 82 - spin_lock(&cadet_io_lock); 83 - outb(3,io); /* Select Decoder Control/Status */ 84 - if((inb(io+1)&0x80)!=0) { 85 - rdsstat|=VIDEO_TUNER_RDS_ON; 86 - } 87 - if((inb(io+1)&0x10)!=0) { 88 - rdsstat|=VIDEO_TUNER_MBS_ON; 89 - } 90 - spin_unlock(&cadet_io_lock); 91 - return rdsstat; 92 - } 93 - 94 - static int cadet_getstereo(void) 95 - { 96 - int ret = 0; 68 + int ret = V4L2_TUNER_SUB_MONO; 97 69 if(curtuner != 0) /* Only FM has stereo capability! */ 98 - return 0; 70 + return V4L2_TUNER_SUB_MONO; 99 71 100 72 spin_lock(&cadet_io_lock); 101 73 outb(7,io); /* Select tuner control */ 102 74 if( (inb(io+1) & 0x40) == 0) 103 - ret = 1; 75 + ret = V4L2_TUNER_SUB_STEREO; 104 76 spin_unlock(&cadet_io_lock); 105 77 return ret; 106 78 } 107 79 108 - static unsigned cadet_gettune(void) 80 + static unsigned 81 + cadet_gettune(void) 109 82 { 110 83 int curvol,i; 111 84 unsigned fifo=0; ··· 122 135 return fifo; 123 136 } 124 137 125 - static unsigned cadet_getfreq(void) 138 + static unsigned 139 + cadet_getfreq(void) 126 140 { 127 141 int i; 128 142 unsigned freq=0,test,fifo=0; ··· 155 167 return freq; 156 168 } 157 169 158 - static void cadet_settune(unsigned fifo) 170 + static void 171 + cadet_settune(unsigned fifo) 159 172 { 160 173 int i; 161 174 unsigned test; ··· 184 195 spin_unlock(&cadet_io_lock); 185 196 } 186 197 187 - static void cadet_setfreq(unsigned freq) 198 + static void 199 + cadet_setfreq(unsigned freq) 188 200 { 189 201 unsigned fifo; 190 202 int i,j,test; ··· 245 255 } 246 256 247 257 248 - static int cadet_getvol(void) 258 + static int 259 + cadet_getvol(void) 249 260 { 250 261 int ret = 0; 251 262 ··· 261 270 } 262 271 263 272 264 - static void cadet_setvol(int vol) 273 + static void 274 + cadet_setvol(int vol) 265 275 { 266 276 spin_lock(&cadet_io_lock); 267 277 outb(7,io); /* Select tuner control */ ··· 273 281 spin_unlock(&cadet_io_lock); 274 282 } 275 283 276 - static void cadet_handler(unsigned long data) 284 + static void 285 + cadet_handler(unsigned long data) 277 286 { 278 287 /* 279 288 * Service the RDS fifo ··· 315 322 316 323 317 324 318 - static ssize_t cadet_read(struct file *file, char __user *data, 319 - size_t count, loff_t *ppos) 325 + static ssize_t 326 + cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos) 320 327 { 321 328 int i=0; 322 329 unsigned char readbuf[RDS_BUFFER]; ··· 352 359 { 353 360 switch(cmd) 354 361 { 355 - case VIDIOCGCAP: 362 + case VIDIOC_QUERYCAP: 356 363 { 357 - struct video_capability *v = arg; 358 - memset(v,0,sizeof(*v)); 359 - v->type=VID_TYPE_TUNER; 360 - v->channels=2; 361 - v->audios=1; 362 - strcpy(v->name, "ADS Cadet"); 364 + struct v4l2_capability *cap = arg; 365 + memset(cap,0,sizeof(*cap)); 366 + cap->capabilities = 367 + V4L2_CAP_TUNER | 368 + V4L2_CAP_READWRITE; 369 + cap->version = CADET_VERSION; 370 + strcpy(cap->driver, "ADS Cadet"); 371 + strcpy(cap->card, "ADS Cadet"); 363 372 return 0; 364 373 } 365 - case VIDIOCGTUNER: 374 + case VIDIOC_G_TUNER: 366 375 { 367 - struct video_tuner *v = arg; 368 - if((v->tuner<0)||(v->tuner>1)) { 376 + struct v4l2_tuner *t = arg; 377 + memset(t,0,sizeof(*t)); 378 + t->type = V4L2_TUNER_RADIO; 379 + switch (t->index) 380 + { 381 + case 0: strcpy(t->name, "FM"); 382 + t->capability = V4L2_TUNER_CAP_STEREO; 383 + t->rangelow = 1400; /* 87.5 MHz */ 384 + t->rangehigh = 1728; /* 108.0 MHz */ 385 + t->rxsubchans=cadet_getstereo(); 386 + switch (t->rxsubchans){ 387 + case V4L2_TUNER_SUB_MONO: 388 + t->audmode = V4L2_TUNER_MODE_MONO; 389 + break; 390 + case V4L2_TUNER_SUB_STEREO: 391 + t->audmode = V4L2_TUNER_MODE_STEREO; 392 + break; 393 + default: ; 394 + } 395 + break; 396 + case 1: strcpy(t->name, "AM"); 397 + t->capability = V4L2_TUNER_CAP_LOW; 398 + t->rangelow = 8320; /* 520 kHz */ 399 + t->rangehigh = 26400; /* 1650 kHz */ 400 + t->rxsubchans = V4L2_TUNER_SUB_MONO; 401 + t->audmode = V4L2_TUNER_MODE_MONO; 402 + break; 403 + default: 404 + return -EINVAL; 405 + } 406 + 407 + t->signal = sigstrength; /* We might need to modify scaling of this */ 408 + return 0; 409 + } 410 + case VIDIOC_S_TUNER: 411 + { 412 + struct v4l2_tuner *t = arg; 413 + if((t->index != 0)&&(t->index != 1)) 414 + return -EINVAL; 415 + 416 + curtuner = t->index; 417 + return 0; 418 + } 419 + case VIDIOC_G_FREQUENCY: 420 + { 421 + struct v4l2_frequency *f = arg; 422 + memset(f,0,sizeof(*f)); 423 + f->tuner = curtuner; 424 + f->type = V4L2_TUNER_RADIO; 425 + f->frequency = cadet_getfreq(); 426 + return 0; 427 + } 428 + case VIDIOC_S_FREQUENCY: 429 + { 430 + struct v4l2_frequency *f = arg; 431 + if (f->type != V4L2_TUNER_RADIO){ 369 432 return -EINVAL; 370 433 } 371 - switch(v->tuner) { 372 - case 0: 373 - strcpy(v->name,"FM"); 374 - v->rangelow=1400; /* 87.5 MHz */ 375 - v->rangehigh=1728; /* 108.0 MHz */ 376 - v->flags=0; 377 - v->mode=0; 378 - v->mode|=VIDEO_MODE_AUTO; 379 - v->signal=sigstrength; 380 - if(cadet_getstereo()==1) { 381 - v->flags|=VIDEO_TUNER_STEREO_ON; 382 - } 383 - v->flags|=cadet_getrds(); 384 - break; 385 - case 1: 386 - strcpy(v->name,"AM"); 387 - v->rangelow=8320; /* 520 kHz */ 388 - v->rangehigh=26400; /* 1650 kHz */ 389 - v->flags=0; 390 - v->flags|=VIDEO_TUNER_LOW; 391 - v->mode=0; 392 - v->mode|=VIDEO_MODE_AUTO; 393 - v->signal=sigstrength; 394 - break; 395 - } 396 - return 0; 397 - } 398 - case VIDIOCSTUNER: 399 - { 400 - struct video_tuner *v = arg; 401 - if((v->tuner<0)||(v->tuner>1)) { 434 + if((curtuner==0)&&((f->frequency<1400)||(f->frequency>1728))) { 402 435 return -EINVAL; 403 436 } 404 - curtuner=v->tuner; 405 - return 0; 406 - } 407 - case VIDIOCGFREQ: 408 - { 409 - unsigned long *freq = arg; 410 - *freq = cadet_getfreq(); 411 - return 0; 412 - } 413 - case VIDIOCSFREQ: 414 - { 415 - unsigned long *freq = arg; 416 - if((curtuner==0)&&((*freq<1400)||(*freq>1728))) { 437 + if((curtuner==1)&&((f->frequency<8320)||(f->frequency>26400))) { 417 438 return -EINVAL; 418 439 } 419 - if((curtuner==1)&&((*freq<8320)||(*freq>26400))) { 420 - return -EINVAL; 421 - } 422 - cadet_setfreq(*freq); 440 + cadet_setfreq(f->frequency); 423 441 return 0; 424 442 } 425 - case VIDIOCGAUDIO: 443 + case VIDIOC_G_CTRL: 426 444 { 427 - struct video_audio *v = arg; 428 - memset(v,0, sizeof(*v)); 429 - v->flags=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME; 430 - if(cadet_getstereo()==0) { 431 - v->mode=VIDEO_SOUND_MONO; 432 - } else { 433 - v->mode=VIDEO_SOUND_STEREO; 445 + struct v4l2_control *c = arg; 446 + switch (c->id){ 447 + case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */ 448 + c->value = (cadet_getvol() == 0); 449 + break; 450 + case V4L2_CID_AUDIO_VOLUME: 451 + c->value = cadet_getvol(); 452 + break; 453 + default: 454 + return -EINVAL; 434 455 } 435 - v->volume=cadet_getvol(); 436 - v->step=0xffff; 437 - strcpy(v->name, "Radio"); 438 456 return 0; 439 457 } 440 - case VIDIOCSAUDIO: 458 + case VIDIOC_S_CTRL: 441 459 { 442 - struct video_audio *v = arg; 443 - if(v->audio) 444 - return -EINVAL; 445 - cadet_setvol(v->volume); 446 - if(v->flags&VIDEO_AUDIO_MUTE) 447 - cadet_setvol(0); 448 - else 449 - cadet_setvol(0xffff); 460 + struct v4l2_control *c = arg; 461 + switch (c->id){ 462 + case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */ 463 + if (c->value) cadet_setvol(0); 464 + else cadet_setvol(0xffff); 465 + break; 466 + case V4L2_CID_AUDIO_VOLUME: 467 + cadet_setvol(c->value); 468 + break; 469 + default: 470 + return -EINVAL; 471 + } 450 472 return 0; 451 473 } 474 + 452 475 default: 453 476 return -ENOIOCTLCMD; 454 477 } 455 478 } 456 479 457 - static int cadet_ioctl(struct inode *inode, struct file *file, 480 + static int 481 + cadet_ioctl(struct inode *inode, struct file *file, 458 482 unsigned int cmd, unsigned long arg) 459 483 { 460 484 return video_usercopy(inode, file, cmd, arg, cadet_do_ioctl); 461 485 } 462 486 463 - static int cadet_open(struct inode *inode, struct file *file) 487 + static int 488 + cadet_open(struct inode *inode, struct file *file) 464 489 { 465 - if(users) 466 - return -EBUSY; 467 490 users++; 468 - init_waitqueue_head(&read_queue); 491 + if (1 == users) init_waitqueue_head(&read_queue); 469 492 return 0; 470 493 } 471 494 472 - static int cadet_release(struct inode *inode, struct file *file) 495 + static int 496 + cadet_release(struct inode *inode, struct file *file) 473 497 { 474 - del_timer_sync(&readtimer); 475 - rdsstat=0; 476 498 users--; 499 + if (0 == users){ 500 + del_timer_sync(&readtimer); 501 + rdsstat=0; 502 + } 503 + return 0; 504 + } 505 + 506 + static unsigned int 507 + cadet_poll(struct file *file, struct poll_table_struct *wait) 508 + { 509 + poll_wait(file,&read_queue,wait); 510 + if(rdsin != rdsout) 511 + return POLLIN | POLLRDNORM; 477 512 return 0; 478 513 } 479 514 ··· 512 491 .release = cadet_release, 513 492 .read = cadet_read, 514 493 .ioctl = cadet_ioctl, 494 + .poll = cadet_poll, 515 495 .compat_ioctl = v4l_compat_ioctl32, 516 496 .llseek = no_llseek, 517 497 }; ··· 522 500 .owner = THIS_MODULE, 523 501 .name = "Cadet radio", 524 502 .type = VID_TYPE_TUNER, 525 - .hardware = VID_HARDWARE_CADET, 526 503 .fops = &cadet_fops, 527 504 }; 528 505