root/drivers/media/radio/radio-raremono.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. to_raremono_dev
  2. raremono_cmd_main
  3. usb_raremono_disconnect
  4. vidioc_querycap
  5. vidioc_enum_freq_bands
  6. vidioc_g_tuner
  7. vidioc_s_tuner
  8. vidioc_s_frequency
  9. vidioc_g_frequency
  10. raremono_device_release
  11. usb_raremono_probe

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * Copyright 2013 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
   4  */
   5 
   6 #include <linux/kernel.h>
   7 #include <linux/module.h>
   8 #include <linux/init.h>
   9 #include <linux/slab.h>
  10 #include <linux/input.h>
  11 #include <linux/usb.h>
  12 #include <linux/hid.h>
  13 #include <linux/mutex.h>
  14 #include <linux/videodev2.h>
  15 #include <asm/unaligned.h>
  16 #include <media/v4l2-device.h>
  17 #include <media/v4l2-ioctl.h>
  18 #include <media/v4l2-ctrls.h>
  19 #include <media/v4l2-event.h>
  20 
  21 /*
  22  * 'Thanko's Raremono' is a Japanese si4734-based AM/FM/SW USB receiver:
  23  *
  24  * http://www.raremono.jp/product/484.html/
  25  *
  26  * The USB protocol has been reversed engineered using wireshark, initially
  27  * by Dinesh Ram <dinesh.ram@cern.ch> and finished by Hans Verkuil
  28  * <hverkuil@xs4all.nl>.
  29  *
  30  * Sadly the firmware used in this product hides lots of goodies since the
  31  * si4734 has more features than are supported by the firmware. Oh well...
  32  */
  33 
  34 /* driver and module definitions */
  35 MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
  36 MODULE_DESCRIPTION("Thanko's Raremono AM/FM/SW Receiver USB driver");
  37 MODULE_LICENSE("GPL v2");
  38 
  39 /*
  40  * The Device announces itself as Cygnal Integrated Products, Inc.
  41  *
  42  * The vendor and product IDs (and in fact all other lsusb information as
  43  * well) are identical to the si470x Silicon Labs USB FM Radio Reference
  44  * Design board, even though this card has a si4734 device. Clearly the
  45  * designer of this product never bothered to change the USB IDs.
  46  */
  47 
  48 /* USB Device ID List */
  49 static const struct usb_device_id usb_raremono_device_table[] = {
  50         {USB_DEVICE_AND_INTERFACE_INFO(0x10c4, 0x818a, USB_CLASS_HID, 0, 0) },
  51         { }                                             /* Terminating entry */
  52 };
  53 
  54 MODULE_DEVICE_TABLE(usb, usb_raremono_device_table);
  55 
  56 #define BUFFER_LENGTH 64
  57 
  58 /* Timeout is set to a high value, could probably be reduced. Need more tests */
  59 #define USB_TIMEOUT 10000
  60 
  61 /* Frequency limits in KHz */
  62 #define FM_FREQ_RANGE_LOW       64000
  63 #define FM_FREQ_RANGE_HIGH      108000
  64 
  65 #define AM_FREQ_RANGE_LOW       520
  66 #define AM_FREQ_RANGE_HIGH      1710
  67 
  68 #define SW_FREQ_RANGE_LOW       2300
  69 #define SW_FREQ_RANGE_HIGH      26100
  70 
  71 enum { BAND_FM, BAND_AM, BAND_SW };
  72 
  73 static const struct v4l2_frequency_band bands[] = {
  74         /* Band FM */
  75         {
  76                 .type = V4L2_TUNER_RADIO,
  77                 .index = 0,
  78                 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
  79                               V4L2_TUNER_CAP_FREQ_BANDS,
  80                 .rangelow   = FM_FREQ_RANGE_LOW * 16,
  81                 .rangehigh  = FM_FREQ_RANGE_HIGH * 16,
  82                 .modulation = V4L2_BAND_MODULATION_FM,
  83         },
  84         /* Band AM */
  85         {
  86                 .type = V4L2_TUNER_RADIO,
  87                 .index = 1,
  88                 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
  89                 .rangelow   = AM_FREQ_RANGE_LOW * 16,
  90                 .rangehigh  = AM_FREQ_RANGE_HIGH * 16,
  91                 .modulation = V4L2_BAND_MODULATION_AM,
  92         },
  93         /* Band SW */
  94         {
  95                 .type = V4L2_TUNER_RADIO,
  96                 .index = 2,
  97                 .capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_FREQ_BANDS,
  98                 .rangelow   = SW_FREQ_RANGE_LOW * 16,
  99                 .rangehigh  = SW_FREQ_RANGE_HIGH * 16,
 100                 .modulation = V4L2_BAND_MODULATION_AM,
 101         },
 102 };
 103 
 104 struct raremono_device {
 105         struct usb_device *usbdev;
 106         struct usb_interface *intf;
 107         struct video_device vdev;
 108         struct v4l2_device v4l2_dev;
 109         struct mutex lock;
 110 
 111         u8 *buffer;
 112         u32 band;
 113         unsigned curfreq;
 114 };
 115 
 116 static inline struct raremono_device *to_raremono_dev(struct v4l2_device *v4l2_dev)
 117 {
 118         return container_of(v4l2_dev, struct raremono_device, v4l2_dev);
 119 }
 120 
 121 /* Set frequency. */
 122 static int raremono_cmd_main(struct raremono_device *radio, unsigned band, unsigned freq)
 123 {
 124         unsigned band_offset;
 125         int ret;
 126 
 127         switch (band) {
 128         case BAND_FM:
 129                 band_offset = 1;
 130                 freq /= 10;
 131                 break;
 132         case BAND_AM:
 133                 band_offset = 0;
 134                 break;
 135         default:
 136                 band_offset = 2;
 137                 break;
 138         }
 139         radio->buffer[0] = 0x04 + band_offset;
 140         radio->buffer[1] = freq >> 8;
 141         radio->buffer[2] = freq & 0xff;
 142 
 143         ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
 144                         HID_REQ_SET_REPORT,
 145                         USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
 146                         0x0300 + radio->buffer[0], 2,
 147                         radio->buffer, 3, USB_TIMEOUT);
 148 
 149         if (ret < 0) {
 150                 dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret);
 151                 return ret;
 152         }
 153         radio->curfreq = (band == BAND_FM) ? freq * 10 : freq;
 154         return 0;
 155 }
 156 
 157 /* Handle unplugging the device.
 158  * We call video_unregister_device in any case.
 159  * The last function called in this procedure is
 160  * usb_raremono_device_release.
 161  */
 162 static void usb_raremono_disconnect(struct usb_interface *intf)
 163 {
 164         struct raremono_device *radio = to_raremono_dev(usb_get_intfdata(intf));
 165 
 166         dev_info(&intf->dev, "Thanko's Raremono disconnected\n");
 167 
 168         mutex_lock(&radio->lock);
 169         usb_set_intfdata(intf, NULL);
 170         video_unregister_device(&radio->vdev);
 171         v4l2_device_disconnect(&radio->v4l2_dev);
 172         mutex_unlock(&radio->lock);
 173         v4l2_device_put(&radio->v4l2_dev);
 174 }
 175 
 176 /*
 177  * Linux Video interface
 178  */
 179 static int vidioc_querycap(struct file *file, void *priv,
 180                                         struct v4l2_capability *v)
 181 {
 182         struct raremono_device *radio = video_drvdata(file);
 183 
 184         strscpy(v->driver, "radio-raremono", sizeof(v->driver));
 185         strscpy(v->card, "Thanko's Raremono", sizeof(v->card));
 186         usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
 187         return 0;
 188 }
 189 
 190 static int vidioc_enum_freq_bands(struct file *file, void *priv,
 191                 struct v4l2_frequency_band *band)
 192 {
 193         if (band->tuner != 0)
 194                 return -EINVAL;
 195 
 196         if (band->index >= ARRAY_SIZE(bands))
 197                 return -EINVAL;
 198 
 199         *band = bands[band->index];
 200 
 201         return 0;
 202 }
 203 
 204 static int vidioc_g_tuner(struct file *file, void *priv,
 205                 struct v4l2_tuner *v)
 206 {
 207         struct raremono_device *radio = video_drvdata(file);
 208         int ret;
 209 
 210         if (v->index > 0)
 211                 return -EINVAL;
 212 
 213         strscpy(v->name, "AM/FM/SW", sizeof(v->name));
 214         v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
 215                 V4L2_TUNER_CAP_FREQ_BANDS;
 216         v->rangelow = AM_FREQ_RANGE_LOW * 16;
 217         v->rangehigh = FM_FREQ_RANGE_HIGH * 16;
 218         v->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
 219         v->audmode = (radio->curfreq < FM_FREQ_RANGE_LOW) ?
 220                 V4L2_TUNER_MODE_MONO : V4L2_TUNER_MODE_STEREO;
 221         memset(radio->buffer, 1, BUFFER_LENGTH);
 222         ret = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
 223                         1, 0xa1, 0x030d, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
 224 
 225         if (ret < 0) {
 226                 dev_warn(radio->v4l2_dev.dev, "%s failed (%d)\n", __func__, ret);
 227                 return ret;
 228         }
 229         v->signal = ((radio->buffer[1] & 0xf) << 8 | radio->buffer[2]) << 4;
 230         return 0;
 231 }
 232 
 233 static int vidioc_s_tuner(struct file *file, void *priv,
 234                                         const struct v4l2_tuner *v)
 235 {
 236         return v->index ? -EINVAL : 0;
 237 }
 238 
 239 static int vidioc_s_frequency(struct file *file, void *priv,
 240                                 const struct v4l2_frequency *f)
 241 {
 242         struct raremono_device *radio = video_drvdata(file);
 243         u32 freq;
 244         unsigned band;
 245 
 246         if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
 247                 return -EINVAL;
 248 
 249         if (f->frequency >= (FM_FREQ_RANGE_LOW + SW_FREQ_RANGE_HIGH) * 8)
 250                 band = BAND_FM;
 251         else if (f->frequency <= (AM_FREQ_RANGE_HIGH + SW_FREQ_RANGE_LOW) * 8)
 252                 band = BAND_AM;
 253         else
 254                 band = BAND_SW;
 255 
 256         freq = clamp_t(u32, f->frequency, bands[band].rangelow, bands[band].rangehigh);
 257         return raremono_cmd_main(radio, band, freq / 16);
 258 }
 259 
 260 static int vidioc_g_frequency(struct file *file, void *priv,
 261                                 struct v4l2_frequency *f)
 262 {
 263         struct raremono_device *radio = video_drvdata(file);
 264 
 265         if (f->tuner != 0)
 266                 return -EINVAL;
 267         f->type = V4L2_TUNER_RADIO;
 268         f->frequency = radio->curfreq * 16;
 269         return 0;
 270 }
 271 
 272 static void raremono_device_release(struct v4l2_device *v4l2_dev)
 273 {
 274         struct raremono_device *radio = to_raremono_dev(v4l2_dev);
 275 
 276         kfree(radio->buffer);
 277         kfree(radio);
 278 }
 279 
 280 /* File system interface */
 281 static const struct v4l2_file_operations usb_raremono_fops = {
 282         .owner          = THIS_MODULE,
 283         .open           = v4l2_fh_open,
 284         .release        = v4l2_fh_release,
 285         .unlocked_ioctl = video_ioctl2,
 286 };
 287 
 288 static const struct v4l2_ioctl_ops usb_raremono_ioctl_ops = {
 289         .vidioc_querycap = vidioc_querycap,
 290         .vidioc_g_tuner = vidioc_g_tuner,
 291         .vidioc_s_tuner = vidioc_s_tuner,
 292         .vidioc_g_frequency = vidioc_g_frequency,
 293         .vidioc_s_frequency = vidioc_s_frequency,
 294         .vidioc_enum_freq_bands = vidioc_enum_freq_bands,
 295 };
 296 
 297 /* check if the device is present and register with v4l and usb if it is */
 298 static int usb_raremono_probe(struct usb_interface *intf,
 299                                 const struct usb_device_id *id)
 300 {
 301         struct raremono_device *radio;
 302         int retval = 0;
 303 
 304         radio = kzalloc(sizeof(*radio), GFP_KERNEL);
 305         if (!radio)
 306                 return -ENOMEM;
 307         radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
 308         if (!radio->buffer) {
 309                 kfree(radio);
 310                 return -ENOMEM;
 311         }
 312 
 313         radio->usbdev = interface_to_usbdev(intf);
 314         radio->intf = intf;
 315 
 316         /*
 317          * This device uses the same USB IDs as the si470x SiLabs reference
 318          * design. So do an additional check: attempt to read the device ID
 319          * from the si470x: the lower 12 bits are 0x0242 for the si470x. The
 320          * Raremono always returns 0x0800 (the meaning of that is unknown, but
 321          * at least it works).
 322          *
 323          * We use this check to determine which device we are dealing with.
 324          */
 325         msleep(20);
 326         retval = usb_control_msg(radio->usbdev,
 327                 usb_rcvctrlpipe(radio->usbdev, 0),
 328                 HID_REQ_GET_REPORT,
 329                 USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
 330                 1, 2,
 331                 radio->buffer, 3, 500);
 332         if (retval != 3 ||
 333             (get_unaligned_be16(&radio->buffer[1]) & 0xfff) == 0x0242) {
 334                 dev_info(&intf->dev, "this is not Thanko's Raremono.\n");
 335                 retval = -ENODEV;
 336                 goto free_mem;
 337         }
 338 
 339         dev_info(&intf->dev, "Thanko's Raremono connected: (%04X:%04X)\n",
 340                         id->idVendor, id->idProduct);
 341 
 342         retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
 343         if (retval < 0) {
 344                 dev_err(&intf->dev, "couldn't register v4l2_device\n");
 345                 goto free_mem;
 346         }
 347 
 348         mutex_init(&radio->lock);
 349 
 350         strscpy(radio->vdev.name, radio->v4l2_dev.name,
 351                 sizeof(radio->vdev.name));
 352         radio->vdev.v4l2_dev = &radio->v4l2_dev;
 353         radio->vdev.fops = &usb_raremono_fops;
 354         radio->vdev.ioctl_ops = &usb_raremono_ioctl_ops;
 355         radio->vdev.lock = &radio->lock;
 356         radio->vdev.release = video_device_release_empty;
 357         radio->vdev.device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 358         radio->v4l2_dev.release = raremono_device_release;
 359 
 360         usb_set_intfdata(intf, &radio->v4l2_dev);
 361 
 362         video_set_drvdata(&radio->vdev, radio);
 363 
 364         raremono_cmd_main(radio, BAND_FM, 95160);
 365 
 366         retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
 367         if (retval == 0) {
 368                 dev_info(&intf->dev, "V4L2 device registered as %s\n",
 369                                 video_device_node_name(&radio->vdev));
 370                 return 0;
 371         }
 372         dev_err(&intf->dev, "could not register video device\n");
 373         v4l2_device_unregister(&radio->v4l2_dev);
 374 
 375 free_mem:
 376         kfree(radio->buffer);
 377         kfree(radio);
 378         return retval;
 379 }
 380 
 381 /* USB subsystem interface */
 382 static struct usb_driver usb_raremono_driver = {
 383         .name                   = "radio-raremono",
 384         .probe                  = usb_raremono_probe,
 385         .disconnect             = usb_raremono_disconnect,
 386         .id_table               = usb_raremono_device_table,
 387 };
 388 
 389 module_usb_driver(usb_raremono_driver);

/* [<][>][^][v][top][bottom][index][help] */