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

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

DEFINITIONS

This source file includes following definitions.
  1. to_keene_dev
  2. keene_cmd_main
  3. keene_cmd_set
  4. usb_keene_disconnect
  5. usb_keene_suspend
  6. usb_keene_resume
  7. vidioc_querycap
  8. vidioc_g_modulator
  9. vidioc_s_modulator
  10. vidioc_s_frequency
  11. vidioc_g_frequency
  12. keene_s_ctrl
  13. usb_keene_video_device_release
  14. usb_keene_probe

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Copyright (c) 2012 Hans Verkuil <hverkuil@xs4all.nl>
   4  */
   5 
   6 /* kernel includes */
   7 #include <linux/kernel.h>
   8 #include <linux/module.h>
   9 #include <linux/init.h>
  10 #include <linux/slab.h>
  11 #include <linux/input.h>
  12 #include <linux/videodev2.h>
  13 #include <media/v4l2-device.h>
  14 #include <media/v4l2-ioctl.h>
  15 #include <media/v4l2-ctrls.h>
  16 #include <media/v4l2-event.h>
  17 #include <linux/usb.h>
  18 #include <linux/mutex.h>
  19 
  20 /* driver and module definitions */
  21 MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
  22 MODULE_DESCRIPTION("Keene FM Transmitter driver");
  23 MODULE_LICENSE("GPL");
  24 
  25 /* Actually, it advertises itself as a Logitech */
  26 #define USB_KEENE_VENDOR 0x046d
  27 #define USB_KEENE_PRODUCT 0x0a0e
  28 
  29 /* Probably USB_TIMEOUT should be modified in module parameter */
  30 #define BUFFER_LENGTH 8
  31 #define USB_TIMEOUT 500
  32 
  33 /* Frequency limits in MHz */
  34 #define FREQ_MIN  76U
  35 #define FREQ_MAX 108U
  36 #define FREQ_MUL 16000U
  37 
  38 /* USB Device ID List */
  39 static const struct usb_device_id usb_keene_device_table[] = {
  40         {USB_DEVICE_AND_INTERFACE_INFO(USB_KEENE_VENDOR, USB_KEENE_PRODUCT,
  41                                                         USB_CLASS_HID, 0, 0) },
  42         { }                                             /* Terminating entry */
  43 };
  44 
  45 MODULE_DEVICE_TABLE(usb, usb_keene_device_table);
  46 
  47 struct keene_device {
  48         struct usb_device *usbdev;
  49         struct usb_interface *intf;
  50         struct video_device vdev;
  51         struct v4l2_device v4l2_dev;
  52         struct v4l2_ctrl_handler hdl;
  53         struct mutex lock;
  54 
  55         u8 *buffer;
  56         unsigned curfreq;
  57         u8 tx;
  58         u8 pa;
  59         bool stereo;
  60         bool muted;
  61         bool preemph_75_us;
  62 };
  63 
  64 static inline struct keene_device *to_keene_dev(struct v4l2_device *v4l2_dev)
  65 {
  66         return container_of(v4l2_dev, struct keene_device, v4l2_dev);
  67 }
  68 
  69 /* Set frequency (if non-0), PA, mute and turn on/off the FM transmitter. */
  70 static int keene_cmd_main(struct keene_device *radio, unsigned freq, bool play)
  71 {
  72         unsigned short freq_send = freq ? (freq - 76 * 16000) / 800 : 0;
  73         int ret;
  74 
  75         radio->buffer[0] = 0x00;
  76         radio->buffer[1] = 0x50;
  77         radio->buffer[2] = (freq_send >> 8) & 0xff;
  78         radio->buffer[3] = freq_send & 0xff;
  79         radio->buffer[4] = radio->pa;
  80         /* If bit 4 is set, then tune to the frequency.
  81            If bit 3 is set, then unmute; if bit 2 is set, then mute.
  82            If bit 1 is set, then enter idle mode; if bit 0 is set,
  83            then enter transmit mode.
  84          */
  85         radio->buffer[5] = (radio->muted ? 4 : 8) | (play ? 1 : 2) |
  86                                                         (freq ? 0x10 : 0);
  87         radio->buffer[6] = 0x00;
  88         radio->buffer[7] = 0x00;
  89 
  90         ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
  91                 9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
  92 
  93         if (ret < 0) {
  94                 dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret);
  95                 return ret;
  96         }
  97         if (freq)
  98                 radio->curfreq = freq;
  99         return 0;
 100 }
 101 
 102 /* Set TX, stereo and preemphasis mode (50 us vs 75 us). */
 103 static int keene_cmd_set(struct keene_device *radio)
 104 {
 105         int ret;
 106 
 107         radio->buffer[0] = 0x00;
 108         radio->buffer[1] = 0x51;
 109         radio->buffer[2] = radio->tx;
 110         /* If bit 0 is set, then transmit mono, otherwise stereo.
 111            If bit 2 is set, then enable 75 us preemphasis, otherwise
 112            it is 50 us. */
 113         radio->buffer[3] = (radio->stereo ? 0 : 1) | (radio->preemph_75_us ? 4 : 0);
 114         radio->buffer[4] = 0x00;
 115         radio->buffer[5] = 0x00;
 116         radio->buffer[6] = 0x00;
 117         radio->buffer[7] = 0x00;
 118 
 119         ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
 120                 9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
 121 
 122         if (ret < 0) {
 123                 dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret);
 124                 return ret;
 125         }
 126         return 0;
 127 }
 128 
 129 /* Handle unplugging the device.
 130  * We call video_unregister_device in any case.
 131  * The last function called in this procedure is
 132  * usb_keene_device_release.
 133  */
 134 static void usb_keene_disconnect(struct usb_interface *intf)
 135 {
 136         struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
 137 
 138         mutex_lock(&radio->lock);
 139         usb_set_intfdata(intf, NULL);
 140         video_unregister_device(&radio->vdev);
 141         v4l2_device_disconnect(&radio->v4l2_dev);
 142         mutex_unlock(&radio->lock);
 143         v4l2_device_put(&radio->v4l2_dev);
 144 }
 145 
 146 static int usb_keene_suspend(struct usb_interface *intf, pm_message_t message)
 147 {
 148         struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
 149 
 150         return keene_cmd_main(radio, 0, false);
 151 }
 152 
 153 static int usb_keene_resume(struct usb_interface *intf)
 154 {
 155         struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
 156 
 157         mdelay(50);
 158         keene_cmd_set(radio);
 159         keene_cmd_main(radio, radio->curfreq, true);
 160         return 0;
 161 }
 162 
 163 static int vidioc_querycap(struct file *file, void *priv,
 164                                         struct v4l2_capability *v)
 165 {
 166         struct keene_device *radio = video_drvdata(file);
 167 
 168         strscpy(v->driver, "radio-keene", sizeof(v->driver));
 169         strscpy(v->card, "Keene FM Transmitter", sizeof(v->card));
 170         usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
 171         return 0;
 172 }
 173 
 174 static int vidioc_g_modulator(struct file *file, void *priv,
 175                                 struct v4l2_modulator *v)
 176 {
 177         struct keene_device *radio = video_drvdata(file);
 178 
 179         if (v->index > 0)
 180                 return -EINVAL;
 181 
 182         strscpy(v->name, "FM", sizeof(v->name));
 183         v->rangelow = FREQ_MIN * FREQ_MUL;
 184         v->rangehigh = FREQ_MAX * FREQ_MUL;
 185         v->txsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
 186         v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
 187         return 0;
 188 }
 189 
 190 static int vidioc_s_modulator(struct file *file, void *priv,
 191                                 const struct v4l2_modulator *v)
 192 {
 193         struct keene_device *radio = video_drvdata(file);
 194 
 195         if (v->index > 0)
 196                 return -EINVAL;
 197 
 198         radio->stereo = (v->txsubchans == V4L2_TUNER_SUB_STEREO);
 199         return keene_cmd_set(radio);
 200 }
 201 
 202 static int vidioc_s_frequency(struct file *file, void *priv,
 203                                 const struct v4l2_frequency *f)
 204 {
 205         struct keene_device *radio = video_drvdata(file);
 206         unsigned freq = f->frequency;
 207 
 208         if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
 209                 return -EINVAL;
 210         freq = clamp(freq, FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL);
 211         return keene_cmd_main(radio, freq, true);
 212 }
 213 
 214 static int vidioc_g_frequency(struct file *file, void *priv,
 215                                 struct v4l2_frequency *f)
 216 {
 217         struct keene_device *radio = video_drvdata(file);
 218 
 219         if (f->tuner != 0)
 220                 return -EINVAL;
 221         f->type = V4L2_TUNER_RADIO;
 222         f->frequency = radio->curfreq;
 223         return 0;
 224 }
 225 
 226 static int keene_s_ctrl(struct v4l2_ctrl *ctrl)
 227 {
 228         static const u8 db2tx[] = {
 229              /*  -15,  -12,   -9,   -6,   -3,    0 dB */
 230                 0x03, 0x13, 0x02, 0x12, 0x22, 0x32,
 231              /*    3,    6,    9,   12,   15,   18 dB */
 232                 0x21, 0x31, 0x20, 0x30, 0x40, 0x50
 233         };
 234         struct keene_device *radio =
 235                 container_of(ctrl->handler, struct keene_device, hdl);
 236 
 237         switch (ctrl->id) {
 238         case V4L2_CID_AUDIO_MUTE:
 239                 radio->muted = ctrl->val;
 240                 return keene_cmd_main(radio, 0, true);
 241 
 242         case V4L2_CID_TUNE_POWER_LEVEL:
 243                 /* To go from dBuV to the register value we apply the
 244                    following formula: */
 245                 radio->pa = (ctrl->val - 71) * 100 / 62;
 246                 return keene_cmd_main(radio, 0, true);
 247 
 248         case V4L2_CID_TUNE_PREEMPHASIS:
 249                 radio->preemph_75_us = ctrl->val == V4L2_PREEMPHASIS_75_uS;
 250                 return keene_cmd_set(radio);
 251 
 252         case V4L2_CID_AUDIO_COMPRESSION_GAIN:
 253                 radio->tx = db2tx[(ctrl->val - (s32)ctrl->minimum) / (s32)ctrl->step];
 254                 return keene_cmd_set(radio);
 255         }
 256         return -EINVAL;
 257 }
 258 
 259 /* File system interface */
 260 static const struct v4l2_file_operations usb_keene_fops = {
 261         .owner          = THIS_MODULE,
 262         .open           = v4l2_fh_open,
 263         .release        = v4l2_fh_release,
 264         .poll           = v4l2_ctrl_poll,
 265         .unlocked_ioctl = video_ioctl2,
 266 };
 267 
 268 static const struct v4l2_ctrl_ops keene_ctrl_ops = {
 269         .s_ctrl = keene_s_ctrl,
 270 };
 271 
 272 static const struct v4l2_ioctl_ops usb_keene_ioctl_ops = {
 273         .vidioc_querycap    = vidioc_querycap,
 274         .vidioc_g_modulator = vidioc_g_modulator,
 275         .vidioc_s_modulator = vidioc_s_modulator,
 276         .vidioc_g_frequency = vidioc_g_frequency,
 277         .vidioc_s_frequency = vidioc_s_frequency,
 278         .vidioc_log_status = v4l2_ctrl_log_status,
 279         .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
 280         .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 281 };
 282 
 283 static void usb_keene_video_device_release(struct v4l2_device *v4l2_dev)
 284 {
 285         struct keene_device *radio = to_keene_dev(v4l2_dev);
 286 
 287         /* free rest memory */
 288         v4l2_ctrl_handler_free(&radio->hdl);
 289         kfree(radio->buffer);
 290         kfree(radio);
 291 }
 292 
 293 /* check if the device is present and register with v4l and usb if it is */
 294 static int usb_keene_probe(struct usb_interface *intf,
 295                                 const struct usb_device_id *id)
 296 {
 297         struct usb_device *dev = interface_to_usbdev(intf);
 298         struct keene_device *radio;
 299         struct v4l2_ctrl_handler *hdl;
 300         int retval = 0;
 301 
 302         /*
 303          * The Keene FM transmitter USB device has the same USB ID as
 304          * the Logitech AudioHub Speaker, but it should ignore the hid.
 305          * Check if the name is that of the Keene device.
 306          * If not, then someone connected the AudioHub and we shouldn't
 307          * attempt to handle this driver.
 308          * For reference: the product name of the AudioHub is
 309          * "AudioHub Speaker".
 310          */
 311         if (dev->product && strcmp(dev->product, "B-LINK USB Audio  "))
 312                 return -ENODEV;
 313 
 314         radio = kzalloc(sizeof(struct keene_device), GFP_KERNEL);
 315         if (radio)
 316                 radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
 317 
 318         if (!radio || !radio->buffer) {
 319                 dev_err(&intf->dev, "kmalloc for keene_device failed\n");
 320                 kfree(radio);
 321                 retval = -ENOMEM;
 322                 goto err;
 323         }
 324 
 325         hdl = &radio->hdl;
 326         v4l2_ctrl_handler_init(hdl, 4);
 327         v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_MUTE,
 328                         0, 1, 1, 0);
 329         v4l2_ctrl_new_std_menu(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS,
 330                         V4L2_PREEMPHASIS_75_uS, 1, V4L2_PREEMPHASIS_50_uS);
 331         v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_POWER_LEVEL,
 332                         84, 118, 1, 118);
 333         v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_COMPRESSION_GAIN,
 334                         -15, 18, 3, 0);
 335         radio->pa = 118;
 336         radio->tx = 0x32;
 337         radio->stereo = true;
 338         if (hdl->error) {
 339                 retval = hdl->error;
 340 
 341                 v4l2_ctrl_handler_free(hdl);
 342                 goto err_v4l2;
 343         }
 344         retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
 345         if (retval < 0) {
 346                 dev_err(&intf->dev, "couldn't register v4l2_device\n");
 347                 goto err_v4l2;
 348         }
 349 
 350         mutex_init(&radio->lock);
 351 
 352         radio->v4l2_dev.ctrl_handler = hdl;
 353         radio->v4l2_dev.release = usb_keene_video_device_release;
 354         strscpy(radio->vdev.name, radio->v4l2_dev.name,
 355                 sizeof(radio->vdev.name));
 356         radio->vdev.v4l2_dev = &radio->v4l2_dev;
 357         radio->vdev.fops = &usb_keene_fops;
 358         radio->vdev.ioctl_ops = &usb_keene_ioctl_ops;
 359         radio->vdev.lock = &radio->lock;
 360         radio->vdev.release = video_device_release_empty;
 361         radio->vdev.vfl_dir = VFL_DIR_TX;
 362         radio->vdev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_MODULATOR;
 363 
 364         radio->usbdev = interface_to_usbdev(intf);
 365         radio->intf = intf;
 366         usb_set_intfdata(intf, &radio->v4l2_dev);
 367 
 368         video_set_drvdata(&radio->vdev, radio);
 369 
 370         /* at least 11ms is needed in order to settle hardware */
 371         msleep(20);
 372         keene_cmd_main(radio, 95.16 * FREQ_MUL, false);
 373 
 374         retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
 375         if (retval < 0) {
 376                 dev_err(&intf->dev, "could not register video device\n");
 377                 goto err_vdev;
 378         }
 379         v4l2_ctrl_handler_setup(hdl);
 380         dev_info(&intf->dev, "V4L2 device registered as %s\n",
 381                         video_device_node_name(&radio->vdev));
 382         return 0;
 383 
 384 err_vdev:
 385         v4l2_device_unregister(&radio->v4l2_dev);
 386 err_v4l2:
 387         kfree(radio->buffer);
 388         kfree(radio);
 389 err:
 390         return retval;
 391 }
 392 
 393 /* USB subsystem interface */
 394 static struct usb_driver usb_keene_driver = {
 395         .name                   = "radio-keene",
 396         .probe                  = usb_keene_probe,
 397         .disconnect             = usb_keene_disconnect,
 398         .id_table               = usb_keene_device_table,
 399         .suspend                = usb_keene_suspend,
 400         .resume                 = usb_keene_resume,
 401         .reset_resume           = usb_keene_resume,
 402 };
 403 
 404 module_usb_driver(usb_keene_driver);
 405 

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