18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the MasterKit MA901 USB FM radio. This device plugs 48c2ecf20Sopenharmony_ci * into the USB port and an analog audio input or headphones, so this thing 58c2ecf20Sopenharmony_ci * only deals with initialization, frequency setting, volume. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2012 Alexey Klimov <klimov.linux@gmail.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/input.h> 158c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 168c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 178c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 188c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 198c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 208c2ecf20Sopenharmony_ci#include <linux/usb.h> 218c2ecf20Sopenharmony_ci#include <linux/mutex.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>" 248c2ecf20Sopenharmony_ci#define DRIVER_DESC "Masterkit MA901 USB FM radio driver" 258c2ecf20Sopenharmony_ci#define DRIVER_VERSION "0.0.1" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 308c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define USB_MA901_VENDOR 0x16c0 338c2ecf20Sopenharmony_ci#define USB_MA901_PRODUCT 0x05df 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* dev_warn macro with driver name */ 368c2ecf20Sopenharmony_ci#define MA901_DRIVER_NAME "radio-ma901" 378c2ecf20Sopenharmony_ci#define ma901radio_dev_warn(dev, fmt, arg...) \ 388c2ecf20Sopenharmony_ci dev_warn(dev, MA901_DRIVER_NAME " - " fmt, ##arg) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define ma901radio_dev_err(dev, fmt, arg...) \ 418c2ecf20Sopenharmony_ci dev_err(dev, MA901_DRIVER_NAME " - " fmt, ##arg) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Probably USB_TIMEOUT should be modified in module parameter */ 448c2ecf20Sopenharmony_ci#define BUFFER_LENGTH 8 458c2ecf20Sopenharmony_ci#define USB_TIMEOUT 500 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define FREQ_MIN 87.5 488c2ecf20Sopenharmony_ci#define FREQ_MAX 108.0 498c2ecf20Sopenharmony_ci#define FREQ_MUL 16000 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define MA901_VOLUME_MAX 16 528c2ecf20Sopenharmony_ci#define MA901_VOLUME_MIN 0 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* Commands that device should understand 558c2ecf20Sopenharmony_ci * List isn't full and will be updated with implementation of new functions 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci#define MA901_RADIO_SET_FREQ 0x03 588c2ecf20Sopenharmony_ci#define MA901_RADIO_SET_VOLUME 0x04 598c2ecf20Sopenharmony_ci#define MA901_RADIO_SET_MONO_STEREO 0x05 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Comfortable defines for ma901radio_set_stereo */ 628c2ecf20Sopenharmony_ci#define MA901_WANT_STEREO 0x50 638c2ecf20Sopenharmony_ci#define MA901_WANT_MONO 0xd0 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* module parameter */ 668c2ecf20Sopenharmony_cistatic int radio_nr = -1; 678c2ecf20Sopenharmony_cimodule_param(radio_nr, int, 0); 688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "Radio file number"); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* Data for one (physical) device */ 718c2ecf20Sopenharmony_cistruct ma901radio_device { 728c2ecf20Sopenharmony_ci /* reference to USB and video device */ 738c2ecf20Sopenharmony_ci struct usb_device *usbdev; 748c2ecf20Sopenharmony_ci struct usb_interface *intf; 758c2ecf20Sopenharmony_ci struct video_device vdev; 768c2ecf20Sopenharmony_ci struct v4l2_device v4l2_dev; 778c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler hdl; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci u8 *buffer; 808c2ecf20Sopenharmony_ci struct mutex lock; /* buffer locking */ 818c2ecf20Sopenharmony_ci int curfreq; 828c2ecf20Sopenharmony_ci u16 volume; 838c2ecf20Sopenharmony_ci int stereo; 848c2ecf20Sopenharmony_ci bool muted; 858c2ecf20Sopenharmony_ci}; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic inline struct ma901radio_device *to_ma901radio_dev(struct v4l2_device *v4l2_dev) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci return container_of(v4l2_dev, struct ma901radio_device, v4l2_dev); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ 938c2ecf20Sopenharmony_cistatic int ma901radio_set_freq(struct ma901radio_device *radio, int freq) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci unsigned int freq_send = 0x300 + (freq >> 5) / 25; 968c2ecf20Sopenharmony_ci int retval; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci radio->buffer[0] = 0x0a; 998c2ecf20Sopenharmony_ci radio->buffer[1] = MA901_RADIO_SET_FREQ; 1008c2ecf20Sopenharmony_ci radio->buffer[2] = ((freq_send >> 8) & 0xff) + 0x80; 1018c2ecf20Sopenharmony_ci radio->buffer[3] = freq_send & 0xff; 1028c2ecf20Sopenharmony_ci radio->buffer[4] = 0x00; 1038c2ecf20Sopenharmony_ci radio->buffer[5] = 0x00; 1048c2ecf20Sopenharmony_ci radio->buffer[6] = 0x00; 1058c2ecf20Sopenharmony_ci radio->buffer[7] = 0x00; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), 1088c2ecf20Sopenharmony_ci 9, 0x21, 0x0300, 0, 1098c2ecf20Sopenharmony_ci radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); 1108c2ecf20Sopenharmony_ci if (retval < 0) 1118c2ecf20Sopenharmony_ci return retval; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci radio->curfreq = freq; 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int ma901radio_set_volume(struct ma901radio_device *radio, u16 vol_to_set) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci int retval; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci radio->buffer[0] = 0x0a; 1228c2ecf20Sopenharmony_ci radio->buffer[1] = MA901_RADIO_SET_VOLUME; 1238c2ecf20Sopenharmony_ci radio->buffer[2] = 0xc2; 1248c2ecf20Sopenharmony_ci radio->buffer[3] = vol_to_set + 0x20; 1258c2ecf20Sopenharmony_ci radio->buffer[4] = 0x00; 1268c2ecf20Sopenharmony_ci radio->buffer[5] = 0x00; 1278c2ecf20Sopenharmony_ci radio->buffer[6] = 0x00; 1288c2ecf20Sopenharmony_ci radio->buffer[7] = 0x00; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), 1318c2ecf20Sopenharmony_ci 9, 0x21, 0x0300, 0, 1328c2ecf20Sopenharmony_ci radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); 1338c2ecf20Sopenharmony_ci if (retval < 0) 1348c2ecf20Sopenharmony_ci return retval; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci radio->volume = vol_to_set; 1378c2ecf20Sopenharmony_ci return retval; 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int ma901_set_stereo(struct ma901radio_device *radio, u8 stereo) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci int retval; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci radio->buffer[0] = 0x0a; 1458c2ecf20Sopenharmony_ci radio->buffer[1] = MA901_RADIO_SET_MONO_STEREO; 1468c2ecf20Sopenharmony_ci radio->buffer[2] = stereo; 1478c2ecf20Sopenharmony_ci radio->buffer[3] = 0x00; 1488c2ecf20Sopenharmony_ci radio->buffer[4] = 0x00; 1498c2ecf20Sopenharmony_ci radio->buffer[5] = 0x00; 1508c2ecf20Sopenharmony_ci radio->buffer[6] = 0x00; 1518c2ecf20Sopenharmony_ci radio->buffer[7] = 0x00; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), 1548c2ecf20Sopenharmony_ci 9, 0x21, 0x0300, 0, 1558c2ecf20Sopenharmony_ci radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (retval < 0) 1588c2ecf20Sopenharmony_ci return retval; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (stereo == MA901_WANT_STEREO) 1618c2ecf20Sopenharmony_ci radio->stereo = V4L2_TUNER_MODE_STEREO; 1628c2ecf20Sopenharmony_ci else 1638c2ecf20Sopenharmony_ci radio->stereo = V4L2_TUNER_MODE_MONO; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci return retval; 1668c2ecf20Sopenharmony_ci} 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci/* Handle unplugging the device. 1698c2ecf20Sopenharmony_ci * We call video_unregister_device in any case. 1708c2ecf20Sopenharmony_ci * The last function called in this procedure is 1718c2ecf20Sopenharmony_ci * usb_ma901radio_device_release. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_cistatic void usb_ma901radio_disconnect(struct usb_interface *intf) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct ma901radio_device *radio = to_ma901radio_dev(usb_get_intfdata(intf)); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci mutex_lock(&radio->lock); 1788c2ecf20Sopenharmony_ci video_unregister_device(&radio->vdev); 1798c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 1808c2ecf20Sopenharmony_ci v4l2_device_disconnect(&radio->v4l2_dev); 1818c2ecf20Sopenharmony_ci mutex_unlock(&radio->lock); 1828c2ecf20Sopenharmony_ci v4l2_device_put(&radio->v4l2_dev); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* vidioc_querycap - query device capabilities */ 1868c2ecf20Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv, 1878c2ecf20Sopenharmony_ci struct v4l2_capability *v) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct ma901radio_device *radio = video_drvdata(file); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci strscpy(v->driver, "radio-ma901", sizeof(v->driver)); 1928c2ecf20Sopenharmony_ci strscpy(v->card, "Masterkit MA901 USB FM Radio", sizeof(v->card)); 1938c2ecf20Sopenharmony_ci usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci/* vidioc_g_tuner - get tuner attributes */ 1988c2ecf20Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv, 1998c2ecf20Sopenharmony_ci struct v4l2_tuner *v) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct ma901radio_device *radio = video_drvdata(file); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (v->index > 0) 2048c2ecf20Sopenharmony_ci return -EINVAL; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci v->signal = 0; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* TODO: the same words like in _probe() goes here. 2098c2ecf20Sopenharmony_ci * When receiving of stats will be implemented then we can call 2108c2ecf20Sopenharmony_ci * ma901radio_get_stat(). 2118c2ecf20Sopenharmony_ci * retval = ma901radio_get_stat(radio, &is_stereo, &v->signal); 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci strscpy(v->name, "FM", sizeof(v->name)); 2158c2ecf20Sopenharmony_ci v->type = V4L2_TUNER_RADIO; 2168c2ecf20Sopenharmony_ci v->rangelow = FREQ_MIN * FREQ_MUL; 2178c2ecf20Sopenharmony_ci v->rangehigh = FREQ_MAX * FREQ_MUL; 2188c2ecf20Sopenharmony_ci v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; 2198c2ecf20Sopenharmony_ci /* v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; */ 2208c2ecf20Sopenharmony_ci v->audmode = radio->stereo ? 2218c2ecf20Sopenharmony_ci V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci/* vidioc_s_tuner - set tuner attributes */ 2268c2ecf20Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv, 2278c2ecf20Sopenharmony_ci const struct v4l2_tuner *v) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct ma901radio_device *radio = video_drvdata(file); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (v->index > 0) 2328c2ecf20Sopenharmony_ci return -EINVAL; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* mono/stereo selector */ 2358c2ecf20Sopenharmony_ci switch (v->audmode) { 2368c2ecf20Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 2378c2ecf20Sopenharmony_ci return ma901_set_stereo(radio, MA901_WANT_MONO); 2388c2ecf20Sopenharmony_ci default: 2398c2ecf20Sopenharmony_ci return ma901_set_stereo(radio, MA901_WANT_STEREO); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* vidioc_s_frequency - set tuner radio frequency */ 2448c2ecf20Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv, 2458c2ecf20Sopenharmony_ci const struct v4l2_frequency *f) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct ma901radio_device *radio = video_drvdata(file); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (f->tuner != 0) 2508c2ecf20Sopenharmony_ci return -EINVAL; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return ma901radio_set_freq(radio, clamp_t(unsigned, f->frequency, 2538c2ecf20Sopenharmony_ci FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/* vidioc_g_frequency - get tuner radio frequency */ 2578c2ecf20Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv, 2588c2ecf20Sopenharmony_ci struct v4l2_frequency *f) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci struct ma901radio_device *radio = video_drvdata(file); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (f->tuner != 0) 2638c2ecf20Sopenharmony_ci return -EINVAL; 2648c2ecf20Sopenharmony_ci f->frequency = radio->curfreq; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int usb_ma901radio_s_ctrl(struct v4l2_ctrl *ctrl) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct ma901radio_device *radio = 2728c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct ma901radio_device, hdl); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci switch (ctrl->id) { 2758c2ecf20Sopenharmony_ci case V4L2_CID_AUDIO_VOLUME: /* set volume */ 2768c2ecf20Sopenharmony_ci return ma901radio_set_volume(radio, (u16)ctrl->val); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return -EINVAL; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci/* TODO: Should we really need to implement suspend and resume functions? 2838c2ecf20Sopenharmony_ci * Radio has it's own memory and will continue playing if power is present 2848c2ecf20Sopenharmony_ci * on usb port and on resume it will start to play again based on freq, volume 2858c2ecf20Sopenharmony_ci * values in device memory. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_cistatic int usb_ma901radio_suspend(struct usb_interface *intf, pm_message_t message) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int usb_ma901radio_resume(struct usb_interface *intf) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops usb_ma901radio_ctrl_ops = { 2988c2ecf20Sopenharmony_ci .s_ctrl = usb_ma901radio_s_ctrl, 2998c2ecf20Sopenharmony_ci}; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* File system interface */ 3028c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations usb_ma901radio_fops = { 3038c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 3048c2ecf20Sopenharmony_ci .open = v4l2_fh_open, 3058c2ecf20Sopenharmony_ci .release = v4l2_fh_release, 3068c2ecf20Sopenharmony_ci .poll = v4l2_ctrl_poll, 3078c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 3088c2ecf20Sopenharmony_ci}; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops usb_ma901radio_ioctl_ops = { 3118c2ecf20Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 3128c2ecf20Sopenharmony_ci .vidioc_g_tuner = vidioc_g_tuner, 3138c2ecf20Sopenharmony_ci .vidioc_s_tuner = vidioc_s_tuner, 3148c2ecf20Sopenharmony_ci .vidioc_g_frequency = vidioc_g_frequency, 3158c2ecf20Sopenharmony_ci .vidioc_s_frequency = vidioc_s_frequency, 3168c2ecf20Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 3178c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 3188c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void usb_ma901radio_release(struct v4l2_device *v4l2_dev) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct ma901radio_device *radio = to_ma901radio_dev(v4l2_dev); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&radio->hdl); 3268c2ecf20Sopenharmony_ci v4l2_device_unregister(&radio->v4l2_dev); 3278c2ecf20Sopenharmony_ci kfree(radio->buffer); 3288c2ecf20Sopenharmony_ci kfree(radio); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/* check if the device is present and register with v4l and usb if it is */ 3328c2ecf20Sopenharmony_cistatic int usb_ma901radio_probe(struct usb_interface *intf, 3338c2ecf20Sopenharmony_ci const struct usb_device_id *id) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct usb_device *dev = interface_to_usbdev(intf); 3368c2ecf20Sopenharmony_ci struct ma901radio_device *radio; 3378c2ecf20Sopenharmony_ci int retval = 0; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* Masterkit MA901 usb radio has the same USB ID as many others 3408c2ecf20Sopenharmony_ci * Atmel V-USB devices. Let's make additional checks to be sure 3418c2ecf20Sopenharmony_ci * that this is our device. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (dev->product && dev->manufacturer && 3458c2ecf20Sopenharmony_ci (strncmp(dev->product, "MA901", 5) != 0 3468c2ecf20Sopenharmony_ci || strncmp(dev->manufacturer, "www.masterkit.ru", 16) != 0)) 3478c2ecf20Sopenharmony_ci return -ENODEV; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci radio = kzalloc(sizeof(struct ma901radio_device), GFP_KERNEL); 3508c2ecf20Sopenharmony_ci if (!radio) { 3518c2ecf20Sopenharmony_ci dev_err(&intf->dev, "kzalloc for ma901radio_device failed\n"); 3528c2ecf20Sopenharmony_ci retval = -ENOMEM; 3538c2ecf20Sopenharmony_ci goto err; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); 3578c2ecf20Sopenharmony_ci if (!radio->buffer) { 3588c2ecf20Sopenharmony_ci dev_err(&intf->dev, "kmalloc for radio->buffer failed\n"); 3598c2ecf20Sopenharmony_ci retval = -ENOMEM; 3608c2ecf20Sopenharmony_ci goto err_nobuf; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); 3648c2ecf20Sopenharmony_ci if (retval < 0) { 3658c2ecf20Sopenharmony_ci dev_err(&intf->dev, "couldn't register v4l2_device\n"); 3668c2ecf20Sopenharmony_ci goto err_v4l2; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(&radio->hdl, 1); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* TODO:It looks like this radio doesn't have mute/unmute control 3728c2ecf20Sopenharmony_ci * and windows program just emulate it using volume control. 3738c2ecf20Sopenharmony_ci * Let's plan to do the same in this driver. 3748c2ecf20Sopenharmony_ci * 3758c2ecf20Sopenharmony_ci * v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, 3768c2ecf20Sopenharmony_ci * V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, 3808c2ecf20Sopenharmony_ci V4L2_CID_AUDIO_VOLUME, MA901_VOLUME_MIN, 3818c2ecf20Sopenharmony_ci MA901_VOLUME_MAX, 1, MA901_VOLUME_MAX); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (radio->hdl.error) { 3848c2ecf20Sopenharmony_ci retval = radio->hdl.error; 3858c2ecf20Sopenharmony_ci dev_err(&intf->dev, "couldn't register control\n"); 3868c2ecf20Sopenharmony_ci goto err_ctrl; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci mutex_init(&radio->lock); 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci radio->v4l2_dev.ctrl_handler = &radio->hdl; 3918c2ecf20Sopenharmony_ci radio->v4l2_dev.release = usb_ma901radio_release; 3928c2ecf20Sopenharmony_ci strscpy(radio->vdev.name, radio->v4l2_dev.name, 3938c2ecf20Sopenharmony_ci sizeof(radio->vdev.name)); 3948c2ecf20Sopenharmony_ci radio->vdev.v4l2_dev = &radio->v4l2_dev; 3958c2ecf20Sopenharmony_ci radio->vdev.fops = &usb_ma901radio_fops; 3968c2ecf20Sopenharmony_ci radio->vdev.ioctl_ops = &usb_ma901radio_ioctl_ops; 3978c2ecf20Sopenharmony_ci radio->vdev.release = video_device_release_empty; 3988c2ecf20Sopenharmony_ci radio->vdev.lock = &radio->lock; 3998c2ecf20Sopenharmony_ci radio->vdev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci radio->usbdev = interface_to_usbdev(intf); 4028c2ecf20Sopenharmony_ci radio->intf = intf; 4038c2ecf20Sopenharmony_ci usb_set_intfdata(intf, &radio->v4l2_dev); 4048c2ecf20Sopenharmony_ci radio->curfreq = 95.21 * FREQ_MUL; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci video_set_drvdata(&radio->vdev, radio); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* TODO: we can get some statistics (freq, volume) from device 4098c2ecf20Sopenharmony_ci * but it's not implemented yet. After insertion in usb-port radio 4108c2ecf20Sopenharmony_ci * setups frequency and starts playing without any initialization. 4118c2ecf20Sopenharmony_ci * So we don't call usb_ma901radio_init/get_stat() here. 4128c2ecf20Sopenharmony_ci * retval = usb_ma901radio_init(radio); 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, 4168c2ecf20Sopenharmony_ci radio_nr); 4178c2ecf20Sopenharmony_ci if (retval < 0) { 4188c2ecf20Sopenharmony_ci dev_err(&intf->dev, "could not register video device\n"); 4198c2ecf20Sopenharmony_ci goto err_vdev; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return 0; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cierr_vdev: 4258c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&radio->hdl); 4268c2ecf20Sopenharmony_cierr_ctrl: 4278c2ecf20Sopenharmony_ci v4l2_device_unregister(&radio->v4l2_dev); 4288c2ecf20Sopenharmony_cierr_v4l2: 4298c2ecf20Sopenharmony_ci kfree(radio->buffer); 4308c2ecf20Sopenharmony_cierr_nobuf: 4318c2ecf20Sopenharmony_ci kfree(radio); 4328c2ecf20Sopenharmony_cierr: 4338c2ecf20Sopenharmony_ci return retval; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci/* USB Device ID List */ 4378c2ecf20Sopenharmony_cistatic const struct usb_device_id usb_ma901radio_device_table[] = { 4388c2ecf20Sopenharmony_ci { USB_DEVICE_AND_INTERFACE_INFO(USB_MA901_VENDOR, USB_MA901_PRODUCT, 4398c2ecf20Sopenharmony_ci USB_CLASS_HID, 0, 0) }, 4408c2ecf20Sopenharmony_ci { } /* Terminating entry */ 4418c2ecf20Sopenharmony_ci}; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usb_ma901radio_device_table); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci/* USB subsystem interface */ 4468c2ecf20Sopenharmony_cistatic struct usb_driver usb_ma901radio_driver = { 4478c2ecf20Sopenharmony_ci .name = MA901_DRIVER_NAME, 4488c2ecf20Sopenharmony_ci .probe = usb_ma901radio_probe, 4498c2ecf20Sopenharmony_ci .disconnect = usb_ma901radio_disconnect, 4508c2ecf20Sopenharmony_ci .suspend = usb_ma901radio_suspend, 4518c2ecf20Sopenharmony_ci .resume = usb_ma901radio_resume, 4528c2ecf20Sopenharmony_ci .reset_resume = usb_ma901radio_resume, 4538c2ecf20Sopenharmony_ci .id_table = usb_ma901radio_device_table, 4548c2ecf20Sopenharmony_ci}; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cimodule_usb_driver(usb_ma901radio_driver); 457