18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* ir-rc6-decoder.c - A decoder for the RC6 IR protocol 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2010 by David Härdeman <david@hardeman.nu> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "rc-core-priv.h" 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci * This decoder currently supports: 128c2ecf20Sopenharmony_ci * RC6-0-16 (standard toggle bit in header) 138c2ecf20Sopenharmony_ci * RC6-6A-20 (no toggle bit) 148c2ecf20Sopenharmony_ci * RC6-6A-24 (no toggle bit) 158c2ecf20Sopenharmony_ci * RC6-6A-32 (MCE version with toggle bit in body) 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define RC6_UNIT 444 /* microseconds */ 198c2ecf20Sopenharmony_ci#define RC6_HEADER_NBITS 4 /* not including toggle bit */ 208c2ecf20Sopenharmony_ci#define RC6_0_NBITS 16 218c2ecf20Sopenharmony_ci#define RC6_6A_32_NBITS 32 228c2ecf20Sopenharmony_ci#define RC6_6A_NBITS 128 /* Variable 8..128 */ 238c2ecf20Sopenharmony_ci#define RC6_PREFIX_PULSE (6 * RC6_UNIT) 248c2ecf20Sopenharmony_ci#define RC6_PREFIX_SPACE (2 * RC6_UNIT) 258c2ecf20Sopenharmony_ci#define RC6_BIT_START (1 * RC6_UNIT) 268c2ecf20Sopenharmony_ci#define RC6_BIT_END (1 * RC6_UNIT) 278c2ecf20Sopenharmony_ci#define RC6_TOGGLE_START (2 * RC6_UNIT) 288c2ecf20Sopenharmony_ci#define RC6_TOGGLE_END (2 * RC6_UNIT) 298c2ecf20Sopenharmony_ci#define RC6_SUFFIX_SPACE (6 * RC6_UNIT) 308c2ecf20Sopenharmony_ci#define RC6_MODE_MASK 0x07 /* for the header bits */ 318c2ecf20Sopenharmony_ci#define RC6_STARTBIT_MASK 0x08 /* for the header bits */ 328c2ecf20Sopenharmony_ci#define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ 338c2ecf20Sopenharmony_ci#define RC6_6A_LCC_MASK 0xffff0000 /* RC6-6A-32 long customer code mask */ 348c2ecf20Sopenharmony_ci#define RC6_6A_MCE_CC 0x800f0000 /* MCE customer code */ 358c2ecf20Sopenharmony_ci#define RC6_6A_ZOTAC_CC 0x80340000 /* Zotac customer code */ 368c2ecf20Sopenharmony_ci#define RC6_6A_KATHREIN_CC 0x80460000 /* Kathrein RCU-676 customer code */ 378c2ecf20Sopenharmony_ci#ifndef CHAR_BIT 388c2ecf20Sopenharmony_ci#define CHAR_BIT 8 /* Normally in <limits.h> */ 398c2ecf20Sopenharmony_ci#endif 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cienum rc6_mode { 428c2ecf20Sopenharmony_ci RC6_MODE_0, 438c2ecf20Sopenharmony_ci RC6_MODE_6A, 448c2ecf20Sopenharmony_ci RC6_MODE_UNKNOWN, 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cienum rc6_state { 488c2ecf20Sopenharmony_ci STATE_INACTIVE, 498c2ecf20Sopenharmony_ci STATE_PREFIX_SPACE, 508c2ecf20Sopenharmony_ci STATE_HEADER_BIT_START, 518c2ecf20Sopenharmony_ci STATE_HEADER_BIT_END, 528c2ecf20Sopenharmony_ci STATE_TOGGLE_START, 538c2ecf20Sopenharmony_ci STATE_TOGGLE_END, 548c2ecf20Sopenharmony_ci STATE_BODY_BIT_START, 558c2ecf20Sopenharmony_ci STATE_BODY_BIT_END, 568c2ecf20Sopenharmony_ci STATE_FINISHED, 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic enum rc6_mode rc6_mode(struct rc6_dec *data) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci switch (data->header & RC6_MODE_MASK) { 628c2ecf20Sopenharmony_ci case 0: 638c2ecf20Sopenharmony_ci return RC6_MODE_0; 648c2ecf20Sopenharmony_ci case 6: 658c2ecf20Sopenharmony_ci if (!data->toggle) 668c2ecf20Sopenharmony_ci return RC6_MODE_6A; 678c2ecf20Sopenharmony_ci fallthrough; 688c2ecf20Sopenharmony_ci default: 698c2ecf20Sopenharmony_ci return RC6_MODE_UNKNOWN; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci/** 748c2ecf20Sopenharmony_ci * ir_rc6_decode() - Decode one RC6 pulse or space 758c2ecf20Sopenharmony_ci * @dev: the struct rc_dev descriptor of the device 768c2ecf20Sopenharmony_ci * @ev: the struct ir_raw_event descriptor of the pulse/space 778c2ecf20Sopenharmony_ci * 788c2ecf20Sopenharmony_ci * This function returns -EINVAL if the pulse violates the state machine 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_cistatic int ir_rc6_decode(struct rc_dev *dev, struct ir_raw_event ev) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct rc6_dec *data = &dev->raw->rc6; 838c2ecf20Sopenharmony_ci u32 scancode; 848c2ecf20Sopenharmony_ci u8 toggle; 858c2ecf20Sopenharmony_ci enum rc_proto protocol; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (!is_timing_event(ev)) { 888c2ecf20Sopenharmony_ci if (ev.reset) 898c2ecf20Sopenharmony_ci data->state = STATE_INACTIVE; 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) 948c2ecf20Sopenharmony_ci goto out; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ciagain: 978c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RC6 decode started at state %i (%uus %s)\n", 988c2ecf20Sopenharmony_ci data->state, ev.duration, TO_STR(ev.pulse)); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (!geq_margin(ev.duration, RC6_UNIT, RC6_UNIT / 2)) 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci switch (data->state) { 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci case STATE_INACTIVE: 1068c2ecf20Sopenharmony_ci if (!ev.pulse) 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Note: larger margin on first pulse since each RC6_UNIT 1108c2ecf20Sopenharmony_ci is quite short and some hardware takes some time to 1118c2ecf20Sopenharmony_ci adjust to the signal */ 1128c2ecf20Sopenharmony_ci if (!eq_margin(ev.duration, RC6_PREFIX_PULSE, RC6_UNIT)) 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci data->state = STATE_PREFIX_SPACE; 1168c2ecf20Sopenharmony_ci data->count = 0; 1178c2ecf20Sopenharmony_ci return 0; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci case STATE_PREFIX_SPACE: 1208c2ecf20Sopenharmony_ci if (ev.pulse) 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!eq_margin(ev.duration, RC6_PREFIX_SPACE, RC6_UNIT / 2)) 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci data->state = STATE_HEADER_BIT_START; 1278c2ecf20Sopenharmony_ci data->header = 0; 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci case STATE_HEADER_BIT_START: 1318c2ecf20Sopenharmony_ci if (!eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci data->header <<= 1; 1358c2ecf20Sopenharmony_ci if (ev.pulse) 1368c2ecf20Sopenharmony_ci data->header |= 1; 1378c2ecf20Sopenharmony_ci data->count++; 1388c2ecf20Sopenharmony_ci data->state = STATE_HEADER_BIT_END; 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci case STATE_HEADER_BIT_END: 1428c2ecf20Sopenharmony_ci if (data->count == RC6_HEADER_NBITS) 1438c2ecf20Sopenharmony_ci data->state = STATE_TOGGLE_START; 1448c2ecf20Sopenharmony_ci else 1458c2ecf20Sopenharmony_ci data->state = STATE_HEADER_BIT_START; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci decrease_duration(&ev, RC6_BIT_END); 1488c2ecf20Sopenharmony_ci goto again; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci case STATE_TOGGLE_START: 1518c2ecf20Sopenharmony_ci if (!eq_margin(ev.duration, RC6_TOGGLE_START, RC6_UNIT / 2)) 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci data->toggle = ev.pulse; 1558c2ecf20Sopenharmony_ci data->state = STATE_TOGGLE_END; 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci case STATE_TOGGLE_END: 1598c2ecf20Sopenharmony_ci if (!(data->header & RC6_STARTBIT_MASK)) { 1608c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RC6 invalid start bit\n"); 1618c2ecf20Sopenharmony_ci break; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci data->state = STATE_BODY_BIT_START; 1658c2ecf20Sopenharmony_ci decrease_duration(&ev, RC6_TOGGLE_END); 1668c2ecf20Sopenharmony_ci data->count = 0; 1678c2ecf20Sopenharmony_ci data->body = 0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci switch (rc6_mode(data)) { 1708c2ecf20Sopenharmony_ci case RC6_MODE_0: 1718c2ecf20Sopenharmony_ci data->wanted_bits = RC6_0_NBITS; 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci case RC6_MODE_6A: 1748c2ecf20Sopenharmony_ci data->wanted_bits = RC6_6A_NBITS; 1758c2ecf20Sopenharmony_ci break; 1768c2ecf20Sopenharmony_ci default: 1778c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RC6 unknown mode\n"); 1788c2ecf20Sopenharmony_ci goto out; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci goto again; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci case STATE_BODY_BIT_START: 1838c2ecf20Sopenharmony_ci if (eq_margin(ev.duration, RC6_BIT_START, RC6_UNIT / 2)) { 1848c2ecf20Sopenharmony_ci /* Discard LSB's that won't fit in data->body */ 1858c2ecf20Sopenharmony_ci if (data->count++ < CHAR_BIT * sizeof data->body) { 1868c2ecf20Sopenharmony_ci data->body <<= 1; 1878c2ecf20Sopenharmony_ci if (ev.pulse) 1888c2ecf20Sopenharmony_ci data->body |= 1; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci data->state = STATE_BODY_BIT_END; 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci } else if (RC6_MODE_6A == rc6_mode(data) && !ev.pulse && 1938c2ecf20Sopenharmony_ci geq_margin(ev.duration, RC6_SUFFIX_SPACE, RC6_UNIT / 2)) { 1948c2ecf20Sopenharmony_ci data->state = STATE_FINISHED; 1958c2ecf20Sopenharmony_ci goto again; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci case STATE_BODY_BIT_END: 2008c2ecf20Sopenharmony_ci if (data->count == data->wanted_bits) 2018c2ecf20Sopenharmony_ci data->state = STATE_FINISHED; 2028c2ecf20Sopenharmony_ci else 2038c2ecf20Sopenharmony_ci data->state = STATE_BODY_BIT_START; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci decrease_duration(&ev, RC6_BIT_END); 2068c2ecf20Sopenharmony_ci goto again; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci case STATE_FINISHED: 2098c2ecf20Sopenharmony_ci if (ev.pulse) 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci switch (rc6_mode(data)) { 2138c2ecf20Sopenharmony_ci case RC6_MODE_0: 2148c2ecf20Sopenharmony_ci scancode = data->body; 2158c2ecf20Sopenharmony_ci toggle = data->toggle; 2168c2ecf20Sopenharmony_ci protocol = RC_PROTO_RC6_0; 2178c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RC6(0) scancode 0x%04x (toggle: %u)\n", 2188c2ecf20Sopenharmony_ci scancode, toggle); 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci case RC6_MODE_6A: 2228c2ecf20Sopenharmony_ci if (data->count > CHAR_BIT * sizeof data->body) { 2238c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RC6 too many (%u) data bits\n", 2248c2ecf20Sopenharmony_ci data->count); 2258c2ecf20Sopenharmony_ci goto out; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci scancode = data->body; 2298c2ecf20Sopenharmony_ci switch (data->count) { 2308c2ecf20Sopenharmony_ci case 20: 2318c2ecf20Sopenharmony_ci protocol = RC_PROTO_RC6_6A_20; 2328c2ecf20Sopenharmony_ci toggle = 0; 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci case 24: 2358c2ecf20Sopenharmony_ci protocol = RC_PROTO_RC6_6A_24; 2368c2ecf20Sopenharmony_ci toggle = 0; 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci case 32: 2398c2ecf20Sopenharmony_ci switch (scancode & RC6_6A_LCC_MASK) { 2408c2ecf20Sopenharmony_ci case RC6_6A_MCE_CC: 2418c2ecf20Sopenharmony_ci case RC6_6A_KATHREIN_CC: 2428c2ecf20Sopenharmony_ci case RC6_6A_ZOTAC_CC: 2438c2ecf20Sopenharmony_ci protocol = RC_PROTO_RC6_MCE; 2448c2ecf20Sopenharmony_ci toggle = !!(scancode & RC6_6A_MCE_TOGGLE_MASK); 2458c2ecf20Sopenharmony_ci scancode &= ~RC6_6A_MCE_TOGGLE_MASK; 2468c2ecf20Sopenharmony_ci break; 2478c2ecf20Sopenharmony_ci default: 2488c2ecf20Sopenharmony_ci protocol = RC_PROTO_RC6_6A_32; 2498c2ecf20Sopenharmony_ci toggle = 0; 2508c2ecf20Sopenharmony_ci break; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci default: 2548c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RC6(6A) unsupported length\n"); 2558c2ecf20Sopenharmony_ci goto out; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RC6(6A) proto 0x%04x, scancode 0x%08x (toggle: %u)\n", 2598c2ecf20Sopenharmony_ci protocol, scancode, toggle); 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci default: 2628c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RC6 unknown mode\n"); 2638c2ecf20Sopenharmony_ci goto out; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci rc_keydown(dev, protocol, scancode, toggle); 2678c2ecf20Sopenharmony_ci data->state = STATE_INACTIVE; 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ciout: 2728c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "RC6 decode failed at state %i (%uus %s)\n", 2738c2ecf20Sopenharmony_ci data->state, ev.duration, TO_STR(ev.pulse)); 2748c2ecf20Sopenharmony_ci data->state = STATE_INACTIVE; 2758c2ecf20Sopenharmony_ci return -EINVAL; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic const struct ir_raw_timings_manchester ir_rc6_timings[4] = { 2798c2ecf20Sopenharmony_ci { 2808c2ecf20Sopenharmony_ci .leader_pulse = RC6_PREFIX_PULSE, 2818c2ecf20Sopenharmony_ci .leader_space = RC6_PREFIX_SPACE, 2828c2ecf20Sopenharmony_ci .clock = RC6_UNIT, 2838c2ecf20Sopenharmony_ci .invert = 1, 2848c2ecf20Sopenharmony_ci }, 2858c2ecf20Sopenharmony_ci { 2868c2ecf20Sopenharmony_ci .clock = RC6_UNIT * 2, 2878c2ecf20Sopenharmony_ci .invert = 1, 2888c2ecf20Sopenharmony_ci }, 2898c2ecf20Sopenharmony_ci { 2908c2ecf20Sopenharmony_ci .clock = RC6_UNIT, 2918c2ecf20Sopenharmony_ci .invert = 1, 2928c2ecf20Sopenharmony_ci .trailer_space = RC6_SUFFIX_SPACE, 2938c2ecf20Sopenharmony_ci }, 2948c2ecf20Sopenharmony_ci}; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci/** 2978c2ecf20Sopenharmony_ci * ir_rc6_encode() - Encode a scancode as a stream of raw events 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * @protocol: protocol to encode 3008c2ecf20Sopenharmony_ci * @scancode: scancode to encode 3018c2ecf20Sopenharmony_ci * @events: array of raw ir events to write into 3028c2ecf20Sopenharmony_ci * @max: maximum size of @events 3038c2ecf20Sopenharmony_ci * 3048c2ecf20Sopenharmony_ci * Returns: The number of events written. 3058c2ecf20Sopenharmony_ci * -ENOBUFS if there isn't enough space in the array to fit the 3068c2ecf20Sopenharmony_ci * encoding. In this case all @max events will have been written. 3078c2ecf20Sopenharmony_ci * -EINVAL if the scancode is ambiguous or invalid. 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_cistatic int ir_rc6_encode(enum rc_proto protocol, u32 scancode, 3108c2ecf20Sopenharmony_ci struct ir_raw_event *events, unsigned int max) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci int ret; 3138c2ecf20Sopenharmony_ci struct ir_raw_event *e = events; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (protocol == RC_PROTO_RC6_0) { 3168c2ecf20Sopenharmony_ci /* Modulate the header (Start Bit & Mode-0) */ 3178c2ecf20Sopenharmony_ci ret = ir_raw_gen_manchester(&e, max - (e - events), 3188c2ecf20Sopenharmony_ci &ir_rc6_timings[0], 3198c2ecf20Sopenharmony_ci RC6_HEADER_NBITS, (1 << 3)); 3208c2ecf20Sopenharmony_ci if (ret < 0) 3218c2ecf20Sopenharmony_ci return ret; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* Modulate Trailer Bit */ 3248c2ecf20Sopenharmony_ci ret = ir_raw_gen_manchester(&e, max - (e - events), 3258c2ecf20Sopenharmony_ci &ir_rc6_timings[1], 1, 0); 3268c2ecf20Sopenharmony_ci if (ret < 0) 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci /* Modulate rest of the data */ 3308c2ecf20Sopenharmony_ci ret = ir_raw_gen_manchester(&e, max - (e - events), 3318c2ecf20Sopenharmony_ci &ir_rc6_timings[2], RC6_0_NBITS, 3328c2ecf20Sopenharmony_ci scancode); 3338c2ecf20Sopenharmony_ci if (ret < 0) 3348c2ecf20Sopenharmony_ci return ret; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci } else { 3378c2ecf20Sopenharmony_ci int bits; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci switch (protocol) { 3408c2ecf20Sopenharmony_ci case RC_PROTO_RC6_MCE: 3418c2ecf20Sopenharmony_ci case RC_PROTO_RC6_6A_32: 3428c2ecf20Sopenharmony_ci bits = 32; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci case RC_PROTO_RC6_6A_24: 3458c2ecf20Sopenharmony_ci bits = 24; 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci case RC_PROTO_RC6_6A_20: 3488c2ecf20Sopenharmony_ci bits = 20; 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci default: 3518c2ecf20Sopenharmony_ci return -EINVAL; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* Modulate the header (Start Bit & Header-version 6 */ 3558c2ecf20Sopenharmony_ci ret = ir_raw_gen_manchester(&e, max - (e - events), 3568c2ecf20Sopenharmony_ci &ir_rc6_timings[0], 3578c2ecf20Sopenharmony_ci RC6_HEADER_NBITS, (1 << 3 | 6)); 3588c2ecf20Sopenharmony_ci if (ret < 0) 3598c2ecf20Sopenharmony_ci return ret; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Modulate Trailer Bit */ 3628c2ecf20Sopenharmony_ci ret = ir_raw_gen_manchester(&e, max - (e - events), 3638c2ecf20Sopenharmony_ci &ir_rc6_timings[1], 1, 0); 3648c2ecf20Sopenharmony_ci if (ret < 0) 3658c2ecf20Sopenharmony_ci return ret; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Modulate rest of the data */ 3688c2ecf20Sopenharmony_ci ret = ir_raw_gen_manchester(&e, max - (e - events), 3698c2ecf20Sopenharmony_ci &ir_rc6_timings[2], 3708c2ecf20Sopenharmony_ci bits, 3718c2ecf20Sopenharmony_ci scancode); 3728c2ecf20Sopenharmony_ci if (ret < 0) 3738c2ecf20Sopenharmony_ci return ret; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci return e - events; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic struct ir_raw_handler rc6_handler = { 3808c2ecf20Sopenharmony_ci .protocols = RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | 3818c2ecf20Sopenharmony_ci RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | 3828c2ecf20Sopenharmony_ci RC_PROTO_BIT_RC6_MCE, 3838c2ecf20Sopenharmony_ci .decode = ir_rc6_decode, 3848c2ecf20Sopenharmony_ci .encode = ir_rc6_encode, 3858c2ecf20Sopenharmony_ci .carrier = 36000, 3868c2ecf20Sopenharmony_ci .min_timeout = RC6_SUFFIX_SPACE, 3878c2ecf20Sopenharmony_ci}; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic int __init ir_rc6_decode_init(void) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci ir_raw_handler_register(&rc6_handler); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci printk(KERN_INFO "IR RC6 protocol handler initialized\n"); 3948c2ecf20Sopenharmony_ci return 0; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic void __exit ir_rc6_decode_exit(void) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci ir_raw_handler_unregister(&rc6_handler); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cimodule_init(ir_rc6_decode_init); 4038c2ecf20Sopenharmony_cimodule_exit(ir_rc6_decode_exit); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4068c2ecf20Sopenharmony_ciMODULE_AUTHOR("David Härdeman <david@hardeman.nu>"); 4078c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("RC6 IR protocol decoder"); 408