diff options
author | Sean Young <sean@mess.org> | 2017-12-03 11:06:54 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@s-opensource.com> | 2018-03-21 11:12:29 -0400 |
commit | 447dcc0cf12922fcda67731559dd970bde7b35a6 (patch) | |
tree | 0fc2c5a035448b53e44d19b9bd150fb52ffdc2e9 | |
parent | 8d4068810d9926250dd2435719a080b889eb44c3 (diff) |
media: rc: add new imon protocol decoder and encoder
This makes it possible to use the various iMON remotes with any raw IR
RC device.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
-rw-r--r-- | drivers/media/rc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/media/rc/Makefile | 1 | ||||
-rw-r--r-- | drivers/media/rc/ir-imon-decoder.c | 193 | ||||
-rw-r--r-- | drivers/media/rc/keymaps/rc-imon-pad.c | 3 | ||||
-rw-r--r-- | drivers/media/rc/rc-core-priv.h | 6 | ||||
-rw-r--r-- | drivers/media/rc/rc-main.c | 3 | ||||
-rw-r--r-- | include/media/rc-map.h | 8 | ||||
-rw-r--r-- | include/uapi/linux/lirc.h | 2 |
8 files changed, 220 insertions, 5 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index 7ad05a6ef350..eb2c3b6eca7f 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig | |||
@@ -111,6 +111,15 @@ config IR_XMP_DECODER | |||
111 | ---help--- | 111 | ---help--- |
112 | Enable this option if you have IR with XMP protocol, and | 112 | Enable this option if you have IR with XMP protocol, and |
113 | if the IR is decoded in software | 113 | if the IR is decoded in software |
114 | |||
115 | config IR_IMON_DECODER | ||
116 | tristate "Enable IR raw decoder for the iMON protocol" | ||
117 | depends on RC_CORE | ||
118 | ---help--- | ||
119 | Enable this option if you have iMON PAD or Antec Veris infrared | ||
120 | remote control and you would like to use it with a raw IR | ||
121 | receiver, or if you wish to use an encoder to transmit this IR. | ||
122 | |||
114 | endif #RC_DECODERS | 123 | endif #RC_DECODERS |
115 | 124 | ||
116 | menuconfig RC_DEVICES | 125 | menuconfig RC_DEVICES |
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index e098e127b26a..2e1c87066f6c 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile | |||
@@ -14,6 +14,7 @@ obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o | |||
14 | obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o | 14 | obj-$(CONFIG_IR_SHARP_DECODER) += ir-sharp-decoder.o |
15 | obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o | 15 | obj-$(CONFIG_IR_MCE_KBD_DECODER) += ir-mce_kbd-decoder.o |
16 | obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o | 16 | obj-$(CONFIG_IR_XMP_DECODER) += ir-xmp-decoder.o |
17 | obj-$(CONFIG_IR_IMON_DECODER) += ir-imon-decoder.o | ||
17 | 18 | ||
18 | # stand-alone IR receivers/transmitters | 19 | # stand-alone IR receivers/transmitters |
19 | obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o | 20 | obj-$(CONFIG_RC_ATI_REMOTE) += ati_remote.o |
diff --git a/drivers/media/rc/ir-imon-decoder.c b/drivers/media/rc/ir-imon-decoder.c new file mode 100644 index 000000000000..a1ff06a26542 --- /dev/null +++ b/drivers/media/rc/ir-imon-decoder.c | |||
@@ -0,0 +1,193 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
2 | // ir-imon-decoder.c - handle iMon protocol | ||
3 | // | ||
4 | // Copyright (C) 2018 by Sean Young <sean@mess.org> | ||
5 | |||
6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include "rc-core-priv.h" | ||
10 | |||
11 | #define IMON_UNIT 415662 /* ns */ | ||
12 | #define IMON_BITS 30 | ||
13 | #define IMON_CHKBITS (BIT(30) | BIT(25) | BIT(24) | BIT(22) | \ | ||
14 | BIT(21) | BIT(20) | BIT(19) | BIT(18) | \ | ||
15 | BIT(17) | BIT(16) | BIT(14) | BIT(13) | \ | ||
16 | BIT(12) | BIT(11) | BIT(10) | BIT(9)) | ||
17 | |||
18 | /* | ||
19 | * This protocol has 30 bits. The format is one IMON_UNIT header pulse, | ||
20 | * followed by 30 bits. Each bit is one IMON_UNIT check field, and then | ||
21 | * one IMON_UNIT field with the actual bit (1=space, 0=pulse). | ||
22 | * The check field is always space for some bits, for others it is pulse if | ||
23 | * both the preceding and current bit are zero, else space. IMON_CHKBITS | ||
24 | * defines which bits are of type check. | ||
25 | * | ||
26 | * There is no way to distinguish an incomplete message from one where | ||
27 | * the lower bits are all set, iow. the last pulse is for the lowest | ||
28 | * bit which is 0. | ||
29 | */ | ||
30 | enum imon_state { | ||
31 | STATE_INACTIVE, | ||
32 | STATE_BIT_CHK, | ||
33 | STATE_BIT_START, | ||
34 | STATE_FINISHED | ||
35 | }; | ||
36 | |||
37 | /** | ||
38 | * ir_imon_decode() - Decode one iMON pulse or space | ||
39 | * @dev: the struct rc_dev descriptor of the device | ||
40 | * @ev: the struct ir_raw_event descriptor of the pulse/space | ||
41 | * | ||
42 | * This function returns -EINVAL if the pulse violates the state machine | ||
43 | */ | ||
44 | static int ir_imon_decode(struct rc_dev *dev, struct ir_raw_event ev) | ||
45 | { | ||
46 | struct imon_dec *data = &dev->raw->imon; | ||
47 | |||
48 | if (!is_timing_event(ev)) { | ||
49 | if (ev.reset) | ||
50 | data->state = STATE_INACTIVE; | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | dev_dbg(&dev->dev, | ||
55 | "iMON decode started at state %d bitno %d (%uus %s)\n", | ||
56 | data->state, data->count, TO_US(ev.duration), | ||
57 | TO_STR(ev.pulse)); | ||
58 | |||
59 | for (;;) { | ||
60 | if (!geq_margin(ev.duration, IMON_UNIT, IMON_UNIT / 2)) | ||
61 | return 0; | ||
62 | |||
63 | decrease_duration(&ev, IMON_UNIT); | ||
64 | |||
65 | switch (data->state) { | ||
66 | case STATE_INACTIVE: | ||
67 | if (ev.pulse) { | ||
68 | data->state = STATE_BIT_CHK; | ||
69 | data->bits = 0; | ||
70 | data->count = IMON_BITS; | ||
71 | } | ||
72 | break; | ||
73 | case STATE_BIT_CHK: | ||
74 | if (IMON_CHKBITS & BIT(data->count)) | ||
75 | data->last_chk = ev.pulse; | ||
76 | else if (ev.pulse) | ||
77 | goto err_out; | ||
78 | data->state = STATE_BIT_START; | ||
79 | break; | ||
80 | case STATE_BIT_START: | ||
81 | data->bits <<= 1; | ||
82 | if (!ev.pulse) | ||
83 | data->bits |= 1; | ||
84 | |||
85 | if (IMON_CHKBITS & BIT(data->count)) { | ||
86 | if (data->last_chk != !(data->bits & 3)) | ||
87 | goto err_out; | ||
88 | } | ||
89 | |||
90 | if (!data->count--) | ||
91 | data->state = STATE_FINISHED; | ||
92 | else | ||
93 | data->state = STATE_BIT_CHK; | ||
94 | break; | ||
95 | case STATE_FINISHED: | ||
96 | if (ev.pulse) | ||
97 | goto err_out; | ||
98 | rc_keydown(dev, RC_PROTO_IMON, data->bits, 0); | ||
99 | data->state = STATE_INACTIVE; | ||
100 | break; | ||
101 | } | ||
102 | } | ||
103 | |||
104 | err_out: | ||
105 | dev_dbg(&dev->dev, | ||
106 | "iMON decode failed at state %d bitno %d (%uus %s)\n", | ||
107 | data->state, data->count, TO_US(ev.duration), | ||
108 | TO_STR(ev.pulse)); | ||
109 | |||
110 | data->state = STATE_INACTIVE; | ||
111 | |||
112 | return -EINVAL; | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * ir_imon_encode() - Encode a scancode as a stream of raw events | ||
117 | * | ||
118 | * @protocol: protocol to encode | ||
119 | * @scancode: scancode to encode | ||
120 | * @events: array of raw ir events to write into | ||
121 | * @max: maximum size of @events | ||
122 | * | ||
123 | * Returns: The number of events written. | ||
124 | * -ENOBUFS if there isn't enough space in the array to fit the | ||
125 | * encoding. In this case all @max events will have been written. | ||
126 | */ | ||
127 | static int ir_imon_encode(enum rc_proto protocol, u32 scancode, | ||
128 | struct ir_raw_event *events, unsigned int max) | ||
129 | { | ||
130 | struct ir_raw_event *e = events; | ||
131 | int i, pulse; | ||
132 | |||
133 | if (!max--) | ||
134 | return -ENOBUFS; | ||
135 | init_ir_raw_event_duration(e, 1, IMON_UNIT); | ||
136 | |||
137 | for (i = IMON_BITS; i >= 0; i--) { | ||
138 | if (BIT(i) & IMON_CHKBITS) | ||
139 | pulse = !(scancode & (BIT(i) | BIT(i + 1))); | ||
140 | else | ||
141 | pulse = 0; | ||
142 | |||
143 | if (pulse == e->pulse) { | ||
144 | e->duration += IMON_UNIT; | ||
145 | } else { | ||
146 | if (!max--) | ||
147 | return -ENOBUFS; | ||
148 | init_ir_raw_event_duration(++e, pulse, IMON_UNIT); | ||
149 | } | ||
150 | |||
151 | pulse = !(scancode & BIT(i)); | ||
152 | |||
153 | if (pulse == e->pulse) { | ||
154 | e->duration += IMON_UNIT; | ||
155 | } else { | ||
156 | if (!max--) | ||
157 | return -ENOBUFS; | ||
158 | init_ir_raw_event_duration(++e, pulse, IMON_UNIT); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | if (e->pulse) | ||
163 | e++; | ||
164 | |||
165 | return e - events; | ||
166 | } | ||
167 | |||
168 | static struct ir_raw_handler imon_handler = { | ||
169 | .protocols = RC_PROTO_BIT_IMON, | ||
170 | .decode = ir_imon_decode, | ||
171 | .encode = ir_imon_encode, | ||
172 | .carrier = 38000, | ||
173 | }; | ||
174 | |||
175 | static int __init ir_imon_decode_init(void) | ||
176 | { | ||
177 | ir_raw_handler_register(&imon_handler); | ||
178 | |||
179 | pr_info("IR iMON protocol handler initialized\n"); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static void __exit ir_imon_decode_exit(void) | ||
184 | { | ||
185 | ir_raw_handler_unregister(&imon_handler); | ||
186 | } | ||
187 | |||
188 | module_init(ir_imon_decode_init); | ||
189 | module_exit(ir_imon_decode_exit); | ||
190 | |||
191 | MODULE_LICENSE("GPL"); | ||
192 | MODULE_AUTHOR("Sean Young <sean@mess.org>"); | ||
193 | MODULE_DESCRIPTION("iMON IR protocol decoder"); | ||
diff --git a/drivers/media/rc/keymaps/rc-imon-pad.c b/drivers/media/rc/keymaps/rc-imon-pad.c index a7296ffbf218..8501cf0a3253 100644 --- a/drivers/media/rc/keymaps/rc-imon-pad.c +++ b/drivers/media/rc/keymaps/rc-imon-pad.c | |||
@@ -134,8 +134,7 @@ static struct rc_map_list imon_pad_map = { | |||
134 | .map = { | 134 | .map = { |
135 | .scan = imon_pad, | 135 | .scan = imon_pad, |
136 | .size = ARRAY_SIZE(imon_pad), | 136 | .size = ARRAY_SIZE(imon_pad), |
137 | /* actual protocol details unknown, hardware decoder */ | 137 | .rc_proto = RC_PROTO_IMON, |
138 | .rc_proto = RC_PROTO_OTHER, | ||
139 | .name = RC_MAP_IMON_PAD, | 138 | .name = RC_MAP_IMON_PAD, |
140 | } | 139 | } |
141 | }; | 140 | }; |
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 5e80b4273e2d..e0e6a17460f6 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h | |||
@@ -118,6 +118,12 @@ struct ir_raw_event_ctrl { | |||
118 | unsigned count; | 118 | unsigned count; |
119 | u32 durations[16]; | 119 | u32 durations[16]; |
120 | } xmp; | 120 | } xmp; |
121 | struct imon_dec { | ||
122 | int state; | ||
123 | int count; | ||
124 | int last_chk; | ||
125 | unsigned int bits; | ||
126 | } imon; | ||
121 | }; | 127 | }; |
122 | 128 | ||
123 | /* macros for IR decoders */ | 129 | /* macros for IR decoders */ |
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 8621761a680f..b67be33bd62f 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c | |||
@@ -68,6 +68,8 @@ static const struct { | |||
68 | .scancode_bits = 0x1fff, .repeat_period = 250 }, | 68 | .scancode_bits = 0x1fff, .repeat_period = 250 }, |
69 | [RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 250 }, | 69 | [RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 250 }, |
70 | [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 550 }, | 70 | [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 550 }, |
71 | [RC_PROTO_IMON] = { .name = "imon", | ||
72 | .scancode_bits = 0x7fffffff, .repeat_period = 250 }, | ||
71 | }; | 73 | }; |
72 | 74 | ||
73 | /* Used to keep track of known keymaps */ | 75 | /* Used to keep track of known keymaps */ |
@@ -1004,6 +1006,7 @@ static const struct { | |||
1004 | RC_PROTO_BIT_MCIR2_MSE, "mce_kbd", "ir-mce_kbd-decoder" }, | 1006 | RC_PROTO_BIT_MCIR2_MSE, "mce_kbd", "ir-mce_kbd-decoder" }, |
1005 | { RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" }, | 1007 | { RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" }, |
1006 | { RC_PROTO_BIT_CEC, "cec", NULL }, | 1008 | { RC_PROTO_BIT_CEC, "cec", NULL }, |
1009 | { RC_PROTO_BIT_IMON, "imon", "ir-imon-decoder" }, | ||
1007 | }; | 1010 | }; |
1008 | 1011 | ||
1009 | /** | 1012 | /** |
diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 7fc84991bd12..bfa3017cecba 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h | |||
@@ -36,6 +36,7 @@ | |||
36 | #define RC_PROTO_BIT_SHARP BIT_ULL(RC_PROTO_SHARP) | 36 | #define RC_PROTO_BIT_SHARP BIT_ULL(RC_PROTO_SHARP) |
37 | #define RC_PROTO_BIT_XMP BIT_ULL(RC_PROTO_XMP) | 37 | #define RC_PROTO_BIT_XMP BIT_ULL(RC_PROTO_XMP) |
38 | #define RC_PROTO_BIT_CEC BIT_ULL(RC_PROTO_CEC) | 38 | #define RC_PROTO_BIT_CEC BIT_ULL(RC_PROTO_CEC) |
39 | #define RC_PROTO_BIT_IMON BIT_ULL(RC_PROTO_IMON) | ||
39 | 40 | ||
40 | #define RC_PROTO_BIT_ALL \ | 41 | #define RC_PROTO_BIT_ALL \ |
41 | (RC_PROTO_BIT_UNKNOWN | RC_PROTO_BIT_OTHER | \ | 42 | (RC_PROTO_BIT_UNKNOWN | RC_PROTO_BIT_OTHER | \ |
@@ -49,7 +50,8 @@ | |||
49 | RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ | 50 | RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ |
50 | RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ | 51 | RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ |
51 | RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ | 52 | RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ |
52 | RC_PROTO_BIT_XMP | RC_PROTO_BIT_CEC) | 53 | RC_PROTO_BIT_XMP | RC_PROTO_BIT_CEC | \ |
54 | RC_PROTO_BIT_IMON) | ||
53 | /* All rc protocols for which we have decoders */ | 55 | /* All rc protocols for which we have decoders */ |
54 | #define RC_PROTO_BIT_ALL_IR_DECODER \ | 56 | #define RC_PROTO_BIT_ALL_IR_DECODER \ |
55 | (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ | 57 | (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ |
@@ -62,7 +64,7 @@ | |||
62 | RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ | 64 | RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ |
63 | RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ | 65 | RC_PROTO_BIT_RC6_6A_24 | RC_PROTO_BIT_RC6_6A_32 | \ |
64 | RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ | 66 | RC_PROTO_BIT_RC6_MCE | RC_PROTO_BIT_SHARP | \ |
65 | RC_PROTO_BIT_XMP) | 67 | RC_PROTO_BIT_XMP | RC_PROTO_BIT_IMON) |
66 | 68 | ||
67 | #define RC_PROTO_BIT_ALL_IR_ENCODER \ | 69 | #define RC_PROTO_BIT_ALL_IR_ENCODER \ |
68 | (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ | 70 | (RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC5X_20 | \ |
@@ -75,7 +77,7 @@ | |||
75 | RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ | 77 | RC_PROTO_BIT_RC6_0 | RC_PROTO_BIT_RC6_6A_20 | \ |
76 | RC_PROTO_BIT_RC6_6A_24 | \ | 78 | RC_PROTO_BIT_RC6_6A_24 | \ |
77 | RC_PROTO_BIT_RC6_6A_32 | RC_PROTO_BIT_RC6_MCE | \ | 79 | RC_PROTO_BIT_RC6_6A_32 | RC_PROTO_BIT_RC6_MCE | \ |
78 | RC_PROTO_BIT_SHARP) | 80 | RC_PROTO_BIT_SHARP | RC_PROTO_BIT_IMON) |
79 | 81 | ||
80 | #define RC_SCANCODE_UNKNOWN(x) (x) | 82 | #define RC_SCANCODE_UNKNOWN(x) (x) |
81 | #define RC_SCANCODE_OTHER(x) (x) | 83 | #define RC_SCANCODE_OTHER(x) (x) |
diff --git a/include/uapi/linux/lirc.h b/include/uapi/linux/lirc.h index 4fe580d36e41..948d9a491083 100644 --- a/include/uapi/linux/lirc.h +++ b/include/uapi/linux/lirc.h | |||
@@ -186,6 +186,7 @@ struct lirc_scancode { | |||
186 | * @RC_PROTO_SHARP: Sharp protocol | 186 | * @RC_PROTO_SHARP: Sharp protocol |
187 | * @RC_PROTO_XMP: XMP protocol | 187 | * @RC_PROTO_XMP: XMP protocol |
188 | * @RC_PROTO_CEC: CEC protocol | 188 | * @RC_PROTO_CEC: CEC protocol |
189 | * @RC_PROTO_IMON: iMon Pad protocol | ||
189 | */ | 190 | */ |
190 | enum rc_proto { | 191 | enum rc_proto { |
191 | RC_PROTO_UNKNOWN = 0, | 192 | RC_PROTO_UNKNOWN = 0, |
@@ -211,6 +212,7 @@ enum rc_proto { | |||
211 | RC_PROTO_SHARP = 20, | 212 | RC_PROTO_SHARP = 20, |
212 | RC_PROTO_XMP = 21, | 213 | RC_PROTO_XMP = 21, |
213 | RC_PROTO_CEC = 22, | 214 | RC_PROTO_CEC = 22, |
215 | RC_PROTO_IMON = 23, | ||
214 | }; | 216 | }; |
215 | 217 | ||
216 | #endif | 218 | #endif |