linux/drivers/media/rc/rc-loopback.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Loopback driver for rc-core,
   4 *
   5 * Copyright (c) 2010 David H\xC3\xA4rdeman <david@hardeman.nu>
   6 *
   7 * This driver receives TX data and passes it back as RX data,
   8 * which is useful for (scripted) debugging of rc-core without
   9 * having to use actual hardware.
  10 */
  11
  12#include <linux/device.h>
  13#include <linux/module.h>
  14#include <linux/sched.h>
  15#include <linux/slab.h>
  16#include <media/rc-core.h>
  17
  18#define DRIVER_NAME     "rc-loopback"
  19#define dprintk(x...)   if (debug) printk(KERN_INFO DRIVER_NAME ": " x)
  20#define RXMASK_REGULAR  0x1
  21#define RXMASK_LEARNING 0x2
  22
  23static bool debug;
  24
  25struct loopback_dev {
  26        struct rc_dev *dev;
  27        u32 txmask;
  28        u32 txcarrier;
  29        u32 txduty;
  30        bool idle;
  31        bool learning;
  32        bool carrierreport;
  33        u32 rxcarriermin;
  34        u32 rxcarriermax;
  35};
  36
  37static struct loopback_dev loopdev;
  38
  39static int loop_set_tx_mask(struct rc_dev *dev, u32 mask)
  40{
  41        struct loopback_dev *lodev = dev->priv;
  42
  43        if ((mask & (RXMASK_REGULAR | RXMASK_LEARNING)) != mask) {
  44                dprintk("invalid tx mask: %u\n", mask);
  45                return -EINVAL;
  46        }
  47
  48        dprintk("setting tx mask: %u\n", mask);
  49        lodev->txmask = mask;
  50        return 0;
  51}
  52
  53static int loop_set_tx_carrier(struct rc_dev *dev, u32 carrier)
  54{
  55        struct loopback_dev *lodev = dev->priv;
  56
  57        dprintk("setting tx carrier: %u\n", carrier);
  58        lodev->txcarrier = carrier;
  59        return 0;
  60}
  61
  62static int loop_set_tx_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
  63{
  64        struct loopback_dev *lodev = dev->priv;
  65
  66        if (duty_cycle < 1 || duty_cycle > 99) {
  67                dprintk("invalid duty cycle: %u\n", duty_cycle);
  68                return -EINVAL;
  69        }
  70
  71        dprintk("setting duty cycle: %u\n", duty_cycle);
  72        lodev->txduty = duty_cycle;
  73        return 0;
  74}
  75
  76static int loop_set_rx_carrier_range(struct rc_dev *dev, u32 min, u32 max)
  77{
  78        struct loopback_dev *lodev = dev->priv;
  79
  80        if (min < 1 || min > max) {
  81                dprintk("invalid rx carrier range %u to %u\n", min, max);
  82                return -EINVAL;
  83        }
  84
  85        dprintk("setting rx carrier range %u to %u\n", min, max);
  86        lodev->rxcarriermin = min;
  87        lodev->rxcarriermax = max;
  88        return 0;
  89}
  90
  91static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
  92{
  93        struct loopback_dev *lodev = dev->priv;
  94        u32 rxmask;
  95        unsigned i;
  96        struct ir_raw_event rawir = {};
  97
  98        if (lodev->txcarrier < lodev->rxcarriermin ||
  99            lodev->txcarrier > lodev->rxcarriermax) {
 100                dprintk("ignoring tx, carrier out of range\n");
 101                goto out;
 102        }
 103
 104        if (lodev->learning)
 105                rxmask = RXMASK_LEARNING;
 106        else
 107                rxmask = RXMASK_REGULAR;
 108
 109        if (!(rxmask & lodev->txmask)) {
 110                dprintk("ignoring tx, rx mask mismatch\n");
 111                goto out;
 112        }
 113
 114        for (i = 0; i < count; i++) {
 115                rawir.pulse = i % 2 ? false : true;
 116                rawir.duration = txbuf[i];
 117                if (rawir.duration)
 118                        ir_raw_event_store_with_filter(dev, &rawir);
 119        }
 120
 121        /* Fake a silence long enough to cause us to go idle */
 122        rawir.pulse = false;
 123        rawir.duration = dev->timeout;
 124        ir_raw_event_store_with_filter(dev, &rawir);
 125
 126        ir_raw_event_handle(dev);
 127
 128out:
 129        return count;
 130}
 131
 132static void loop_set_idle(struct rc_dev *dev, bool enable)
 133{
 134        struct loopback_dev *lodev = dev->priv;
 135
 136        if (lodev->idle != enable) {
 137                dprintk("%sing idle mode\n", enable ? "enter" : "exit");
 138                lodev->idle = enable;
 139        }
 140}
 141
 142static int loop_set_learning_mode(struct rc_dev *dev, int enable)
 143{
 144        struct loopback_dev *lodev = dev->priv;
 145
 146        if (lodev->learning != enable) {
 147                dprintk("%sing learning mode\n", enable ? "enter" : "exit");
 148                lodev->learning = !!enable;
 149        }
 150
 151        return 0;
 152}
 153
 154static int loop_set_carrier_report(struct rc_dev *dev, int enable)
 155{
 156        struct loopback_dev *lodev = dev->priv;
 157
 158        if (lodev->carrierreport != enable) {
 159                dprintk("%sabling carrier reports\n", enable ? "en" : "dis");
 160                lodev->carrierreport = !!enable;
 161        }
 162
 163        return 0;
 164}
 165
 166static int loop_set_wakeup_filter(struct rc_dev *dev,
 167                                  struct rc_scancode_filter *sc)
 168{
 169        static const unsigned int max = 512;
 170        struct ir_raw_event *raw;
 171        int ret;
 172        int i;
 173
 174        /* fine to disable filter */
 175        if (!sc->mask)
 176                return 0;
 177
 178        /* encode the specified filter and loop it back */
 179        raw = kmalloc_array(max, sizeof(*raw), GFP_KERNEL);
 180        if (!raw)
 181                return -ENOMEM;
 182
 183        ret = ir_raw_encode_scancode(dev->wakeup_protocol, sc->data, raw, max);
 184        /* still loop back the partial raw IR even if it's incomplete */
 185        if (ret == -ENOBUFS)
 186                ret = max;
 187        if (ret >= 0) {
 188                /* do the loopback */
 189                for (i = 0; i < ret; ++i)
 190                        ir_raw_event_store(dev, &raw[i]);
 191                ir_raw_event_handle(dev);
 192
 193                ret = 0;
 194        }
 195
 196        kfree(raw);
 197
 198        return ret;
 199}
 200
 201static int __init loop_init(void)
 202{
 203        struct rc_dev *rc;
 204        int ret;
 205
 206        rc = rc_allocate_device(RC_DRIVER_IR_RAW);
 207        if (!rc) {
 208                printk(KERN_ERR DRIVER_NAME ": rc_dev allocation failed\n");
 209                return -ENOMEM;
 210        }
 211
 212        rc->device_name         = "rc-core loopback device";
 213        rc->input_phys          = "rc-core/virtual";
 214        rc->input_id.bustype    = BUS_VIRTUAL;
 215        rc->input_id.version    = 1;
 216        rc->driver_name         = DRIVER_NAME;
 217        rc->map_name            = RC_MAP_EMPTY;
 218        rc->priv                = &loopdev;
 219        rc->allowed_protocols   = RC_PROTO_BIT_ALL_IR_DECODER;
 220        rc->allowed_wakeup_protocols = RC_PROTO_BIT_ALL_IR_ENCODER;
 221        rc->encode_wakeup       = true;
 222        rc->timeout             = MS_TO_US(100); /* 100 ms */
 223        rc->min_timeout         = 1;
 224        rc->max_timeout         = UINT_MAX;
 225        rc->rx_resolution       = 1;
 226        rc->tx_resolution       = 1;
 227        rc->s_tx_mask           = loop_set_tx_mask;
 228        rc->s_tx_carrier        = loop_set_tx_carrier;
 229        rc->s_tx_duty_cycle     = loop_set_tx_duty_cycle;
 230        rc->s_rx_carrier_range  = loop_set_rx_carrier_range;
 231        rc->tx_ir               = loop_tx_ir;
 232        rc->s_idle              = loop_set_idle;
 233        rc->s_learning_mode     = loop_set_learning_mode;
 234        rc->s_carrier_report    = loop_set_carrier_report;
 235        rc->s_wakeup_filter     = loop_set_wakeup_filter;
 236
 237        loopdev.txmask          = RXMASK_REGULAR;
 238        loopdev.txcarrier       = 36000;
 239        loopdev.txduty          = 50;
 240        loopdev.rxcarriermin    = 1;
 241        loopdev.rxcarriermax    = ~0;
 242        loopdev.idle            = true;
 243        loopdev.learning        = false;
 244        loopdev.carrierreport   = false;
 245
 246        ret = rc_register_device(rc);
 247        if (ret < 0) {
 248                printk(KERN_ERR DRIVER_NAME ": rc_dev registration failed\n");
 249                rc_free_device(rc);
 250                return ret;
 251        }
 252
 253        loopdev.dev = rc;
 254        return 0;
 255}
 256
 257static void __exit loop_exit(void)
 258{
 259        rc_unregister_device(loopdev.dev);
 260}
 261
 262module_init(loop_init);
 263module_exit(loop_exit);
 264
 265module_param(debug, bool, S_IRUGO | S_IWUSR);
 266MODULE_PARM_DESC(debug, "Enable debug messages");
 267
 268MODULE_DESCRIPTION("Loopback device for rc-core debugging");
 269MODULE_AUTHOR("David H\xC3\xA4rdeman <david@hardeman.nu>");
 270MODULE_LICENSE("GPL");
 271