256 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| // ir-rcmm-decoder.c - A decoder for the RCMM IR protocol
 | |
| //
 | |
| // Copyright (C) 2018 by Patrick Lerda <patrick9876@free.fr>
 | |
| 
 | |
| #include "rc-core-priv.h"
 | |
| #include <linux/module.h>
 | |
| 
 | |
| #define RCMM_UNIT		166  /* microseconds */
 | |
| #define RCMM_PREFIX_PULSE	417  /* 166.666666666666*2.5 */
 | |
| #define RCMM_PULSE_0            278  /* 166.666666666666*(1+2/3) */
 | |
| #define RCMM_PULSE_1            444  /* 166.666666666666*(2+2/3) */
 | |
| #define RCMM_PULSE_2            611  /* 166.666666666666*(3+2/3) */
 | |
| #define RCMM_PULSE_3            778  /* 166.666666666666*(4+2/3) */
 | |
| 
 | |
| enum rcmm_state {
 | |
| 	STATE_INACTIVE,
 | |
| 	STATE_LOW,
 | |
| 	STATE_BUMP,
 | |
| 	STATE_VALUE,
 | |
| 	STATE_FINISHED,
 | |
| };
 | |
| 
 | |
| static bool rcmm_mode(const struct rcmm_dec *data)
 | |
| {
 | |
| 	return !((0x000c0000 & data->bits) == 0x000c0000);
 | |
| }
 | |
| 
 | |
| static int rcmm_miscmode(struct rc_dev *dev, struct rcmm_dec *data)
 | |
| {
 | |
| 	switch (data->count) {
 | |
| 	case 24:
 | |
| 		if (dev->enabled_protocols & RC_PROTO_BIT_RCMM24) {
 | |
| 			rc_keydown(dev, RC_PROTO_RCMM24, data->bits, 0);
 | |
| 			data->state = STATE_INACTIVE;
 | |
| 			return 0;
 | |
| 		}
 | |
| 		return -1;
 | |
| 
 | |
| 	case 12:
 | |
| 		if (dev->enabled_protocols & RC_PROTO_BIT_RCMM12) {
 | |
| 			rc_keydown(dev, RC_PROTO_RCMM12, data->bits, 0);
 | |
| 			data->state = STATE_INACTIVE;
 | |
| 			return 0;
 | |
| 		}
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * ir_rcmm_decode() - Decode one RCMM pulse or space
 | |
|  * @dev:	the struct rc_dev descriptor of the device
 | |
|  * @ev:		the struct ir_raw_event descriptor of the pulse/space
 | |
|  *
 | |
|  * This function returns -EINVAL if the pulse violates the state machine
 | |
|  */
 | |
| static int ir_rcmm_decode(struct rc_dev *dev, struct ir_raw_event ev)
 | |
| {
 | |
| 	struct rcmm_dec *data = &dev->raw->rcmm;
 | |
| 	u32 scancode;
 | |
| 	u8 toggle;
 | |
| 	int value;
 | |
| 
 | |
| 	if (!(dev->enabled_protocols & (RC_PROTO_BIT_RCMM32 |
 | |
| 					RC_PROTO_BIT_RCMM24 |
 | |
| 					RC_PROTO_BIT_RCMM12)))
 | |
| 		return 0;
 | |
| 
 | |
| 	if (!is_timing_event(ev)) {
 | |
| 		if (ev.reset)
 | |
| 			data->state = STATE_INACTIVE;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	switch (data->state) {
 | |
| 	case STATE_INACTIVE:
 | |
| 		if (!ev.pulse)
 | |
| 			break;
 | |
| 
 | |
| 		if (!eq_margin(ev.duration, RCMM_PREFIX_PULSE, RCMM_UNIT))
 | |
| 			break;
 | |
| 
 | |
| 		data->state = STATE_LOW;
 | |
| 		data->count = 0;
 | |
| 		data->bits  = 0;
 | |
| 		return 0;
 | |
| 
 | |
| 	case STATE_LOW:
 | |
| 		if (ev.pulse)
 | |
| 			break;
 | |
| 
 | |
| 		if (!eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT))
 | |
| 			break;
 | |
| 
 | |
| 		data->state = STATE_BUMP;
 | |
| 		return 0;
 | |
| 
 | |
| 	case STATE_BUMP:
 | |
| 		if (!ev.pulse)
 | |
| 			break;
 | |
| 
 | |
| 		if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
 | |
| 			break;
 | |
| 
 | |
| 		data->state = STATE_VALUE;
 | |
| 		return 0;
 | |
| 
 | |
| 	case STATE_VALUE:
 | |
| 		if (ev.pulse)
 | |
| 			break;
 | |
| 
 | |
| 		if (eq_margin(ev.duration, RCMM_PULSE_0, RCMM_UNIT / 2))
 | |
| 			value = 0;
 | |
| 		else if (eq_margin(ev.duration, RCMM_PULSE_1, RCMM_UNIT / 2))
 | |
| 			value = 1;
 | |
| 		else if (eq_margin(ev.duration, RCMM_PULSE_2, RCMM_UNIT / 2))
 | |
| 			value = 2;
 | |
| 		else if (eq_margin(ev.duration, RCMM_PULSE_3, RCMM_UNIT / 2))
 | |
| 			value = 3;
 | |
| 		else
 | |
| 			value = -1;
 | |
| 
 | |
| 		if (value == -1) {
 | |
| 			if (!rcmm_miscmode(dev, data))
 | |
| 				return 0;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		data->bits <<= 2;
 | |
| 		data->bits |= value;
 | |
| 
 | |
| 		data->count += 2;
 | |
| 
 | |
| 		if (data->count < 32)
 | |
| 			data->state = STATE_BUMP;
 | |
| 		else
 | |
| 			data->state = STATE_FINISHED;
 | |
| 
 | |
| 		return 0;
 | |
| 
 | |
| 	case STATE_FINISHED:
 | |
| 		if (!ev.pulse)
 | |
| 			break;
 | |
| 
 | |
| 		if (!eq_margin(ev.duration, RCMM_UNIT, RCMM_UNIT / 2))
 | |
| 			break;
 | |
| 
 | |
| 		if (rcmm_mode(data)) {
 | |
| 			toggle = !!(0x8000 & data->bits);
 | |
| 			scancode = data->bits & ~0x8000;
 | |
| 		} else {
 | |
| 			toggle = 0;
 | |
| 			scancode = data->bits;
 | |
| 		}
 | |
| 
 | |
| 		if (dev->enabled_protocols & RC_PROTO_BIT_RCMM32) {
 | |
| 			rc_keydown(dev, RC_PROTO_RCMM32, scancode, toggle);
 | |
| 			data->state = STATE_INACTIVE;
 | |
| 			return 0;
 | |
| 		}
 | |
| 
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	dev_dbg(&dev->dev, "RC-MM decode failed at count %d state %d (%uus %s)\n",
 | |
| 		data->count, data->state, ev.duration, TO_STR(ev.pulse));
 | |
| 	data->state = STATE_INACTIVE;
 | |
| 	return -EINVAL;
 | |
| }
 | |
| 
 | |
| static const int rcmmspace[] = {
 | |
| 	RCMM_PULSE_0,
 | |
| 	RCMM_PULSE_1,
 | |
| 	RCMM_PULSE_2,
 | |
| 	RCMM_PULSE_3,
 | |
| };
 | |
| 
 | |
| static int ir_rcmm_rawencoder(struct ir_raw_event **ev, unsigned int max,
 | |
| 			      unsigned int n, u32 data)
 | |
| {
 | |
| 	int i;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = ir_raw_gen_pulse_space(ev, &max, RCMM_PREFIX_PULSE, RCMM_PULSE_0);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	for (i = n - 2; i >= 0; i -= 2) {
 | |
| 		const unsigned int space = rcmmspace[(data >> i) & 3];
 | |
| 
 | |
| 		ret = ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, space);
 | |
| 		if (ret)
 | |
| 			return ret;
 | |
| 	}
 | |
| 
 | |
| 	return ir_raw_gen_pulse_space(ev, &max, RCMM_UNIT, RCMM_PULSE_3 * 2);
 | |
| }
 | |
| 
 | |
| static int ir_rcmm_encode(enum rc_proto protocol, u32 scancode,
 | |
| 			  struct ir_raw_event *events, unsigned int max)
 | |
| {
 | |
| 	struct ir_raw_event *e = events;
 | |
| 	int ret;
 | |
| 
 | |
| 	switch (protocol) {
 | |
| 	case RC_PROTO_RCMM32:
 | |
| 		ret = ir_rcmm_rawencoder(&e, max, 32, scancode);
 | |
| 		break;
 | |
| 	case RC_PROTO_RCMM24:
 | |
| 		ret = ir_rcmm_rawencoder(&e, max, 24, scancode);
 | |
| 		break;
 | |
| 	case RC_PROTO_RCMM12:
 | |
| 		ret = ir_rcmm_rawencoder(&e, max, 12, scancode);
 | |
| 		break;
 | |
| 	default:
 | |
| 		ret = -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (ret < 0)
 | |
| 		return ret;
 | |
| 
 | |
| 	return e - events;
 | |
| }
 | |
| 
 | |
| static struct ir_raw_handler rcmm_handler = {
 | |
| 	.protocols	= RC_PROTO_BIT_RCMM32 |
 | |
| 			  RC_PROTO_BIT_RCMM24 |
 | |
| 			  RC_PROTO_BIT_RCMM12,
 | |
| 	.decode		= ir_rcmm_decode,
 | |
| 	.encode         = ir_rcmm_encode,
 | |
| 	.carrier        = 36000,
 | |
| 	.min_timeout	= RCMM_PULSE_3 + RCMM_UNIT,
 | |
| };
 | |
| 
 | |
| static int __init ir_rcmm_decode_init(void)
 | |
| {
 | |
| 	ir_raw_handler_register(&rcmm_handler);
 | |
| 
 | |
| 	pr_info("IR RCMM protocol handler initialized\n");
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void __exit ir_rcmm_decode_exit(void)
 | |
| {
 | |
| 	ir_raw_handler_unregister(&rcmm_handler);
 | |
| }
 | |
| 
 | |
| module_init(ir_rcmm_decode_init);
 | |
| module_exit(ir_rcmm_decode_exit);
 | |
| 
 | |
| MODULE_LICENSE("GPL");
 | |
| MODULE_AUTHOR("Patrick Lerda");
 | |
| MODULE_DESCRIPTION("RCMM IR protocol decoder");
 |