cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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");