brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · a8a3443 Raw
256 lines · c
1// SPDX-License-Identifier: GPL-2.0+2// ir-rcmm-decoder.c - A decoder for the RCMM IR protocol3//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 space54 * @dev:	the struct rc_dev descriptor of the device55 * @ev:		the struct ir_raw_event descriptor of the pulse/space56 *57 * This function returns -EINVAL if the pulse violates the state machine58 */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		else123			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		else139			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");256