| // SPDX-License-Identifier: GPL-2.0 | 
 | // ir-sanyo-decoder.c - handle SANYO IR Pulse/Space protocol | 
 | // | 
 | // Copyright (C) 2011 by Mauro Carvalho Chehab | 
 | // | 
 | // This protocol uses the NEC protocol timings. However, data is formatted as: | 
 | //	13 bits Custom Code | 
 | //	13 bits NOT(Custom Code) | 
 | //	8 bits Key data | 
 | //	8 bits NOT(Key data) | 
 | // | 
 | // According with LIRC, this protocol is used on Sanyo, Aiwa and Chinon | 
 | // Information for this protocol is available at the Sanyo LC7461 datasheet. | 
 |  | 
 | #include <linux/module.h> | 
 | #include <linux/bitrev.h> | 
 | #include "rc-core-priv.h" | 
 |  | 
 | #define SANYO_NBITS		(13+13+8+8) | 
 | #define SANYO_UNIT		562500  /* ns */ | 
 | #define SANYO_HEADER_PULSE	(16  * SANYO_UNIT) | 
 | #define SANYO_HEADER_SPACE	(8   * SANYO_UNIT) | 
 | #define SANYO_BIT_PULSE		(1   * SANYO_UNIT) | 
 | #define SANYO_BIT_0_SPACE	(1   * SANYO_UNIT) | 
 | #define SANYO_BIT_1_SPACE	(3   * SANYO_UNIT) | 
 | #define SANYO_REPEAT_SPACE	(150 * SANYO_UNIT) | 
 | #define	SANYO_TRAILER_PULSE	(1   * SANYO_UNIT) | 
 | #define	SANYO_TRAILER_SPACE	(10  * SANYO_UNIT)	/* in fact, 42 */ | 
 |  | 
 | enum sanyo_state { | 
 | 	STATE_INACTIVE, | 
 | 	STATE_HEADER_SPACE, | 
 | 	STATE_BIT_PULSE, | 
 | 	STATE_BIT_SPACE, | 
 | 	STATE_TRAILER_PULSE, | 
 | 	STATE_TRAILER_SPACE, | 
 | }; | 
 |  | 
 | /** | 
 |  * ir_sanyo_decode() - Decode one SANYO 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_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) | 
 | { | 
 | 	struct sanyo_dec *data = &dev->raw->sanyo; | 
 | 	u32 scancode; | 
 | 	u16 address; | 
 | 	u8 command, not_command; | 
 |  | 
 | 	if (!is_timing_event(ev)) { | 
 | 		if (ev.reset) { | 
 | 			dev_dbg(&dev->dev, "SANYO event reset received. reset to state 0\n"); | 
 | 			data->state = STATE_INACTIVE; | 
 | 		} | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	dev_dbg(&dev->dev, "SANYO decode started at state %d (%uus %s)\n", | 
 | 		data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | 
 |  | 
 | 	switch (data->state) { | 
 |  | 
 | 	case STATE_INACTIVE: | 
 | 		if (!ev.pulse) | 
 | 			break; | 
 |  | 
 | 		if (eq_margin(ev.duration, SANYO_HEADER_PULSE, SANYO_UNIT / 2)) { | 
 | 			data->count = 0; | 
 | 			data->state = STATE_HEADER_SPACE; | 
 | 			return 0; | 
 | 		} | 
 | 		break; | 
 |  | 
 |  | 
 | 	case STATE_HEADER_SPACE: | 
 | 		if (ev.pulse) | 
 | 			break; | 
 |  | 
 | 		if (eq_margin(ev.duration, SANYO_HEADER_SPACE, SANYO_UNIT / 2)) { | 
 | 			data->state = STATE_BIT_PULSE; | 
 | 			return 0; | 
 | 		} | 
 |  | 
 | 		break; | 
 |  | 
 | 	case STATE_BIT_PULSE: | 
 | 		if (!ev.pulse) | 
 | 			break; | 
 |  | 
 | 		if (!eq_margin(ev.duration, SANYO_BIT_PULSE, SANYO_UNIT / 2)) | 
 | 			break; | 
 |  | 
 | 		data->state = STATE_BIT_SPACE; | 
 | 		return 0; | 
 |  | 
 | 	case STATE_BIT_SPACE: | 
 | 		if (ev.pulse) | 
 | 			break; | 
 |  | 
 | 		if (!data->count && geq_margin(ev.duration, SANYO_REPEAT_SPACE, SANYO_UNIT / 2)) { | 
 | 			rc_repeat(dev); | 
 | 			dev_dbg(&dev->dev, "SANYO repeat last key\n"); | 
 | 			data->state = STATE_INACTIVE; | 
 | 			return 0; | 
 | 		} | 
 |  | 
 | 		data->bits <<= 1; | 
 | 		if (eq_margin(ev.duration, SANYO_BIT_1_SPACE, SANYO_UNIT / 2)) | 
 | 			data->bits |= 1; | 
 | 		else if (!eq_margin(ev.duration, SANYO_BIT_0_SPACE, SANYO_UNIT / 2)) | 
 | 			break; | 
 | 		data->count++; | 
 |  | 
 | 		if (data->count == SANYO_NBITS) | 
 | 			data->state = STATE_TRAILER_PULSE; | 
 | 		else | 
 | 			data->state = STATE_BIT_PULSE; | 
 |  | 
 | 		return 0; | 
 |  | 
 | 	case STATE_TRAILER_PULSE: | 
 | 		if (!ev.pulse) | 
 | 			break; | 
 |  | 
 | 		if (!eq_margin(ev.duration, SANYO_TRAILER_PULSE, SANYO_UNIT / 2)) | 
 | 			break; | 
 |  | 
 | 		data->state = STATE_TRAILER_SPACE; | 
 | 		return 0; | 
 |  | 
 | 	case STATE_TRAILER_SPACE: | 
 | 		if (ev.pulse) | 
 | 			break; | 
 |  | 
 | 		if (!geq_margin(ev.duration, SANYO_TRAILER_SPACE, SANYO_UNIT / 2)) | 
 | 			break; | 
 |  | 
 | 		address     = bitrev16((data->bits >> 29) & 0x1fff) >> 3; | 
 | 		/* not_address = bitrev16((data->bits >> 16) & 0x1fff) >> 3; */ | 
 | 		command	    = bitrev8((data->bits >>  8) & 0xff); | 
 | 		not_command = bitrev8((data->bits >>  0) & 0xff); | 
 |  | 
 | 		if ((command ^ not_command) != 0xff) { | 
 | 			dev_dbg(&dev->dev, "SANYO checksum error: received 0x%08llx\n", | 
 | 				data->bits); | 
 | 			data->state = STATE_INACTIVE; | 
 | 			return 0; | 
 | 		} | 
 |  | 
 | 		scancode = address << 8 | command; | 
 | 		dev_dbg(&dev->dev, "SANYO scancode: 0x%06x\n", scancode); | 
 | 		rc_keydown(dev, RC_PROTO_SANYO, scancode, 0); | 
 | 		data->state = STATE_INACTIVE; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	dev_dbg(&dev->dev, "SANYO decode failed at count %d state %d (%uus %s)\n", | 
 | 		data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); | 
 | 	data->state = STATE_INACTIVE; | 
 | 	return -EINVAL; | 
 | } | 
 |  | 
 | static const struct ir_raw_timings_pd ir_sanyo_timings = { | 
 | 	.header_pulse  = SANYO_HEADER_PULSE, | 
 | 	.header_space  = SANYO_HEADER_SPACE, | 
 | 	.bit_pulse     = SANYO_BIT_PULSE, | 
 | 	.bit_space[0]  = SANYO_BIT_0_SPACE, | 
 | 	.bit_space[1]  = SANYO_BIT_1_SPACE, | 
 | 	.trailer_pulse = SANYO_TRAILER_PULSE, | 
 | 	.trailer_space = SANYO_TRAILER_SPACE, | 
 | 	.msb_first     = 1, | 
 | }; | 
 |  | 
 | /** | 
 |  * ir_sanyo_encode() - Encode a scancode as a stream of raw events | 
 |  * | 
 |  * @protocol:	protocol to encode | 
 |  * @scancode:	scancode to encode | 
 |  * @events:	array of raw ir events to write into | 
 |  * @max:	maximum size of @events | 
 |  * | 
 |  * Returns:	The number of events written. | 
 |  *		-ENOBUFS if there isn't enough space in the array to fit the | 
 |  *		encoding. In this case all @max events will have been written. | 
 |  */ | 
 | static int ir_sanyo_encode(enum rc_proto protocol, u32 scancode, | 
 | 			   struct ir_raw_event *events, unsigned int max) | 
 | { | 
 | 	struct ir_raw_event *e = events; | 
 | 	int ret; | 
 | 	u64 raw; | 
 |  | 
 | 	raw = ((u64)(bitrev16(scancode >> 8) & 0xfff8) << (8 + 8 + 13 - 3)) | | 
 | 	      ((u64)(bitrev16(~scancode >> 8) & 0xfff8) << (8 + 8 +  0 - 3)) | | 
 | 	      ((bitrev8(scancode) & 0xff) << 8) | | 
 | 	      (bitrev8(~scancode) & 0xff); | 
 |  | 
 | 	ret = ir_raw_gen_pd(&e, max, &ir_sanyo_timings, SANYO_NBITS, raw); | 
 | 	if (ret < 0) | 
 | 		return ret; | 
 |  | 
 | 	return e - events; | 
 | } | 
 |  | 
 | static struct ir_raw_handler sanyo_handler = { | 
 | 	.protocols	= RC_PROTO_BIT_SANYO, | 
 | 	.decode		= ir_sanyo_decode, | 
 | 	.encode		= ir_sanyo_encode, | 
 | 	.carrier	= 38000, | 
 | 	.min_timeout	= SANYO_TRAILER_SPACE, | 
 | }; | 
 |  | 
 | static int __init ir_sanyo_decode_init(void) | 
 | { | 
 | 	ir_raw_handler_register(&sanyo_handler); | 
 |  | 
 | 	printk(KERN_INFO "IR SANYO protocol handler initialized\n"); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void __exit ir_sanyo_decode_exit(void) | 
 | { | 
 | 	ir_raw_handler_unregister(&sanyo_handler); | 
 | } | 
 |  | 
 | module_init(ir_sanyo_decode_init); | 
 | module_exit(ir_sanyo_decode_exit); | 
 |  | 
 | MODULE_LICENSE("GPL v2"); | 
 | MODULE_AUTHOR("Mauro Carvalho Chehab"); | 
 | MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); | 
 | MODULE_DESCRIPTION("SANYO IR protocol decoder"); |