162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the MasterKit MA901 USB FM radio. This device plugs 462306a36Sopenharmony_ci * into the USB port and an analog audio input or headphones, so this thing 562306a36Sopenharmony_ci * only deals with initialization, frequency setting, volume. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2012 Alexey Klimov <klimov.linux@gmail.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/input.h> 1562306a36Sopenharmony_ci#include <linux/videodev2.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#include <linux/usb.h> 2162306a36Sopenharmony_ci#include <linux/mutex.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>" 2462306a36Sopenharmony_ci#define DRIVER_DESC "Masterkit MA901 USB FM radio driver" 2562306a36Sopenharmony_ci#define DRIVER_VERSION "0.0.1" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR); 2862306a36Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC); 2962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3062306a36Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define USB_MA901_VENDOR 0x16c0 3362306a36Sopenharmony_ci#define USB_MA901_PRODUCT 0x05df 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* dev_warn macro with driver name */ 3662306a36Sopenharmony_ci#define MA901_DRIVER_NAME "radio-ma901" 3762306a36Sopenharmony_ci#define ma901radio_dev_warn(dev, fmt, arg...) \ 3862306a36Sopenharmony_ci dev_warn(dev, MA901_DRIVER_NAME " - " fmt, ##arg) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define ma901radio_dev_err(dev, fmt, arg...) \ 4162306a36Sopenharmony_ci dev_err(dev, MA901_DRIVER_NAME " - " fmt, ##arg) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Probably USB_TIMEOUT should be modified in module parameter */ 4462306a36Sopenharmony_ci#define BUFFER_LENGTH 8 4562306a36Sopenharmony_ci#define USB_TIMEOUT 500 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci#define FREQ_MIN 87.5 4862306a36Sopenharmony_ci#define FREQ_MAX 108.0 4962306a36Sopenharmony_ci#define FREQ_MUL 16000 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define MA901_VOLUME_MAX 16 5262306a36Sopenharmony_ci#define MA901_VOLUME_MIN 0 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Commands that device should understand 5562306a36Sopenharmony_ci * List isn't full and will be updated with implementation of new functions 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#define MA901_RADIO_SET_FREQ 0x03 5862306a36Sopenharmony_ci#define MA901_RADIO_SET_VOLUME 0x04 5962306a36Sopenharmony_ci#define MA901_RADIO_SET_MONO_STEREO 0x05 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Comfortable defines for ma901radio_set_stereo */ 6262306a36Sopenharmony_ci#define MA901_WANT_STEREO 0x50 6362306a36Sopenharmony_ci#define MA901_WANT_MONO 0xd0 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* module parameter */ 6662306a36Sopenharmony_cistatic int radio_nr = -1; 6762306a36Sopenharmony_cimodule_param(radio_nr, int, 0); 6862306a36Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "Radio file number"); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Data for one (physical) device */ 7162306a36Sopenharmony_cistruct ma901radio_device { 7262306a36Sopenharmony_ci /* reference to USB and video device */ 7362306a36Sopenharmony_ci struct usb_device *usbdev; 7462306a36Sopenharmony_ci struct usb_interface *intf; 7562306a36Sopenharmony_ci struct video_device vdev; 7662306a36Sopenharmony_ci struct v4l2_device v4l2_dev; 7762306a36Sopenharmony_ci struct v4l2_ctrl_handler hdl; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci u8 *buffer; 8062306a36Sopenharmony_ci struct mutex lock; /* buffer locking */ 8162306a36Sopenharmony_ci int curfreq; 8262306a36Sopenharmony_ci u16 volume; 8362306a36Sopenharmony_ci int stereo; 8462306a36Sopenharmony_ci bool muted; 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic inline struct ma901radio_device *to_ma901radio_dev(struct v4l2_device *v4l2_dev) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci return container_of(v4l2_dev, struct ma901radio_device, v4l2_dev); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ 9362306a36Sopenharmony_cistatic int ma901radio_set_freq(struct ma901radio_device *radio, int freq) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci unsigned int freq_send = 0x300 + (freq >> 5) / 25; 9662306a36Sopenharmony_ci int retval; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci radio->buffer[0] = 0x0a; 9962306a36Sopenharmony_ci radio->buffer[1] = MA901_RADIO_SET_FREQ; 10062306a36Sopenharmony_ci radio->buffer[2] = ((freq_send >> 8) & 0xff) + 0x80; 10162306a36Sopenharmony_ci radio->buffer[3] = freq_send & 0xff; 10262306a36Sopenharmony_ci radio->buffer[4] = 0x00; 10362306a36Sopenharmony_ci radio->buffer[5] = 0x00; 10462306a36Sopenharmony_ci radio->buffer[6] = 0x00; 10562306a36Sopenharmony_ci radio->buffer[7] = 0x00; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), 10862306a36Sopenharmony_ci 9, 0x21, 0x0300, 0, 10962306a36Sopenharmony_ci radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); 11062306a36Sopenharmony_ci if (retval < 0) 11162306a36Sopenharmony_ci return retval; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci radio->curfreq = freq; 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int ma901radio_set_volume(struct ma901radio_device *radio, u16 vol_to_set) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci int retval; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci radio->buffer[0] = 0x0a; 12262306a36Sopenharmony_ci radio->buffer[1] = MA901_RADIO_SET_VOLUME; 12362306a36Sopenharmony_ci radio->buffer[2] = 0xc2; 12462306a36Sopenharmony_ci radio->buffer[3] = vol_to_set + 0x20; 12562306a36Sopenharmony_ci radio->buffer[4] = 0x00; 12662306a36Sopenharmony_ci radio->buffer[5] = 0x00; 12762306a36Sopenharmony_ci radio->buffer[6] = 0x00; 12862306a36Sopenharmony_ci radio->buffer[7] = 0x00; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), 13162306a36Sopenharmony_ci 9, 0x21, 0x0300, 0, 13262306a36Sopenharmony_ci radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); 13362306a36Sopenharmony_ci if (retval < 0) 13462306a36Sopenharmony_ci return retval; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci radio->volume = vol_to_set; 13762306a36Sopenharmony_ci return retval; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int ma901_set_stereo(struct ma901radio_device *radio, u8 stereo) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci int retval; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci radio->buffer[0] = 0x0a; 14562306a36Sopenharmony_ci radio->buffer[1] = MA901_RADIO_SET_MONO_STEREO; 14662306a36Sopenharmony_ci radio->buffer[2] = stereo; 14762306a36Sopenharmony_ci radio->buffer[3] = 0x00; 14862306a36Sopenharmony_ci radio->buffer[4] = 0x00; 14962306a36Sopenharmony_ci radio->buffer[5] = 0x00; 15062306a36Sopenharmony_ci radio->buffer[6] = 0x00; 15162306a36Sopenharmony_ci radio->buffer[7] = 0x00; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci retval = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0), 15462306a36Sopenharmony_ci 9, 0x21, 0x0300, 0, 15562306a36Sopenharmony_ci radio->buffer, BUFFER_LENGTH, USB_TIMEOUT); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (retval < 0) 15862306a36Sopenharmony_ci return retval; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (stereo == MA901_WANT_STEREO) 16162306a36Sopenharmony_ci radio->stereo = V4L2_TUNER_MODE_STEREO; 16262306a36Sopenharmony_ci else 16362306a36Sopenharmony_ci radio->stereo = V4L2_TUNER_MODE_MONO; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return retval; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* Handle unplugging the device. 16962306a36Sopenharmony_ci * We call video_unregister_device in any case. 17062306a36Sopenharmony_ci * The last function called in this procedure is 17162306a36Sopenharmony_ci * usb_ma901radio_device_release. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic void usb_ma901radio_disconnect(struct usb_interface *intf) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct ma901radio_device *radio = to_ma901radio_dev(usb_get_intfdata(intf)); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci mutex_lock(&radio->lock); 17862306a36Sopenharmony_ci video_unregister_device(&radio->vdev); 17962306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 18062306a36Sopenharmony_ci v4l2_device_disconnect(&radio->v4l2_dev); 18162306a36Sopenharmony_ci mutex_unlock(&radio->lock); 18262306a36Sopenharmony_ci v4l2_device_put(&radio->v4l2_dev); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* vidioc_querycap - query device capabilities */ 18662306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void *priv, 18762306a36Sopenharmony_ci struct v4l2_capability *v) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct ma901radio_device *radio = video_drvdata(file); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci strscpy(v->driver, "radio-ma901", sizeof(v->driver)); 19262306a36Sopenharmony_ci strscpy(v->card, "Masterkit MA901 USB FM Radio", sizeof(v->card)); 19362306a36Sopenharmony_ci usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* vidioc_g_tuner - get tuner attributes */ 19862306a36Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv, 19962306a36Sopenharmony_ci struct v4l2_tuner *v) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct ma901radio_device *radio = video_drvdata(file); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (v->index > 0) 20462306a36Sopenharmony_ci return -EINVAL; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci v->signal = 0; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* TODO: the same words like in _probe() goes here. 20962306a36Sopenharmony_ci * When receiving of stats will be implemented then we can call 21062306a36Sopenharmony_ci * ma901radio_get_stat(). 21162306a36Sopenharmony_ci * retval = ma901radio_get_stat(radio, &is_stereo, &v->signal); 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci strscpy(v->name, "FM", sizeof(v->name)); 21562306a36Sopenharmony_ci v->type = V4L2_TUNER_RADIO; 21662306a36Sopenharmony_ci v->rangelow = FREQ_MIN * FREQ_MUL; 21762306a36Sopenharmony_ci v->rangehigh = FREQ_MAX * FREQ_MUL; 21862306a36Sopenharmony_ci v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; 21962306a36Sopenharmony_ci /* v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; */ 22062306a36Sopenharmony_ci v->audmode = radio->stereo ? 22162306a36Sopenharmony_ci V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* vidioc_s_tuner - set tuner attributes */ 22662306a36Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv, 22762306a36Sopenharmony_ci const struct v4l2_tuner *v) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct ma901radio_device *radio = video_drvdata(file); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci if (v->index > 0) 23262306a36Sopenharmony_ci return -EINVAL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* mono/stereo selector */ 23562306a36Sopenharmony_ci switch (v->audmode) { 23662306a36Sopenharmony_ci case V4L2_TUNER_MODE_MONO: 23762306a36Sopenharmony_ci return ma901_set_stereo(radio, MA901_WANT_MONO); 23862306a36Sopenharmony_ci default: 23962306a36Sopenharmony_ci return ma901_set_stereo(radio, MA901_WANT_STEREO); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* vidioc_s_frequency - set tuner radio frequency */ 24462306a36Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv, 24562306a36Sopenharmony_ci const struct v4l2_frequency *f) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct ma901radio_device *radio = video_drvdata(file); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (f->tuner != 0) 25062306a36Sopenharmony_ci return -EINVAL; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return ma901radio_set_freq(radio, clamp_t(unsigned, f->frequency, 25362306a36Sopenharmony_ci FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci/* vidioc_g_frequency - get tuner radio frequency */ 25762306a36Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv, 25862306a36Sopenharmony_ci struct v4l2_frequency *f) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct ma901radio_device *radio = video_drvdata(file); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (f->tuner != 0) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci f->frequency = radio->curfreq; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci return 0; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int usb_ma901radio_s_ctrl(struct v4l2_ctrl *ctrl) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci struct ma901radio_device *radio = 27262306a36Sopenharmony_ci container_of(ctrl->handler, struct ma901radio_device, hdl); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci switch (ctrl->id) { 27562306a36Sopenharmony_ci case V4L2_CID_AUDIO_VOLUME: /* set volume */ 27662306a36Sopenharmony_ci return ma901radio_set_volume(radio, (u16)ctrl->val); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci/* TODO: Should we really need to implement suspend and resume functions? 28362306a36Sopenharmony_ci * Radio has it's own memory and will continue playing if power is present 28462306a36Sopenharmony_ci * on usb port and on resume it will start to play again based on freq, volume 28562306a36Sopenharmony_ci * values in device memory. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_cistatic int usb_ma901radio_suspend(struct usb_interface *intf, pm_message_t message) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int usb_ma901radio_resume(struct usb_interface *intf) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops usb_ma901radio_ctrl_ops = { 29862306a36Sopenharmony_ci .s_ctrl = usb_ma901radio_s_ctrl, 29962306a36Sopenharmony_ci}; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* File system interface */ 30262306a36Sopenharmony_cistatic const struct v4l2_file_operations usb_ma901radio_fops = { 30362306a36Sopenharmony_ci .owner = THIS_MODULE, 30462306a36Sopenharmony_ci .open = v4l2_fh_open, 30562306a36Sopenharmony_ci .release = v4l2_fh_release, 30662306a36Sopenharmony_ci .poll = v4l2_ctrl_poll, 30762306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 30862306a36Sopenharmony_ci}; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops usb_ma901radio_ioctl_ops = { 31162306a36Sopenharmony_ci .vidioc_querycap = vidioc_querycap, 31262306a36Sopenharmony_ci .vidioc_g_tuner = vidioc_g_tuner, 31362306a36Sopenharmony_ci .vidioc_s_tuner = vidioc_s_tuner, 31462306a36Sopenharmony_ci .vidioc_g_frequency = vidioc_g_frequency, 31562306a36Sopenharmony_ci .vidioc_s_frequency = vidioc_s_frequency, 31662306a36Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 31762306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 31862306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 31962306a36Sopenharmony_ci}; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void usb_ma901radio_release(struct v4l2_device *v4l2_dev) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct ma901radio_device *radio = to_ma901radio_dev(v4l2_dev); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci v4l2_ctrl_handler_free(&radio->hdl); 32662306a36Sopenharmony_ci v4l2_device_unregister(&radio->v4l2_dev); 32762306a36Sopenharmony_ci kfree(radio->buffer); 32862306a36Sopenharmony_ci kfree(radio); 32962306a36Sopenharmony_ci} 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci/* check if the device is present and register with v4l and usb if it is */ 33262306a36Sopenharmony_cistatic int usb_ma901radio_probe(struct usb_interface *intf, 33362306a36Sopenharmony_ci const struct usb_device_id *id) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct usb_device *dev = interface_to_usbdev(intf); 33662306a36Sopenharmony_ci struct ma901radio_device *radio; 33762306a36Sopenharmony_ci int retval = 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* Masterkit MA901 usb radio has the same USB ID as many others 34062306a36Sopenharmony_ci * Atmel V-USB devices. Let's make additional checks to be sure 34162306a36Sopenharmony_ci * that this is our device. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (dev->product && dev->manufacturer && 34562306a36Sopenharmony_ci (strncmp(dev->product, "MA901", 5) != 0 34662306a36Sopenharmony_ci || strncmp(dev->manufacturer, "www.masterkit.ru", 16) != 0)) 34762306a36Sopenharmony_ci return -ENODEV; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci radio = kzalloc(sizeof(struct ma901radio_device), GFP_KERNEL); 35062306a36Sopenharmony_ci if (!radio) { 35162306a36Sopenharmony_ci dev_err(&intf->dev, "kzalloc for ma901radio_device failed\n"); 35262306a36Sopenharmony_ci retval = -ENOMEM; 35362306a36Sopenharmony_ci goto err; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); 35762306a36Sopenharmony_ci if (!radio->buffer) { 35862306a36Sopenharmony_ci dev_err(&intf->dev, "kmalloc for radio->buffer failed\n"); 35962306a36Sopenharmony_ci retval = -ENOMEM; 36062306a36Sopenharmony_ci goto err_nobuf; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); 36462306a36Sopenharmony_ci if (retval < 0) { 36562306a36Sopenharmony_ci dev_err(&intf->dev, "couldn't register v4l2_device\n"); 36662306a36Sopenharmony_ci goto err_v4l2; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci v4l2_ctrl_handler_init(&radio->hdl, 1); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* TODO:It looks like this radio doesn't have mute/unmute control 37262306a36Sopenharmony_ci * and windows program just emulate it using volume control. 37362306a36Sopenharmony_ci * Let's plan to do the same in this driver. 37462306a36Sopenharmony_ci * 37562306a36Sopenharmony_ci * v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, 37662306a36Sopenharmony_ci * V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); 37762306a36Sopenharmony_ci */ 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci v4l2_ctrl_new_std(&radio->hdl, &usb_ma901radio_ctrl_ops, 38062306a36Sopenharmony_ci V4L2_CID_AUDIO_VOLUME, MA901_VOLUME_MIN, 38162306a36Sopenharmony_ci MA901_VOLUME_MAX, 1, MA901_VOLUME_MAX); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (radio->hdl.error) { 38462306a36Sopenharmony_ci retval = radio->hdl.error; 38562306a36Sopenharmony_ci dev_err(&intf->dev, "couldn't register control\n"); 38662306a36Sopenharmony_ci goto err_ctrl; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci mutex_init(&radio->lock); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci radio->v4l2_dev.ctrl_handler = &radio->hdl; 39162306a36Sopenharmony_ci radio->v4l2_dev.release = usb_ma901radio_release; 39262306a36Sopenharmony_ci strscpy(radio->vdev.name, radio->v4l2_dev.name, 39362306a36Sopenharmony_ci sizeof(radio->vdev.name)); 39462306a36Sopenharmony_ci radio->vdev.v4l2_dev = &radio->v4l2_dev; 39562306a36Sopenharmony_ci radio->vdev.fops = &usb_ma901radio_fops; 39662306a36Sopenharmony_ci radio->vdev.ioctl_ops = &usb_ma901radio_ioctl_ops; 39762306a36Sopenharmony_ci radio->vdev.release = video_device_release_empty; 39862306a36Sopenharmony_ci radio->vdev.lock = &radio->lock; 39962306a36Sopenharmony_ci radio->vdev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci radio->usbdev = interface_to_usbdev(intf); 40262306a36Sopenharmony_ci radio->intf = intf; 40362306a36Sopenharmony_ci usb_set_intfdata(intf, &radio->v4l2_dev); 40462306a36Sopenharmony_ci radio->curfreq = 95.21 * FREQ_MUL; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci video_set_drvdata(&radio->vdev, radio); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* TODO: we can get some statistics (freq, volume) from device 40962306a36Sopenharmony_ci * but it's not implemented yet. After insertion in usb-port radio 41062306a36Sopenharmony_ci * setups frequency and starts playing without any initialization. 41162306a36Sopenharmony_ci * So we don't call usb_ma901radio_init/get_stat() here. 41262306a36Sopenharmony_ci * retval = usb_ma901radio_init(radio); 41362306a36Sopenharmony_ci */ 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, 41662306a36Sopenharmony_ci radio_nr); 41762306a36Sopenharmony_ci if (retval < 0) { 41862306a36Sopenharmony_ci dev_err(&intf->dev, "could not register video device\n"); 41962306a36Sopenharmony_ci goto err_vdev; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return 0; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cierr_vdev: 42562306a36Sopenharmony_ci v4l2_ctrl_handler_free(&radio->hdl); 42662306a36Sopenharmony_cierr_ctrl: 42762306a36Sopenharmony_ci v4l2_device_unregister(&radio->v4l2_dev); 42862306a36Sopenharmony_cierr_v4l2: 42962306a36Sopenharmony_ci kfree(radio->buffer); 43062306a36Sopenharmony_cierr_nobuf: 43162306a36Sopenharmony_ci kfree(radio); 43262306a36Sopenharmony_cierr: 43362306a36Sopenharmony_ci return retval; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/* USB Device ID List */ 43762306a36Sopenharmony_cistatic const struct usb_device_id usb_ma901radio_device_table[] = { 43862306a36Sopenharmony_ci { USB_DEVICE_AND_INTERFACE_INFO(USB_MA901_VENDOR, USB_MA901_PRODUCT, 43962306a36Sopenharmony_ci USB_CLASS_HID, 0, 0) }, 44062306a36Sopenharmony_ci { } /* Terminating entry */ 44162306a36Sopenharmony_ci}; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usb_ma901radio_device_table); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/* USB subsystem interface */ 44662306a36Sopenharmony_cistatic struct usb_driver usb_ma901radio_driver = { 44762306a36Sopenharmony_ci .name = MA901_DRIVER_NAME, 44862306a36Sopenharmony_ci .probe = usb_ma901radio_probe, 44962306a36Sopenharmony_ci .disconnect = usb_ma901radio_disconnect, 45062306a36Sopenharmony_ci .suspend = usb_ma901radio_suspend, 45162306a36Sopenharmony_ci .resume = usb_ma901radio_resume, 45262306a36Sopenharmony_ci .reset_resume = usb_ma901radio_resume, 45362306a36Sopenharmony_ci .id_table = usb_ma901radio_device_table, 45462306a36Sopenharmony_ci}; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cimodule_usb_driver(usb_ma901radio_driver); 457