linux/drivers/media/rc/ir-rc5-decoder.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// ir-rc5-decoder.c - decoder for RC5(x) and StreamZap protocols
   3//
   4// Copyright (C) 2010 by Mauro Carvalho Chehab
   5// Copyright (C) 2010 by Jarod Wilson <jarod@redhat.com>
   6
   7/*
   8 * This decoder handles the 14 bit RC5 protocol, 15 bit "StreamZap" protocol
   9 * and 20 bit RC5x protocol.
  10 */
  11
  12#include "rc-core-priv.h"
  13#include <linux/module.h>
  14
  15#define RC5_NBITS               14
  16#define RC5_SZ_NBITS            15
  17#define RC5X_NBITS              20
  18#define CHECK_RC5X_NBITS        8
  19#define RC5_UNIT                889 /* us */
  20#define RC5_BIT_START           (1 * RC5_UNIT)
  21#define RC5_BIT_END             (1 * RC5_UNIT)
  22#define RC5X_SPACE              (4 * RC5_UNIT)
  23#define RC5_TRAILER             (6 * RC5_UNIT) /* In reality, approx 100 */
  24
  25enum rc5_state {
  26        STATE_INACTIVE,
  27        STATE_BIT_START,
  28        STATE_BIT_END,
  29        STATE_CHECK_RC5X,
  30        STATE_FINISHED,
  31};
  32
  33/**
  34 * ir_rc5_decode() - Decode one RC-5 pulse or space
  35 * @dev:        the struct rc_dev descriptor of the device
  36 * @ev:         the struct ir_raw_event descriptor of the pulse/space
  37 *
  38 * This function returns -EINVAL if the pulse violates the state machine
  39 */
  40static int ir_rc5_decode(struct rc_dev *dev, struct ir_raw_event ev)
  41{
  42        struct rc5_dec *data = &dev->raw->rc5;
  43        u8 toggle;
  44        u32 scancode;
  45        enum rc_proto protocol;
  46
  47        if (!is_timing_event(ev)) {
  48                if (ev.reset)
  49                        data->state = STATE_INACTIVE;
  50                return 0;
  51        }
  52
  53        if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
  54                goto out;
  55
  56again:
  57        dev_dbg(&dev->dev, "RC5(x/sz) decode started at state %i (%uus %s)\n",
  58                data->state, ev.duration, TO_STR(ev.pulse));
  59
  60        if (!geq_margin(ev.duration, RC5_UNIT, RC5_UNIT / 2))
  61                return 0;
  62
  63        switch (data->state) {
  64
  65        case STATE_INACTIVE:
  66                if (!ev.pulse)
  67                        break;
  68
  69                data->state = STATE_BIT_START;
  70                data->count = 1;
  71                decrease_duration(&ev, RC5_BIT_START);
  72                goto again;
  73
  74        case STATE_BIT_START:
  75                if (!ev.pulse && geq_margin(ev.duration, RC5_TRAILER, RC5_UNIT / 2)) {
  76                        data->state = STATE_FINISHED;
  77                        goto again;
  78                }
  79
  80                if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2))
  81                        break;
  82
  83                data->bits <<= 1;
  84                if (!ev.pulse)
  85                        data->bits |= 1;
  86                data->count++;
  87                data->state = STATE_BIT_END;
  88                return 0;
  89
  90        case STATE_BIT_END:
  91                if (data->count == CHECK_RC5X_NBITS)
  92                        data->state = STATE_CHECK_RC5X;
  93                else
  94                        data->state = STATE_BIT_START;
  95
  96                decrease_duration(&ev, RC5_BIT_END);
  97                goto again;
  98
  99        case STATE_CHECK_RC5X:
 100                if (!ev.pulse && geq_margin(ev.duration, RC5X_SPACE, RC5_UNIT / 2)) {
 101                        data->is_rc5x = true;
 102                        decrease_duration(&ev, RC5X_SPACE);
 103                } else
 104                        data->is_rc5x = false;
 105                data->state = STATE_BIT_START;
 106                goto again;
 107
 108        case STATE_FINISHED:
 109                if (ev.pulse)
 110                        break;
 111
 112                if (data->is_rc5x && data->count == RC5X_NBITS) {
 113                        /* RC5X */
 114                        u8 xdata, command, system;
 115                        if (!(dev->enabled_protocols & RC_PROTO_BIT_RC5X_20)) {
 116                                data->state = STATE_INACTIVE;
 117                                return 0;
 118                        }
 119                        xdata    = (data->bits & 0x0003F) >> 0;
 120                        command  = (data->bits & 0x00FC0) >> 6;
 121                        system   = (data->bits & 0x1F000) >> 12;
 122                        toggle   = (data->bits & 0x20000) ? 1 : 0;
 123                        command += (data->bits & 0x40000) ? 0 : 0x40;
 124                        scancode = system << 16 | command << 8 | xdata;
 125                        protocol = RC_PROTO_RC5X_20;
 126
 127                } else if (!data->is_rc5x && data->count == RC5_NBITS) {
 128                        /* RC5 */
 129                        u8 command, system;
 130                        if (!(dev->enabled_protocols & RC_PROTO_BIT_RC5)) {
 131                                data->state = STATE_INACTIVE;
 132                                return 0;
 133                        }
 134                        command  = (data->bits & 0x0003F) >> 0;
 135                        system   = (data->bits & 0x007C0) >> 6;
 136                        toggle   = (data->bits & 0x00800) ? 1 : 0;
 137                        command += (data->bits & 0x01000) ? 0 : 0x40;
 138                        scancode = system << 8 | command;
 139                        protocol = RC_PROTO_RC5;
 140
 141                } else if (!data->is_rc5x && data->count == RC5_SZ_NBITS) {
 142                        /* RC5 StreamZap */
 143                        u8 command, system;
 144                        if (!(dev->enabled_protocols & RC_PROTO_BIT_RC5_SZ)) {
 145                                data->state = STATE_INACTIVE;
 146                                return 0;
 147                        }
 148                        command  = (data->bits & 0x0003F) >> 0;
 149                        system   = (data->bits & 0x02FC0) >> 6;
 150                        toggle   = (data->bits & 0x01000) ? 1 : 0;
 151                        scancode = system << 6 | command;
 152                        protocol = RC_PROTO_RC5_SZ;
 153
 154                } else
 155                        break;
 156
 157                dev_dbg(&dev->dev, "RC5(x/sz) scancode 0x%06x (p: %u, t: %u)\n",
 158                        scancode, protocol, toggle);
 159
 160                rc_keydown(dev, protocol, scancode, toggle);
 161                data->state = STATE_INACTIVE;
 162                return 0;
 163        }
 164
 165out:
 166        dev_dbg(&dev->dev, "RC5(x/sz) decode failed at state %i count %d (%uus %s)\n",
 167                data->state, data->count, ev.duration, TO_STR(ev.pulse));
 168        data->state = STATE_INACTIVE;
 169        return -EINVAL;
 170}
 171
 172static const struct ir_raw_timings_manchester ir_rc5_timings = {
 173        .leader_pulse           = RC5_UNIT,
 174        .clock                  = RC5_UNIT,
 175        .trailer_space          = RC5_UNIT * 10,
 176};
 177
 178static const struct ir_raw_timings_manchester ir_rc5x_timings[2] = {
 179        {
 180                .leader_pulse           = RC5_UNIT,
 181                .clock                  = RC5_UNIT,
 182                .trailer_space          = RC5X_SPACE,
 183        },
 184        {
 185                .clock                  = RC5_UNIT,
 186                .trailer_space          = RC5_UNIT * 10,
 187        },
 188};
 189
 190static const struct ir_raw_timings_manchester ir_rc5_sz_timings = {
 191        .leader_pulse                   = RC5_UNIT,
 192        .clock                          = RC5_UNIT,
 193        .trailer_space                  = RC5_UNIT * 10,
 194};
 195
 196/**
 197 * ir_rc5_encode() - Encode a scancode as a stream of raw events
 198 *
 199 * @protocol:   protocol variant to encode
 200 * @scancode:   scancode to encode
 201 * @events:     array of raw ir events to write into
 202 * @max:        maximum size of @events
 203 *
 204 * Returns:     The number of events written.
 205 *              -ENOBUFS if there isn't enough space in the array to fit the
 206 *              encoding. In this case all @max events will have been written.
 207 *              -EINVAL if the scancode is ambiguous or invalid.
 208 */
 209static int ir_rc5_encode(enum rc_proto protocol, u32 scancode,
 210                         struct ir_raw_event *events, unsigned int max)
 211{
 212        int ret;
 213        struct ir_raw_event *e = events;
 214        unsigned int data, xdata, command, commandx, system, pre_space_data;
 215
 216        /* Detect protocol and convert scancode to raw data */
 217        if (protocol == RC_PROTO_RC5) {
 218                /* decode scancode */
 219                command  = (scancode & 0x003f) >> 0;
 220                commandx = (scancode & 0x0040) >> 6;
 221                system   = (scancode & 0x1f00) >> 8;
 222                /* encode data */
 223                data = !commandx << 12 | system << 6 | command;
 224
 225                /* First bit is encoded by leader_pulse */
 226                ret = ir_raw_gen_manchester(&e, max, &ir_rc5_timings,
 227                                            RC5_NBITS - 1, data);
 228                if (ret < 0)
 229                        return ret;
 230        } else if (protocol == RC_PROTO_RC5X_20) {
 231                /* decode scancode */
 232                xdata    = (scancode & 0x00003f) >> 0;
 233                command  = (scancode & 0x003f00) >> 8;
 234                commandx = !(scancode & 0x004000);
 235                system   = (scancode & 0x1f0000) >> 16;
 236
 237                /* encode data */
 238                data = commandx << 18 | system << 12 | command << 6 | xdata;
 239
 240                /* First bit is encoded by leader_pulse */
 241                pre_space_data = data >> (RC5X_NBITS - CHECK_RC5X_NBITS);
 242                ret = ir_raw_gen_manchester(&e, max, &ir_rc5x_timings[0],
 243                                            CHECK_RC5X_NBITS - 1,
 244                                            pre_space_data);
 245                if (ret < 0)
 246                        return ret;
 247                ret = ir_raw_gen_manchester(&e, max - (e - events),
 248                                            &ir_rc5x_timings[1],
 249                                            RC5X_NBITS - CHECK_RC5X_NBITS,
 250                                            data);
 251                if (ret < 0)
 252                        return ret;
 253        } else if (protocol == RC_PROTO_RC5_SZ) {
 254                /* RC5-SZ scancode is raw enough for Manchester as it is */
 255                /* First bit is encoded by leader_pulse */
 256                ret = ir_raw_gen_manchester(&e, max, &ir_rc5_sz_timings,
 257                                            RC5_SZ_NBITS - 1,
 258                                            scancode & 0x2fff);
 259                if (ret < 0)
 260                        return ret;
 261        } else {
 262                return -EINVAL;
 263        }
 264
 265        return e - events;
 266}
 267
 268static struct ir_raw_handler rc5_handler = {
 269        .protocols      = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 |
 270                                                        RC_PROTO_BIT_RC5_SZ,
 271        .decode         = ir_rc5_decode,
 272        .encode         = ir_rc5_encode,
 273        .carrier        = 36000,
 274        .min_timeout    = RC5_TRAILER,
 275};
 276
 277static int __init ir_rc5_decode_init(void)
 278{
 279        ir_raw_handler_register(&rc5_handler);
 280
 281        printk(KERN_INFO "IR RC5(x/sz) protocol handler initialized\n");
 282        return 0;
 283}
 284
 285static void __exit ir_rc5_decode_exit(void)
 286{
 287        ir_raw_handler_unregister(&rc5_handler);
 288}
 289
 290module_init(ir_rc5_decode_init);
 291module_exit(ir_rc5_decode_exit);
 292
 293MODULE_LICENSE("GPL v2");
 294MODULE_AUTHOR("Mauro Carvalho Chehab and Jarod Wilson");
 295MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
 296MODULE_DESCRIPTION("RC5(x/sz) IR protocol decoder");
 297