diff options
author | Marcel J.E. Mol <marcel@mesa.nl> | 2014-07-26 16:28:26 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-07-26 18:38:04 -0400 |
commit | 1dee9b59d69a15d566c16ee6fbd7216108ad5cac (patch) | |
tree | 75d5c4bba63dfba3b04cb2d7178f2c1834242eb9 /drivers/media/rc | |
parent | 0a6824bc10de58dac8beca4ad6744753f1cf9a6f (diff) |
[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>
Diffstat (limited to 'drivers/media/rc')
-rw-r--r-- | drivers/media/rc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/media/rc/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/rc/ir-xmp-decoder.c | 225 | ||||
-rw-r--r-- | drivers/media/rc/rc-core-priv.h | 12 | ||||
-rw-r--r-- | drivers/media/rc/rc-ir-raw.c | 1 | ||||
-rw-r--r-- | drivers/media/rc/rc-main.c | 1 |
6 files changed, 250 insertions, 0 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 9d15a57ac590..5e626af8e313 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig | |||
@@ -113,6 +113,16 @@ config IR_MCE_KBD_DECODER | |||
113 | Enable this option if you have a Microsoft Remote Keyboard for | 113 | Enable this option if you have a Microsoft Remote Keyboard for |
114 | Windows Media Center Edition, which you would like to use with | 114 | Windows Media Center Edition, which you would like to use with |
115 | a raw IR receiver in your system. | 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 | endif #RC_DECODERS | 126 | endif #RC_DECODERS |
117 | 127 | ||
118 | menuconfig RC_DEVICES | 128 | menuconfig RC_DEVICES |
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 096eb0692a04..9f9843a1af5f 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile | |||
@@ -13,6 +13,7 @@ obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o | |||
13 | obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o | 13 | obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o |
14 | obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o | 14 | obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o |
15 | obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o | 15 | obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o |
16 | obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o | ||
16 | 17 | ||
17 | # stand-alone IR receivers/transmitters | 18 | # stand-alone IR receivers/transmitters |
18 | obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o | 19 | obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o |
diff --git a/drivers/media/rc/ir-xmp-decoder.c b/drivers/media/rc/ir-xmp-decoder.c new file mode 100644 index 000000000000..1017d4816e8d --- /dev/null +++ b/drivers/media/rc/ir-xmp-decoder.c | |||
@@ -0,0 +1,225 @@ | |||
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"); | ||
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index dea7aff15a35..b68d4f762734 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h | |||
@@ -110,6 +110,11 @@ struct ir_raw_event_ctrl { | |||
110 | bool send_timeout_reports; | 110 | bool send_timeout_reports; |
111 | 111 | ||
112 | } lirc; | 112 | } lirc; |
113 | struct xmp_dec { | ||
114 | int state; | ||
115 | unsigned count; | ||
116 | u32 durations[16]; | ||
117 | } xmp; | ||
113 | }; | 118 | }; |
114 | 119 | ||
115 | /* macros for IR decoders */ | 120 | /* macros for IR decoders */ |
@@ -225,5 +230,12 @@ static inline void load_mce_kbd_decode(void) { } | |||
225 | static inline void load_lirc_codec(void) { } | 230 | static inline void load_lirc_codec(void) { } |
226 | #endif | 231 | #endif |
227 | 232 | ||
233 | /* from ir-xmp-decoder.c */ | ||
234 | #ifdef CONFIG_IR_XMP_DECODER_MODULE | ||
235 | #define load_xmp_decode() request_module_nowait("ir-xmp-decoder") | ||
236 | #else | ||
237 | static inline void load_xmp_decode(void) { } | ||
238 | #endif | ||
239 | |||
228 | 240 | ||
229 | #endif /* _RC_CORE_PRIV */ | 241 | #endif /* _RC_CORE_PRIV */ |
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index e772c4cf9f61..e8fff2add265 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c | |||
@@ -362,6 +362,7 @@ void ir_raw_init(void) | |||
362 | load_sharp_decode(); | 362 | load_sharp_decode(); |
363 | load_mce_kbd_decode(); | 363 | load_mce_kbd_decode(); |
364 | load_lirc_codec(); | 364 | load_lirc_codec(); |
365 | load_xmp_decode(); | ||
365 | 366 | ||
366 | /* If needed, we may later add some init code. In this case, | 367 | /* If needed, we may later add some init code. In this case, |
367 | it is needed to change the CONFIG_MODULE test at rc-core.h | 368 | it is needed to change the CONFIG_MODULE test at rc-core.h |
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 39e4b5ffe141..dfceeb4e34a8 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c | |||
@@ -800,6 +800,7 @@ static struct { | |||
800 | { RC_BIT_SHARP, "sharp" }, | 800 | { RC_BIT_SHARP, "sharp" }, |
801 | { RC_BIT_MCE_KBD, "mce_kbd" }, | 801 | { RC_BIT_MCE_KBD, "mce_kbd" }, |
802 | { RC_BIT_LIRC, "lirc" }, | 802 | { RC_BIT_LIRC, "lirc" }, |
803 | { RC_BIT_XMP, "xmp" }, | ||
803 | }; | 804 | }; |
804 | 805 | ||
805 | /** | 806 | /** |