162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/input.h>
1162306a36Sopenharmony_ci#include <linux/usb.h>
1262306a36Sopenharmony_ci#include <linux/hid.h>
1362306a36Sopenharmony_ci#include <linux/mutex.h>
1462306a36Sopenharmony_ci#include <linux/videodev2.h>
1562306a36Sopenharmony_ci#include <asm/unaligned.h>
1662306a36Sopenharmony_ci#include <media/v4l2-device.h>
1762306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
1862306a36Sopenharmony_ci#include <media/v4l2-ctrls.h>
1962306a36Sopenharmony_ci#include <media/v4l2-event.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * 'Thanko's Raremono' is a Japanese si4734-based AM/FM/SW USB receiver:
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * http://www.raremono.jp/product/484.html/
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * The USB protocol has been reversed engineered using wireshark, initially
2762306a36Sopenharmony_ci * by Dinesh Ram <dinesh.ram@cern.ch> and finished by Hans Verkuil
2862306a36Sopenharmony_ci * <hverkuil@xs4all.nl>.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * Sadly the firmware used in this product hides lots of goodies since the
3162306a36Sopenharmony_ci * si4734 has more features than are supported by the firmware. Oh well...
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* driver and module definitions */
3562306a36Sopenharmony_ciMODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
3662306a36Sopenharmony_ciMODULE_DESCRIPTION("Thanko's Raremono AM/FM/SW Receiver USB driver");
3762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/*
4062306a36Sopenharmony_ci * The Device announces itself as Cygnal Integrated Products, Inc.
4162306a36Sopenharmony_ci *
4262306a36Sopenharmony_ci * The vendor and product IDs (and in fact all other lsusb information as
4362306a36Sopenharmony_ci * well) are identical to the si470x Silicon Labs USB FM Radio Reference
4462306a36Sopenharmony_ci * Design board, even though this card has a si4734 device. Clearly the
4562306a36Sopenharmony_ci * designer of this product never bothered to change the USB IDs.
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* USB Device ID List */
4962306a36Sopenharmony_cistatic const struct usb_device_id usb_raremono_device_table[] = {
5062306a36Sopenharmony_ci	{USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
5162306a36Sopenharmony_ci	{ }						/* Terminating entry */
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usb_raremono_device_table);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#define BUFFER_LENGTH 64
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* Timeout is set to a high value, could probably be reduced. Need more tests */
5962306a36Sopenharmony_ci#define USB_TIMEOUT 10000
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Frequency limits in KHz */
6262306a36Sopenharmony_ci#define FM_FREQ_RANGE_LOW	64000
6362306a36Sopenharmony_ci#define FM_FREQ_RANGE_HIGH	108000
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define AM_FREQ_RANGE_LOW	520
6662306a36Sopenharmony_ci#define AM_FREQ_RANGE_HIGH	1710
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define SW_FREQ_RANGE_LOW	2300
6962306a36Sopenharmony_ci#define SW_FREQ_RANGE_HIGH	26100
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cienum { BAND_FM, BAND_AM, BAND_SW };
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic const struct v4l2_frequency_band bands[] = {
7462306a36Sopenharmony_ci	/* Band FM */
7562306a36Sopenharmony_ci	{
7662306a36Sopenharmony_ci		.type = V4L2_TUNER_RADIO,
7762306a36Sopenharmony_ci		.index = 0,
7862306a36Sopenharmony_ci		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
7962306a36Sopenharmony_ci			      V4L2_TUNER_CAP_FREQ_BANDS,
8062306a36Sopenharmony_ci		.rangelow   = FM_FREQ_RANGE_LOW * 16,
8162306a36Sopenharmony_ci		.rangehigh  = FM_FREQ_RANGE_HIGH * 16,
8262306a36Sopenharmony_ci		.modulation = V4L2_BAND_MODULATION_FM,
8362306a36Sopenharmony_ci	},
8462306a36Sopenharmony_ci	/* Band AM */
8562306a36Sopenharmony_ci	{
8662306a36Sopenharmony_ci		.type = V4L2_TUNER_RADIO,
8762306a36Sopenharmony_ci		.index = 1,
8862306a36Sopenharmony_ci		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
8962306a36Sopenharmony_ci		.rangelow   = AM_FREQ_RANGE_LOW * 16,
9062306a36Sopenharmony_ci		.rangehigh  = AM_FREQ_RANGE_HIGH * 16,
9162306a36Sopenharmony_ci		.modulation = V4L2_BAND_MODULATION_AM,
9262306a36Sopenharmony_ci	},
9362306a36Sopenharmony_ci	/* Band SW */
9462306a36Sopenharmony_ci	{
9562306a36Sopenharmony_ci		.type = V4L2_TUNER_RADIO,
9662306a36Sopenharmony_ci		.index = 2,
9762306a36Sopenharmony_ci		.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
9862306a36Sopenharmony_ci		.rangelow   = SW_FREQ_RANGE_LOW * 16,
9962306a36Sopenharmony_ci		.rangehigh  = SW_FREQ_RANGE_HIGH * 16,
10062306a36Sopenharmony_ci		.modulation = V4L2_BAND_MODULATION_AM,
10162306a36Sopenharmony_ci	},
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistruct raremono_device {
10562306a36Sopenharmony_ci	struct usb_device *usbdev;
10662306a36Sopenharmony_ci	struct usb_interface *intf;
10762306a36Sopenharmony_ci	struct video_device vdev;
10862306a36Sopenharmony_ci	struct v4l2_device v4l2_dev;
10962306a36Sopenharmony_ci	struct mutex lock;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	u8 *buffer;
11262306a36Sopenharmony_ci	u32 band;
11362306a36Sopenharmony_ci	unsigned curfreq;
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic inline struct raremono_device *to_raremono_dev(struct v4l2_device *v4l2_dev)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	return container_of(v4l2_dev, struct raremono_device, v4l2_dev);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/* Set frequency. */
12262306a36Sopenharmony_cistatic int raremono_cmd_main(struct raremono_device *radio, unsigned band, unsigned freq)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	unsigned band_offset;
12562306a36Sopenharmony_ci	int ret;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	switch (band) {
12862306a36Sopenharmony_ci	case BAND_FM:
12962306a36Sopenharmony_ci		band_offset = 1;
13062306a36Sopenharmony_ci		freq /= 10;
13162306a36Sopenharmony_ci		break;
13262306a36Sopenharmony_ci	case BAND_AM:
13362306a36Sopenharmony_ci		band_offset = 0;
13462306a36Sopenharmony_ci		break;
13562306a36Sopenharmony_ci	default:
13662306a36Sopenharmony_ci		band_offset = 2;
13762306a36Sopenharmony_ci		break;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci	radio->buffer[0] = 0x04 + band_offset;
14062306a36Sopenharmony_ci	radio->buffer[1] = freq >> 8;
14162306a36Sopenharmony_ci	radio->buffer[2] = freq & 0xff;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
14462306a36Sopenharmony_ci			HID_REQ_SET_REPORT,
14562306a36Sopenharmony_ci			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
14662306a36Sopenharmony_ci			0x0300 + radio->buffer[0], 2,
14762306a36Sopenharmony_ci			radio->buffer, 3, USB_TIMEOUT);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (ret < 0) {
15062306a36Sopenharmony_ci		dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret);
15162306a36Sopenharmony_ci		return ret;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci	radio->curfreq = (band == BAND_FM) ? freq * 10 : freq;
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/* Handle unplugging the device.
15862306a36Sopenharmony_ci * We call video_unregister_device in any case.
15962306a36Sopenharmony_ci * The last function called in this procedure is
16062306a36Sopenharmony_ci * usb_raremono_device_release.
16162306a36Sopenharmony_ci */
16262306a36Sopenharmony_cistatic void usb_raremono_disconnect(struct usb_interface *intf)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct raremono_device *radio = to_raremono_dev(usb_get_intfdata(intf));
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	dev_info(&intf->dev, "Thanko's Raremono disconnected\n");
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	mutex_lock(&radio->lock);
16962306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
17062306a36Sopenharmony_ci	video_unregister_device(&radio->vdev);
17162306a36Sopenharmony_ci	v4l2_device_disconnect(&radio->v4l2_dev);
17262306a36Sopenharmony_ci	mutex_unlock(&radio->lock);
17362306a36Sopenharmony_ci	v4l2_device_put(&radio->v4l2_dev);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/*
17762306a36Sopenharmony_ci * Linux Video interface
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv,
18062306a36Sopenharmony_ci					struct v4l2_capability *v)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct raremono_device *radio = video_drvdata(file);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	strscpy(v->driver, "radio-raremono", sizeof(v->driver));
18562306a36Sopenharmony_ci	strscpy(v->card, "Thanko's Raremono", sizeof(v->card));
18662306a36Sopenharmony_ci	usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
18762306a36Sopenharmony_ci	return 0;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int vidioc_enum_freq_bands(struct file *file, void *priv,
19162306a36Sopenharmony_ci		struct v4l2_frequency_band *band)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	if (band->tuner != 0)
19462306a36Sopenharmony_ci		return -EINVAL;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (band->index >= ARRAY_SIZE(bands))
19762306a36Sopenharmony_ci		return -EINVAL;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	*band = bands[band->index];
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	return 0;
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv,
20562306a36Sopenharmony_ci		struct v4l2_tuner *v)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct raremono_device *radio = video_drvdata(file);
20862306a36Sopenharmony_ci	int ret;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (v->index > 0)
21162306a36Sopenharmony_ci		return -EINVAL;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	strscpy(v->name, "AM/FM/SW", sizeof(v->name));
21462306a36Sopenharmony_ci	v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
21562306a36Sopenharmony_ci		V4L2_TUNER_CAP_FREQ_BANDS;
21662306a36Sopenharmony_ci	v->rangelow = AM_FREQ_RANGE_LOW * 16;
21762306a36Sopenharmony_ci	v->rangehigh = FM_FREQ_RANGE_HIGH * 16;
21862306a36Sopenharmony_ci	v->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
21962306a36Sopenharmony_ci	v->audmode = (radio->curfreq < FM_FREQ_RANGE_LOW) ?
22062306a36Sopenharmony_ci		V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
22162306a36Sopenharmony_ci	memset(radio->buffer, 1, BUFFER_LENGTH);
22262306a36Sopenharmony_ci	ret = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
22362306a36Sopenharmony_ci			1, 0xa1, 0x030d, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (ret < 0) {
22662306a36Sopenharmony_ci		dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret);
22762306a36Sopenharmony_ci		return ret;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci	v->signal = ((radio->buffer[1] & 0xf) << 8 | radio->buffer[2]) << 4;
23062306a36Sopenharmony_ci	return 0;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv,
23462306a36Sopenharmony_ci					const struct v4l2_tuner *v)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	return v->index ? -EINVAL : 0;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv,
24062306a36Sopenharmony_ci				const struct v4l2_frequency *f)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct raremono_device *radio = video_drvdata(file);
24362306a36Sopenharmony_ci	u32 freq;
24462306a36Sopenharmony_ci	unsigned band;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
24762306a36Sopenharmony_ci		return -EINVAL;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (f->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) * 8)
25062306a36Sopenharmony_ci		band = BAND_FM;
25162306a36Sopenharmony_ci	else if (f->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) * 8)
25262306a36Sopenharmony_ci		band = BAND_AM;
25362306a36Sopenharmony_ci	else
25462306a36Sopenharmony_ci		band = BAND_SW;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	freq = clamp_t(u32, f->frequency, bands[band].rangelow, bands[band].rangehigh);
25762306a36Sopenharmony_ci	return raremono_cmd_main(radio, band, freq / 16);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv,
26162306a36Sopenharmony_ci				struct v4l2_frequency *f)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct raremono_device *radio = video_drvdata(file);
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (f->tuner != 0)
26662306a36Sopenharmony_ci		return -EINVAL;
26762306a36Sopenharmony_ci	f->type = V4L2_TUNER_RADIO;
26862306a36Sopenharmony_ci	f->frequency = radio->curfreq * 16;
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void raremono_device_release(struct v4l2_device *v4l2_dev)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct raremono_device *radio = to_raremono_dev(v4l2_dev);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	kfree(radio->buffer);
27762306a36Sopenharmony_ci	kfree(radio);
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/* File system interface */
28162306a36Sopenharmony_cistatic const struct v4l2_file_operations usb_raremono_fops = {
28262306a36Sopenharmony_ci	.owner		= THIS_MODULE,
28362306a36Sopenharmony_ci	.open           = v4l2_fh_open,
28462306a36Sopenharmony_ci	.release        = v4l2_fh_release,
28562306a36Sopenharmony_ci	.unlocked_ioctl	= video_ioctl2,
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops usb_raremono_ioctl_ops = {
28962306a36Sopenharmony_ci	.vidioc_querycap = vidioc_querycap,
29062306a36Sopenharmony_ci	.vidioc_g_tuner = vidioc_g_tuner,
29162306a36Sopenharmony_ci	.vidioc_s_tuner = vidioc_s_tuner,
29262306a36Sopenharmony_ci	.vidioc_g_frequency = vidioc_g_frequency,
29362306a36Sopenharmony_ci	.vidioc_s_frequency = vidioc_s_frequency,
29462306a36Sopenharmony_ci	.vidioc_enum_freq_bands = vidioc_enum_freq_bands,
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci/* check if the device is present and register with v4l and usb if it is */
29862306a36Sopenharmony_cistatic int usb_raremono_probe(struct usb_interface *intf,
29962306a36Sopenharmony_ci				const struct usb_device_id *id)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct raremono_device *radio;
30262306a36Sopenharmony_ci	int retval = 0;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	radio = kzalloc(sizeof(*radio), GFP_KERNEL);
30562306a36Sopenharmony_ci	if (!radio)
30662306a36Sopenharmony_ci		return -ENOMEM;
30762306a36Sopenharmony_ci	radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
30862306a36Sopenharmony_ci	if (!radio->buffer) {
30962306a36Sopenharmony_ci		kfree(radio);
31062306a36Sopenharmony_ci		return -ENOMEM;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	radio->usbdev = interface_to_usbdev(intf);
31462306a36Sopenharmony_ci	radio->intf = intf;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/*
31762306a36Sopenharmony_ci	 * This device uses the same USB IDs as the si470x SiLabs reference
31862306a36Sopenharmony_ci	 * design. So do an additional check: attempt to read the device ID
31962306a36Sopenharmony_ci	 * from the si470x: the lower 12 bits are 0x0242 for the si470x. The
32062306a36Sopenharmony_ci	 * Raremono always returns 0x0800 (the meaning of that is unknown, but
32162306a36Sopenharmony_ci	 * at least it works).
32262306a36Sopenharmony_ci	 *
32362306a36Sopenharmony_ci	 * We use this check to determine which device we are dealing with.
32462306a36Sopenharmony_ci	 */
32562306a36Sopenharmony_ci	msleep(20);
32662306a36Sopenharmony_ci	retval = usb_control_msg(radio->usbdev,
32762306a36Sopenharmony_ci		usb_rcvctrlpipe(radio->usbdev, 0),
32862306a36Sopenharmony_ci		HID_REQ_GET_REPORT,
32962306a36Sopenharmony_ci		USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
33062306a36Sopenharmony_ci		1, 2,
33162306a36Sopenharmony_ci		radio->buffer, 3, 500);
33262306a36Sopenharmony_ci	if (retval != 3 ||
33362306a36Sopenharmony_ci	    (get_unaligned_be16(&radio->buffer[1]) & 0xfff) == 0x0242) {
33462306a36Sopenharmony_ci		dev_info(&intf->dev, "this is not Thanko's Raremono.\n");
33562306a36Sopenharmony_ci		retval = -ENODEV;
33662306a36Sopenharmony_ci		goto free_mem;
33762306a36Sopenharmony_ci	}
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	dev_info(&intf->dev, "Thanko's Raremono connected: (%04X:%04X)\n",
34062306a36Sopenharmony_ci			id->idVendor, id->idProduct);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
34362306a36Sopenharmony_ci	if (retval < 0) {
34462306a36Sopenharmony_ci		dev_err(&intf->dev, "couldn't register v4l2_device\n");
34562306a36Sopenharmony_ci		goto free_mem;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	mutex_init(&radio->lock);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	strscpy(radio->vdev.name, radio->v4l2_dev.name,
35162306a36Sopenharmony_ci		sizeof(radio->vdev.name));
35262306a36Sopenharmony_ci	radio->vdev.v4l2_dev = &radio->v4l2_dev;
35362306a36Sopenharmony_ci	radio->vdev.fops = &usb_raremono_fops;
35462306a36Sopenharmony_ci	radio->vdev.ioctl_ops = &usb_raremono_ioctl_ops;
35562306a36Sopenharmony_ci	radio->vdev.lock = &radio->lock;
35662306a36Sopenharmony_ci	radio->vdev.release = video_device_release_empty;
35762306a36Sopenharmony_ci	radio->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
35862306a36Sopenharmony_ci	radio->v4l2_dev.release = raremono_device_release;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	usb_set_intfdata(intf, &radio->v4l2_dev);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	video_set_drvdata(&radio->vdev, radio);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	raremono_cmd_main(radio, BAND_FM, 95160);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
36762306a36Sopenharmony_ci	if (retval == 0) {
36862306a36Sopenharmony_ci		dev_info(&intf->dev, "V4L2 device registered as %s\n",
36962306a36Sopenharmony_ci				video_device_node_name(&radio->vdev));
37062306a36Sopenharmony_ci		return 0;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci	dev_err(&intf->dev, "could not register video device\n");
37362306a36Sopenharmony_ci	v4l2_device_unregister(&radio->v4l2_dev);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_cifree_mem:
37662306a36Sopenharmony_ci	kfree(radio->buffer);
37762306a36Sopenharmony_ci	kfree(radio);
37862306a36Sopenharmony_ci	return retval;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci/* USB subsystem interface */
38262306a36Sopenharmony_cistatic struct usb_driver usb_raremono_driver = {
38362306a36Sopenharmony_ci	.name			= "radio-raremono",
38462306a36Sopenharmony_ci	.probe			= usb_raremono_probe,
38562306a36Sopenharmony_ci	.disconnect		= usb_raremono_disconnect,
38662306a36Sopenharmony_ci	.id_table		= usb_raremono_device_table,
38762306a36Sopenharmony_ci};
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cimodule_usb_driver(usb_raremono_driver);
390