Encode keymaps using xbox-dvd, pulse_distance, pulse_length and manchester BPF decoders. The kernel has no idea how to encode them so this must be done in userspace. Signed-off-by: Sean Young <sean@xxxxxxxx> --- utils/common/ir-encode.c | 26 ++++++- utils/common/keymap.c | 1 + utils/ir-ctl/Makefile.am | 2 +- utils/ir-ctl/bpf_encoder.c | 137 +++++++++++++++++++++++++++++++++++++ utils/ir-ctl/bpf_encoder.h | 7 ++ utils/ir-ctl/ir-ctl.c | 31 ++++++--- 6 files changed, 193 insertions(+), 11 deletions(-) create mode 100644 utils/ir-ctl/bpf_encoder.c create mode 100644 utils/ir-ctl/bpf_encoder.h diff --git a/utils/common/ir-encode.c b/utils/common/ir-encode.c index 47e294b1..e4fde403 100644 --- a/utils/common/ir-encode.c +++ b/utils/common/ir-encode.c @@ -342,6 +342,30 @@ static int rc6_encode(enum rc_proto proto, unsigned scancode, unsigned *buf) return (n % 2) ? n : n + 1; } +static int xbox_dvd_encode(enum rc_proto proto, unsigned scancode, unsigned *buf) +{ + int len = 0; + + buf[len++] = 4000; + buf[len++] = 3900; + + scancode &= 0xfff; + scancode |= (~scancode << 12) & 0xfff000; + + for (int i=23; i >=0; i--) { + buf[len++] = 550; + + if (scancode & (1 << i)) + buf[len++] = 1900; + else + buf[len++] = 900; + } + + buf[len++]= 550; + + return len; +} + static const struct { char name[10]; unsigned scancode_mask; @@ -376,7 +400,7 @@ static const struct { [RC_PROTO_RCMM12] = { "rc-mm-12", 0x0fff }, [RC_PROTO_RCMM24] = { "rc-mm-24", 0xffffff }, [RC_PROTO_RCMM32] = { "rc-mm-32", 0xffffffff }, - [RC_PROTO_XBOX_DVD] = { "xbox-dvd", 0xfff }, + [RC_PROTO_XBOX_DVD] = { "xbox-dvd", 0xfff, 68, 38000, xbox_dvd_encode }, }; static bool str_like(const char *a, const char *b) diff --git a/utils/common/keymap.c b/utils/common/keymap.c index e3a162ab..20026cc4 100644 --- a/utils/common/keymap.c +++ b/utils/common/keymap.c @@ -404,6 +404,7 @@ static error_t parse_toml_protocol(const char *fname, struct toml_table_t *proot } struct scancode_entry **next = &map->scancode; + i = 0; for (;;) { struct scancode_entry *se; diff --git a/utils/ir-ctl/Makefile.am b/utils/ir-ctl/Makefile.am index df53a965..ad90b84e 100644 --- a/utils/ir-ctl/Makefile.am +++ b/utils/ir-ctl/Makefile.am @@ -1,6 +1,6 @@ bin_PROGRAMS = ir-ctl man_MANS = ir-ctl.1 -ir_ctl_SOURCES = ir-ctl.c ir-encode.c ir-encode.h toml.c toml.h keymap.c keymap.h +ir_ctl_SOURCES = ir-ctl.c ir-encode.c ir-encode.h toml.c toml.h keymap.c keymap.h bpf_encoder.c bpf_encoder.h ir_ctl_LDADD = @LIBINTL@ ir_ctl_LDFLAGS = $(ARGP_LIBS) diff --git a/utils/ir-ctl/bpf_encoder.c b/utils/ir-ctl/bpf_encoder.c new file mode 100644 index 00000000..82d12cc0 --- /dev/null +++ b/utils/ir-ctl/bpf_encoder.c @@ -0,0 +1,137 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include <stdbool.h> +#include <stdint.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> + +#include "keymap.h" + +// Some keymaps use BPF decoders, so the kernel has no idea how to encode +// them. We need user-space encoders for these. +// +// This encoders should match what the BPF decoders in +// utils/keytable/bpf_protocols/*.c decode. + +static void encode_pulse_distance(struct keymap *map, uint32_t scancode, int *buf, int *length) +{ + int len = 0, bits, i; + + buf[len++] = keymap_param(map, "header_pulse", 2125); + buf[len++] = keymap_param(map, "header_space", 1875); + + bits = keymap_param(map, "bits", 4); + + if (keymap_param(map, "reverse", 0)) { + for (i = 0; i < bits; i++) { + buf[len++] = keymap_param(map, "bit_pulse", 625); + + if (scancode & (1 << i)) + buf[len++] = keymap_param(map, "bit_1_space", 1625); + else + buf[len++] = keymap_param(map, "bit_0_space", 375); + } + } else { + for (i = bits - 1; i >= 0; i--) { + buf[len++] = keymap_param(map, "bit_pulse", 625); + + if (scancode & (1 << i)) + buf[len++] = keymap_param(map, "bit_1_space", 1625); + else + buf[len++] = keymap_param(map, "bit_0_space", 375); + } + } + + buf[len++] = keymap_param(map, "trailer_pulse", 625); + + *length = len; +} + +static void encode_pulse_length(struct keymap *map, uint32_t scancode, int *buf, int *length) +{ + int len = 0, bits, i; + + buf[len++] = keymap_param(map, "header_pulse", 2125); + buf[len++] = keymap_param(map, "header_space", 1875); + + bits = keymap_param(map, "bits", 4); + + if (keymap_param(map, "reverse", 0)) { + for (i = 0; i < bits; i++) { + if (scancode & (1 << i)) + buf[len++] = keymap_param(map, "bit_1_space", 1625); + else + buf[len++] = keymap_param(map, "bit_0_space", 375); + + buf[len++] = keymap_param(map, "bit_pulse", 625); + } + } else { + for (i = bits - 1; i >= 0; i--) { + if (scancode & (1 << i)) + buf[len++] = keymap_param(map, "bit_1_space", 1625); + else + buf[len++] = keymap_param(map, "bit_0_space", 375); + + buf[len++] = keymap_param(map, "bit_pulse", 625); + } + } + + *length = len; +} + +static void encode_manchester(struct keymap *map, uint32_t scancode, int *buf, int *length) +{ + int len = 0, bits, i; + + void advance_space(unsigned length) + { + if (len % 2) + buf[len] += length; + else + buf[++len] = length; + } + + void advance_pulse(unsigned length) + { + if (len % 2) + buf[++len] = length; + else + buf[len] += length; + } + + bits = keymap_param(map, "bits", 14); + + for (i = bits - 1; i >= 0; i--) { + if (scancode & (1 << i)) { + advance_pulse(keymap_param(map, "one_pulse", 888)); + advance_space(keymap_param(map, "one_space", 888)); + } else { + advance_space(keymap_param(map, "zero_space", 888)); + advance_pulse(keymap_param(map, "zero_pulse", 888)); + } + } + + /* drop any trailing pulse */ + *length = (len % 2) ? len : len + 1; +} + +bool encode_bpf_protocol(struct keymap *map, uint32_t scancode, int *buf, int *length) +{ + if (!strcmp(map->protocol, "pulse_distance")) { + encode_pulse_distance(map, scancode, buf, length); + return true; + } + + if (!strcmp(map->protocol, "pulse_length")) { + encode_pulse_length(map, scancode, buf, length); + return true; + } + + if (!strcmp(map->protocol, "manchester")) { + encode_manchester(map, scancode, buf, length); + return true; + } + + return false; +} diff --git a/utils/ir-ctl/bpf_encoder.h b/utils/ir-ctl/bpf_encoder.h new file mode 100644 index 00000000..5d1d43a9 --- /dev/null +++ b/utils/ir-ctl/bpf_encoder.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __BPF_ENCODER_H +#define __BPF_ENCODER_H + +bool encode_bpf_protocol(struct keymap *map, uint32_t scancode, int *buf, int *length); + +#endif diff --git a/utils/ir-ctl/ir-ctl.c b/utils/ir-ctl/ir-ctl.c index 94a22043..586461ac 100644 --- a/utils/ir-ctl/ir-ctl.c +++ b/utils/ir-ctl/ir-ctl.c @@ -17,6 +17,7 @@ #include <unistd.h> #include <stdlib.h> #include <stdbool.h> +#include <stdint.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> @@ -31,6 +32,7 @@ #include "ir-encode.h" #include "keymap.h" +#include "bpf_encoder.h" #ifdef ENABLE_NLS # define _(string) gettext(string) @@ -754,28 +756,39 @@ static struct send* convert_keycode(struct keymap *map, const char *keycode) } for (se = map->scancode; se; se = se->next) { + int buf[LIRCBUF_SIZE], length; + const char *proto_str; + enum rc_proto proto; + if (strcmp(se->keycode, keycode)) continue; count++; - if (!s) { - const char *proto_str; - enum rc_proto proto; - - proto_str = map->variant ?: map->protocol; + if (s) + continue; - if (!protocol_match(proto_str, &proto)) { - fprintf(stderr, _("error: protocol '%s' not suppoted\n"), proto_str); - return NULL; - } + proto_str = map->variant ?: map->protocol; + if (protocol_match(proto_str, &proto)) { s = malloc(sizeof(*s)); s->protocol = proto; s->scancode = se->scancode; s->is_scancode = true; s->is_keycode = false; s->next = NULL; + } else if (encode_bpf_protocol(map, se->scancode, + buf, &length)) { + s = malloc(sizeof(*s) + sizeof(int) * length); + s->len = length; + memcpy(s->buf, buf, length * sizeof(int)); + s->is_scancode = false; + s->is_keycode = false; + s->carrier = keymap_param(map, "carrier", 0); + s->next = NULL; + } else { + fprintf(stderr, _("error: protocol '%s' not supported\n"), proto_str); + return NULL; } } } -- 2.21.0