cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

radio-miropcm20.c (13247B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Miro PCM20 radio driver for Linux radio support
      4 * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
      5 * Thanks to Norberto Pellici for the ACI device interface specification
      6 * The API part is based on the radiotrack driver by M. Kirkwood
      7 * This driver relies on the aci mixer provided by the snd-miro
      8 * ALSA driver.
      9 * Look there for further info...
     10 *
     11 * From the original miro RDS sources:
     12 *
     13 *  (c) 2001 Robert Siemer <Robert.Siemer@gmx.de>
     14 *
     15 *  Many thanks to Fred Seidel <seidel@metabox.de>, the
     16 *  designer of the RDS decoder hardware. With his help
     17 *  I was able to code this driver.
     18 *  Thanks also to Norberto Pellicci, Dominic Mounteney
     19 *  <DMounteney@pinnaclesys.com> and www.teleauskunft.de
     20 *  for good hints on finding Fred. It was somewhat hard
     21 *  to locate him here in Germany... [:
     22 *
     23 * This code has been reintroduced and converted to use
     24 * the new V4L2 RDS API by:
     25 *
     26 * Hans Verkuil <hans.verkuil@cisco.com>
     27 */
     28
     29#include <linux/module.h>
     30#include <linux/init.h>
     31#include <linux/io.h>
     32#include <linux/delay.h>
     33#include <linux/videodev2.h>
     34#include <linux/kthread.h>
     35#include <media/v4l2-device.h>
     36#include <media/v4l2-ioctl.h>
     37#include <media/v4l2-ctrls.h>
     38#include <media/v4l2-fh.h>
     39#include <media/v4l2-event.h>
     40#include <sound/aci.h>
     41
     42#define RDS_DATASHIFT          2   /* Bit 2 */
     43#define RDS_DATAMASK        (1 << RDS_DATASHIFT)
     44#define RDS_BUSYMASK        0x10   /* Bit 4 */
     45#define RDS_CLOCKMASK       0x08   /* Bit 3 */
     46#define RDS_DATA(x)         (((x) >> RDS_DATASHIFT) & 1)
     47
     48#define RDS_STATUS      0x01
     49#define RDS_STATIONNAME 0x02
     50#define RDS_TEXT        0x03
     51#define RDS_ALTFREQ     0x04
     52#define RDS_TIMEDATE    0x05
     53#define RDS_PI_CODE     0x06
     54#define RDS_PTYTATP     0x07
     55#define RDS_RESET       0x08
     56#define RDS_RXVALUE     0x09
     57
     58static int radio_nr = -1;
     59module_param(radio_nr, int, 0);
     60MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX).  Default: -1 (autodetect)");
     61
     62struct pcm20 {
     63	struct v4l2_device v4l2_dev;
     64	struct video_device vdev;
     65	struct v4l2_ctrl_handler ctrl_handler;
     66	struct v4l2_ctrl *rds_pty;
     67	struct v4l2_ctrl *rds_ps_name;
     68	struct v4l2_ctrl *rds_radio_test;
     69	struct v4l2_ctrl *rds_ta;
     70	struct v4l2_ctrl *rds_tp;
     71	struct v4l2_ctrl *rds_ms;
     72	/* thread for periodic RDS status checking */
     73	struct task_struct *kthread;
     74	unsigned long freq;
     75	u32 audmode;
     76	struct snd_miro_aci *aci;
     77	struct mutex lock;
     78};
     79
     80static struct pcm20 pcm20_card = {
     81	.freq = 87 * 16000,
     82	.audmode = V4L2_TUNER_MODE_STEREO,
     83};
     84
     85
     86static int rds_waitread(struct snd_miro_aci *aci)
     87{
     88	u8 byte;
     89	int i = 2000;
     90
     91	do {
     92		byte = inb(aci->aci_port + ACI_REG_RDS);
     93		i--;
     94	} while ((byte & RDS_BUSYMASK) && i);
     95
     96	/*
     97	 * It's magic, but without this the data that you read later on
     98	 * is unreliable and full of bit errors. With this 1 usec delay
     99	 * everything is fine.
    100	 */
    101	udelay(1);
    102	return i ? byte : -1;
    103}
    104
    105static int rds_rawwrite(struct snd_miro_aci *aci, u8 byte)
    106{
    107	if (rds_waitread(aci) >= 0) {
    108		outb(byte, aci->aci_port + ACI_REG_RDS);
    109		return 0;
    110	}
    111	return -1;
    112}
    113
    114static int rds_write(struct snd_miro_aci *aci, u8 byte)
    115{
    116	u8 sendbuffer[8];
    117	int i;
    118
    119	for (i = 7; i >= 0; i--)
    120		sendbuffer[7 - i] = (byte & (1 << i)) ? RDS_DATAMASK : 0;
    121	sendbuffer[0] |= RDS_CLOCKMASK;
    122
    123	for (i = 0; i < 8; i++)
    124		rds_rawwrite(aci, sendbuffer[i]);
    125	return 0;
    126}
    127
    128static int rds_readcycle_nowait(struct snd_miro_aci *aci)
    129{
    130	outb(0, aci->aci_port + ACI_REG_RDS);
    131	return rds_waitread(aci);
    132}
    133
    134static int rds_readcycle(struct snd_miro_aci *aci)
    135{
    136	if (rds_rawwrite(aci, 0) < 0)
    137		return -1;
    138	return rds_waitread(aci);
    139}
    140
    141static int rds_ack(struct snd_miro_aci *aci)
    142{
    143	int i = rds_readcycle(aci);
    144
    145	if (i < 0)
    146		return -1;
    147	if (i & RDS_DATAMASK)
    148		return 0;  /* ACK  */
    149	return 1;  /* NACK */
    150}
    151
    152static int rds_cmd(struct snd_miro_aci *aci, u8 cmd, u8 databuffer[], u8 datasize)
    153{
    154	int i, j;
    155
    156	rds_write(aci, cmd);
    157
    158	/* RDS_RESET doesn't need further processing */
    159	if (cmd == RDS_RESET)
    160		return 0;
    161	if (rds_ack(aci))
    162		return -EIO;
    163	if (datasize == 0)
    164		return 0;
    165
    166	/* to be able to use rds_readcycle_nowait()
    167	   I have to waitread() here */
    168	if (rds_waitread(aci) < 0)
    169		return -1;
    170
    171	memset(databuffer, 0, datasize);
    172
    173	for (i = 0; i < 8 * datasize; i++) {
    174		j = rds_readcycle_nowait(aci);
    175		if (j < 0)
    176			return -EIO;
    177		databuffer[i / 8] |= RDS_DATA(j) << (7 - (i % 8));
    178	}
    179	return 0;
    180}
    181
    182static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq)
    183{
    184	unsigned char freql;
    185	unsigned char freqh;
    186	struct snd_miro_aci *aci = dev->aci;
    187
    188	freq /= 160;
    189	if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0))
    190		freq /= 10;  /* I don't know exactly which version
    191			      * needs this hack */
    192	freql = freq & 0xff;
    193	freqh = freq >> 8;
    194
    195	rds_cmd(aci, RDS_RESET, NULL, 0);
    196	return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh);
    197}
    198
    199static int vidioc_querycap(struct file *file, void *priv,
    200				struct v4l2_capability *v)
    201{
    202	struct pcm20 *dev = video_drvdata(file);
    203
    204	strscpy(v->driver, "Miro PCM20", sizeof(v->driver));
    205	strscpy(v->card, "Miro PCM20", sizeof(v->card));
    206	snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", dev->v4l2_dev.name);
    207	return 0;
    208}
    209
    210static bool sanitize(char *p, int size)
    211{
    212	int i;
    213	bool ret = true;
    214
    215	for (i = 0; i < size; i++) {
    216		if (p[i] < 32) {
    217			p[i] = ' ';
    218			ret = false;
    219		}
    220	}
    221	return ret;
    222}
    223
    224static int vidioc_g_tuner(struct file *file, void *priv,
    225				struct v4l2_tuner *v)
    226{
    227	struct pcm20 *dev = video_drvdata(file);
    228	int res;
    229	u8 buf;
    230
    231	if (v->index)
    232		return -EINVAL;
    233	strscpy(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);
    248	if (res >= 0 && buf)
    249		v->rxsubchans |= V4L2_TUNER_SUB_RDS;
    250	return 0;
    251}
    252
    253static int vidioc_s_tuner(struct file *file, void *priv,
    254				const struct v4l2_tuner *v)
    255{
    256	struct pcm20 *dev = video_drvdata(file);
    257
    258	if (v->index)
    259		return -EINVAL;
    260	if (v->audmode > V4L2_TUNER_MODE_STEREO)
    261		dev->audmode = V4L2_TUNER_MODE_STEREO;
    262	else
    263		dev->audmode = v->audmode;
    264	snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO,
    265			dev->audmode == V4L2_TUNER_MODE_MONO, -1);
    266	return 0;
    267}
    268
    269static int vidioc_g_frequency(struct file *file, void *priv,
    270				struct v4l2_frequency *f)
    271{
    272	struct pcm20 *dev = video_drvdata(file);
    273
    274	if (f->tuner != 0)
    275		return -EINVAL;
    276
    277	f->type = V4L2_TUNER_RADIO;
    278	f->frequency = dev->freq;
    279	return 0;
    280}
    281
    282
    283static int vidioc_s_frequency(struct file *file, void *priv,
    284				const struct v4l2_frequency *f)
    285{
    286	struct pcm20 *dev = video_drvdata(file);
    287
    288	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
    289		return -EINVAL;
    290
    291	dev->freq = clamp_t(u32, f->frequency, 87 * 16000U, 108 * 16000U);
    292	pcm20_setfreq(dev, dev->freq);
    293	return 0;
    294}
    295
    296static int pcm20_s_ctrl(struct v4l2_ctrl *ctrl)
    297{
    298	struct pcm20 *dev = container_of(ctrl->handler, struct pcm20, ctrl_handler);
    299
    300	switch (ctrl->id) {
    301	case V4L2_CID_AUDIO_MUTE:
    302		snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, ctrl->val, -1);
    303		return 0;
    304	}
    305	return -EINVAL;
    306}
    307
    308static int pcm20_thread(void *data)
    309{
    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;
    314
    315	for (;;) {
    316		char text_buffer[66];
    317		u8 buf;
    318		int res;
    319
    320		msleep_interruptible(sleep_msecs);
    321
    322		if (kthread_should_stop())
    323			break;
    324
    325		res = rds_cmd(dev->aci, RDS_RXVALUE, &buf, 1);
    326		if (res)
    327			continue;
    328		if (buf == 0) {
    329			if (no_rds_counter == 0)
    330				continue;
    331			no_rds_counter--;
    332			if (no_rds_counter)
    333				continue;
    334
    335			/*
    336			 * No RDS seen for no_rds_start_counter * sleep_msecs
    337			 * milliseconds, clear all RDS controls to their
    338			 * default values.
    339			 */
    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, "");
    346			continue;
    347		}
    348		no_rds_counter = no_rds_start_counter;
    349
    350		res = rds_cmd(dev->aci, RDS_STATUS, &buf, 1);
    351		if (res)
    352			continue;
    353		if ((buf >> 3) & 1) {
    354			res = rds_cmd(dev->aci, RDS_STATIONNAME, text_buffer, 8);
    355			text_buffer[8] = 0;
    356			if (!res && sanitize(text_buffer, 8))
    357				v4l2_ctrl_s_ctrl_string(dev->rds_ps_name, text_buffer);
    358		}
    359		if ((buf >> 6) & 1) {
    360			u8 pty;
    361
    362			res = rds_cmd(dev->aci, RDS_PTYTATP, &pty, 1);
    363			if (!res) {
    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);
    368			}
    369		}
    370		if ((buf >> 4) & 1) {
    371			res = rds_cmd(dev->aci, RDS_TEXT, text_buffer, 65);
    372			text_buffer[65] = 0;
    373			if (!res && sanitize(text_buffer + 1, 64))
    374				v4l2_ctrl_s_ctrl_string(dev->rds_radio_test, text_buffer + 1);
    375		}
    376	}
    377	return 0;
    378}
    379
    380static int pcm20_open(struct file *file)
    381{
    382	struct pcm20 *dev = video_drvdata(file);
    383	int res = v4l2_fh_open(file);
    384
    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",
    388					   dev->v4l2_dev.name);
    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);
    393		}
    394	}
    395	return res;
    396}
    397
    398static int pcm20_release(struct file *file)
    399{
    400	struct pcm20 *dev = video_drvdata(file);
    401
    402	if (v4l2_fh_is_singular_file(file) && !IS_ERR_OR_NULL(dev->kthread)) {
    403		kthread_stop(dev->kthread);
    404		dev->kthread = NULL;
    405	}
    406	return v4l2_fh_release(file);
    407}
    408
    409static const struct v4l2_file_operations pcm20_fops = {
    410	.owner		= THIS_MODULE,
    411	.open		= pcm20_open,
    412	.poll		= v4l2_ctrl_poll,
    413	.release	= pcm20_release,
    414	.unlocked_ioctl	= video_ioctl2,
    415};
    416
    417static 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,
    426};
    427
    428static const struct v4l2_ctrl_ops pcm20_ctrl_ops = {
    429	.s_ctrl = pcm20_s_ctrl,
    430};
    431
    432static int __init pcm20_init(void)
    433{
    434	struct pcm20 *dev = &pcm20_card;
    435	struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
    436	struct v4l2_ctrl_handler *hdl;
    437	int res;
    438
    439	dev->aci = snd_aci_get_aci();
    440	if (dev->aci == NULL) {
    441		v4l2_err(v4l2_dev,
    442			 "you must load the snd-miro driver first!\n");
    443		return -ENODEV;
    444	}
    445	strscpy(v4l2_dev->name, "radio-miropcm20", sizeof(v4l2_dev->name));
    446	mutex_init(&dev->lock);
    447
    448	res = v4l2_device_register(NULL, v4l2_dev);
    449	if (res < 0) {
    450		v4l2_err(v4l2_dev, "could not register v4l2_device\n");
    451		return -EINVAL;
    452	}
    453
    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;
    471	if (hdl->error) {
    472		res = hdl->error;
    473		v4l2_err(v4l2_dev, "Could not register control\n");
    474		goto err_hdl;
    475	}
    476	strscpy(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	dev->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO |
    483				V4L2_CAP_RDS_CAPTURE;
    484	video_set_drvdata(&dev->vdev, dev);
    485	snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO,
    486			dev->audmode == V4L2_TUNER_MODE_MONO, -1);
    487	pcm20_setfreq(dev, dev->freq);
    488
    489	if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0)
    490		goto err_hdl;
    491
    492	v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n");
    493	return 0;
    494err_hdl:
    495	v4l2_ctrl_handler_free(hdl);
    496	v4l2_device_unregister(v4l2_dev);
    497	return -EINVAL;
    498}
    499
    500MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
    501MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
    502MODULE_LICENSE("GPL");
    503
    504static void __exit pcm20_cleanup(void)
    505{
    506	struct pcm20 *dev = &pcm20_card;
    507
    508	video_unregister_device(&dev->vdev);
    509	snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, 1, -1);
    510	v4l2_ctrl_handler_free(&dev->ctrl_handler);
    511	v4l2_device_unregister(&dev->v4l2_dev);
    512}
    513
    514module_init(pcm20_init);
    515module_exit(pcm20_cleanup);