Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] rc: Add support for decoding XMP protocol

This protocol is found on Dreambox remotes

[m.chehab@samsung.com: CodingStyle fixes and conflict fix]
Signed-off-by: Marcel Mol <marcel@mesa.nl>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>

authored by

Marcel J.E. Mol and committed by
Mauro Carvalho Chehab
1dee9b59 0a6824bc

+255 -1
+10
drivers/media/rc/Kconfig
··· 113 113 Enable this option if you have a Microsoft Remote Keyboard for 114 114 Windows Media Center Edition, which you would like to use with 115 115 a raw IR receiver in your system. 116 + 117 + config IR_XMP_DECODER 118 + tristate "Enable IR raw decoder for the XMP protocol" 119 + depends on RC_CORE 120 + select BITREVERSE 121 + default y 122 + 123 + ---help--- 124 + Enable this option if you have IR with XMP protocol, and 125 + if the IR is decoded in software 116 126 endif #RC_DECODERS 117 127 118 128 menuconfig RC_DEVICES
+1
drivers/media/rc/Makefile
··· 13 13 obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o 14 14 obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o 15 15 obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o 16 + obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o 16 17 17 18 # stand-alone IR receivers/transmitters 18 19 obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o
+225
drivers/media/rc/ir-xmp-decoder.c
··· 1 + /* ir-xmp-decoder.c - handle XMP IR Pulse/Space protocol 2 + * 3 + * Copyright (C) 2014 by Marcel Mol 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License as published by 7 + * the Free Software Foundation version 2 of the License. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * - Based on info from http://www.hifi-remote.com 15 + * - Ignore Toggle=9 frames 16 + * - Ignore XMP-1 XMP-2 difference, always store 16 bit OBC 17 + */ 18 + 19 + #include <linux/bitrev.h> 20 + #include <linux/module.h> 21 + #include "rc-core-priv.h" 22 + 23 + #define XMP_UNIT 136000 /* ns */ 24 + #define XMP_LEADER 210000 /* ns */ 25 + #define XMP_NIBBLE_PREFIX 760000 /* ns */ 26 + #define XMP_HALFFRAME_SPACE 13800000 /* ns */ 27 + #define XMP_TRAILER_SPACE 20000000 /* should be 80ms but not all dureation supliers can go that high */ 28 + 29 + enum xmp_state { 30 + STATE_INACTIVE, 31 + STATE_LEADER_PULSE, 32 + STATE_NIBBLE_SPACE, 33 + }; 34 + 35 + /** 36 + * ir_xmp_decode() - Decode one XMP pulse or space 37 + * @dev: the struct rc_dev descriptor of the device 38 + * @duration: the struct ir_raw_event descriptor of the pulse/space 39 + * 40 + * This function returns -EINVAL if the pulse violates the state machine 41 + */ 42 + static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev) 43 + { 44 + struct xmp_dec *data = &dev->raw->xmp; 45 + 46 + if (!(dev->enabled_protocols & RC_BIT_XMP)) 47 + return 0; 48 + 49 + if (!is_timing_event(ev)) { 50 + if (ev.reset) 51 + data->state = STATE_INACTIVE; 52 + return 0; 53 + } 54 + 55 + IR_dprintk(2, "XMP decode started at state %d %d (%uus %s)\n", 56 + data->state, data->count, TO_US(ev.duration), TO_STR(ev.pulse)); 57 + 58 + switch (data->state) { 59 + 60 + case STATE_INACTIVE: 61 + if (!ev.pulse) 62 + break; 63 + 64 + if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2)) { 65 + data->count = 0; 66 + data->state = STATE_NIBBLE_SPACE; 67 + } 68 + 69 + return 0; 70 + 71 + case STATE_LEADER_PULSE: 72 + if (!ev.pulse) 73 + break; 74 + 75 + if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2)) 76 + data->state = STATE_NIBBLE_SPACE; 77 + 78 + return 0; 79 + 80 + case STATE_NIBBLE_SPACE: 81 + if (ev.pulse) 82 + break; 83 + 84 + if (geq_margin(ev.duration, XMP_TRAILER_SPACE, XMP_NIBBLE_PREFIX)) { 85 + int divider, i; 86 + u8 addr, subaddr, subaddr2, toggle, oem, obc1, obc2, sum1, sum2; 87 + u32 *n; 88 + u32 scancode; 89 + 90 + if (data->count != 16) { 91 + IR_dprintk(2, "received TRAILER period at index %d: %u\n", 92 + data->count, ev.duration); 93 + data->state = STATE_INACTIVE; 94 + return -EINVAL; 95 + } 96 + 97 + n = data->durations; 98 + /* 99 + * the 4th nibble should be 15 so base the divider on this 100 + * to transform durations into nibbles. Substract 2000 from 101 + * the divider to compensate for fluctuations in the signal 102 + */ 103 + divider = (n[3] - XMP_NIBBLE_PREFIX) / 15 - 2000; 104 + if (divider < 50) { 105 + IR_dprintk(2, "divider to small %d.\n", divider); 106 + data->state = STATE_INACTIVE; 107 + return -EINVAL; 108 + } 109 + 110 + /* convert to nibbles and do some sanity checks */ 111 + for (i = 0; i < 16; i++) 112 + n[i] = (n[i] - XMP_NIBBLE_PREFIX) / divider; 113 + sum1 = (15 + n[0] + n[1] + n[2] + n[3] + 114 + n[4] + n[5] + n[6] + n[7]) % 16; 115 + sum2 = (15 + n[8] + n[9] + n[10] + n[11] + 116 + n[12] + n[13] + n[14] + n[15]) % 16; 117 + 118 + if (sum1 != 15 || sum2 != 15) { 119 + IR_dprintk(2, "checksum errors sum1=0x%X sum2=0x%X\n", 120 + sum1, sum2); 121 + data->state = STATE_INACTIVE; 122 + return -EINVAL; 123 + } 124 + 125 + subaddr = n[0] << 4 | n[2]; 126 + subaddr2 = n[8] << 4 | n[11]; 127 + oem = n[4] << 4 | n[5]; 128 + addr = n[6] << 4 | n[7]; 129 + toggle = n[10]; 130 + obc1 = n[12] << 4 | n[13]; 131 + obc2 = n[14] << 4 | n[15]; 132 + if (subaddr != subaddr2) { 133 + IR_dprintk(2, "subaddress nibbles mismatch 0x%02X != 0x%02X\n", 134 + subaddr, subaddr2); 135 + data->state = STATE_INACTIVE; 136 + return -EINVAL; 137 + } 138 + if (oem != 0x44) 139 + IR_dprintk(1, "Warning: OEM nibbles 0x%02X. Expected 0x44\n", 140 + oem); 141 + 142 + scancode = addr << 24 | subaddr << 16 | 143 + obc1 << 8 | obc2; 144 + IR_dprintk(1, "XMP scancode 0x%06x\n", scancode); 145 + 146 + if (toggle == 0) { 147 + rc_keydown(dev, RC_TYPE_XMP, scancode, 0); 148 + } else { 149 + rc_repeat(dev); 150 + IR_dprintk(1, "Repeat last key\n"); 151 + } 152 + data->state = STATE_INACTIVE; 153 + 154 + return 0; 155 + 156 + } else if (geq_margin(ev.duration, XMP_HALFFRAME_SPACE, XMP_NIBBLE_PREFIX)) { 157 + /* Expect 8 or 16 nibble pulses. 16 in case of 'final' frame */ 158 + if (data->count == 16) { 159 + IR_dprintk(2, "received half frame pulse at index %d. Probably a final frame key-up event: %u\n", 160 + data->count, ev.duration); 161 + /* 162 + * TODO: for now go back to half frame position 163 + * so trailer can be found and key press 164 + * can be handled. 165 + */ 166 + data->count = 8; 167 + } 168 + 169 + else if (data->count != 8) 170 + IR_dprintk(2, "received half frame pulse at index %d: %u\n", 171 + data->count, ev.duration); 172 + data->state = STATE_LEADER_PULSE; 173 + 174 + return 0; 175 + 176 + } else if (geq_margin(ev.duration, XMP_NIBBLE_PREFIX, XMP_UNIT)) { 177 + /* store nibble raw data, decode after trailer */ 178 + if (data->count == 16) { 179 + IR_dprintk(2, "to many pulses (%d) ignoring: %u\n", 180 + data->count, ev.duration); 181 + data->state = STATE_INACTIVE; 182 + return -EINVAL; 183 + } 184 + data->durations[data->count] = ev.duration; 185 + data->count++; 186 + data->state = STATE_LEADER_PULSE; 187 + 188 + return 0; 189 + 190 + } 191 + 192 + break; 193 + } 194 + 195 + IR_dprintk(1, "XMP decode failed at count %d state %d (%uus %s)\n", 196 + data->count, data->state, TO_US(ev.duration), TO_STR(ev.pulse)); 197 + data->state = STATE_INACTIVE; 198 + return -EINVAL; 199 + } 200 + 201 + static struct ir_raw_handler xmp_handler = { 202 + .protocols = RC_BIT_XMP, 203 + .decode = ir_xmp_decode, 204 + }; 205 + 206 + static int __init ir_xmp_decode_init(void) 207 + { 208 + ir_raw_handler_register(&xmp_handler); 209 + 210 + printk(KERN_INFO "IR XMP protocol handler initialized\n"); 211 + return 0; 212 + } 213 + 214 + static void __exit ir_xmp_decode_exit(void) 215 + { 216 + ir_raw_handler_unregister(&xmp_handler); 217 + } 218 + 219 + module_init(ir_xmp_decode_init); 220 + module_exit(ir_xmp_decode_exit); 221 + 222 + MODULE_LICENSE("GPL"); 223 + MODULE_AUTHOR("Marcel Mol <marcel@mesa.nl>"); 224 + MODULE_AUTHOR("MESA Consulting (http://www.mesa.nl)"); 225 + MODULE_DESCRIPTION("XMP IR protocol decoder");
+12
drivers/media/rc/rc-core-priv.h
··· 110 110 bool send_timeout_reports; 111 111 112 112 } lirc; 113 + struct xmp_dec { 114 + int state; 115 + unsigned count; 116 + u32 durations[16]; 117 + } xmp; 113 118 }; 114 119 115 120 /* macros for IR decoders */ ··· 228 223 #define load_lirc_codec() request_module_nowait("ir-lirc-codec") 229 224 #else 230 225 static inline void load_lirc_codec(void) { } 226 + #endif 227 + 228 + /* from ir-xmp-decoder.c */ 229 + #ifdef CONFIG_IR_XMP_DECODER_MODULE 230 + #define load_xmp_decode() request_module_nowait("ir-xmp-decoder") 231 + #else 232 + static inline void load_xmp_decode(void) { } 231 233 #endif 232 234 233 235
+1
drivers/media/rc/rc-ir-raw.c
··· 362 362 load_sharp_decode(); 363 363 load_mce_kbd_decode(); 364 364 load_lirc_codec(); 365 + load_xmp_decode(); 365 366 366 367 /* If needed, we may later add some init code. In this case, 367 368 it is needed to change the CONFIG_MODULE test at rc-core.h
+1
drivers/media/rc/rc-main.c
··· 800 800 { RC_BIT_SHARP, "sharp" }, 801 801 { RC_BIT_MCE_KBD, "mce_kbd" }, 802 802 { RC_BIT_LIRC, "lirc" }, 803 + { RC_BIT_XMP, "xmp" }, 803 804 }; 804 805 805 806 /**
+5 -1
include/media/rc-map.h
··· 31 31 RC_TYPE_RC6_6A_32 = 16, /* Philips RC6-6A-32 protocol */ 32 32 RC_TYPE_RC6_MCE = 17, /* MCE (Philips RC6-6A-32 subtype) protocol */ 33 33 RC_TYPE_SHARP = 18, /* Sharp protocol */ 34 + RC_TYPE_XMP = 19, /* XMP protocol */ 34 35 }; 35 36 36 37 #define RC_BIT_NONE 0 ··· 54 53 #define RC_BIT_RC6_6A_32 (1 << RC_TYPE_RC6_6A_32) 55 54 #define RC_BIT_RC6_MCE (1 << RC_TYPE_RC6_MCE) 56 55 #define RC_BIT_SHARP (1 << RC_TYPE_SHARP) 56 + #define RC_BIT_XMP (1 << RC_TYPE_XMP) 57 57 58 58 #define RC_BIT_ALL (RC_BIT_UNKNOWN | RC_BIT_OTHER | RC_BIT_LIRC | \ 59 59 RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ | \ ··· 62 60 RC_BIT_SONY12 | RC_BIT_SONY15 | RC_BIT_SONY20 | \ 63 61 RC_BIT_NEC | RC_BIT_SANYO | RC_BIT_MCE_KBD | \ 64 62 RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | \ 65 - RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP) 63 + RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE | RC_BIT_SHARP | \ 64 + RC_BIT_XMP) 65 + 66 66 67 67 #define RC_SCANCODE_UNKNOWN(x) (x) 68 68 #define RC_SCANCODE_OTHER(x) (x)