297 lines · c
1// SPDX-License-Identifier: GPL-2.02// ir-rc5-decoder.c - decoder for RC5(x) and StreamZap protocols3//4// Copyright (C) 2010 by Mauro Carvalho Chehab5// Copyright (C) 2010 by Jarod Wilson <jarod@redhat.com>6 7/*8 * This decoder handles the 14 bit RC5 protocol, 15 bit "StreamZap" protocol9 * and 20 bit RC5x protocol.10 */11 12#include "rc-core-priv.h"13#include <linux/module.h>14 15#define RC5_NBITS 1416#define RC5_SZ_NBITS 1517#define RC5X_NBITS 2018#define CHECK_RC5X_NBITS 819#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 space35 * @dev: the struct rc_dev descriptor of the device36 * @ev: the struct ir_raw_event descriptor of the pulse/space37 *38 * This function returns -EINVAL if the pulse violates the state machine39 */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.overflow)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 else94 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 } else104 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 } else155 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 events198 *199 * @protocol: protocol variant to encode200 * @scancode: scancode to encode201 * @events: array of raw ir events to write into202 * @max: maximum size of @events203 *204 * Returns: The number of events written.205 * -ENOBUFS if there isn't enough space in the array to fit the206 * 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