2 * Miro PCM20 radio driver for Linux radio support
3 * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
4 * Thanks to Norberto Pellici for the ACI device interface specification
5 * The API part is based on the radiotrack driver by M. Kirkwood
6 * This driver relies on the aci mixer provided by the snd-miro
8 * Look there for further info...
10 * From the original miro RDS sources:
12 * (c) 2001 Robert Siemer <Robert.Siemer@gmx.de>
14 * Many thanks to Fred Seidel <seidel@metabox.de>, the
15 * designer of the RDS decoder hardware. With his help
16 * I was able to code this driver.
17 * Thanks also to Norberto Pellicci, Dominic Mounteney
18 * <DMounteney@pinnaclesys.com> and www.teleauskunft.de
19 * for good hints on finding Fred. It was somewhat hard
20 * to locate him here in Germany... [:
22 * This code has been reintroduced and converted to use
23 * the new V4L2 RDS API by:
25 * Hans Verkuil <hans.verkuil@cisco.com>
28 #include <linux/module.h>
29 #include <linux/init.h>
30 #include <linux/delay.h>
31 #include <linux/videodev2.h>
32 #include <linux/kthread.h>
33 #include <media/v4l2-device.h>
34 #include <media/v4l2-ioctl.h>
35 #include <media/v4l2-ctrls.h>
36 #include <media/v4l2-fh.h>
37 #include <media/v4l2-event.h>
38 #include <sound/aci.h>
40 #define RDS_DATASHIFT 2 /* Bit 2 */
41 #define RDS_DATAMASK (1 << RDS_DATASHIFT)
42 #define RDS_BUSYMASK 0x10 /* Bit 4 */
43 #define RDS_CLOCKMASK 0x08 /* Bit 3 */
44 #define RDS_DATA(x) (((x) >> RDS_DATASHIFT) & 1)
46 #define RDS_STATUS 0x01
47 #define RDS_STATIONNAME 0x02
49 #define RDS_ALTFREQ 0x04
50 #define RDS_TIMEDATE 0x05
51 #define RDS_PI_CODE 0x06
52 #define RDS_PTYTATP 0x07
53 #define RDS_RESET 0x08
54 #define RDS_RXVALUE 0x09
56 static int radio_nr
= -1;
57 module_param(radio_nr
, int, 0);
58 MODULE_PARM_DESC(radio_nr
, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
61 struct v4l2_device v4l2_dev
;
62 struct video_device vdev
;
63 struct v4l2_ctrl_handler ctrl_handler
;
64 struct v4l2_ctrl
*rds_pty
;
65 struct v4l2_ctrl
*rds_ps_name
;
66 struct v4l2_ctrl
*rds_radio_test
;
67 struct v4l2_ctrl
*rds_ta
;
68 struct v4l2_ctrl
*rds_tp
;
69 struct v4l2_ctrl
*rds_ms
;
70 /* thread for periodic RDS status checking */
71 struct task_struct
*kthread
;
74 struct snd_miro_aci
*aci
;
78 static struct pcm20 pcm20_card
= {
80 .audmode
= V4L2_TUNER_MODE_STEREO
,
84 static int rds_waitread(struct snd_miro_aci
*aci
)
90 byte
= inb(aci
->aci_port
+ ACI_REG_RDS
);
92 } while ((byte
& RDS_BUSYMASK
) && i
);
95 * It's magic, but without this the data that you read later on
96 * is unreliable and full of bit errors. With this 1 usec delay
100 return i
? byte
: -1;
103 static int rds_rawwrite(struct snd_miro_aci
*aci
, u8 byte
)
105 if (rds_waitread(aci
) >= 0) {
106 outb(byte
, aci
->aci_port
+ ACI_REG_RDS
);
112 static int rds_write(struct snd_miro_aci
*aci
, u8 byte
)
117 for (i
= 7; i
>= 0; i
--)
118 sendbuffer
[7 - i
] = (byte
& (1 << i
)) ? RDS_DATAMASK
: 0;
119 sendbuffer
[0] |= RDS_CLOCKMASK
;
121 for (i
= 0; i
< 8; i
++)
122 rds_rawwrite(aci
, sendbuffer
[i
]);
126 static int rds_readcycle_nowait(struct snd_miro_aci
*aci
)
128 outb(0, aci
->aci_port
+ ACI_REG_RDS
);
129 return rds_waitread(aci
);
132 static int rds_readcycle(struct snd_miro_aci
*aci
)
134 if (rds_rawwrite(aci
, 0) < 0)
136 return rds_waitread(aci
);
139 static int rds_ack(struct snd_miro_aci
*aci
)
141 int i
= rds_readcycle(aci
);
145 if (i
& RDS_DATAMASK
)
150 static int rds_cmd(struct snd_miro_aci
*aci
, u8 cmd
, u8 databuffer
[], u8 datasize
)
156 /* RDS_RESET doesn't need further processing */
157 if (cmd
== RDS_RESET
)
164 /* to be able to use rds_readcycle_nowait()
165 I have to waitread() here */
166 if (rds_waitread(aci
) < 0)
169 memset(databuffer
, 0, datasize
);
171 for (i
= 0; i
< 8 * datasize
; i
++) {
172 j
= rds_readcycle_nowait(aci
);
175 databuffer
[i
/ 8] |= RDS_DATA(j
) << (7 - (i
% 8));
180 static int pcm20_setfreq(struct pcm20
*dev
, unsigned long freq
)
184 struct snd_miro_aci
*aci
= dev
->aci
;
187 if (!(aci
->aci_version
== 0x07 || aci
->aci_version
>= 0xb0))
188 freq
/= 10; /* I don't know exactly which version
193 rds_cmd(aci
, RDS_RESET
, 0, 0);
194 return snd_aci_cmd(aci
, ACI_WRITE_TUNE
, freql
, freqh
);
197 static int vidioc_querycap(struct file
*file
, void *priv
,
198 struct v4l2_capability
*v
)
200 struct pcm20
*dev
= video_drvdata(file
);
202 strlcpy(v
->driver
, "Miro PCM20", sizeof(v
->driver
));
203 strlcpy(v
->card
, "Miro PCM20", sizeof(v
->card
));
204 snprintf(v
->bus_info
, sizeof(v
->bus_info
), "ISA:%s", dev
->v4l2_dev
.name
);
205 v
->device_caps
= V4L2_CAP_TUNER
| V4L2_CAP_RADIO
| V4L2_CAP_RDS_CAPTURE
;
206 v
->capabilities
= v
->device_caps
| V4L2_CAP_DEVICE_CAPS
;
210 static bool sanitize(char *p
, int size
)
215 for (i
= 0; i
< size
; i
++) {
216 if (p
[i
] < 32 || p
[i
] >= 128) {
224 static int vidioc_g_tuner(struct file
*file
, void *priv
,
225 struct v4l2_tuner
*v
)
227 struct pcm20
*dev
= video_drvdata(file
);
233 strlcpy(v
->name
, "FM", sizeof(v
->name
));
234 v
->type
= V4L2_TUNER_RADIO
;
235 v
->rangelow
= 87*16000;
236 v
->rangehigh
= 108*16000;
237 res
= snd_aci_cmd(dev
->aci
, ACI_READ_TUNERSTATION
, -1, -1);
238 v
->signal
= (res
& 0x80) ? 0 : 0xffff;
239 /* Note: stereo detection does not work if the audio is muted,
240 it will default to mono in that case. */
241 res
= snd_aci_cmd(dev
->aci
, ACI_READ_TUNERSTEREO
, -1, -1);
242 v
->rxsubchans
= (res
& 0x40) ? V4L2_TUNER_SUB_MONO
:
243 V4L2_TUNER_SUB_STEREO
;
244 v
->capability
= V4L2_TUNER_CAP_LOW
| V4L2_TUNER_CAP_STEREO
|
245 V4L2_TUNER_CAP_RDS
| V4L2_TUNER_CAP_RDS_CONTROLS
;
246 v
->audmode
= dev
->audmode
;
247 res
= rds_cmd(dev
->aci
, RDS_RXVALUE
, &buf
, 1);
249 v
->rxsubchans
|= V4L2_TUNER_SUB_RDS
;
253 static int vidioc_s_tuner(struct file
*file
, void *priv
,
254 const struct v4l2_tuner
*v
)
256 struct pcm20
*dev
= video_drvdata(file
);
260 if (v
->audmode
> V4L2_TUNER_MODE_STEREO
)
261 dev
->audmode
= V4L2_TUNER_MODE_STEREO
;
263 dev
->audmode
= v
->audmode
;
264 snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMONO
,
265 dev
->audmode
== V4L2_TUNER_MODE_MONO
, -1);
269 static int vidioc_g_frequency(struct file
*file
, void *priv
,
270 struct v4l2_frequency
*f
)
272 struct pcm20
*dev
= video_drvdata(file
);
277 f
->type
= V4L2_TUNER_RADIO
;
278 f
->frequency
= dev
->freq
;
283 static int vidioc_s_frequency(struct file
*file
, void *priv
,
284 const struct v4l2_frequency
*f
)
286 struct pcm20
*dev
= video_drvdata(file
);
288 if (f
->tuner
!= 0 || f
->type
!= V4L2_TUNER_RADIO
)
291 dev
->freq
= clamp_t(u32
, f
->frequency
, 87 * 16000U, 108 * 16000U);
292 pcm20_setfreq(dev
, dev
->freq
);
296 static int pcm20_s_ctrl(struct v4l2_ctrl
*ctrl
)
298 struct pcm20
*dev
= container_of(ctrl
->handler
, struct pcm20
, ctrl_handler
);
301 case V4L2_CID_AUDIO_MUTE
:
302 snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMUTE
, ctrl
->val
, -1);
308 static int pcm20_thread(void *data
)
310 struct pcm20
*dev
= data
;
311 const unsigned no_rds_start_counter
= 5;
312 const unsigned sleep_msecs
= 2000;
313 unsigned no_rds_counter
= no_rds_start_counter
;
316 char text_buffer
[66];
320 msleep_interruptible(sleep_msecs
);
322 if (kthread_should_stop())
325 res
= rds_cmd(dev
->aci
, RDS_RXVALUE
, &buf
, 1);
329 if (no_rds_counter
== 0)
336 * No RDS seen for no_rds_start_counter * sleep_msecs
337 * milliseconds, clear all RDS controls to their
340 v4l2_ctrl_s_ctrl_string(dev
->rds_ps_name
, "");
341 v4l2_ctrl_s_ctrl(dev
->rds_ms
, 1);
342 v4l2_ctrl_s_ctrl(dev
->rds_ta
, 0);
343 v4l2_ctrl_s_ctrl(dev
->rds_tp
, 0);
344 v4l2_ctrl_s_ctrl(dev
->rds_pty
, 0);
345 v4l2_ctrl_s_ctrl_string(dev
->rds_radio_test
, "");
348 no_rds_counter
= no_rds_start_counter
;
350 res
= rds_cmd(dev
->aci
, RDS_STATUS
, &buf
, 1);
353 if ((buf
>> 3) & 1) {
354 res
= rds_cmd(dev
->aci
, RDS_STATIONNAME
, text_buffer
, 8);
356 if (!res
&& sanitize(text_buffer
, 8))
357 v4l2_ctrl_s_ctrl_string(dev
->rds_ps_name
, text_buffer
);
359 if ((buf
>> 6) & 1) {
362 res
= rds_cmd(dev
->aci
, RDS_PTYTATP
, &pty
, 1);
364 v4l2_ctrl_s_ctrl(dev
->rds_ms
, !!(pty
& 0x01));
365 v4l2_ctrl_s_ctrl(dev
->rds_ta
, !!(pty
& 0x02));
366 v4l2_ctrl_s_ctrl(dev
->rds_tp
, !!(pty
& 0x80));
367 v4l2_ctrl_s_ctrl(dev
->rds_pty
, (pty
>> 2) & 0x1f);
370 if ((buf
>> 4) & 1) {
371 res
= rds_cmd(dev
->aci
, RDS_TEXT
, text_buffer
, 65);
373 if (!res
&& sanitize(text_buffer
+ 1, 64))
374 v4l2_ctrl_s_ctrl_string(dev
->rds_radio_test
, text_buffer
+ 1);
380 static int pcm20_open(struct file
*file
)
382 struct pcm20
*dev
= video_drvdata(file
);
383 int res
= v4l2_fh_open(file
);
385 if (!res
&& v4l2_fh_is_singular_file(file
) &&
386 IS_ERR_OR_NULL(dev
->kthread
)) {
387 dev
->kthread
= kthread_run(pcm20_thread
, dev
, "%s",
389 if (IS_ERR(dev
->kthread
)) {
390 v4l2_err(&dev
->v4l2_dev
, "kernel_thread() failed\n");
391 v4l2_fh_release(file
);
392 return PTR_ERR(dev
->kthread
);
398 static int pcm20_release(struct file
*file
)
400 struct pcm20
*dev
= video_drvdata(file
);
402 if (v4l2_fh_is_singular_file(file
) && !IS_ERR_OR_NULL(dev
->kthread
)) {
403 kthread_stop(dev
->kthread
);
406 return v4l2_fh_release(file
);
409 static const struct v4l2_file_operations pcm20_fops
= {
410 .owner
= THIS_MODULE
,
412 .poll
= v4l2_ctrl_poll
,
413 .release
= pcm20_release
,
414 .unlocked_ioctl
= video_ioctl2
,
417 static const struct v4l2_ioctl_ops pcm20_ioctl_ops
= {
418 .vidioc_querycap
= vidioc_querycap
,
419 .vidioc_g_tuner
= vidioc_g_tuner
,
420 .vidioc_s_tuner
= vidioc_s_tuner
,
421 .vidioc_g_frequency
= vidioc_g_frequency
,
422 .vidioc_s_frequency
= vidioc_s_frequency
,
423 .vidioc_log_status
= v4l2_ctrl_log_status
,
424 .vidioc_subscribe_event
= v4l2_ctrl_subscribe_event
,
425 .vidioc_unsubscribe_event
= v4l2_event_unsubscribe
,
428 static const struct v4l2_ctrl_ops pcm20_ctrl_ops
= {
429 .s_ctrl
= pcm20_s_ctrl
,
432 static int __init
pcm20_init(void)
434 struct pcm20
*dev
= &pcm20_card
;
435 struct v4l2_device
*v4l2_dev
= &dev
->v4l2_dev
;
436 struct v4l2_ctrl_handler
*hdl
;
439 dev
->aci
= snd_aci_get_aci();
440 if (dev
->aci
== NULL
) {
442 "you must load the snd-miro driver first!\n");
445 strlcpy(v4l2_dev
->name
, "radio-miropcm20", sizeof(v4l2_dev
->name
));
446 mutex_init(&dev
->lock
);
448 res
= v4l2_device_register(NULL
, v4l2_dev
);
450 v4l2_err(v4l2_dev
, "could not register v4l2_device\n");
454 hdl
= &dev
->ctrl_handler
;
455 v4l2_ctrl_handler_init(hdl
, 7);
456 v4l2_ctrl_new_std(hdl
, &pcm20_ctrl_ops
,
457 V4L2_CID_AUDIO_MUTE
, 0, 1, 1, 1);
458 dev
->rds_pty
= v4l2_ctrl_new_std(hdl
, NULL
,
459 V4L2_CID_RDS_RX_PTY
, 0, 0x1f, 1, 0);
460 dev
->rds_ps_name
= v4l2_ctrl_new_std(hdl
, NULL
,
461 V4L2_CID_RDS_RX_PS_NAME
, 0, 8, 8, 0);
462 dev
->rds_radio_test
= v4l2_ctrl_new_std(hdl
, NULL
,
463 V4L2_CID_RDS_RX_RADIO_TEXT
, 0, 64, 64, 0);
464 dev
->rds_ta
= v4l2_ctrl_new_std(hdl
, NULL
,
465 V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT
, 0, 1, 1, 0);
466 dev
->rds_tp
= v4l2_ctrl_new_std(hdl
, NULL
,
467 V4L2_CID_RDS_RX_TRAFFIC_PROGRAM
, 0, 1, 1, 0);
468 dev
->rds_ms
= v4l2_ctrl_new_std(hdl
, NULL
,
469 V4L2_CID_RDS_RX_MUSIC_SPEECH
, 0, 1, 1, 1);
470 v4l2_dev
->ctrl_handler
= hdl
;
473 v4l2_err(v4l2_dev
, "Could not register control\n");
476 strlcpy(dev
->vdev
.name
, v4l2_dev
->name
, sizeof(dev
->vdev
.name
));
477 dev
->vdev
.v4l2_dev
= v4l2_dev
;
478 dev
->vdev
.fops
= &pcm20_fops
;
479 dev
->vdev
.ioctl_ops
= &pcm20_ioctl_ops
;
480 dev
->vdev
.release
= video_device_release_empty
;
481 dev
->vdev
.lock
= &dev
->lock
;
482 video_set_drvdata(&dev
->vdev
, dev
);
483 snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMONO
,
484 dev
->audmode
== V4L2_TUNER_MODE_MONO
, -1);
485 pcm20_setfreq(dev
, dev
->freq
);
487 if (video_register_device(&dev
->vdev
, VFL_TYPE_RADIO
, radio_nr
) < 0)
490 v4l2_info(v4l2_dev
, "Mirosound PCM20 Radio tuner\n");
493 v4l2_ctrl_handler_free(hdl
);
494 v4l2_device_unregister(v4l2_dev
);
498 MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
499 MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
500 MODULE_LICENSE("GPL");
502 static void __exit
pcm20_cleanup(void)
504 struct pcm20
*dev
= &pcm20_card
;
506 video_unregister_device(&dev
->vdev
);
507 snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMUTE
, 1, -1);
508 v4l2_ctrl_handler_free(&dev
->ctrl_handler
);
509 v4l2_device_unregister(&dev
->v4l2_dev
);
512 module_init(pcm20_init
);
513 module_exit(pcm20_cleanup
);