162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21. 362306a36Sopenharmony_ci * The device plugs into both the USB and an analog audio input, so this thing 462306a36Sopenharmony_ci * only deals with initialisation and frequency setting, the 562306a36Sopenharmony_ci * audio data has to be handled by a sound driver. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Major issue: I can't find out where the device reports the signal 862306a36Sopenharmony_ci * strength, and indeed the windows software appearantly just looks 962306a36Sopenharmony_ci * at the stereo indicator as well. So, scanning will only find 1062306a36Sopenharmony_ci * stereo stations. Sad, but I can't help it. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Also, the windows program sends oodles of messages over to the 1362306a36Sopenharmony_ci * device, and I couldn't figure out their meaning. My suspicion 1462306a36Sopenharmony_ci * is that they don't have any:-) 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * You might find some interesting stuff about this module at 1762306a36Sopenharmony_ci * http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.de> 2262306a36Sopenharmony_ci*/ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/init.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <linux/input.h> 2962306a36Sopenharmony_ci#include <linux/videodev2.h> 3062306a36Sopenharmony_ci#include <linux/usb.h> 3162306a36Sopenharmony_ci#include <media/v4l2-device.h> 3262306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 3362306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 3462306a36Sopenharmony_ci#include <media/v4l2-event.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* 3762306a36Sopenharmony_ci * Version Information 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ciMODULE_AUTHOR("Markus Demleitner <msdemlei@tucana.harvard.edu>"); 4062306a36Sopenharmony_ciMODULE_DESCRIPTION("D-Link DSB-R100 USB FM radio driver"); 4162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4262306a36Sopenharmony_ciMODULE_VERSION("1.1.0"); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define DSB100_VENDOR 0x04b4 4562306a36Sopenharmony_ci#define DSB100_PRODUCT 0x1002 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Commands the device appears to understand */ 4862306a36Sopenharmony_ci#define DSB100_TUNE 1 4962306a36Sopenharmony_ci#define DSB100_ONOFF 2 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define TB_LEN 16 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* Frequency limits in MHz -- these are European values. For Japanese 5462306a36Sopenharmony_cidevices, that would be 76 and 91. */ 5562306a36Sopenharmony_ci#define FREQ_MIN 87.5 5662306a36Sopenharmony_ci#define FREQ_MAX 108.0 5762306a36Sopenharmony_ci#define FREQ_MUL 16000 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define v4l2_dev_to_radio(d) container_of(d, struct dsbr100_device, v4l2_dev) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int radio_nr = -1; 6262306a36Sopenharmony_cimodule_param(radio_nr, int, 0); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Data for one (physical) device */ 6562306a36Sopenharmony_cistruct dsbr100_device { 6662306a36Sopenharmony_ci struct usb_device *usbdev; 6762306a36Sopenharmony_ci struct video_device videodev; 6862306a36Sopenharmony_ci struct v4l2_device v4l2_dev; 6962306a36Sopenharmony_ci struct v4l2_ctrl_handler hdl; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci u8 *transfer_buffer; 7262306a36Sopenharmony_ci struct mutex v4l2_lock; 7362306a36Sopenharmony_ci int curfreq; 7462306a36Sopenharmony_ci bool stereo; 7562306a36Sopenharmony_ci bool muted; 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* Low-level device interface begins here */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ 8162306a36Sopenharmony_cistatic int dsbr100_setfreq(struct dsbr100_device *radio, unsigned freq) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci unsigned f = (freq / 16 * 80) / 1000 + 856; 8462306a36Sopenharmony_ci int retval = 0; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!radio->muted) { 8762306a36Sopenharmony_ci retval = usb_control_msg(radio->usbdev, 8862306a36Sopenharmony_ci usb_rcvctrlpipe(radio->usbdev, 0), 8962306a36Sopenharmony_ci DSB100_TUNE, 9062306a36Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 9162306a36Sopenharmony_ci (f >> 8) & 0x00ff, f & 0xff, 9262306a36Sopenharmony_ci radio->transfer_buffer, 8, 300); 9362306a36Sopenharmony_ci if (retval >= 0) 9462306a36Sopenharmony_ci mdelay(1); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (retval >= 0) { 9862306a36Sopenharmony_ci radio->curfreq = freq; 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci dev_err(&radio->usbdev->dev, 10262306a36Sopenharmony_ci "%s - usb_control_msg returned %i, request %i\n", 10362306a36Sopenharmony_ci __func__, retval, DSB100_TUNE); 10462306a36Sopenharmony_ci return retval; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* switch on radio */ 10862306a36Sopenharmony_cistatic int dsbr100_start(struct dsbr100_device *radio) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int retval = usb_control_msg(radio->usbdev, 11162306a36Sopenharmony_ci usb_rcvctrlpipe(radio->usbdev, 0), 11262306a36Sopenharmony_ci DSB100_ONOFF, 11362306a36Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 11462306a36Sopenharmony_ci 0x01, 0x00, radio->transfer_buffer, 8, 300); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (retval >= 0) 11762306a36Sopenharmony_ci return dsbr100_setfreq(radio, radio->curfreq); 11862306a36Sopenharmony_ci dev_err(&radio->usbdev->dev, 11962306a36Sopenharmony_ci "%s - usb_control_msg returned %i, request %i\n", 12062306a36Sopenharmony_ci __func__, retval, DSB100_ONOFF); 12162306a36Sopenharmony_ci return retval; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* switch off radio */ 12662306a36Sopenharmony_cistatic int dsbr100_stop(struct dsbr100_device *radio) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci int retval = usb_control_msg(radio->usbdev, 12962306a36Sopenharmony_ci usb_rcvctrlpipe(radio->usbdev, 0), 13062306a36Sopenharmony_ci DSB100_ONOFF, 13162306a36Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 13262306a36Sopenharmony_ci 0x00, 0x00, radio->transfer_buffer, 8, 300); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (retval >= 0) 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci dev_err(&radio->usbdev->dev, 13762306a36Sopenharmony_ci "%s - usb_control_msg returned %i, request %i\n", 13862306a36Sopenharmony_ci __func__, retval, DSB100_ONOFF); 13962306a36Sopenharmony_ci return retval; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* return the device status. This is, in effect, just whether it 14462306a36Sopenharmony_cisees a stereo signal or not. Pity. */ 14562306a36Sopenharmony_cistatic void dsbr100_getstat(struct dsbr100_device *radio) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int retval = usb_control_msg(radio->usbdev, 14862306a36Sopenharmony_ci usb_rcvctrlpipe(radio->usbdev, 0), 14962306a36Sopenharmony_ci USB_REQ_GET_STATUS, 15062306a36Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 15162306a36Sopenharmony_ci 0x00, 0x24, radio->transfer_buffer, 8, 300); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (retval < 0) { 15462306a36Sopenharmony_ci radio->stereo = false; 15562306a36Sopenharmony_ci dev_err(&radio->usbdev->dev, 15662306a36Sopenharmony_ci "%s - usb_control_msg returned %i, request %i\n", 15762306a36Sopenharmony_ci __func__, retval, USB_REQ_GET_STATUS); 15862306a36Sopenharmony_ci } else { 15962306a36Sopenharmony_ci radio->stereo = !(radio->transfer_buffer[0] & 0x01); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv, 16462306a36Sopenharmony_ci struct v4l2_capability *v) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct dsbr100_device *radio = video_drvdata(file); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci strscpy(v->driver, "dsbr100", sizeof(v->driver)); 16962306a36Sopenharmony_ci strscpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); 17062306a36Sopenharmony_ci usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv, 17562306a36Sopenharmony_ci struct v4l2_tuner *v) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct dsbr100_device *radio = video_drvdata(file); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (v->index > 0) 18062306a36Sopenharmony_ci return -EINVAL; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci dsbr100_getstat(radio); 18362306a36Sopenharmony_ci strscpy(v->name, "FM", sizeof(v->name)); 18462306a36Sopenharmony_ci v->type = V4L2_TUNER_RADIO; 18562306a36Sopenharmony_ci v->rangelow = FREQ_MIN * FREQ_MUL; 18662306a36Sopenharmony_ci v->rangehigh = FREQ_MAX * FREQ_MUL; 18762306a36Sopenharmony_ci v->rxsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO : 18862306a36Sopenharmony_ci V4L2_TUNER_SUB_MONO; 18962306a36Sopenharmony_ci v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; 19062306a36Sopenharmony_ci v->audmode = V4L2_TUNER_MODE_STEREO; 19162306a36Sopenharmony_ci v->signal = radio->stereo ? 0xffff : 0; /* We can't get the signal strength */ 19262306a36Sopenharmony_ci return 0; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv, 19662306a36Sopenharmony_ci const struct v4l2_tuner *v) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci return v->index ? -EINVAL : 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv, 20262306a36Sopenharmony_ci const struct v4l2_frequency *f) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct dsbr100_device *radio = video_drvdata(file); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) 20762306a36Sopenharmony_ci return -EINVAL; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return dsbr100_setfreq(radio, clamp_t(unsigned, f->frequency, 21062306a36Sopenharmony_ci FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv, 21462306a36Sopenharmony_ci struct v4l2_frequency *f) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct dsbr100_device *radio = video_drvdata(file); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (f->tuner) 21962306a36Sopenharmony_ci return -EINVAL; 22062306a36Sopenharmony_ci f->type = V4L2_TUNER_RADIO; 22162306a36Sopenharmony_ci f->frequency = radio->curfreq; 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int usb_dsbr100_s_ctrl(struct v4l2_ctrl *ctrl) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct dsbr100_device *radio = 22862306a36Sopenharmony_ci container_of(ctrl->handler, struct dsbr100_device, hdl); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci switch (ctrl->id) { 23162306a36Sopenharmony_ci case V4L2_CID_AUDIO_MUTE: 23262306a36Sopenharmony_ci radio->muted = ctrl->val; 23362306a36Sopenharmony_ci return radio->muted ? dsbr100_stop(radio) : dsbr100_start(radio); 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci return -EINVAL; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* USB subsystem interface begins here */ 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* 24262306a36Sopenharmony_ci * Handle unplugging of the device. 24362306a36Sopenharmony_ci * We call video_unregister_device in any case. 24462306a36Sopenharmony_ci * The last function called in this procedure is 24562306a36Sopenharmony_ci * usb_dsbr100_video_device_release 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_cistatic void usb_dsbr100_disconnect(struct usb_interface *intf) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct dsbr100_device *radio = usb_get_intfdata(intf); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci mutex_lock(&radio->v4l2_lock); 25262306a36Sopenharmony_ci /* 25362306a36Sopenharmony_ci * Disconnect is also called on unload, and in that case we need to 25462306a36Sopenharmony_ci * mute the device. This call will silently fail if it is called 25562306a36Sopenharmony_ci * after a physical disconnect. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci usb_control_msg(radio->usbdev, 25862306a36Sopenharmony_ci usb_rcvctrlpipe(radio->usbdev, 0), 25962306a36Sopenharmony_ci DSB100_ONOFF, 26062306a36Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 26162306a36Sopenharmony_ci 0x00, 0x00, radio->transfer_buffer, 8, 300); 26262306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 26362306a36Sopenharmony_ci video_unregister_device(&radio->videodev); 26462306a36Sopenharmony_ci v4l2_device_disconnect(&radio->v4l2_dev); 26562306a36Sopenharmony_ci mutex_unlock(&radio->v4l2_lock); 26662306a36Sopenharmony_ci v4l2_device_put(&radio->v4l2_dev); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* Suspend device - stop device. */ 27162306a36Sopenharmony_cistatic int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct dsbr100_device *radio = usb_get_intfdata(intf); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci mutex_lock(&radio->v4l2_lock); 27662306a36Sopenharmony_ci if (!radio->muted && dsbr100_stop(radio) < 0) 27762306a36Sopenharmony_ci dev_warn(&intf->dev, "dsbr100_stop failed\n"); 27862306a36Sopenharmony_ci mutex_unlock(&radio->v4l2_lock); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci dev_info(&intf->dev, "going into suspend..\n"); 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/* Resume device - start device. */ 28562306a36Sopenharmony_cistatic int usb_dsbr100_resume(struct usb_interface *intf) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct dsbr100_device *radio = usb_get_intfdata(intf); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci mutex_lock(&radio->v4l2_lock); 29062306a36Sopenharmony_ci if (!radio->muted && dsbr100_start(radio) < 0) 29162306a36Sopenharmony_ci dev_warn(&intf->dev, "dsbr100_start failed\n"); 29262306a36Sopenharmony_ci mutex_unlock(&radio->v4l2_lock); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci dev_info(&intf->dev, "coming out of suspend..\n"); 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/* free data structures */ 29962306a36Sopenharmony_cistatic void usb_dsbr100_release(struct v4l2_device *v4l2_dev) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct dsbr100_device *radio = v4l2_dev_to_radio(v4l2_dev); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci v4l2_ctrl_handler_free(&radio->hdl); 30462306a36Sopenharmony_ci v4l2_device_unregister(&radio->v4l2_dev); 30562306a36Sopenharmony_ci kfree(radio->transfer_buffer); 30662306a36Sopenharmony_ci kfree(radio); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops usb_dsbr100_ctrl_ops = { 31062306a36Sopenharmony_ci .s_ctrl = usb_dsbr100_s_ctrl, 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci/* File system interface */ 31462306a36Sopenharmony_cistatic const struct v4l2_file_operations usb_dsbr100_fops = { 31562306a36Sopenharmony_ci .owner = THIS_MODULE, 31662306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 31762306a36Sopenharmony_ci .open = v4l2_fh_open, 31862306a36Sopenharmony_ci .release = v4l2_fh_release, 31962306a36Sopenharmony_ci .poll = v4l2_ctrl_poll, 32062306a36Sopenharmony_ci}; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { 32362306a36Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 32462306a36Sopenharmony_ci .vidioc_g_tuner = vidioc_g_tuner, 32562306a36Sopenharmony_ci .vidioc_s_tuner = vidioc_s_tuner, 32662306a36Sopenharmony_ci .vidioc_g_frequency = vidioc_g_frequency, 32762306a36Sopenharmony_ci .vidioc_s_frequency = vidioc_s_frequency, 32862306a36Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 32962306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 33062306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 33162306a36Sopenharmony_ci}; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* check if the device is present and register with v4l and usb if it is */ 33462306a36Sopenharmony_cistatic int usb_dsbr100_probe(struct usb_interface *intf, 33562306a36Sopenharmony_ci const struct usb_device_id *id) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct dsbr100_device *radio; 33862306a36Sopenharmony_ci struct v4l2_device *v4l2_dev; 33962306a36Sopenharmony_ci int retval; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci radio = kzalloc(sizeof(struct dsbr100_device), GFP_KERNEL); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (!radio) 34462306a36Sopenharmony_ci return -ENOMEM; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (!(radio->transfer_buffer)) { 34962306a36Sopenharmony_ci kfree(radio); 35062306a36Sopenharmony_ci return -ENOMEM; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci v4l2_dev = &radio->v4l2_dev; 35462306a36Sopenharmony_ci v4l2_dev->release = usb_dsbr100_release; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci retval = v4l2_device_register(&intf->dev, v4l2_dev); 35762306a36Sopenharmony_ci if (retval < 0) { 35862306a36Sopenharmony_ci v4l2_err(v4l2_dev, "couldn't register v4l2_device\n"); 35962306a36Sopenharmony_ci goto err_reg_dev; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci v4l2_ctrl_handler_init(&radio->hdl, 1); 36362306a36Sopenharmony_ci v4l2_ctrl_new_std(&radio->hdl, &usb_dsbr100_ctrl_ops, 36462306a36Sopenharmony_ci V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 36562306a36Sopenharmony_ci if (radio->hdl.error) { 36662306a36Sopenharmony_ci retval = radio->hdl.error; 36762306a36Sopenharmony_ci v4l2_err(v4l2_dev, "couldn't register control\n"); 36862306a36Sopenharmony_ci goto err_reg_ctrl; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci mutex_init(&radio->v4l2_lock); 37162306a36Sopenharmony_ci strscpy(radio->videodev.name, v4l2_dev->name, 37262306a36Sopenharmony_ci sizeof(radio->videodev.name)); 37362306a36Sopenharmony_ci radio->videodev.v4l2_dev = v4l2_dev; 37462306a36Sopenharmony_ci radio->videodev.fops = &usb_dsbr100_fops; 37562306a36Sopenharmony_ci radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops; 37662306a36Sopenharmony_ci radio->videodev.release = video_device_release_empty; 37762306a36Sopenharmony_ci radio->videodev.lock = &radio->v4l2_lock; 37862306a36Sopenharmony_ci radio->videodev.ctrl_handler = &radio->hdl; 37962306a36Sopenharmony_ci radio->videodev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci radio->usbdev = interface_to_usbdev(intf); 38262306a36Sopenharmony_ci radio->curfreq = FREQ_MIN * FREQ_MUL; 38362306a36Sopenharmony_ci radio->muted = true; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci video_set_drvdata(&radio->videodev, radio); 38662306a36Sopenharmony_ci usb_set_intfdata(intf, radio); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); 38962306a36Sopenharmony_ci if (retval == 0) 39062306a36Sopenharmony_ci return 0; 39162306a36Sopenharmony_ci v4l2_err(v4l2_dev, "couldn't register video device\n"); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cierr_reg_ctrl: 39462306a36Sopenharmony_ci v4l2_ctrl_handler_free(&radio->hdl); 39562306a36Sopenharmony_ci v4l2_device_unregister(v4l2_dev); 39662306a36Sopenharmony_cierr_reg_dev: 39762306a36Sopenharmony_ci kfree(radio->transfer_buffer); 39862306a36Sopenharmony_ci kfree(radio); 39962306a36Sopenharmony_ci return retval; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cistatic const struct usb_device_id usb_dsbr100_device_table[] = { 40362306a36Sopenharmony_ci { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, 40462306a36Sopenharmony_ci { } /* Terminating entry */ 40562306a36Sopenharmony_ci}; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usb_dsbr100_device_table); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/* USB subsystem interface */ 41062306a36Sopenharmony_cistatic struct usb_driver usb_dsbr100_driver = { 41162306a36Sopenharmony_ci .name = "dsbr100", 41262306a36Sopenharmony_ci .probe = usb_dsbr100_probe, 41362306a36Sopenharmony_ci .disconnect = usb_dsbr100_disconnect, 41462306a36Sopenharmony_ci .id_table = usb_dsbr100_device_table, 41562306a36Sopenharmony_ci .suspend = usb_dsbr100_suspend, 41662306a36Sopenharmony_ci .resume = usb_dsbr100_resume, 41762306a36Sopenharmony_ci .reset_resume = usb_dsbr100_resume, 41862306a36Sopenharmony_ci}; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cimodule_usb_driver(usb_dsbr100_driver); 421