This makes it possible to use the various iMON remotes with any raw IR RC device.
Unified · Split
| @@ -111,6 +111,15 @@ config IR_XMP_DECODER · 110 unmodified lines | |||
| 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 |
| @@ -14,6 +14,7 @@ obj-$(CONFIG_IR_SANYO_DECODER) += ir-sanyo-decoder.o · 13 unmodified lines | |||
| 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 |
| @@ -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"); | ||
| @@ -134,8 +134,7 @@ static struct rc_map_list imon_pad_map = { · 133 unmodified lines | |||
| 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 | }; |
| @@ -118,6 +118,12 @@ struct ir_raw_event_ctrl { · 117 unmodified lines | |||
| 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 */ |
| @@ -68,6 +68,8 @@ static const struct { · 67 unmodified lines | |||
| 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 { · 930 unmodified lines | |||
| 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 | /** |
| @@ -36,6 +36,7 @@ · 35 unmodified lines | |||
| 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 @@ · 7 unmodified lines | |||
| 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 @@ · 6 unmodified lines | |||
| 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 @@ · 6 unmodified lines | |||
| 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) |
| @@ -186,6 +186,7 @@ struct lirc_scancode { · 185 unmodified lines | |||
| 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 { · 19 unmodified lines | |||
| 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 |