linux/drivers/media/rc/ir-rcmm-decoder.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2// ir-rcmm-decoder.c - A decoder for the RCMM IR protocol
   3//
   4// Copyright (C) 2018 by Patrick Lerda <patrick9876@free.fr>
   5
   6#include "rc-core-priv.h"
   7#include <linux/module.h>
   8
   9#define RCMM_UNIT               166  /* microseconds */
  10#define RCMM_PREFIX_PULSE       417  /* 166.666666666666*2.5 */
  11#define RCMM_PULSE_0            278  /* 166.666666666666*(1+2/3) */
  12#define RCMM_PULSE_1            444  /* 166.666666666666*(2+2/3) */
  13#define RCMM_PULSE_2            611  /* 166.666666666666*(3+2/3) */
  14#define RCMM_PULSE_3            778  /* 166.666666666666*(4+2/3) */
  15
  16enum rcmm_state {
  17        STATE_INACTIVE,
  18        STATE_LOW,
  19        STATE_BUMP,
  20        STATE_VALUE,
  21        STATE_FINISHED,
  22};
  23
  24static bool rcmm_mode(const struct rcmm_dec *data)
  25{
  26        return !((0x000c0000 & data->bits) == 0x000c0000);
  27}
  28
  29static int rcmm_miscmode(struct rc_dev *dev, struct rcmm_dec *data)
  30{
  31        switch (data->count) {
  32        case 24:
  33                if (dev->enabled_protocols & RC_PROTO_BIT_RCMM24) {
  34                        rc_keydown(dev, RC_PROTO_RCMM24, data->bits, 0);
  35                        data->state = STATE_INACTIVE;
  36                        return 0;
  37                }
  38                return -1;
  39
  40        case 12:
  41                if (dev->enabled_protocols & RC_PROTO_BIT_RCMM12) {
  42                        rc_keydown(dev, RC_PROTO_RCMM12, data->bits, 0);
  43                        data->state = STATE_INACTIVE;
  44                        return 0;
  45                }
  46                return -1;
  47        }
  48
  49        return -1;
  50}
  51
  52/**
  53 * ir_rcmm_decode() - Decode one RCMM pulse or space
  54 * @dev:        the struct rc_dev descriptor of the device
  55 * @ev:         the struct ir_raw_event descriptor of the pulse/space
  56 *
  57 * This function returns -EINVAL if the pulse violates the state machine
  58 */
  59static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev)
  60{
  61        struct rcmm_dec *data = &dev->raw->rcmm;
  62        u32 scancode;
  63        u8 toggle;
  64        int value;
  65
  66        if (!(dev->enabled_protocols & (RC_PROTO_BIT_RCMM32 |
  67                                        RC_PROTO_BIT_RCMM24 |
  68                                        RC_PROTO_BIT_RCMM12)))
  69                return 0;
  70
  71        if (!is_timing_event(ev)) {
  72                if (ev.reset)
  73                        data->state = STATE_INACTIVE;
  74                return 0;
  75        }
  76
  77        switch (data->state) {
  78        case STATE_INACTIVE:
  79                if (!ev.pulse)
  80                        break;
  81
  82                if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT))
  83                        break;
  84
  85                data->state = STATE_LOW;
  86                data->count = 0;
  87                data->bits  = 0;
  88                return 0;
  89
  90        case STATE_LOW:
  91                if (ev.pulse)
  92                        break;
  93
  94                if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT))
  95                        break;
  96
  97                data->state = STATE_BUMP;
  98                return 0;
  99
 100        case STATE_BUMP:
 101                if (!ev.pulse)
 102                        break;
 103
 104                if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
 105                        break;
 106
 107                data->state = STATE_VALUE;
 108                return 0;
 109
 110        case STATE_VALUE:
 111                if (ev.pulse)
 112                        break;
 113
 114                if (eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2))
 115                        value = 0;
 116                else if (eq_margin(ev.duration, RCMM_PULSE_1, RCMM_UNIT / 2))
 117                        value = 1;
 118                else if (eq_margin(ev.duration, RCMM_PULSE_2, RCMM_UNIT / 2))
 119                        value = 2;
 120                else if (eq_margin(ev.duration, RCMM_PULSE_3, RCMM_UNIT / 2))
 121                        value = 3;
 122                else
 123                        value = -1;
 124
 125                if (value == -1) {
 126                        if (!rcmm_miscmode(dev, data))
 127                                return 0;
 128                        break;
 129                }
 130
 131                data->bits <<= 2;
 132                data->bits |= value;
 133
 134                data->count += 2;
 135
 136                if (data->count < 32)
 137                        data->state = STATE_BUMP;
 138                else
 139                        data->state = STATE_FINISHED;
 140
 141                return 0;
 142
 143        case STATE_FINISHED:
 144                if (!ev.pulse)
 145                        break;
 146
 147                if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
 148                        break;
 149
 150                if (rcmm_mode(data)) {
 151                        toggle = !!(0x8000 & data->bits);
 152                        scancode = data->bits & ~0x8000;
 153                } else {
 154                        toggle = 0;
 155                        scancode = data->bits;
 156                }
 157
 158                if (dev->enabled_protocols & RC_PROTO_BIT_RCMM32) {
 159                        rc_keydown(dev, RC_PROTO_RCMM32, scancode, toggle);
 160                        data->state = STATE_INACTIVE;
 161                        return 0;
 162                }
 163
 164                break;
 165        }
 166
 167        dev_dbg(&dev->dev, "RC-MM decode failed at count %d state %d (%uus %s)\n",
 168                data->count, data->state, ev.duration, TO_STR(ev.pulse));
 169        data->state = STATE_INACTIVE;
 170        return -EINVAL;
 171}
 172
 173static const int rcmmspace[] = {
 174        RCMM_PULSE_0,
 175        RCMM_PULSE_1,
 176        RCMM_PULSE_2,
 177        RCMM_PULSE_3,
 178};
 179
 180static int ir_rcmm_rawencoder(struct ir_raw_event **ev, unsigned int max,
 181                              unsigned int n, u32 data)
 182{
 183        int i;
 184        int ret;
 185
 186        ret = ir_raw_gen_pulse_space(ev, &max, RCMM_PREFIX_PULSE, RCMM_PULSE_0);
 187        if (ret)
 188                return ret;
 189
 190        for (i = n - 2; i >= 0; i -= 2) {
 191                const unsigned int space = rcmmspace[(data >> i) & 3];
 192
 193                ret = ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, space);
 194                if (ret)
 195                        return ret;
 196        }
 197
 198        return ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, RCMM_PULSE_3 * 2);
 199}
 200
 201static int ir_rcmm_encode(enum rc_proto protocol, u32 scancode,
 202                          struct ir_raw_event *events, unsigned int max)
 203{
 204        struct ir_raw_event *e = events;
 205        int ret;
 206
 207        switch (protocol) {
 208        case RC_PROTO_RCMM32:
 209                ret = ir_rcmm_rawencoder(&e, max, 32, scancode);
 210                break;
 211        case RC_PROTO_RCMM24:
 212                ret = ir_rcmm_rawencoder(&e, max, 24, scancode);
 213                break;
 214        case RC_PROTO_RCMM12:
 215                ret = ir_rcmm_rawencoder(&e, max, 12, scancode);
 216                break;
 217        default:
 218                ret = -EINVAL;
 219        }
 220
 221        if (ret < 0)
 222                return ret;
 223
 224        return e - events;
 225}
 226
 227static struct ir_raw_handler rcmm_handler = {
 228        .protocols      = RC_PROTO_BIT_RCMM32 |
 229                          RC_PROTO_BIT_RCMM24 |
 230                          RC_PROTO_BIT_RCMM12,
 231        .decode         = ir_rcmm_decode,
 232        .encode         = ir_rcmm_encode,
 233        .carrier        = 36000,
 234        .min_timeout    = RCMM_PULSE_3 + RCMM_UNIT,
 235};
 236
 237static int __init ir_rcmm_decode_init(void)
 238{
 239        ir_raw_handler_register(&rcmm_handler);
 240
 241        pr_info("IR RCMM protocol handler initialized\n");
 242        return 0;
 243}
 244
 245static void __exit ir_rcmm_decode_exit(void)
 246{
 247        ir_raw_handler_unregister(&rcmm_handler);
 248}
 249
 250module_init(ir_rcmm_decode_init);
 251module_exit(ir_rcmm_decode_exit);
 252
 253MODULE_LICENSE("GPL");
 254MODULE_AUTHOR("Patrick Lerda");
 255MODULE_DESCRIPTION("RCMM IR protocol decoder");
 256