1 /* radio-cadet.c - A video4linux driver for the ADS Cadet AM/FM Radio Card 2 * 3 * by Fred Gleason <fredg@wava.com> 4 * Version 0.3.3 5 * 6 * (Loosely) based on code for the Aztech radio card by 7 * 8 * Russell Kroll (rkroll@exploits.org) 9 * Quay Ly 10 * Donald Song 11 * Jason Lewis (jlewis@twilight.vtc.vsc.edu) 12 * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) 13 * William McGrath (wmcgrath@twilight.vtc.vsc.edu) 14 * 15 * History: 16 * 2000-04-29 Russell Kroll <rkroll@exploits.org> 17 * Added ISAPnP detection for Linux 2.3/2.4 18 * 19 * 2001-01-10 Russell Kroll <rkroll@exploits.org> 20 * Removed dead CONFIG_RADIO_CADET_PORT code 21 * PnP detection on load is now default (no args necessary) 22 * 23 * 2002-01-17 Adam Belay <ambx1@neo.rr.com> 24 * Updated to latest pnp code 25 * 26 * 2003-01-31 Alan Cox <alan@redhat.com> 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 31 */ 32 33 #include <linux/version.h> 34 #include <linux/module.h> /* Modules */ 35 #include <linux/init.h> /* Initdata */ 36 #include <linux/ioport.h> /* request_region */ 37 #include <linux/delay.h> /* udelay */ 38 #include <asm/io.h> /* outb, outb_p */ 39 #include <asm/uaccess.h> /* copy to/from user */ 40 #include <linux/videodev2.h> /* V4L2 API defs */ 41 #include <media/v4l2-common.h> 42 #include <media/v4l2-ioctl.h> 43 #include <linux/param.h> 44 #include <linux/pnp.h> 45 46 #define RDS_BUFFER 256 47 #define RDS_RX_FLAG 1 48 #define MBS_RX_FLAG 2 49 50 #define CADET_VERSION KERNEL_VERSION(0,3,3) 51 52 static struct v4l2_queryctrl radio_qctrl[] = { 53 { 54 .id = V4L2_CID_AUDIO_MUTE, 55 .name = "Mute", 56 .minimum = 0, 57 .maximum = 1, 58 .default_value = 1, 59 .type = V4L2_CTRL_TYPE_BOOLEAN, 60 },{ 61 .id = V4L2_CID_AUDIO_VOLUME, 62 .name = "Volume", 63 .minimum = 0, 64 .maximum = 0xff, 65 .step = 1, 66 .default_value = 0xff, 67 .type = V4L2_CTRL_TYPE_INTEGER, 68 } 69 }; 70 71 static int io=-1; /* default to isapnp activation */ 72 static int radio_nr = -1; 73 static int users; 74 static int curtuner; 75 static int tunestat; 76 static int sigstrength; 77 static wait_queue_head_t read_queue; 78 static struct timer_list readtimer; 79 static __u8 rdsin, rdsout, rdsstat; 80 static unsigned char rdsbuf[RDS_BUFFER]; 81 static spinlock_t cadet_io_lock; 82 83 static int cadet_probe(void); 84 85 /* 86 * Signal Strength Threshold Values 87 * The V4L API spec does not define any particular unit for the signal 88 * strength value. These values are in microvolts of RF at the tuner's input. 89 */ 90 static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}}; 91 92 93 static int 94 cadet_getstereo(void) 95 { 96 int ret = V4L2_TUNER_SUB_MONO; 97 if(curtuner != 0) /* Only FM has stereo capability! */ 98 return V4L2_TUNER_SUB_MONO; 99 100 spin_lock(&cadet_io_lock); 101 outb(7,io); /* Select tuner control */ 102 if( (inb(io+1) & 0x40) == 0) 103 ret = V4L2_TUNER_SUB_STEREO; 104 spin_unlock(&cadet_io_lock); 105 return ret; 106 } 107 108 static unsigned 109 cadet_gettune(void) 110 { 111 int curvol,i; 112 unsigned fifo=0; 113 114 /* 115 * Prepare for read 116 */ 117 118 spin_lock(&cadet_io_lock); 119 120 outb(7,io); /* Select tuner control */ 121 curvol=inb(io+1); /* Save current volume/mute setting */ 122 outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */ 123 tunestat=0xffff; 124 125 /* 126 * Read the shift register 127 */ 128 for(i=0;i<25;i++) { 129 fifo=(fifo<<1)|((inb(io+1)>>7)&0x01); 130 if(i<24) { 131 outb(0x01,io+1); 132 tunestat&=inb(io+1); 133 outb(0x00,io+1); 134 } 135 } 136 137 /* 138 * Restore volume/mute setting 139 */ 140 outb(curvol,io+1); 141 spin_unlock(&cadet_io_lock); 142 143 return fifo; 144 } 145 146 static unsigned 147 cadet_getfreq(void) 148 { 149 int i; 150 unsigned freq=0,test,fifo=0; 151 152 /* 153 * Read current tuning 154 */ 155 fifo=cadet_gettune(); 156 157 /* 158 * Convert to actual frequency 159 */ 160 if(curtuner==0) { /* FM */ 161 test=12500; 162 for(i=0;i<14;i++) { 163 if((fifo&0x01)!=0) { 164 freq+=test; 165 } 166 test=test<<1; 167 fifo=fifo>>1; 168 } 169 freq-=10700000; /* IF frequency is 10.7 MHz */ 170 freq=(freq*16)/1000000; /* Make it 1/16 MHz */ 171 } 172 if(curtuner==1) { /* AM */ 173 freq=((fifo&0x7fff)-2010)*16; 174 } 175 176 return freq; 177 } 178 179 static void 180 cadet_settune(unsigned fifo) 181 { 182 int i; 183 unsigned test; 184 185 spin_lock(&cadet_io_lock); 186 187 outb(7,io); /* Select tuner control */ 188 /* 189 * Write the shift register 190 */ 191 test=0; 192 test=(fifo>>23)&0x02; /* Align data for SDO */ 193 test|=0x1c; /* SDM=1, SWE=1, SEN=1, SCK=0 */ 194 outb(7,io); /* Select tuner control */ 195 outb(test,io+1); /* Initialize for write */ 196 for(i=0;i<25;i++) { 197 test|=0x01; /* Toggle SCK High */ 198 outb(test,io+1); 199 test&=0xfe; /* Toggle SCK Low */ 200 outb(test,io+1); 201 fifo=fifo<<1; /* Prepare the next bit */ 202 test=0x1c|((fifo>>23)&0x02); 203 outb(test,io+1); 204 } 205 spin_unlock(&cadet_io_lock); 206 } 207 208 static void 209 cadet_setfreq(unsigned freq) 210 { 211 unsigned fifo; 212 int i,j,test; 213 int curvol; 214 215 /* 216 * Formulate a fifo command 217 */ 218 fifo=0; 219 if(curtuner==0) { /* FM */ 220 test=102400; 221 freq=(freq*1000)/16; /* Make it kHz */ 222 freq+=10700; /* IF is 10700 kHz */ 223 for(i=0;i<14;i++) { 224 fifo=fifo<<1; 225 if(freq>=test) { 226 fifo|=0x01; 227 freq-=test; 228 } 229 test=test>>1; 230 } 231 } 232 if(curtuner==1) { /* AM */ 233 fifo=(freq/16)+2010; /* Make it kHz */ 234 fifo|=0x100000; /* Select AM Band */ 235 } 236 237 /* 238 * Save current volume/mute setting 239 */ 240 241 spin_lock(&cadet_io_lock); 242 outb(7,io); /* Select tuner control */ 243 curvol=inb(io+1); 244 spin_unlock(&cadet_io_lock); 245 246 /* 247 * Tune the card 248 */ 249 for(j=3;j>-1;j--) { 250 cadet_settune(fifo|(j<<16)); 251 252 spin_lock(&cadet_io_lock); 253 outb(7,io); /* Select tuner control */ 254 outb(curvol,io+1); 255 spin_unlock(&cadet_io_lock); 256 257 msleep(100); 258 259 cadet_gettune(); 260 if((tunestat & 0x40) == 0) { /* Tuned */ 261 sigstrength=sigtable[curtuner][j]; 262 return; 263 } 264 } 265 sigstrength=0; 266 } 267 268 269 static int 270 cadet_getvol(void) 271 { 272 int ret = 0; 273 274 spin_lock(&cadet_io_lock); 275 276 outb(7,io); /* Select tuner control */ 277 if((inb(io + 1) & 0x20) != 0) 278 ret = 0xffff; 279 280 spin_unlock(&cadet_io_lock); 281 return ret; 282 } 283 284 285 static void 286 cadet_setvol(int vol) 287 { 288 spin_lock(&cadet_io_lock); 289 outb(7,io); /* Select tuner control */ 290 if(vol>0) 291 outb(0x20,io+1); 292 else 293 outb(0x00,io+1); 294 spin_unlock(&cadet_io_lock); 295 } 296 297 static void 298 cadet_handler(unsigned long data) 299 { 300 /* 301 * Service the RDS fifo 302 */ 303 304 if(spin_trylock(&cadet_io_lock)) 305 { 306 outb(0x3,io); /* Select RDS Decoder Control */ 307 if((inb(io+1)&0x20)!=0) { 308 printk(KERN_CRIT "cadet: RDS fifo overflow\n"); 309 } 310 outb(0x80,io); /* Select RDS fifo */ 311 while((inb(io)&0x80)!=0) { 312 rdsbuf[rdsin]=inb(io+1); 313 if(rdsin==rdsout) 314 printk(KERN_WARNING "cadet: RDS buffer overflow\n"); 315 else 316 rdsin++; 317 } 318 spin_unlock(&cadet_io_lock); 319 } 320 321 /* 322 * Service pending read 323 */ 324 if( rdsin!=rdsout) 325 wake_up_interruptible(&read_queue); 326 327 /* 328 * Clean up and exit 329 */ 330 init_timer(&readtimer); 331 readtimer.function=cadet_handler; 332 readtimer.data=(unsigned long)0; 333 readtimer.expires=jiffies+msecs_to_jiffies(50); 334 add_timer(&readtimer); 335 } 336 337 338 339 static ssize_t 340 cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos) 341 { 342 int i=0; 343 unsigned char readbuf[RDS_BUFFER]; 344 345 if(rdsstat==0) { 346 spin_lock(&cadet_io_lock); 347 rdsstat=1; 348 outb(0x80,io); /* Select RDS fifo */ 349 spin_unlock(&cadet_io_lock); 350 init_timer(&readtimer); 351 readtimer.function=cadet_handler; 352 readtimer.data=(unsigned long)0; 353 readtimer.expires=jiffies+msecs_to_jiffies(50); 354 add_timer(&readtimer); 355 } 356 if(rdsin==rdsout) { 357 if (file->f_flags & O_NONBLOCK) 358 return -EWOULDBLOCK; 359 interruptible_sleep_on(&read_queue); 360 } 361 while( i<count && rdsin!=rdsout) 362 readbuf[i++]=rdsbuf[rdsout++]; 363 364 if (copy_to_user(data,readbuf,i)) 365 return -EFAULT; 366 return i; 367 } 368 369 370 static int vidioc_querycap(struct file *file, void *priv, 371 struct v4l2_capability *v) 372 { 373 v->capabilities = 374 V4L2_CAP_TUNER | 375 V4L2_CAP_READWRITE; 376 v->version = CADET_VERSION; 377 strcpy(v->driver, "ADS Cadet"); 378 strcpy(v->card, "ADS Cadet"); 379 return 0; 380 } 381 382 static int vidioc_g_tuner(struct file *file, void *priv, 383 struct v4l2_tuner *v) 384 { 385 v->type = V4L2_TUNER_RADIO; 386 switch (v->index) { 387 case 0: 388 strcpy(v->name, "FM"); 389 v->capability = V4L2_TUNER_CAP_STEREO; 390 v->rangelow = 1400; /* 87.5 MHz */ 391 v->rangehigh = 1728; /* 108.0 MHz */ 392 v->rxsubchans=cadet_getstereo(); 393 switch (v->rxsubchans){ 394 case V4L2_TUNER_SUB_MONO: 395 v->audmode = V4L2_TUNER_MODE_MONO; 396 break; 397 case V4L2_TUNER_SUB_STEREO: 398 v->audmode = V4L2_TUNER_MODE_STEREO; 399 break; 400 default: ; 401 } 402 break; 403 case 1: 404 strcpy(v->name, "AM"); 405 v->capability = V4L2_TUNER_CAP_LOW; 406 v->rangelow = 8320; /* 520 kHz */ 407 v->rangehigh = 26400; /* 1650 kHz */ 408 v->rxsubchans = V4L2_TUNER_SUB_MONO; 409 v->audmode = V4L2_TUNER_MODE_MONO; 410 break; 411 default: 412 return -EINVAL; 413 } 414 v->signal = sigstrength; /* We might need to modify scaling of this */ 415 return 0; 416 } 417 418 static int vidioc_s_tuner(struct file *file, void *priv, 419 struct v4l2_tuner *v) 420 { 421 if((v->index != 0)&&(v->index != 1)) 422 return -EINVAL; 423 curtuner = v->index; 424 return 0; 425 } 426 427 static int vidioc_g_frequency(struct file *file, void *priv, 428 struct v4l2_frequency *f) 429 { 430 f->tuner = curtuner; 431 f->type = V4L2_TUNER_RADIO; 432 f->frequency = cadet_getfreq(); 433 return 0; 434 } 435 436 437 static int vidioc_s_frequency(struct file *file, void *priv, 438 struct v4l2_frequency *f) 439 { 440 if (f->type != V4L2_TUNER_RADIO) 441 return -EINVAL; 442 if((curtuner==0)&&((f->frequency<1400)||(f->frequency>1728))) 443 return -EINVAL; 444 if((curtuner==1)&&((f->frequency<8320)||(f->frequency>26400))) 445 return -EINVAL; 446 cadet_setfreq(f->frequency); 447 return 0; 448 } 449 450 static int vidioc_queryctrl(struct file *file, void *priv, 451 struct v4l2_queryctrl *qc) 452 { 453 int i; 454 455 for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { 456 if (qc->id && qc->id == radio_qctrl[i].id) { 457 memcpy(qc, &(radio_qctrl[i]), 458 sizeof(*qc)); 459 return 0; 460 } 461 } 462 return -EINVAL; 463 } 464 465 static int vidioc_g_ctrl(struct file *file, void *priv, 466 struct v4l2_control *ctrl) 467 { 468 switch (ctrl->id){ 469 case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */ 470 ctrl->value = (cadet_getvol() == 0); 471 break; 472 case V4L2_CID_AUDIO_VOLUME: 473 ctrl->value = cadet_getvol(); 474 break; 475 default: 476 return -EINVAL; 477 } 478 return 0; 479 } 480 481 static int vidioc_s_ctrl(struct file *file, void *priv, 482 struct v4l2_control *ctrl) 483 { 484 switch (ctrl->id){ 485 case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */ 486 if (ctrl->value) 487 cadet_setvol(0); 488 else 489 cadet_setvol(0xffff); 490 break; 491 case V4L2_CID_AUDIO_VOLUME: 492 cadet_setvol(ctrl->value); 493 break; 494 default: 495 return -EINVAL; 496 } 497 return 0; 498 } 499 500 static int vidioc_g_audio(struct file *file, void *priv, 501 struct v4l2_audio *a) 502 { 503 if (a->index > 1) 504 return -EINVAL; 505 strcpy(a->name, "Radio"); 506 a->capability = V4L2_AUDCAP_STEREO; 507 return 0; 508 } 509 510 static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) 511 { 512 *i = 0; 513 return 0; 514 } 515 516 static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 517 { 518 if (i != 0) 519 return -EINVAL; 520 return 0; 521 } 522 523 static int vidioc_s_audio(struct file *file, void *priv, 524 struct v4l2_audio *a) 525 { 526 if (a->index != 0) 527 return -EINVAL; 528 return 0; 529 } 530 531 static int 532 cadet_open(struct inode *inode, struct file *file) 533 { 534 users++; 535 if (1 == users) init_waitqueue_head(&read_queue); 536 return 0; 537 } 538 539 static int 540 cadet_release(struct inode *inode, struct file *file) 541 { 542 users--; 543 if (0 == users){ 544 del_timer_sync(&readtimer); 545 rdsstat=0; 546 } 547 return 0; 548 } 549 550 static unsigned int 551 cadet_poll(struct file *file, struct poll_table_struct *wait) 552 { 553 poll_wait(file,&read_queue,wait); 554 if(rdsin != rdsout) 555 return POLLIN | POLLRDNORM; 556 return 0; 557 } 558 559 560 static const struct file_operations cadet_fops = { 561 .owner = THIS_MODULE, 562 .open = cadet_open, 563 .release = cadet_release, 564 .read = cadet_read, 565 .ioctl = video_ioctl2, 566 .poll = cadet_poll, 567 #ifdef CONFIG_COMPAT 568 .compat_ioctl = v4l_compat_ioctl32, 569 #endif 570 .llseek = no_llseek, 571 }; 572 573 static const struct v4l2_ioctl_ops cadet_ioctl_ops = { 574 .vidioc_querycap = vidioc_querycap, 575 .vidioc_g_tuner = vidioc_g_tuner, 576 .vidioc_s_tuner = vidioc_s_tuner, 577 .vidioc_g_frequency = vidioc_g_frequency, 578 .vidioc_s_frequency = vidioc_s_frequency, 579 .vidioc_queryctrl = vidioc_queryctrl, 580 .vidioc_g_ctrl = vidioc_g_ctrl, 581 .vidioc_s_ctrl = vidioc_s_ctrl, 582 .vidioc_g_audio = vidioc_g_audio, 583 .vidioc_s_audio = vidioc_s_audio, 584 .vidioc_g_input = vidioc_g_input, 585 .vidioc_s_input = vidioc_s_input, 586 }; 587 588 static struct video_device cadet_radio = { 589 .name = "Cadet radio", 590 .fops = &cadet_fops, 591 .ioctl_ops = &cadet_ioctl_ops, 592 .release = video_device_release_empty, 593 }; 594 595 #ifdef CONFIG_PNP 596 597 static struct pnp_device_id cadet_pnp_devices[] = { 598 /* ADS Cadet AM/FM Radio Card */ 599 {.id = "MSM0c24", .driver_data = 0}, 600 {.id = ""} 601 }; 602 603 MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices); 604 605 static int cadet_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id) 606 { 607 if (!dev) 608 return -ENODEV; 609 /* only support one device */ 610 if (io > 0) 611 return -EBUSY; 612 613 if (!pnp_port_valid(dev, 0)) { 614 return -ENODEV; 615 } 616 617 io = pnp_port_start(dev, 0); 618 619 printk ("radio-cadet: PnP reports device at %#x\n", io); 620 621 return io; 622 } 623 624 static struct pnp_driver cadet_pnp_driver = { 625 .name = "radio-cadet", 626 .id_table = cadet_pnp_devices, 627 .probe = cadet_pnp_probe, 628 .remove = NULL, 629 }; 630 631 #else 632 static struct pnp_driver cadet_pnp_driver; 633 #endif 634 635 static int cadet_probe(void) 636 { 637 static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e}; 638 int i; 639 640 for(i=0;i<8;i++) { 641 io=iovals[i]; 642 if (request_region(io, 2, "cadet-probe")) { 643 cadet_setfreq(1410); 644 if(cadet_getfreq()==1410) { 645 release_region(io, 2); 646 return io; 647 } 648 release_region(io, 2); 649 } 650 } 651 return -1; 652 } 653 654 /* 655 * io should only be set if the user has used something like 656 * isapnp (the userspace program) to initialize this card for us 657 */ 658 659 static int __init cadet_init(void) 660 { 661 spin_lock_init(&cadet_io_lock); 662 663 /* 664 * If a probe was requested then probe ISAPnP first (safest) 665 */ 666 if (io < 0) 667 pnp_register_driver(&cadet_pnp_driver); 668 /* 669 * If that fails then probe unsafely if probe is requested 670 */ 671 if(io < 0) 672 io = cadet_probe (); 673 674 /* 675 * Else we bail out 676 */ 677 678 if(io < 0) { 679 #ifdef MODULE 680 printk(KERN_ERR "You must set an I/O address with io=0x???\n"); 681 #endif 682 goto fail; 683 } 684 if (!request_region(io,2,"cadet")) 685 goto fail; 686 if (video_register_device(&cadet_radio, VFL_TYPE_RADIO, radio_nr) < 0) { 687 release_region(io,2); 688 goto fail; 689 } 690 printk(KERN_INFO "ADS Cadet Radio Card at 0x%x\n",io); 691 return 0; 692 fail: 693 pnp_unregister_driver(&cadet_pnp_driver); 694 return -1; 695 } 696 697 698 699 MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); 700 MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card."); 701 MODULE_LICENSE("GPL"); 702 703 module_param(io, int, 0); 704 MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)"); 705 module_param(radio_nr, int, 0); 706 707 static void __exit cadet_cleanup_module(void) 708 { 709 video_unregister_device(&cadet_radio); 710 release_region(io,2); 711 pnp_unregister_driver(&cadet_pnp_driver); 712 } 713 714 module_init(cadet_init); 715 module_exit(cadet_cleanup_module); 716 717