^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /* ir-xmp-decoder.c - handle XMP IR Pulse/Space protocol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright (C) 2014 by Marcel Mol
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * - Based on info from http://www.hifi-remote.com
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * - Ignore Toggle=9 frames
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * - Ignore XMP-1 XMP-2 difference, always store 16 bit OBC
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/bitrev.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include "rc-core-priv.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #define XMP_UNIT 136 /* us */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #define XMP_LEADER 210 /* us */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #define XMP_NIBBLE_PREFIX 760 /* us */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define XMP_HALFFRAME_SPACE 13800 /* us */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) /* should be 80ms but not all duration supliers can go that high */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define XMP_TRAILER_SPACE 20000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) enum xmp_state {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) STATE_INACTIVE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) STATE_LEADER_PULSE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) STATE_NIBBLE_SPACE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) * ir_xmp_decode() - Decode one XMP pulse or space
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * @dev: the struct rc_dev descriptor of the device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * @ev: the struct ir_raw_event descriptor of the pulse/space
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) * This function returns -EINVAL if the pulse violates the state machine
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static int ir_xmp_decode(struct rc_dev *dev, struct ir_raw_event ev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct xmp_dec *data = &dev->raw->xmp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) if (!is_timing_event(ev)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) if (ev.reset)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) data->state = STATE_INACTIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) dev_dbg(&dev->dev, "XMP decode started at state %d %d (%uus %s)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) data->state, data->count, ev.duration, TO_STR(ev.pulse));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) switch (data->state) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) case STATE_INACTIVE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) if (!ev.pulse)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) data->count = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) data->state = STATE_NIBBLE_SPACE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) case STATE_LEADER_PULSE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) if (!ev.pulse)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) if (eq_margin(ev.duration, XMP_LEADER, XMP_UNIT / 2))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) data->state = STATE_NIBBLE_SPACE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) case STATE_NIBBLE_SPACE:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) if (ev.pulse)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) if (geq_margin(ev.duration, XMP_TRAILER_SPACE, XMP_NIBBLE_PREFIX)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) int divider, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) u8 addr, subaddr, subaddr2, toggle, oem, obc1, obc2, sum1, sum2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) u32 *n;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) u32 scancode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) if (data->count != 16) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) dev_dbg(&dev->dev, "received TRAILER period at index %d: %u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) data->count, ev.duration);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) data->state = STATE_INACTIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) n = data->durations;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) * the 4th nibble should be 15 so base the divider on this
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) * to transform durations into nibbles. Subtract 2000 from
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) * the divider to compensate for fluctuations in the signal
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) divider = (n[3] - XMP_NIBBLE_PREFIX) / 15 - 2000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) if (divider < 50) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) dev_dbg(&dev->dev, "divider to small %d.\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) divider);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) data->state = STATE_INACTIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) /* convert to nibbles and do some sanity checks */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) for (i = 0; i < 16; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) n[i] = (n[i] - XMP_NIBBLE_PREFIX) / divider;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) sum1 = (15 + n[0] + n[1] + n[2] + n[3] +
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) n[4] + n[5] + n[6] + n[7]) % 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) sum2 = (15 + n[8] + n[9] + n[10] + n[11] +
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) n[12] + n[13] + n[14] + n[15]) % 16;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (sum1 != 15 || sum2 != 15) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) dev_dbg(&dev->dev, "checksum errors sum1=0x%X sum2=0x%X\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) sum1, sum2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) data->state = STATE_INACTIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) subaddr = n[0] << 4 | n[2];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) subaddr2 = n[8] << 4 | n[11];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) oem = n[4] << 4 | n[5];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) addr = n[6] << 4 | n[7];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) toggle = n[10];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) obc1 = n[12] << 4 | n[13];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) obc2 = n[14] << 4 | n[15];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) if (subaddr != subaddr2) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) dev_dbg(&dev->dev, "subaddress nibbles mismatch 0x%02X != 0x%02X\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) subaddr, subaddr2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) data->state = STATE_INACTIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) if (oem != 0x44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) dev_dbg(&dev->dev, "Warning: OEM nibbles 0x%02X. Expected 0x44\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) oem);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) scancode = addr << 24 | subaddr << 16 |
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) obc1 << 8 | obc2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) dev_dbg(&dev->dev, "XMP scancode 0x%06x\n", scancode);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) if (toggle == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) rc_keydown(dev, RC_PROTO_XMP, scancode, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) rc_repeat(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) dev_dbg(&dev->dev, "Repeat last key\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) data->state = STATE_INACTIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) } else if (geq_margin(ev.duration, XMP_HALFFRAME_SPACE, XMP_NIBBLE_PREFIX)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) /* Expect 8 or 16 nibble pulses. 16 in case of 'final' frame */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) if (data->count == 16) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) dev_dbg(&dev->dev, "received half frame pulse at index %d. Probably a final frame key-up event: %u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) data->count, ev.duration);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) * TODO: for now go back to half frame position
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) * so trailer can be found and key press
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) * can be handled.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) data->count = 8;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) else if (data->count != 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) dev_dbg(&dev->dev, "received half frame pulse at index %d: %u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) data->count, ev.duration);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) data->state = STATE_LEADER_PULSE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) } else if (geq_margin(ev.duration, XMP_NIBBLE_PREFIX, XMP_UNIT)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) /* store nibble raw data, decode after trailer */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) if (data->count == 16) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) dev_dbg(&dev->dev, "too many pulses (%d) ignoring: %u\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) data->count, ev.duration);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) data->state = STATE_INACTIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) data->durations[data->count] = ev.duration;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) data->count++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) data->state = STATE_LEADER_PULSE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) dev_dbg(&dev->dev, "XMP decode failed at count %d state %d (%uus %s)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) data->count, data->state, ev.duration, TO_STR(ev.pulse));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) data->state = STATE_INACTIVE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) static struct ir_raw_handler xmp_handler = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) .protocols = RC_PROTO_BIT_XMP,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) .decode = ir_xmp_decode,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) .min_timeout = XMP_TRAILER_SPACE,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) static int __init ir_xmp_decode_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) ir_raw_handler_register(&xmp_handler);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) printk(KERN_INFO "IR XMP protocol handler initialized\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) static void __exit ir_xmp_decode_exit(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) ir_raw_handler_unregister(&xmp_handler);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) module_init(ir_xmp_decode_init);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) module_exit(ir_xmp_decode_exit);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) MODULE_AUTHOR("Marcel Mol <marcel@mesa.nl>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) MODULE_AUTHOR("MESA Consulting (http://www.mesa.nl)");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) MODULE_DESCRIPTION("XMP IR protocol decoder");