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