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-raremono.c (10689B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
      4 */
      5
      6#include <linux/kernel.h>
      7#include <linux/module.h>
      8#include <linux/init.h>
      9#include <linux/slab.h>
     10#include <linux/input.h>
     11#include <linux/usb.h>
     12#include <linux/hid.h>
     13#include <linux/mutex.h>
     14#include <linux/videodev2.h>
     15#include <asm/unaligned.h>
     16#include <media/v4l2-device.h>
     17#include <media/v4l2-ioctl.h>
     18#include <media/v4l2-ctrls.h>
     19#include <media/v4l2-event.h>
     20
     21/*
     22 * 'Thanko's Raremono' is a Japanese si4734-based AM/FM/SW USB receiver:
     23 *
     24 * http://www.raremono.jp/product/484.html/
     25 *
     26 * The USB protocol has been reversed engineered using wireshark, initially
     27 * by Dinesh Ram <dinesh.ram@cern.ch> and finished by Hans Verkuil
     28 * <hverkuil@xs4all.nl>.
     29 *
     30 * Sadly the firmware used in this product hides lots of goodies since the
     31 * si4734 has more features than are supported by the firmware. Oh well...
     32 */
     33
     34/* driver and module definitions */
     35MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
     36MODULE_DESCRIPTION("Thanko's Raremono AM/FM/SW Receiver USB driver");
     37MODULE_LICENSE("GPL v2");
     38
     39/*
     40 * The Device announces itself as Cygnal Integrated Products, Inc.
     41 *
     42 * The vendor and product IDs (and in fact all other lsusb information as
     43 * well) are identical to the si470x Silicon Labs USB FM Radio Reference
     44 * Design board, even though this card has a si4734 device. Clearly the
     45 * designer of this product never bothered to change the USB IDs.
     46 */
     47
     48/* USB Device ID List */
     49static const struct usb_device_id usb_raremono_device_table[] = {
     50	{USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
     51	{ }						/* Terminating entry */
     52};
     53
     54MODULE_DEVICE_TABLE(usb, usb_raremono_device_table);
     55
     56#define BUFFER_LENGTH 64
     57
     58/* Timeout is set to a high value, could probably be reduced. Need more tests */
     59#define USB_TIMEOUT 10000
     60
     61/* Frequency limits in KHz */
     62#define FM_FREQ_RANGE_LOW	64000
     63#define FM_FREQ_RANGE_HIGH	108000
     64
     65#define AM_FREQ_RANGE_LOW	520
     66#define AM_FREQ_RANGE_HIGH	1710
     67
     68#define SW_FREQ_RANGE_LOW	2300
     69#define SW_FREQ_RANGE_HIGH	26100
     70
     71enum { BAND_FM, BAND_AM, BAND_SW };
     72
     73static const struct v4l2_frequency_band bands[] = {
     74	/* Band FM */
     75	{
     76		.type = V4L2_TUNER_RADIO,
     77		.index = 0,
     78		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
     79			      V4L2_TUNER_CAP_FREQ_BANDS,
     80		.rangelow   = FM_FREQ_RANGE_LOW * 16,
     81		.rangehigh  = FM_FREQ_RANGE_HIGH * 16,
     82		.modulation = V4L2_BAND_MODULATION_FM,
     83	},
     84	/* Band AM */
     85	{
     86		.type = V4L2_TUNER_RADIO,
     87		.index = 1,
     88		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
     89		.rangelow   = AM_FREQ_RANGE_LOW * 16,
     90		.rangehigh  = AM_FREQ_RANGE_HIGH * 16,
     91		.modulation = V4L2_BAND_MODULATION_AM,
     92	},
     93	/* Band SW */
     94	{
     95		.type = V4L2_TUNER_RADIO,
     96		.index = 2,
     97		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
     98		.rangelow   = SW_FREQ_RANGE_LOW * 16,
     99		.rangehigh  = SW_FREQ_RANGE_HIGH * 16,
    100		.modulation = V4L2_BAND_MODULATION_AM,
    101	},
    102};
    103
    104struct raremono_device {
    105	struct usb_device *usbdev;
    106	struct usb_interface *intf;
    107	struct video_device vdev;
    108	struct v4l2_device v4l2_dev;
    109	struct mutex lock;
    110
    111	u8 *buffer;
    112	u32 band;
    113	unsigned curfreq;
    114};
    115
    116static inline struct raremono_device *to_raremono_dev(struct v4l2_device *v4l2_dev)
    117{
    118	return container_of(v4l2_dev, struct raremono_device, v4l2_dev);
    119}
    120
    121/* Set frequency. */
    122static int raremono_cmd_main(struct raremono_device *radio, unsigned band, unsigned freq)
    123{
    124	unsigned band_offset;
    125	int ret;
    126
    127	switch (band) {
    128	case BAND_FM:
    129		band_offset = 1;
    130		freq /= 10;
    131		break;
    132	case BAND_AM:
    133		band_offset = 0;
    134		break;
    135	default:
    136		band_offset = 2;
    137		break;
    138	}
    139	radio->buffer[0] = 0x04 + band_offset;
    140	radio->buffer[1] = freq >> 8;
    141	radio->buffer[2] = freq & 0xff;
    142
    143	ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
    144			HID_REQ_SET_REPORT,
    145			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
    146			0x0300 + radio->buffer[0], 2,
    147			radio->buffer, 3, USB_TIMEOUT);
    148
    149	if (ret < 0) {
    150		dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret);
    151		return ret;
    152	}
    153	radio->curfreq = (band == BAND_FM) ? freq * 10 : freq;
    154	return 0;
    155}
    156
    157/* Handle unplugging the device.
    158 * We call video_unregister_device in any case.
    159 * The last function called in this procedure is
    160 * usb_raremono_device_release.
    161 */
    162static void usb_raremono_disconnect(struct usb_interface *intf)
    163{
    164	struct raremono_device *radio = to_raremono_dev(usb_get_intfdata(intf));
    165
    166	dev_info(&intf->dev, "Thanko's Raremono disconnected\n");
    167
    168	mutex_lock(&radio->lock);
    169	usb_set_intfdata(intf, NULL);
    170	video_unregister_device(&radio->vdev);
    171	v4l2_device_disconnect(&radio->v4l2_dev);
    172	mutex_unlock(&radio->lock);
    173	v4l2_device_put(&radio->v4l2_dev);
    174}
    175
    176/*
    177 * Linux Video interface
    178 */
    179static int vidioc_querycap(struct file *file, void *priv,
    180					struct v4l2_capability *v)
    181{
    182	struct raremono_device *radio = video_drvdata(file);
    183
    184	strscpy(v->driver, "radio-raremono", sizeof(v->driver));
    185	strscpy(v->card, "Thanko's Raremono", sizeof(v->card));
    186	usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
    187	return 0;
    188}
    189
    190static int vidioc_enum_freq_bands(struct file *file, void *priv,
    191		struct v4l2_frequency_band *band)
    192{
    193	if (band->tuner != 0)
    194		return -EINVAL;
    195
    196	if (band->index >= ARRAY_SIZE(bands))
    197		return -EINVAL;
    198
    199	*band = bands[band->index];
    200
    201	return 0;
    202}
    203
    204static int vidioc_g_tuner(struct file *file, void *priv,
    205		struct v4l2_tuner *v)
    206{
    207	struct raremono_device *radio = video_drvdata(file);
    208	int ret;
    209
    210	if (v->index > 0)
    211		return -EINVAL;
    212
    213	strscpy(v->name, "AM/FM/SW", sizeof(v->name));
    214	v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
    215		V4L2_TUNER_CAP_FREQ_BANDS;
    216	v->rangelow = AM_FREQ_RANGE_LOW * 16;
    217	v->rangehigh = FM_FREQ_RANGE_HIGH * 16;
    218	v->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
    219	v->audmode = (radio->curfreq < FM_FREQ_RANGE_LOW) ?
    220		V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
    221	memset(radio->buffer, 1, BUFFER_LENGTH);
    222	ret = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
    223			1, 0xa1, 0x030d, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
    224
    225	if (ret < 0) {
    226		dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret);
    227		return ret;
    228	}
    229	v->signal = ((radio->buffer[1] & 0xf) << 8 | radio->buffer[2]) << 4;
    230	return 0;
    231}
    232
    233static int vidioc_s_tuner(struct file *file, void *priv,
    234					const struct v4l2_tuner *v)
    235{
    236	return v->index ? -EINVAL : 0;
    237}
    238
    239static int vidioc_s_frequency(struct file *file, void *priv,
    240				const struct v4l2_frequency *f)
    241{
    242	struct raremono_device *radio = video_drvdata(file);
    243	u32 freq;
    244	unsigned band;
    245
    246	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
    247		return -EINVAL;
    248
    249	if (f->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) * 8)
    250		band = BAND_FM;
    251	else if (f->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) * 8)
    252		band = BAND_AM;
    253	else
    254		band = BAND_SW;
    255
    256	freq = clamp_t(u32, f->frequency, bands[band].rangelow, bands[band].rangehigh);
    257	return raremono_cmd_main(radio, band, freq / 16);
    258}
    259
    260static int vidioc_g_frequency(struct file *file, void *priv,
    261				struct v4l2_frequency *f)
    262{
    263	struct raremono_device *radio = video_drvdata(file);
    264
    265	if (f->tuner != 0)
    266		return -EINVAL;
    267	f->type = V4L2_TUNER_RADIO;
    268	f->frequency = radio->curfreq * 16;
    269	return 0;
    270}
    271
    272static void raremono_device_release(struct v4l2_device *v4l2_dev)
    273{
    274	struct raremono_device *radio = to_raremono_dev(v4l2_dev);
    275
    276	kfree(radio->buffer);
    277	kfree(radio);
    278}
    279
    280/* File system interface */
    281static const struct v4l2_file_operations usb_raremono_fops = {
    282	.owner		= THIS_MODULE,
    283	.open           = v4l2_fh_open,
    284	.release        = v4l2_fh_release,
    285	.unlocked_ioctl	= video_ioctl2,
    286};
    287
    288static const struct v4l2_ioctl_ops usb_raremono_ioctl_ops = {
    289	.vidioc_querycap = vidioc_querycap,
    290	.vidioc_g_tuner = vidioc_g_tuner,
    291	.vidioc_s_tuner = vidioc_s_tuner,
    292	.vidioc_g_frequency = vidioc_g_frequency,
    293	.vidioc_s_frequency = vidioc_s_frequency,
    294	.vidioc_enum_freq_bands = vidioc_enum_freq_bands,
    295};
    296
    297/* check if the device is present and register with v4l and usb if it is */
    298static int usb_raremono_probe(struct usb_interface *intf,
    299				const struct usb_device_id *id)
    300{
    301	struct raremono_device *radio;
    302	int retval = 0;
    303
    304	radio = kzalloc(sizeof(*radio), GFP_KERNEL);
    305	if (!radio)
    306		return -ENOMEM;
    307	radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
    308	if (!radio->buffer) {
    309		kfree(radio);
    310		return -ENOMEM;
    311	}
    312
    313	radio->usbdev = interface_to_usbdev(intf);
    314	radio->intf = intf;
    315
    316	/*
    317	 * This device uses the same USB IDs as the si470x SiLabs reference
    318	 * design. So do an additional check: attempt to read the device ID
    319	 * from the si470x: the lower 12 bits are 0x0242 for the si470x. The
    320	 * Raremono always returns 0x0800 (the meaning of that is unknown, but
    321	 * at least it works).
    322	 *
    323	 * We use this check to determine which device we are dealing with.
    324	 */
    325	msleep(20);
    326	retval = usb_control_msg(radio->usbdev,
    327		usb_rcvctrlpipe(radio->usbdev, 0),
    328		HID_REQ_GET_REPORT,
    329		USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
    330		1, 2,
    331		radio->buffer, 3, 500);
    332	if (retval != 3 ||
    333	    (get_unaligned_be16(&radio->buffer[1]) & 0xfff) == 0x0242) {
    334		dev_info(&intf->dev, "this is not Thanko's Raremono.\n");
    335		retval = -ENODEV;
    336		goto free_mem;
    337	}
    338
    339	dev_info(&intf->dev, "Thanko's Raremono connected: (%04X:%04X)\n",
    340			id->idVendor, id->idProduct);
    341
    342	retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
    343	if (retval < 0) {
    344		dev_err(&intf->dev, "couldn't register v4l2_device\n");
    345		goto free_mem;
    346	}
    347
    348	mutex_init(&radio->lock);
    349
    350	strscpy(radio->vdev.name, radio->v4l2_dev.name,
    351		sizeof(radio->vdev.name));
    352	radio->vdev.v4l2_dev = &radio->v4l2_dev;
    353	radio->vdev.fops = &usb_raremono_fops;
    354	radio->vdev.ioctl_ops = &usb_raremono_ioctl_ops;
    355	radio->vdev.lock = &radio->lock;
    356	radio->vdev.release = video_device_release_empty;
    357	radio->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
    358	radio->v4l2_dev.release = raremono_device_release;
    359
    360	usb_set_intfdata(intf, &radio->v4l2_dev);
    361
    362	video_set_drvdata(&radio->vdev, radio);
    363
    364	raremono_cmd_main(radio, BAND_FM, 95160);
    365
    366	retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
    367	if (retval == 0) {
    368		dev_info(&intf->dev, "V4L2 device registered as %s\n",
    369				video_device_node_name(&radio->vdev));
    370		return 0;
    371	}
    372	dev_err(&intf->dev, "could not register video device\n");
    373	v4l2_device_unregister(&radio->v4l2_dev);
    374
    375free_mem:
    376	kfree(radio->buffer);
    377	kfree(radio);
    378	return retval;
    379}
    380
    381/* USB subsystem interface */
    382static struct usb_driver usb_raremono_driver = {
    383	.name			= "radio-raremono",
    384	.probe			= usb_raremono_probe,
    385	.disconnect		= usb_raremono_disconnect,
    386	.id_table		= usb_raremono_device_table,
    387};
    388
    389module_usb_driver(usb_raremono_driver);