18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21. 38c2ecf20Sopenharmony_ci * The device plugs into both the USB and an analog audio input, so this thing 48c2ecf20Sopenharmony_ci * only deals with initialisation and frequency setting, the 58c2ecf20Sopenharmony_ci * audio data has to be handled by a sound driver. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Major issue: I can't find out where the device reports the signal 88c2ecf20Sopenharmony_ci * strength, and indeed the windows software appearantly just looks 98c2ecf20Sopenharmony_ci * at the stereo indicator as well. So, scanning will only find 108c2ecf20Sopenharmony_ci * stereo stations. Sad, but I can't help it. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Also, the windows program sends oodles of messages over to the 138c2ecf20Sopenharmony_ci * device, and I couldn't figure out their meaning. My suspicion 148c2ecf20Sopenharmony_ci * is that they don't have any:-) 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * You might find some interesting stuff about this module at 178c2ecf20Sopenharmony_ci * http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * Copyright (c) 2000 Markus Demleitner <msdemlei@cl.uni-heidelberg.de> 228c2ecf20Sopenharmony_ci*/ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/kernel.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/init.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/input.h> 298c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 308c2ecf20Sopenharmony_ci#include <linux/usb.h> 318c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 328c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 338c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 348c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * Version Information 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Markus Demleitner <msdemlei@tucana.harvard.edu>"); 408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("D-Link DSB-R100 USB FM radio driver"); 418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 428c2ecf20Sopenharmony_ciMODULE_VERSION("1.1.0"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define DSB100_VENDOR 0x04b4 458c2ecf20Sopenharmony_ci#define DSB100_PRODUCT 0x1002 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* Commands the device appears to understand */ 488c2ecf20Sopenharmony_ci#define DSB100_TUNE 1 498c2ecf20Sopenharmony_ci#define DSB100_ONOFF 2 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define TB_LEN 16 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Frequency limits in MHz -- these are European values. For Japanese 548c2ecf20Sopenharmony_cidevices, that would be 76 and 91. */ 558c2ecf20Sopenharmony_ci#define FREQ_MIN 87.5 568c2ecf20Sopenharmony_ci#define FREQ_MAX 108.0 578c2ecf20Sopenharmony_ci#define FREQ_MUL 16000 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci#define v4l2_dev_to_radio(d) container_of(d, struct dsbr100_device, v4l2_dev) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int radio_nr = -1; 628c2ecf20Sopenharmony_cimodule_param(radio_nr, int, 0); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Data for one (physical) device */ 658c2ecf20Sopenharmony_cistruct dsbr100_device { 668c2ecf20Sopenharmony_ci struct usb_device *usbdev; 678c2ecf20Sopenharmony_ci struct video_device videodev; 688c2ecf20Sopenharmony_ci struct v4l2_device v4l2_dev; 698c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci u8 *transfer_buffer; 728c2ecf20Sopenharmony_ci struct mutex v4l2_lock; 738c2ecf20Sopenharmony_ci int curfreq; 748c2ecf20Sopenharmony_ci bool stereo; 758c2ecf20Sopenharmony_ci bool muted; 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Low-level device interface begins here */ 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ 818c2ecf20Sopenharmony_cistatic int dsbr100_setfreq(struct dsbr100_device *radio, unsigned freq) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci unsigned f = (freq / 16 * 80) / 1000 + 856; 848c2ecf20Sopenharmony_ci int retval = 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!radio->muted) { 878c2ecf20Sopenharmony_ci retval = usb_control_msg(radio->usbdev, 888c2ecf20Sopenharmony_ci usb_rcvctrlpipe(radio->usbdev, 0), 898c2ecf20Sopenharmony_ci DSB100_TUNE, 908c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 918c2ecf20Sopenharmony_ci (f >> 8) & 0x00ff, f & 0xff, 928c2ecf20Sopenharmony_ci radio->transfer_buffer, 8, 300); 938c2ecf20Sopenharmony_ci if (retval >= 0) 948c2ecf20Sopenharmony_ci mdelay(1); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (retval >= 0) { 988c2ecf20Sopenharmony_ci radio->curfreq = freq; 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci dev_err(&radio->usbdev->dev, 1028c2ecf20Sopenharmony_ci "%s - usb_control_msg returned %i, request %i\n", 1038c2ecf20Sopenharmony_ci __func__, retval, DSB100_TUNE); 1048c2ecf20Sopenharmony_ci return retval; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* switch on radio */ 1088c2ecf20Sopenharmony_cistatic int dsbr100_start(struct dsbr100_device *radio) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci int retval = usb_control_msg(radio->usbdev, 1118c2ecf20Sopenharmony_ci usb_rcvctrlpipe(radio->usbdev, 0), 1128c2ecf20Sopenharmony_ci DSB100_ONOFF, 1138c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 1148c2ecf20Sopenharmony_ci 0x01, 0x00, radio->transfer_buffer, 8, 300); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (retval >= 0) 1178c2ecf20Sopenharmony_ci return dsbr100_setfreq(radio, radio->curfreq); 1188c2ecf20Sopenharmony_ci dev_err(&radio->usbdev->dev, 1198c2ecf20Sopenharmony_ci "%s - usb_control_msg returned %i, request %i\n", 1208c2ecf20Sopenharmony_ci __func__, retval, DSB100_ONOFF); 1218c2ecf20Sopenharmony_ci return retval; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* switch off radio */ 1268c2ecf20Sopenharmony_cistatic int dsbr100_stop(struct dsbr100_device *radio) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci int retval = usb_control_msg(radio->usbdev, 1298c2ecf20Sopenharmony_ci usb_rcvctrlpipe(radio->usbdev, 0), 1308c2ecf20Sopenharmony_ci DSB100_ONOFF, 1318c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 1328c2ecf20Sopenharmony_ci 0x00, 0x00, radio->transfer_buffer, 8, 300); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (retval >= 0) 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci dev_err(&radio->usbdev->dev, 1378c2ecf20Sopenharmony_ci "%s - usb_control_msg returned %i, request %i\n", 1388c2ecf20Sopenharmony_ci __func__, retval, DSB100_ONOFF); 1398c2ecf20Sopenharmony_ci return retval; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* return the device status. This is, in effect, just whether it 1448c2ecf20Sopenharmony_cisees a stereo signal or not. Pity. */ 1458c2ecf20Sopenharmony_cistatic void dsbr100_getstat(struct dsbr100_device *radio) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci int retval = usb_control_msg(radio->usbdev, 1488c2ecf20Sopenharmony_ci usb_rcvctrlpipe(radio->usbdev, 0), 1498c2ecf20Sopenharmony_ci USB_REQ_GET_STATUS, 1508c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 1518c2ecf20Sopenharmony_ci 0x00, 0x24, radio->transfer_buffer, 8, 300); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (retval < 0) { 1548c2ecf20Sopenharmony_ci radio->stereo = false; 1558c2ecf20Sopenharmony_ci dev_err(&radio->usbdev->dev, 1568c2ecf20Sopenharmony_ci "%s - usb_control_msg returned %i, request %i\n", 1578c2ecf20Sopenharmony_ci __func__, retval, USB_REQ_GET_STATUS); 1588c2ecf20Sopenharmony_ci } else { 1598c2ecf20Sopenharmony_ci radio->stereo = !(radio->transfer_buffer[0] & 0x01); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv, 1648c2ecf20Sopenharmony_ci struct v4l2_capability *v) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct dsbr100_device *radio = video_drvdata(file); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci strscpy(v->driver, "dsbr100", sizeof(v->driver)); 1698c2ecf20Sopenharmony_ci strscpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); 1708c2ecf20Sopenharmony_ci usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); 1718c2ecf20Sopenharmony_ci return 0; 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv, 1758c2ecf20Sopenharmony_ci struct v4l2_tuner *v) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct dsbr100_device *radio = video_drvdata(file); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (v->index > 0) 1808c2ecf20Sopenharmony_ci return -EINVAL; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci dsbr100_getstat(radio); 1838c2ecf20Sopenharmony_ci strscpy(v->name, "FM", sizeof(v->name)); 1848c2ecf20Sopenharmony_ci v->type = V4L2_TUNER_RADIO; 1858c2ecf20Sopenharmony_ci v->rangelow = FREQ_MIN * FREQ_MUL; 1868c2ecf20Sopenharmony_ci v->rangehigh = FREQ_MAX * FREQ_MUL; 1878c2ecf20Sopenharmony_ci v->rxsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO : 1888c2ecf20Sopenharmony_ci V4L2_TUNER_SUB_MONO; 1898c2ecf20Sopenharmony_ci v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; 1908c2ecf20Sopenharmony_ci v->audmode = V4L2_TUNER_MODE_STEREO; 1918c2ecf20Sopenharmony_ci v->signal = radio->stereo ? 0xffff : 0; /* We can't get the signal strength */ 1928c2ecf20Sopenharmony_ci return 0; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv, 1968c2ecf20Sopenharmony_ci const struct v4l2_tuner *v) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci return v->index ? -EINVAL : 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv, 2028c2ecf20Sopenharmony_ci const struct v4l2_frequency *f) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct dsbr100_device *radio = video_drvdata(file); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) 2078c2ecf20Sopenharmony_ci return -EINVAL; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci return dsbr100_setfreq(radio, clamp_t(unsigned, f->frequency, 2108c2ecf20Sopenharmony_ci FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv, 2148c2ecf20Sopenharmony_ci struct v4l2_frequency *f) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct dsbr100_device *radio = video_drvdata(file); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (f->tuner) 2198c2ecf20Sopenharmony_ci return -EINVAL; 2208c2ecf20Sopenharmony_ci f->type = V4L2_TUNER_RADIO; 2218c2ecf20Sopenharmony_ci f->frequency = radio->curfreq; 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int usb_dsbr100_s_ctrl(struct v4l2_ctrl *ctrl) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct dsbr100_device *radio = 2288c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct dsbr100_device, hdl); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci switch (ctrl->id) { 2318c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_MUTE: 2328c2ecf20Sopenharmony_ci radio->muted = ctrl->val; 2338c2ecf20Sopenharmony_ci return radio->muted ? dsbr100_stop(radio) : dsbr100_start(radio); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci return -EINVAL; 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* USB subsystem interface begins here */ 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* 2428c2ecf20Sopenharmony_ci * Handle unplugging of the device. 2438c2ecf20Sopenharmony_ci * We call video_unregister_device in any case. 2448c2ecf20Sopenharmony_ci * The last function called in this procedure is 2458c2ecf20Sopenharmony_ci * usb_dsbr100_video_device_release 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic void usb_dsbr100_disconnect(struct usb_interface *intf) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct dsbr100_device *radio = usb_get_intfdata(intf); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci mutex_lock(&radio->v4l2_lock); 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * Disconnect is also called on unload, and in that case we need to 2548c2ecf20Sopenharmony_ci * mute the device. This call will silently fail if it is called 2558c2ecf20Sopenharmony_ci * after a physical disconnect. 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_ci usb_control_msg(radio->usbdev, 2588c2ecf20Sopenharmony_ci usb_rcvctrlpipe(radio->usbdev, 0), 2598c2ecf20Sopenharmony_ci DSB100_ONOFF, 2608c2ecf20Sopenharmony_ci USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 2618c2ecf20Sopenharmony_ci 0x00, 0x00, radio->transfer_buffer, 8, 300); 2628c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 2638c2ecf20Sopenharmony_ci video_unregister_device(&radio->videodev); 2648c2ecf20Sopenharmony_ci v4l2_device_disconnect(&radio->v4l2_dev); 2658c2ecf20Sopenharmony_ci mutex_unlock(&radio->v4l2_lock); 2668c2ecf20Sopenharmony_ci v4l2_device_put(&radio->v4l2_dev); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* Suspend device - stop device. */ 2718c2ecf20Sopenharmony_cistatic int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct dsbr100_device *radio = usb_get_intfdata(intf); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci mutex_lock(&radio->v4l2_lock); 2768c2ecf20Sopenharmony_ci if (!radio->muted && dsbr100_stop(radio) < 0) 2778c2ecf20Sopenharmony_ci dev_warn(&intf->dev, "dsbr100_stop failed\n"); 2788c2ecf20Sopenharmony_ci mutex_unlock(&radio->v4l2_lock); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci dev_info(&intf->dev, "going into suspend..\n"); 2818c2ecf20Sopenharmony_ci return 0; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci/* Resume device - start device. */ 2858c2ecf20Sopenharmony_cistatic int usb_dsbr100_resume(struct usb_interface *intf) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci struct dsbr100_device *radio = usb_get_intfdata(intf); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci mutex_lock(&radio->v4l2_lock); 2908c2ecf20Sopenharmony_ci if (!radio->muted && dsbr100_start(radio) < 0) 2918c2ecf20Sopenharmony_ci dev_warn(&intf->dev, "dsbr100_start failed\n"); 2928c2ecf20Sopenharmony_ci mutex_unlock(&radio->v4l2_lock); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci dev_info(&intf->dev, "coming out of suspend..\n"); 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/* free data structures */ 2998c2ecf20Sopenharmony_cistatic void usb_dsbr100_release(struct v4l2_device *v4l2_dev) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct dsbr100_device *radio = v4l2_dev_to_radio(v4l2_dev); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&radio->hdl); 3048c2ecf20Sopenharmony_ci v4l2_device_unregister(&radio->v4l2_dev); 3058c2ecf20Sopenharmony_ci kfree(radio->transfer_buffer); 3068c2ecf20Sopenharmony_ci kfree(radio); 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops usb_dsbr100_ctrl_ops = { 3108c2ecf20Sopenharmony_ci .s_ctrl = usb_dsbr100_s_ctrl, 3118c2ecf20Sopenharmony_ci}; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* File system interface */ 3148c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations usb_dsbr100_fops = { 3158c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3168c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 3178c2ecf20Sopenharmony_ci .open = v4l2_fh_open, 3188c2ecf20Sopenharmony_ci .release = v4l2_fh_release, 3198c2ecf20Sopenharmony_ci .poll = v4l2_ctrl_poll, 3208c2ecf20Sopenharmony_ci}; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { 3238c2ecf20Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 3248c2ecf20Sopenharmony_ci .vidioc_g_tuner = vidioc_g_tuner, 3258c2ecf20Sopenharmony_ci .vidioc_s_tuner = vidioc_s_tuner, 3268c2ecf20Sopenharmony_ci .vidioc_g_frequency = vidioc_g_frequency, 3278c2ecf20Sopenharmony_ci .vidioc_s_frequency = vidioc_s_frequency, 3288c2ecf20Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 3298c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 3308c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 3318c2ecf20Sopenharmony_ci}; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* check if the device is present and register with v4l and usb if it is */ 3348c2ecf20Sopenharmony_cistatic int usb_dsbr100_probe(struct usb_interface *intf, 3358c2ecf20Sopenharmony_ci const struct usb_device_id *id) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct dsbr100_device *radio; 3388c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev; 3398c2ecf20Sopenharmony_ci int retval; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci radio = kzalloc(sizeof(struct dsbr100_device), GFP_KERNEL); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (!radio) 3448c2ecf20Sopenharmony_ci return -ENOMEM; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci radio->transfer_buffer = kmalloc(TB_LEN, GFP_KERNEL); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (!(radio->transfer_buffer)) { 3498c2ecf20Sopenharmony_ci kfree(radio); 3508c2ecf20Sopenharmony_ci return -ENOMEM; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci v4l2_dev = &radio->v4l2_dev; 3548c2ecf20Sopenharmony_ci v4l2_dev->release = usb_dsbr100_release; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci retval = v4l2_device_register(&intf->dev, v4l2_dev); 3578c2ecf20Sopenharmony_ci if (retval < 0) { 3588c2ecf20Sopenharmony_ci v4l2_err(v4l2_dev, "couldn't register v4l2_device\n"); 3598c2ecf20Sopenharmony_ci goto err_reg_dev; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&radio->hdl, 1); 3638c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&radio->hdl, &usb_dsbr100_ctrl_ops, 3648c2ecf20Sopenharmony_ci V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 3658c2ecf20Sopenharmony_ci if (radio->hdl.error) { 3668c2ecf20Sopenharmony_ci retval = radio->hdl.error; 3678c2ecf20Sopenharmony_ci v4l2_err(v4l2_dev, "couldn't register control\n"); 3688c2ecf20Sopenharmony_ci goto err_reg_ctrl; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci mutex_init(&radio->v4l2_lock); 3718c2ecf20Sopenharmony_ci strscpy(radio->videodev.name, v4l2_dev->name, 3728c2ecf20Sopenharmony_ci sizeof(radio->videodev.name)); 3738c2ecf20Sopenharmony_ci radio->videodev.v4l2_dev = v4l2_dev; 3748c2ecf20Sopenharmony_ci radio->videodev.fops = &usb_dsbr100_fops; 3758c2ecf20Sopenharmony_ci radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops; 3768c2ecf20Sopenharmony_ci radio->videodev.release = video_device_release_empty; 3778c2ecf20Sopenharmony_ci radio->videodev.lock = &radio->v4l2_lock; 3788c2ecf20Sopenharmony_ci radio->videodev.ctrl_handler = &radio->hdl; 3798c2ecf20Sopenharmony_ci radio->videodev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci radio->usbdev = interface_to_usbdev(intf); 3828c2ecf20Sopenharmony_ci radio->curfreq = FREQ_MIN * FREQ_MUL; 3838c2ecf20Sopenharmony_ci radio->muted = true; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci video_set_drvdata(&radio->videodev, radio); 3868c2ecf20Sopenharmony_ci usb_set_intfdata(intf, radio); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); 3898c2ecf20Sopenharmony_ci if (retval == 0) 3908c2ecf20Sopenharmony_ci return 0; 3918c2ecf20Sopenharmony_ci v4l2_err(v4l2_dev, "couldn't register video device\n"); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cierr_reg_ctrl: 3948c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&radio->hdl); 3958c2ecf20Sopenharmony_ci v4l2_device_unregister(v4l2_dev); 3968c2ecf20Sopenharmony_cierr_reg_dev: 3978c2ecf20Sopenharmony_ci kfree(radio->transfer_buffer); 3988c2ecf20Sopenharmony_ci kfree(radio); 3998c2ecf20Sopenharmony_ci return retval; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic const struct usb_device_id usb_dsbr100_device_table[] = { 4038c2ecf20Sopenharmony_ci { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, 4048c2ecf20Sopenharmony_ci { } /* Terminating entry */ 4058c2ecf20Sopenharmony_ci}; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usb_dsbr100_device_table); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* USB subsystem interface */ 4108c2ecf20Sopenharmony_cistatic struct usb_driver usb_dsbr100_driver = { 4118c2ecf20Sopenharmony_ci .name = "dsbr100", 4128c2ecf20Sopenharmony_ci .probe = usb_dsbr100_probe, 4138c2ecf20Sopenharmony_ci .disconnect = usb_dsbr100_disconnect, 4148c2ecf20Sopenharmony_ci .id_table = usb_dsbr100_device_table, 4158c2ecf20Sopenharmony_ci .suspend = usb_dsbr100_suspend, 4168c2ecf20Sopenharmony_ci .resume = usb_dsbr100_resume, 4178c2ecf20Sopenharmony_ci .reset_resume = usb_dsbr100_resume, 4188c2ecf20Sopenharmony_ci}; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cimodule_usb_driver(usb_dsbr100_driver); 421