ir-rcmm-decoder.c (5473B)
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.overflow) 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");