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