Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (c) 2012 Hans Verkuil <hverkuil@xs4all.nl>
0004  */
0005 
0006 /* kernel includes */
0007 #include <linux/kernel.h>
0008 #include <linux/module.h>
0009 #include <linux/init.h>
0010 #include <linux/slab.h>
0011 #include <linux/input.h>
0012 #include <linux/videodev2.h>
0013 #include <media/v4l2-device.h>
0014 #include <media/v4l2-ioctl.h>
0015 #include <media/v4l2-ctrls.h>
0016 #include <media/v4l2-event.h>
0017 #include <linux/usb.h>
0018 #include <linux/mutex.h>
0019 
0020 /* driver and module definitions */
0021 MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
0022 MODULE_DESCRIPTION("Keene FM Transmitter driver");
0023 MODULE_LICENSE("GPL");
0024 
0025 /* Actually, it advertises itself as a Logitech */
0026 #define USB_KEENE_VENDOR 0x046d
0027 #define USB_KEENE_PRODUCT 0x0a0e
0028 
0029 /* Probably USB_TIMEOUT should be modified in module parameter */
0030 #define BUFFER_LENGTH 8
0031 #define USB_TIMEOUT 500
0032 
0033 /* Frequency limits in MHz */
0034 #define FREQ_MIN  76U
0035 #define FREQ_MAX 108U
0036 #define FREQ_MUL 16000U
0037 
0038 /* USB Device ID List */
0039 static const struct usb_device_id usb_keene_device_table[] = {
0040     {USB_DEVICE_AND_INTERFACE_INFO(USB_KEENE_VENDOR, USB_KEENE_PRODUCT,
0041                             USB_CLASS_HID, 0, 0) },
0042     { }                     /* Terminating entry */
0043 };
0044 
0045 MODULE_DEVICE_TABLE(usb, usb_keene_device_table);
0046 
0047 struct keene_device {
0048     struct usb_device *usbdev;
0049     struct usb_interface *intf;
0050     struct video_device vdev;
0051     struct v4l2_device v4l2_dev;
0052     struct v4l2_ctrl_handler hdl;
0053     struct mutex lock;
0054 
0055     u8 *buffer;
0056     unsigned curfreq;
0057     u8 tx;
0058     u8 pa;
0059     bool stereo;
0060     bool muted;
0061     bool preemph_75_us;
0062 };
0063 
0064 static inline struct keene_device *to_keene_dev(struct v4l2_device *v4l2_dev)
0065 {
0066     return container_of(v4l2_dev, struct keene_device, v4l2_dev);
0067 }
0068 
0069 /* Set frequency (if non-0), PA, mute and turn on/off the FM transmitter. */
0070 static int keene_cmd_main(struct keene_device *radio, unsigned freq, bool play)
0071 {
0072     unsigned short freq_send = freq ? (freq - 76 * 16000) / 800 : 0;
0073     int ret;
0074 
0075     radio->buffer[0] = 0x00;
0076     radio->buffer[1] = 0x50;
0077     radio->buffer[2] = (freq_send >> 8) & 0xff;
0078     radio->buffer[3] = freq_send & 0xff;
0079     radio->buffer[4] = radio->pa;
0080     /* If bit 4 is set, then tune to the frequency.
0081        If bit 3 is set, then unmute; if bit 2 is set, then mute.
0082        If bit 1 is set, then enter idle mode; if bit 0 is set,
0083        then enter transmit mode.
0084      */
0085     radio->buffer[5] = (radio->muted ? 4 : 8) | (play ? 1 : 2) |
0086                             (freq ? 0x10 : 0);
0087     radio->buffer[6] = 0x00;
0088     radio->buffer[7] = 0x00;
0089 
0090     ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
0091         9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
0092 
0093     if (ret < 0) {
0094         dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret);
0095         return ret;
0096     }
0097     if (freq)
0098         radio->curfreq = freq;
0099     return 0;
0100 }
0101 
0102 /* Set TX, stereo and preemphasis mode (50 us vs 75 us). */
0103 static int keene_cmd_set(struct keene_device *radio)
0104 {
0105     int ret;
0106 
0107     radio->buffer[0] = 0x00;
0108     radio->buffer[1] = 0x51;
0109     radio->buffer[2] = radio->tx;
0110     /* If bit 0 is set, then transmit mono, otherwise stereo.
0111        If bit 2 is set, then enable 75 us preemphasis, otherwise
0112        it is 50 us. */
0113     radio->buffer[3] = (radio->stereo ? 0 : 1) | (radio->preemph_75_us ? 4 : 0);
0114     radio->buffer[4] = 0x00;
0115     radio->buffer[5] = 0x00;
0116     radio->buffer[6] = 0x00;
0117     radio->buffer[7] = 0x00;
0118 
0119     ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
0120         9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
0121 
0122     if (ret < 0) {
0123         dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret);
0124         return ret;
0125     }
0126     return 0;
0127 }
0128 
0129 /* Handle unplugging the device.
0130  * We call video_unregister_device in any case.
0131  * The last function called in this procedure is
0132  * usb_keene_device_release.
0133  */
0134 static void usb_keene_disconnect(struct usb_interface *intf)
0135 {
0136     struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
0137 
0138     mutex_lock(&radio->lock);
0139     usb_set_intfdata(intf, NULL);
0140     video_unregister_device(&radio->vdev);
0141     v4l2_device_disconnect(&radio->v4l2_dev);
0142     mutex_unlock(&radio->lock);
0143     v4l2_device_put(&radio->v4l2_dev);
0144 }
0145 
0146 static int usb_keene_suspend(struct usb_interface *intf, pm_message_t message)
0147 {
0148     struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
0149 
0150     return keene_cmd_main(radio, 0, false);
0151 }
0152 
0153 static int usb_keene_resume(struct usb_interface *intf)
0154 {
0155     struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
0156 
0157     mdelay(50);
0158     keene_cmd_set(radio);
0159     keene_cmd_main(radio, radio->curfreq, true);
0160     return 0;
0161 }
0162 
0163 static int vidioc_querycap(struct file *file, void *priv,
0164                     struct v4l2_capability *v)
0165 {
0166     struct keene_device *radio = video_drvdata(file);
0167 
0168     strscpy(v->driver, "radio-keene", sizeof(v->driver));
0169     strscpy(v->card, "Keene FM Transmitter", sizeof(v->card));
0170     usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
0171     return 0;
0172 }
0173 
0174 static int vidioc_g_modulator(struct file *file, void *priv,
0175                 struct v4l2_modulator *v)
0176 {
0177     struct keene_device *radio = video_drvdata(file);
0178 
0179     if (v->index > 0)
0180         return -EINVAL;
0181 
0182     strscpy(v->name, "FM", sizeof(v->name));
0183     v->rangelow = FREQ_MIN * FREQ_MUL;
0184     v->rangehigh = FREQ_MAX * FREQ_MUL;
0185     v->txsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
0186     v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
0187     return 0;
0188 }
0189 
0190 static int vidioc_s_modulator(struct file *file, void *priv,
0191                 const struct v4l2_modulator *v)
0192 {
0193     struct keene_device *radio = video_drvdata(file);
0194 
0195     if (v->index > 0)
0196         return -EINVAL;
0197 
0198     radio->stereo = (v->txsubchans == V4L2_TUNER_SUB_STEREO);
0199     return keene_cmd_set(radio);
0200 }
0201 
0202 static int vidioc_s_frequency(struct file *file, void *priv,
0203                 const struct v4l2_frequency *f)
0204 {
0205     struct keene_device *radio = video_drvdata(file);
0206     unsigned freq = f->frequency;
0207 
0208     if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
0209         return -EINVAL;
0210     freq = clamp(freq, FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL);
0211     return keene_cmd_main(radio, freq, true);
0212 }
0213 
0214 static int vidioc_g_frequency(struct file *file, void *priv,
0215                 struct v4l2_frequency *f)
0216 {
0217     struct keene_device *radio = video_drvdata(file);
0218 
0219     if (f->tuner != 0)
0220         return -EINVAL;
0221     f->type = V4L2_TUNER_RADIO;
0222     f->frequency = radio->curfreq;
0223     return 0;
0224 }
0225 
0226 static int keene_s_ctrl(struct v4l2_ctrl *ctrl)
0227 {
0228     static const u8 db2tx[] = {
0229          /*  -15,  -12,   -9,   -6,   -3,    0 dB */
0230         0x03, 0x13, 0x02, 0x12, 0x22, 0x32,
0231          /*    3,    6,    9,   12,   15,   18 dB */
0232         0x21, 0x31, 0x20, 0x30, 0x40, 0x50
0233     };
0234     struct keene_device *radio =
0235         container_of(ctrl->handler, struct keene_device, hdl);
0236 
0237     switch (ctrl->id) {
0238     case V4L2_CID_AUDIO_MUTE:
0239         radio->muted = ctrl->val;
0240         return keene_cmd_main(radio, 0, true);
0241 
0242     case V4L2_CID_TUNE_POWER_LEVEL:
0243         /* To go from dBuV to the register value we apply the
0244            following formula: */
0245         radio->pa = (ctrl->val - 71) * 100 / 62;
0246         return keene_cmd_main(radio, 0, true);
0247 
0248     case V4L2_CID_TUNE_PREEMPHASIS:
0249         radio->preemph_75_us = ctrl->val == V4L2_PREEMPHASIS_75_uS;
0250         return keene_cmd_set(radio);
0251 
0252     case V4L2_CID_AUDIO_COMPRESSION_GAIN:
0253         radio->tx = db2tx[(ctrl->val - (s32)ctrl->minimum) / (s32)ctrl->step];
0254         return keene_cmd_set(radio);
0255     }
0256     return -EINVAL;
0257 }
0258 
0259 /* File system interface */
0260 static const struct v4l2_file_operations usb_keene_fops = {
0261     .owner      = THIS_MODULE,
0262     .open           = v4l2_fh_open,
0263     .release        = v4l2_fh_release,
0264     .poll       = v4l2_ctrl_poll,
0265     .unlocked_ioctl = video_ioctl2,
0266 };
0267 
0268 static const struct v4l2_ctrl_ops keene_ctrl_ops = {
0269     .s_ctrl = keene_s_ctrl,
0270 };
0271 
0272 static const struct v4l2_ioctl_ops usb_keene_ioctl_ops = {
0273     .vidioc_querycap    = vidioc_querycap,
0274     .vidioc_g_modulator = vidioc_g_modulator,
0275     .vidioc_s_modulator = vidioc_s_modulator,
0276     .vidioc_g_frequency = vidioc_g_frequency,
0277     .vidioc_s_frequency = vidioc_s_frequency,
0278     .vidioc_log_status = v4l2_ctrl_log_status,
0279     .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
0280     .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
0281 };
0282 
0283 static void usb_keene_video_device_release(struct v4l2_device *v4l2_dev)
0284 {
0285     struct keene_device *radio = to_keene_dev(v4l2_dev);
0286 
0287     /* free rest memory */
0288     v4l2_ctrl_handler_free(&radio->hdl);
0289     kfree(radio->buffer);
0290     kfree(radio);
0291 }
0292 
0293 /* check if the device is present and register with v4l and usb if it is */
0294 static int usb_keene_probe(struct usb_interface *intf,
0295                 const struct usb_device_id *id)
0296 {
0297     struct usb_device *dev = interface_to_usbdev(intf);
0298     struct keene_device *radio;
0299     struct v4l2_ctrl_handler *hdl;
0300     int retval = 0;
0301 
0302     /*
0303      * The Keene FM transmitter USB device has the same USB ID as
0304      * the Logitech AudioHub Speaker, but it should ignore the hid.
0305      * Check if the name is that of the Keene device.
0306      * If not, then someone connected the AudioHub and we shouldn't
0307      * attempt to handle this driver.
0308      * For reference: the product name of the AudioHub is
0309      * "AudioHub Speaker".
0310      */
0311     if (dev->product && strcmp(dev->product, "B-LINK USB Audio  "))
0312         return -ENODEV;
0313 
0314     radio = kzalloc(sizeof(struct keene_device), GFP_KERNEL);
0315     if (radio)
0316         radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
0317 
0318     if (!radio || !radio->buffer) {
0319         dev_err(&intf->dev, "kmalloc for keene_device failed\n");
0320         kfree(radio);
0321         retval = -ENOMEM;
0322         goto err;
0323     }
0324 
0325     hdl = &radio->hdl;
0326     v4l2_ctrl_handler_init(hdl, 4);
0327     v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_MUTE,
0328             0, 1, 1, 0);
0329     v4l2_ctrl_new_std_menu(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS,
0330             V4L2_PREEMPHASIS_75_uS, 1, V4L2_PREEMPHASIS_50_uS);
0331     v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_POWER_LEVEL,
0332             84, 118, 1, 118);
0333     v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_COMPRESSION_GAIN,
0334             -15, 18, 3, 0);
0335     radio->pa = 118;
0336     radio->tx = 0x32;
0337     radio->stereo = true;
0338     if (hdl->error) {
0339         retval = hdl->error;
0340 
0341         v4l2_ctrl_handler_free(hdl);
0342         goto err_v4l2;
0343     }
0344     retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
0345     if (retval < 0) {
0346         dev_err(&intf->dev, "couldn't register v4l2_device\n");
0347         goto err_v4l2;
0348     }
0349 
0350     mutex_init(&radio->lock);
0351 
0352     radio->v4l2_dev.ctrl_handler = hdl;
0353     radio->v4l2_dev.release = usb_keene_video_device_release;
0354     strscpy(radio->vdev.name, radio->v4l2_dev.name,
0355         sizeof(radio->vdev.name));
0356     radio->vdev.v4l2_dev = &radio->v4l2_dev;
0357     radio->vdev.fops = &usb_keene_fops;
0358     radio->vdev.ioctl_ops = &usb_keene_ioctl_ops;
0359     radio->vdev.lock = &radio->lock;
0360     radio->vdev.release = video_device_release_empty;
0361     radio->vdev.vfl_dir = VFL_DIR_TX;
0362     radio->vdev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_MODULATOR;
0363 
0364     radio->usbdev = interface_to_usbdev(intf);
0365     radio->intf = intf;
0366     usb_set_intfdata(intf, &radio->v4l2_dev);
0367 
0368     video_set_drvdata(&radio->vdev, radio);
0369 
0370     /* at least 11ms is needed in order to settle hardware */
0371     msleep(20);
0372     keene_cmd_main(radio, 95.16 * FREQ_MUL, false);
0373 
0374     retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
0375     if (retval < 0) {
0376         dev_err(&intf->dev, "could not register video device\n");
0377         goto err_vdev;
0378     }
0379     v4l2_ctrl_handler_setup(hdl);
0380     dev_info(&intf->dev, "V4L2 device registered as %s\n",
0381             video_device_node_name(&radio->vdev));
0382     return 0;
0383 
0384 err_vdev:
0385     v4l2_device_unregister(&radio->v4l2_dev);
0386 err_v4l2:
0387     kfree(radio->buffer);
0388     kfree(radio);
0389 err:
0390     return retval;
0391 }
0392 
0393 /* USB subsystem interface */
0394 static struct usb_driver usb_keene_driver = {
0395     .name           = "radio-keene",
0396     .probe          = usb_keene_probe,
0397     .disconnect     = usb_keene_disconnect,
0398     .id_table       = usb_keene_device_table,
0399     .suspend        = usb_keene_suspend,
0400     .resume         = usb_keene_resume,
0401     .reset_resume       = usb_keene_resume,
0402 };
0403 
0404 module_usb_driver(usb_keene_driver);
0405