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