diff options
author | Hans J. Koch <koch@hjk-az.de> | 2006-08-08 08:10:12 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2006-09-26 10:53:32 -0400 |
commit | c0c7fa096623c0cfdd9d253de9d874558066cdbb (patch) | |
tree | 7a6fff35c6b6ba9367b1aabcbfbf5716b932eab9 /drivers/media/radio/radio-cadet.c | |
parent | 176ac9da4f09820a43fd48f0e74b1486fc3603ba (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.c | 249 |
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 | ||
43 | static int io=-1; /* default to isapnp activation */ | 50 | static int io=-1; /* default to isapnp activation */ |
44 | static int radio_nr = -1; | 51 | static int radio_nr = -1; |
@@ -61,44 +68,24 @@ static int cadet_probe(void); | |||
61 | */ | 68 | */ |
62 | static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}}; | 69 | static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}}; |
63 | 70 | ||
64 | static 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 | ||
87 | static int cadet_getstereo(void) | 72 | static int |
73 | cadet_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 | ||
101 | static unsigned cadet_gettune(void) | 87 | static unsigned |
88 | cadet_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 | ||
138 | static unsigned cadet_getfreq(void) | 125 | static unsigned |
126 | cadet_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 | ||
170 | static void cadet_settune(unsigned fifo) | 158 | static void |
159 | cadet_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 | ||
198 | static void cadet_setfreq(unsigned freq) | 187 | static void |
188 | cadet_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 | ||
258 | static int cadet_getvol(void) | 248 | static int |
249 | cadet_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 | ||
273 | static void cadet_setvol(int vol) | 264 | static void |
265 | cadet_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 | ||
284 | static void cadet_handler(unsigned long data) | 276 | static void |
277 | cadet_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 | ||
325 | static ssize_t cadet_read(struct file *file, char __user *data, | 318 | static ssize_t |
326 | size_t count, loff_t *ppos) | 319 | cadet_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 | ||
464 | static int cadet_ioctl(struct inode *inode, struct file *file, | 473 | static int |
474 | cadet_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 | ||
470 | static int cadet_open(struct inode *inode, struct file *file) | 480 | static int |
481 | cadet_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 | ||
479 | static int cadet_release(struct inode *inode, struct file *file) | 488 | static int |
489 | cadet_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 | |||
499 | static unsigned int | ||
500 | cadet_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 | ||