xref: /linux/drivers/media/rc/ir-rc6-decoder.c (revision 950170d6)
18e8e69d6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
232cf86f6SMauro Carvalho Chehab /* ir-rc6-decoder.c - A decoder for the RC6 IR protocol
332cf86f6SMauro Carvalho Chehab  *
432cf86f6SMauro Carvalho Chehab  * Copyright (C) 2010 by David Härdeman <david@hardeman.nu>
532cf86f6SMauro Carvalho Chehab  */
632cf86f6SMauro Carvalho Chehab 
7f62de675SMauro Carvalho Chehab #include "rc-core-priv.h"
87a707b89SPaul Gortmaker #include <linux/module.h>
932cf86f6SMauro Carvalho Chehab 
1032cf86f6SMauro Carvalho Chehab /*
1132cf86f6SMauro Carvalho Chehab  * This decoder currently supports:
1232cf86f6SMauro Carvalho Chehab  * RC6-0-16	(standard toggle bit in header)
13db9bc660Slawrence rust  * RC6-6A-20	(no toggle bit)
1432cf86f6SMauro Carvalho Chehab  * RC6-6A-24	(no toggle bit)
1532cf86f6SMauro Carvalho Chehab  * RC6-6A-32	(MCE version with toggle bit in body)
1632cf86f6SMauro Carvalho Chehab  */
1732cf86f6SMauro Carvalho Chehab 
18528222d8SSean Young #define RC6_UNIT		444	/* microseconds */
1932cf86f6SMauro Carvalho Chehab #define RC6_HEADER_NBITS	4	/* not including toggle bit */
2032cf86f6SMauro Carvalho Chehab #define RC6_0_NBITS		16
21db9bc660Slawrence rust #define RC6_6A_32_NBITS		32
22db9bc660Slawrence rust #define RC6_6A_NBITS		128	/* Variable 8..128 */
2332cf86f6SMauro Carvalho Chehab #define RC6_PREFIX_PULSE	(6 * RC6_UNIT)
2432cf86f6SMauro Carvalho Chehab #define RC6_PREFIX_SPACE	(2 * RC6_UNIT)
2532cf86f6SMauro Carvalho Chehab #define RC6_BIT_START		(1 * RC6_UNIT)
2632cf86f6SMauro Carvalho Chehab #define RC6_BIT_END		(1 * RC6_UNIT)
2732cf86f6SMauro Carvalho Chehab #define RC6_TOGGLE_START	(2 * RC6_UNIT)
2832cf86f6SMauro Carvalho Chehab #define RC6_TOGGLE_END		(2 * RC6_UNIT)
29db9bc660Slawrence rust #define RC6_SUFFIX_SPACE	(6 * RC6_UNIT)
3032cf86f6SMauro Carvalho Chehab #define RC6_MODE_MASK		0x07	/* for the header bits */
3132cf86f6SMauro Carvalho Chehab #define RC6_STARTBIT_MASK	0x08	/* for the header bits */
3232cf86f6SMauro Carvalho Chehab #define RC6_6A_MCE_TOGGLE_MASK	0x8000	/* for the body bits */
33db9bc660Slawrence rust #define RC6_6A_LCC_MASK		0xffff0000 /* RC6-6A-32 long customer code mask */
34db9bc660Slawrence rust #define RC6_6A_MCE_CC		0x800f0000 /* MCE customer code */
35b1f37757SMatthias Reichl #define RC6_6A_ZOTAC_CC		0x80340000 /* Zotac customer code */
3685e4af0aSMatthias Reichl #define RC6_6A_KATHREIN_CC	0x80460000 /* Kathrein RCU-676 customer code */
37db9bc660Slawrence rust #ifndef CHAR_BIT
38db9bc660Slawrence rust #define CHAR_BIT 8	/* Normally in <limits.h> */
39db9bc660Slawrence rust #endif
4032cf86f6SMauro Carvalho Chehab 
4132cf86f6SMauro Carvalho Chehab enum rc6_mode {
4232cf86f6SMauro Carvalho Chehab 	RC6_MODE_0,
4332cf86f6SMauro Carvalho Chehab 	RC6_MODE_6A,
4432cf86f6SMauro Carvalho Chehab 	RC6_MODE_UNKNOWN,
4532cf86f6SMauro Carvalho Chehab };
4632cf86f6SMauro Carvalho Chehab 
4732cf86f6SMauro Carvalho Chehab enum rc6_state {
4832cf86f6SMauro Carvalho Chehab 	STATE_INACTIVE,
4932cf86f6SMauro Carvalho Chehab 	STATE_PREFIX_SPACE,
5032cf86f6SMauro Carvalho Chehab 	STATE_HEADER_BIT_START,
5132cf86f6SMauro Carvalho Chehab 	STATE_HEADER_BIT_END,
5232cf86f6SMauro Carvalho Chehab 	STATE_TOGGLE_START,
5332cf86f6SMauro Carvalho Chehab 	STATE_TOGGLE_END,
5432cf86f6SMauro Carvalho Chehab 	STATE_BODY_BIT_START,
5532cf86f6SMauro Carvalho Chehab 	STATE_BODY_BIT_END,
5632cf86f6SMauro Carvalho Chehab 	STATE_FINISHED,
5732cf86f6SMauro Carvalho Chehab };
5832cf86f6SMauro Carvalho Chehab 
rc6_mode(struct rc6_dec * data)5932cf86f6SMauro Carvalho Chehab static enum rc6_mode rc6_mode(struct rc6_dec *data)
6032cf86f6SMauro Carvalho Chehab {
6132cf86f6SMauro Carvalho Chehab 	switch (data->header & RC6_MODE_MASK) {
6232cf86f6SMauro Carvalho Chehab 	case 0:
6332cf86f6SMauro Carvalho Chehab 		return RC6_MODE_0;
6432cf86f6SMauro Carvalho Chehab 	case 6:
6532cf86f6SMauro Carvalho Chehab 		if (!data->toggle)
6632cf86f6SMauro Carvalho Chehab 			return RC6_MODE_6A;
67df561f66SGustavo A. R. Silva 		fallthrough;
6832cf86f6SMauro Carvalho Chehab 	default:
6932cf86f6SMauro Carvalho Chehab 		return RC6_MODE_UNKNOWN;
7032cf86f6SMauro Carvalho Chehab 	}
7132cf86f6SMauro Carvalho Chehab }
7232cf86f6SMauro Carvalho Chehab 
7332cf86f6SMauro Carvalho Chehab /**
7432cf86f6SMauro Carvalho Chehab  * ir_rc6_decode() - Decode one RC6 pulse or space
75d8b4b582SDavid Härdeman  * @dev:	the struct rc_dev descriptor of the device
7632cf86f6SMauro Carvalho Chehab  * @ev:		the struct ir_raw_event descriptor of the pulse/space
7732cf86f6SMauro Carvalho Chehab  *
7832cf86f6SMauro Carvalho Chehab  * This function returns -EINVAL if the pulse violates the state machine
7932cf86f6SMauro Carvalho Chehab  */
ir_rc6_decode(struct rc_dev * dev,struct ir_raw_event ev)80d8b4b582SDavid Härdeman static int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev)
8132cf86f6SMauro Carvalho Chehab {
82d8b4b582SDavid Härdeman 	struct rc6_dec *data = &dev->raw->rc6;
8332cf86f6SMauro Carvalho Chehab 	u32 scancode;
8432cf86f6SMauro Carvalho Chehab 	u8 toggle;
856d741bfeSSean Young 	enum rc_proto protocol;
8632cf86f6SMauro Carvalho Chehab 
8732cf86f6SMauro Carvalho Chehab 	if (!is_timing_event(ev)) {
88950170d6SSean Young 		if (ev.overflow)
8932cf86f6SMauro Carvalho Chehab 			data->state = STATE_INACTIVE;
9032cf86f6SMauro Carvalho Chehab 		return 0;
9132cf86f6SMauro Carvalho Chehab 	}
9232cf86f6SMauro Carvalho Chehab 
9332cf86f6SMauro Carvalho Chehab 	if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2))
9432cf86f6SMauro Carvalho Chehab 		goto out;
9532cf86f6SMauro Carvalho Chehab 
9632cf86f6SMauro Carvalho Chehab again:
9750078a90SSean Young 	dev_dbg(&dev->dev, "RC6 decode started at state %i (%uus %s)\n",
98528222d8SSean Young 		data->state, ev.duration, TO_STR(ev.pulse));
9932cf86f6SMauro Carvalho Chehab 
10032cf86f6SMauro Carvalho Chehab 	if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2))
10132cf86f6SMauro Carvalho Chehab 		return 0;
10232cf86f6SMauro Carvalho Chehab 
10332cf86f6SMauro Carvalho Chehab 	switch (data->state) {
10432cf86f6SMauro Carvalho Chehab 
10532cf86f6SMauro Carvalho Chehab 	case STATE_INACTIVE:
10632cf86f6SMauro Carvalho Chehab 		if (!ev.pulse)
10732cf86f6SMauro Carvalho Chehab 			break;
10832cf86f6SMauro Carvalho Chehab 
10932cf86f6SMauro Carvalho Chehab 		/* Note: larger margin on first pulse since each RC6_UNIT
11032cf86f6SMauro Carvalho Chehab 		   is quite short and some hardware takes some time to
11132cf86f6SMauro Carvalho Chehab 		   adjust to the signal */
11232cf86f6SMauro Carvalho Chehab 		if (!eq_margin(ev.duration, RC6_PREFIX_PULSE, RC6_UNIT))
11332cf86f6SMauro Carvalho Chehab 			break;
11432cf86f6SMauro Carvalho Chehab 
11532cf86f6SMauro Carvalho Chehab 		data->state = STATE_PREFIX_SPACE;
11632cf86f6SMauro Carvalho Chehab 		data->count = 0;
11732cf86f6SMauro Carvalho Chehab 		return 0;
11832cf86f6SMauro Carvalho Chehab 
11932cf86f6SMauro Carvalho Chehab 	case STATE_PREFIX_SPACE:
12032cf86f6SMauro Carvalho Chehab 		if (ev.pulse)
12132cf86f6SMauro Carvalho Chehab 			break;
12232cf86f6SMauro Carvalho Chehab 
12332cf86f6SMauro Carvalho Chehab 		if (!eq_margin(ev.duration, RC6_PREFIX_SPACE, RC6_UNIT / 2))
12432cf86f6SMauro Carvalho Chehab 			break;
12532cf86f6SMauro Carvalho Chehab 
12632cf86f6SMauro Carvalho Chehab 		data->state = STATE_HEADER_BIT_START;
127db9bc660Slawrence rust 		data->header = 0;
12832cf86f6SMauro Carvalho Chehab 		return 0;
12932cf86f6SMauro Carvalho Chehab 
13032cf86f6SMauro Carvalho Chehab 	case STATE_HEADER_BIT_START:
13132cf86f6SMauro Carvalho Chehab 		if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2))
13232cf86f6SMauro Carvalho Chehab 			break;
13332cf86f6SMauro Carvalho Chehab 
13432cf86f6SMauro Carvalho Chehab 		data->header <<= 1;
13532cf86f6SMauro Carvalho Chehab 		if (ev.pulse)
13632cf86f6SMauro Carvalho Chehab 			data->header |= 1;
13732cf86f6SMauro Carvalho Chehab 		data->count++;
13832cf86f6SMauro Carvalho Chehab 		data->state = STATE_HEADER_BIT_END;
13932cf86f6SMauro Carvalho Chehab 		return 0;
14032cf86f6SMauro Carvalho Chehab 
14132cf86f6SMauro Carvalho Chehab 	case STATE_HEADER_BIT_END:
14232cf86f6SMauro Carvalho Chehab 		if (data->count == RC6_HEADER_NBITS)
14332cf86f6SMauro Carvalho Chehab 			data->state = STATE_TOGGLE_START;
14432cf86f6SMauro Carvalho Chehab 		else
14532cf86f6SMauro Carvalho Chehab 			data->state = STATE_HEADER_BIT_START;
14632cf86f6SMauro Carvalho Chehab 
14732cf86f6SMauro Carvalho Chehab 		decrease_duration(&ev, RC6_BIT_END);
14832cf86f6SMauro Carvalho Chehab 		goto again;
14932cf86f6SMauro Carvalho Chehab 
15032cf86f6SMauro Carvalho Chehab 	case STATE_TOGGLE_START:
15132cf86f6SMauro Carvalho Chehab 		if (!eq_margin(ev.duration, RC6_TOGGLE_START, RC6_UNIT / 2))
15232cf86f6SMauro Carvalho Chehab 			break;
15332cf86f6SMauro Carvalho Chehab 
15432cf86f6SMauro Carvalho Chehab 		data->toggle = ev.pulse;
15532cf86f6SMauro Carvalho Chehab 		data->state = STATE_TOGGLE_END;
15632cf86f6SMauro Carvalho Chehab 		return 0;
15732cf86f6SMauro Carvalho Chehab 
15832cf86f6SMauro Carvalho Chehab 	case STATE_TOGGLE_END:
15932cf86f6SMauro Carvalho Chehab 		if (!(data->header & RC6_STARTBIT_MASK)) {
16050078a90SSean Young 			dev_dbg(&dev->dev, "RC6 invalid start bit\n");
16132cf86f6SMauro Carvalho Chehab 			break;
16232cf86f6SMauro Carvalho Chehab 		}
16332cf86f6SMauro Carvalho Chehab 
16432cf86f6SMauro Carvalho Chehab 		data->state = STATE_BODY_BIT_START;
16532cf86f6SMauro Carvalho Chehab 		decrease_duration(&ev, RC6_TOGGLE_END);
16632cf86f6SMauro Carvalho Chehab 		data->count = 0;
167db9bc660Slawrence rust 		data->body = 0;
16832cf86f6SMauro Carvalho Chehab 
16932cf86f6SMauro Carvalho Chehab 		switch (rc6_mode(data)) {
17032cf86f6SMauro Carvalho Chehab 		case RC6_MODE_0:
17132cf86f6SMauro Carvalho Chehab 			data->wanted_bits = RC6_0_NBITS;
17232cf86f6SMauro Carvalho Chehab 			break;
17332cf86f6SMauro Carvalho Chehab 		case RC6_MODE_6A:
174db9bc660Slawrence rust 			data->wanted_bits = RC6_6A_NBITS;
17532cf86f6SMauro Carvalho Chehab 			break;
17632cf86f6SMauro Carvalho Chehab 		default:
17750078a90SSean Young 			dev_dbg(&dev->dev, "RC6 unknown mode\n");
17832cf86f6SMauro Carvalho Chehab 			goto out;
17932cf86f6SMauro Carvalho Chehab 		}
18032cf86f6SMauro Carvalho Chehab 		goto again;
18132cf86f6SMauro Carvalho Chehab 
18232cf86f6SMauro Carvalho Chehab 	case STATE_BODY_BIT_START:
183db9bc660Slawrence rust 		if (eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) {
184db9bc660Slawrence rust 			/* Discard LSB's that won't fit in data->body */
185db9bc660Slawrence rust 			if (data->count++ < CHAR_BIT * sizeof data->body) {
18632cf86f6SMauro Carvalho Chehab 				data->body <<= 1;
18732cf86f6SMauro Carvalho Chehab 				if (ev.pulse)
18832cf86f6SMauro Carvalho Chehab 					data->body |= 1;
189db9bc660Slawrence rust 			}
19032cf86f6SMauro Carvalho Chehab 			data->state = STATE_BODY_BIT_END;
19132cf86f6SMauro Carvalho Chehab 			return 0;
192db9bc660Slawrence rust 		} else if (RC6_MODE_6A == rc6_mode(data) && !ev.pulse &&
193db9bc660Slawrence rust 				geq_margin(ev.duration, RC6_SUFFIX_SPACE, RC6_UNIT / 2)) {
194db9bc660Slawrence rust 			data->state = STATE_FINISHED;
195db9bc660Slawrence rust 			goto again;
196db9bc660Slawrence rust 		}
197db9bc660Slawrence rust 		break;
19832cf86f6SMauro Carvalho Chehab 
19932cf86f6SMauro Carvalho Chehab 	case STATE_BODY_BIT_END:
20032cf86f6SMauro Carvalho Chehab 		if (data->count == data->wanted_bits)
20132cf86f6SMauro Carvalho Chehab 			data->state = STATE_FINISHED;
20232cf86f6SMauro Carvalho Chehab 		else
20332cf86f6SMauro Carvalho Chehab 			data->state = STATE_BODY_BIT_START;
20432cf86f6SMauro Carvalho Chehab 
20532cf86f6SMauro Carvalho Chehab 		decrease_duration(&ev, RC6_BIT_END);
20632cf86f6SMauro Carvalho Chehab 		goto again;
20732cf86f6SMauro Carvalho Chehab 
20832cf86f6SMauro Carvalho Chehab 	case STATE_FINISHED:
20932cf86f6SMauro Carvalho Chehab 		if (ev.pulse)
21032cf86f6SMauro Carvalho Chehab 			break;
21132cf86f6SMauro Carvalho Chehab 
21232cf86f6SMauro Carvalho Chehab 		switch (rc6_mode(data)) {
21332cf86f6SMauro Carvalho Chehab 		case RC6_MODE_0:
214db9bc660Slawrence rust 			scancode = data->body;
21532cf86f6SMauro Carvalho Chehab 			toggle = data->toggle;
2166d741bfeSSean Young 			protocol = RC_PROTO_RC6_0;
21750078a90SSean Young 			dev_dbg(&dev->dev, "RC6(0) scancode 0x%04x (toggle: %u)\n",
21832cf86f6SMauro Carvalho Chehab 				scancode, toggle);
21932cf86f6SMauro Carvalho Chehab 			break;
220120703f9SDavid Härdeman 
22132cf86f6SMauro Carvalho Chehab 		case RC6_MODE_6A:
222db9bc660Slawrence rust 			if (data->count > CHAR_BIT * sizeof data->body) {
22350078a90SSean Young 				dev_dbg(&dev->dev, "RC6 too many (%u) data bits\n",
224db9bc660Slawrence rust 					data->count);
225db9bc660Slawrence rust 				goto out;
22632cf86f6SMauro Carvalho Chehab 			}
22732cf86f6SMauro Carvalho Chehab 
228db9bc660Slawrence rust 			scancode = data->body;
229120703f9SDavid Härdeman 			switch (data->count) {
230120703f9SDavid Härdeman 			case 20:
2316d741bfeSSean Young 				protocol = RC_PROTO_RC6_6A_20;
232120703f9SDavid Härdeman 				toggle = 0;
233120703f9SDavid Härdeman 				break;
234120703f9SDavid Härdeman 			case 24:
2356d741bfeSSean Young 				protocol = RC_PROTO_RC6_6A_24;
236120703f9SDavid Härdeman 				toggle = 0;
237120703f9SDavid Härdeman 				break;
238120703f9SDavid Härdeman 			case 32:
23985e4af0aSMatthias Reichl 				switch (scancode & RC6_6A_LCC_MASK) {
24085e4af0aSMatthias Reichl 				case RC6_6A_MCE_CC:
24185e4af0aSMatthias Reichl 				case RC6_6A_KATHREIN_CC:
242b1f37757SMatthias Reichl 				case RC6_6A_ZOTAC_CC:
2436d741bfeSSean Young 					protocol = RC_PROTO_RC6_MCE;
244120703f9SDavid Härdeman 					toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK);
245d2a74581SDavid Härdeman 					scancode &= ~RC6_6A_MCE_TOGGLE_MASK;
24685e4af0aSMatthias Reichl 					break;
24785e4af0aSMatthias Reichl 				default:
2486d741bfeSSean Young 					protocol = RC_PROTO_RC6_6A_32;
249db9bc660Slawrence rust 					toggle = 0;
25085e4af0aSMatthias Reichl 					break;
251db9bc660Slawrence rust 				}
252120703f9SDavid Härdeman 				break;
253120703f9SDavid Härdeman 			default:
25450078a90SSean Young 				dev_dbg(&dev->dev, "RC6(6A) unsupported length\n");
255120703f9SDavid Härdeman 				goto out;
256120703f9SDavid Härdeman 			}
257120703f9SDavid Härdeman 
25850078a90SSean Young 			dev_dbg(&dev->dev, "RC6(6A) proto 0x%04x, scancode 0x%08x (toggle: %u)\n",
259120703f9SDavid Härdeman 				protocol, scancode, toggle);
26032cf86f6SMauro Carvalho Chehab 			break;
26132cf86f6SMauro Carvalho Chehab 		default:
26250078a90SSean Young 			dev_dbg(&dev->dev, "RC6 unknown mode\n");
26332cf86f6SMauro Carvalho Chehab 			goto out;
26432cf86f6SMauro Carvalho Chehab 		}
26532cf86f6SMauro Carvalho Chehab 
266120703f9SDavid Härdeman 		rc_keydown(dev, protocol, scancode, toggle);
26732cf86f6SMauro Carvalho Chehab 		data->state = STATE_INACTIVE;
26832cf86f6SMauro Carvalho Chehab 		return 0;
26932cf86f6SMauro Carvalho Chehab 	}
27032cf86f6SMauro Carvalho Chehab 
27132cf86f6SMauro Carvalho Chehab out:
27250078a90SSean Young 	dev_dbg(&dev->dev, "RC6 decode failed at state %i (%uus %s)\n",
273528222d8SSean Young 		data->state, ev.duration, TO_STR(ev.pulse));
27432cf86f6SMauro Carvalho Chehab 	data->state = STATE_INACTIVE;
27532cf86f6SMauro Carvalho Chehab 	return -EINVAL;
27632cf86f6SMauro Carvalho Chehab }
27732cf86f6SMauro Carvalho Chehab 
2789d974e49SAntti Seppälä static const struct ir_raw_timings_manchester ir_rc6_timings[4] = {
2799d974e49SAntti Seppälä 	{
280ddf9c1bbSSean Young 		.leader_pulse		= RC6_PREFIX_PULSE,
281ddf9c1bbSSean Young 		.leader_space		= RC6_PREFIX_SPACE,
2829d974e49SAntti Seppälä 		.clock			= RC6_UNIT,
2839d974e49SAntti Seppälä 		.invert			= 1,
2849d974e49SAntti Seppälä 	},
2859d974e49SAntti Seppälä 	{
2869d974e49SAntti Seppälä 		.clock			= RC6_UNIT * 2,
2879d974e49SAntti Seppälä 		.invert			= 1,
2889d974e49SAntti Seppälä 	},
2899d974e49SAntti Seppälä 	{
2909d974e49SAntti Seppälä 		.clock			= RC6_UNIT,
2919d974e49SAntti Seppälä 		.invert			= 1,
2929d974e49SAntti Seppälä 		.trailer_space		= RC6_SUFFIX_SPACE,
2939d974e49SAntti Seppälä 	},
2949d974e49SAntti Seppälä };
2959d974e49SAntti Seppälä 
2969d974e49SAntti Seppälä /**
2979d974e49SAntti Seppälä  * ir_rc6_encode() - Encode a scancode as a stream of raw events
2989d974e49SAntti Seppälä  *
2999d974e49SAntti Seppälä  * @protocol:	protocol to encode
3009d974e49SAntti Seppälä  * @scancode:	scancode to encode
3019d974e49SAntti Seppälä  * @events:	array of raw ir events to write into
3029d974e49SAntti Seppälä  * @max:	maximum size of @events
3039d974e49SAntti Seppälä  *
3049d974e49SAntti Seppälä  * Returns:	The number of events written.
3059d974e49SAntti Seppälä  *		-ENOBUFS if there isn't enough space in the array to fit the
3069d974e49SAntti Seppälä  *		encoding. In this case all @max events will have been written.
3079d974e49SAntti Seppälä  *		-EINVAL if the scancode is ambiguous or invalid.
3089d974e49SAntti Seppälä  */
ir_rc6_encode(enum rc_proto protocol,u32 scancode,struct ir_raw_event * events,unsigned int max)3096d741bfeSSean Young static int ir_rc6_encode(enum rc_proto protocol, u32 scancode,
3109d974e49SAntti Seppälä 			 struct ir_raw_event *events, unsigned int max)
3119d974e49SAntti Seppälä {
3129d974e49SAntti Seppälä 	int ret;
3139d974e49SAntti Seppälä 	struct ir_raw_event *e = events;
3149d974e49SAntti Seppälä 
3156d741bfeSSean Young 	if (protocol == RC_PROTO_RC6_0) {
3169d974e49SAntti Seppälä 		/* Modulate the header (Start Bit & Mode-0) */
3179d974e49SAntti Seppälä 		ret = ir_raw_gen_manchester(&e, max - (e - events),
318ddf9c1bbSSean Young 					    &ir_rc6_timings[0],
31980008ddbSSean Young 					    RC6_HEADER_NBITS, (1 << 3));
3209d974e49SAntti Seppälä 		if (ret < 0)
3219d974e49SAntti Seppälä 			return ret;
3229d974e49SAntti Seppälä 
3239d974e49SAntti Seppälä 		/* Modulate Trailer Bit */
3249d974e49SAntti Seppälä 		ret = ir_raw_gen_manchester(&e, max - (e - events),
325ddf9c1bbSSean Young 					    &ir_rc6_timings[1], 1, 0);
3269d974e49SAntti Seppälä 		if (ret < 0)
3279d974e49SAntti Seppälä 			return ret;
3289d974e49SAntti Seppälä 
3299d974e49SAntti Seppälä 		/* Modulate rest of the data */
3309d974e49SAntti Seppälä 		ret = ir_raw_gen_manchester(&e, max - (e - events),
331ddf9c1bbSSean Young 					    &ir_rc6_timings[2], RC6_0_NBITS,
3329d974e49SAntti Seppälä 					    scancode);
3339d974e49SAntti Seppälä 		if (ret < 0)
3349d974e49SAntti Seppälä 			return ret;
3359d974e49SAntti Seppälä 
3369d974e49SAntti Seppälä 	} else {
3379d974e49SAntti Seppälä 		int bits;
3389d974e49SAntti Seppälä 
3399d974e49SAntti Seppälä 		switch (protocol) {
3406d741bfeSSean Young 		case RC_PROTO_RC6_MCE:
3416d741bfeSSean Young 		case RC_PROTO_RC6_6A_32:
3429d974e49SAntti Seppälä 			bits = 32;
3439d974e49SAntti Seppälä 			break;
3446d741bfeSSean Young 		case RC_PROTO_RC6_6A_24:
3459d974e49SAntti Seppälä 			bits = 24;
3469d974e49SAntti Seppälä 			break;
3476d741bfeSSean Young 		case RC_PROTO_RC6_6A_20:
3489d974e49SAntti Seppälä 			bits = 20;
3499d974e49SAntti Seppälä 			break;
3509d974e49SAntti Seppälä 		default:
3519d974e49SAntti Seppälä 			return -EINVAL;
3529d974e49SAntti Seppälä 		}
3539d974e49SAntti Seppälä 
3549d974e49SAntti Seppälä 		/* Modulate the header (Start Bit & Header-version 6 */
3559d974e49SAntti Seppälä 		ret = ir_raw_gen_manchester(&e, max - (e - events),
356ddf9c1bbSSean Young 					    &ir_rc6_timings[0],
35780008ddbSSean Young 					    RC6_HEADER_NBITS, (1 << 3 | 6));
3589d974e49SAntti Seppälä 		if (ret < 0)
3599d974e49SAntti Seppälä 			return ret;
3609d974e49SAntti Seppälä 
3619d974e49SAntti Seppälä 		/* Modulate Trailer Bit */
3629d974e49SAntti Seppälä 		ret = ir_raw_gen_manchester(&e, max - (e - events),
363ddf9c1bbSSean Young 					    &ir_rc6_timings[1], 1, 0);
3649d974e49SAntti Seppälä 		if (ret < 0)
3659d974e49SAntti Seppälä 			return ret;
3669d974e49SAntti Seppälä 
3679d974e49SAntti Seppälä 		/* Modulate rest of the data */
3689d974e49SAntti Seppälä 		ret = ir_raw_gen_manchester(&e, max - (e - events),
369ddf9c1bbSSean Young 					    &ir_rc6_timings[2],
3709d974e49SAntti Seppälä 					    bits,
3719d974e49SAntti Seppälä 					    scancode);
3729d974e49SAntti Seppälä 		if (ret < 0)
3739d974e49SAntti Seppälä 			return ret;
3749d974e49SAntti Seppälä 	}
3759d974e49SAntti Seppälä 
3769d974e49SAntti Seppälä 	return e - events;
3779d974e49SAntti Seppälä }
3789d974e49SAntti Seppälä 
37932cf86f6SMauro Carvalho Chehab static struct ir_raw_handler rc6_handler = {
3806d741bfeSSean Young 	.protocols	= RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 |
3816d741bfeSSean Young 			  RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 |
3826d741bfeSSean Young 			  RC_PROTO_BIT_RC6_MCE,
38332cf86f6SMauro Carvalho Chehab 	.decode		= ir_rc6_decode,
3849d974e49SAntti Seppälä 	.encode		= ir_rc6_encode,
385cdfaa01cSSean Young 	.carrier	= 36000,
386a86d6df8SSean Young 	.min_timeout	= RC6_SUFFIX_SPACE,
38732cf86f6SMauro Carvalho Chehab };
38832cf86f6SMauro Carvalho Chehab 
ir_rc6_decode_init(void)38932cf86f6SMauro Carvalho Chehab static int __init ir_rc6_decode_init(void)
39032cf86f6SMauro Carvalho Chehab {
39132cf86f6SMauro Carvalho Chehab 	ir_raw_handler_register(&rc6_handler);
39232cf86f6SMauro Carvalho Chehab 
39332cf86f6SMauro Carvalho Chehab 	printk(KERN_INFO "IR RC6 protocol handler initialized\n");
39432cf86f6SMauro Carvalho Chehab 	return 0;
39532cf86f6SMauro Carvalho Chehab }
39632cf86f6SMauro Carvalho Chehab 
ir_rc6_decode_exit(void)39732cf86f6SMauro Carvalho Chehab static void __exit ir_rc6_decode_exit(void)
39832cf86f6SMauro Carvalho Chehab {
39932cf86f6SMauro Carvalho Chehab 	ir_raw_handler_unregister(&rc6_handler);
40032cf86f6SMauro Carvalho Chehab }
40132cf86f6SMauro Carvalho Chehab 
40232cf86f6SMauro Carvalho Chehab module_init(ir_rc6_decode_init);
40332cf86f6SMauro Carvalho Chehab module_exit(ir_rc6_decode_exit);
40432cf86f6SMauro Carvalho Chehab 
40532cf86f6SMauro Carvalho Chehab MODULE_LICENSE("GPL");
40632cf86f6SMauro Carvalho Chehab MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
40732cf86f6SMauro Carvalho Chehab MODULE_DESCRIPTION("RC6 IR protocol decoder");
408