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