aboutsummaryrefslogtreecommitdiffstats
path: root/utils/keytable
diff options
context:
space:
mode:
authorSean Young <sean@mess.org>2018-05-20 21:10:47 +0100
committerSean Young <sean@mess.org>2018-08-13 10:15:52 +0100
commitd6025b0e8c7f57b0f9390f987acc5eed57360d80 (patch)
tree1922f486adc601bbc198aa67fbc7aef81361a4d4 /utils/keytable
parent83062bda386de80f6bcf86da83ec490678b66081 (diff)
keytable: add support for BPF based protocols
We use a modified version of samples/bpf/bpf_load.c from the linux kernel tree to load elf based BPF protocols and display the BPF program names. Any global int variables can be overrided from the command line. The immediate values are patched in the BPF LD instructions. Write a BPF protocol decoder in C, and compile with: clang -O2 -c --target=bpf foo.c Now you can load the object file like so: ir-keytable -p foo.o -v Any parameters can be specified using -e name=value. Signed-off-by: Sean Young <sean@mess.org>
Diffstat (limited to 'utils/keytable')
-rw-r--r--utils/keytable/Makefile.am7
-rw-r--r--utils/keytable/bpf.c515
-rw-r--r--utils/keytable/bpf.h110
-rw-r--r--utils/keytable/bpf_load.c455
-rw-r--r--utils/keytable/bpf_load.h43
-rw-r--r--utils/keytable/ir-keytable.1.in14
-rw-r--r--utils/keytable/keytable.c318
7 files changed, 1433 insertions, 29 deletions
diff --git a/utils/keytable/Makefile.am b/utils/keytable/Makefile.am
index 904aa96b..ad251915 100644
--- a/utils/keytable/Makefile.am
+++ b/utils/keytable/Makefile.am
@@ -5,8 +5,13 @@ keytablesystem_DATA = $(srcdir)/rc_keymaps/*
udevrules_DATA = 70-infrared.rules
ir_keytable_SOURCES = keytable.c parse.h ir-encode.c ir-encode.h
+
+if HAVE_LIBELF
+ir_keytable_SOURCES += bpf.c bpf_load.c bpf.h bpf_load.h
+endif
+
ir_keytable_LDADD = @LIBINTL@
-ir_keytable_LDFLAGS = $(ARGP_LIBS)
+ir_keytable_LDFLAGS = $(ARGP_LIBS) $(LIBELF_LIBS)
EXTRA_DIST = 70-infrared.rules rc_keymaps rc_keymaps_userspace gen_keytables.pl ir-keytable.1 rc_maps.cfg
diff --git a/utils/keytable/bpf.c b/utils/keytable/bpf.c
new file mode 100644
index 00000000..b1cd2507
--- /dev/null
+++ b/utils/keytable/bpf.c
@@ -0,0 +1,515 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * common eBPF ELF operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * Copyright (C) 2015 Huawei Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses>
+ */
+
+#include <stdlib.h>
+#include <memory.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+#include <linux/bpf.h>
+#include "bpf.h"
+#include <errno.h>
+
+/*
+ * When building perf, unistd.h is overridden. __NR_bpf is
+ * required to be defined explicitly.
+ */
+#ifndef __NR_bpf
+# if defined(__i386__)
+# define __NR_bpf 357
+# elif defined(__x86_64__)
+# define __NR_bpf 321
+# elif defined(__aarch64__)
+# define __NR_bpf 280
+# elif defined(__sparc__)
+# define __NR_bpf 349
+# elif defined(__s390__)
+# define __NR_bpf 351
+# else
+# error __NR_bpf not defined. libbpf does not support your arch.
+# endif
+#endif
+
+#ifndef min
+#define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+
+static inline __u64 ptr_to_u64(const void *ptr)
+{
+ return (__u64) (unsigned long) ptr;
+}
+
+static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
+ unsigned int size)
+{
+ return syscall(__NR_bpf, cmd, attr, size);
+}
+
+int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr)
+{
+ __u32 name_len = create_attr->name ? strlen(create_attr->name) : 0;
+ union bpf_attr attr;
+
+ memset(&attr, '\0', sizeof(attr));
+
+ attr.map_type = create_attr->map_type;
+ attr.key_size = create_attr->key_size;
+ attr.value_size = create_attr->value_size;
+ attr.max_entries = create_attr->max_entries;
+ attr.map_flags = create_attr->map_flags;
+ memcpy(attr.map_name, create_attr->name,
+ min(name_len, BPF_OBJ_NAME_LEN - 1));
+ attr.numa_node = create_attr->numa_node;
+ attr.btf_fd = create_attr->btf_fd;
+ attr.btf_key_type_id = create_attr->btf_key_type_id;
+ attr.btf_value_type_id = create_attr->btf_value_type_id;
+ attr.map_ifindex = create_attr->map_ifindex;
+
+ return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
+}
+
+int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
+ int key_size, int value_size, int max_entries,
+ __u32 map_flags, int node)
+{
+ struct bpf_create_map_attr map_attr = {};
+
+ map_attr.name = name;
+ map_attr.map_type = map_type;
+ map_attr.map_flags = map_flags;
+ map_attr.key_size = key_size;
+ map_attr.value_size = value_size;
+ map_attr.max_entries = max_entries;
+ if (node >= 0) {
+ map_attr.numa_node = node;
+ map_attr.map_flags |= BPF_F_NUMA_NODE;
+ }
+
+ return bpf_create_map_xattr(&map_attr);
+}
+
+int bpf_create_map(enum bpf_map_type map_type, int key_size,
+ int value_size, int max_entries, __u32 map_flags)
+{
+ struct bpf_create_map_attr map_attr = {};
+
+ map_attr.map_type = map_type;
+ map_attr.map_flags = map_flags;
+ map_attr.key_size = key_size;
+ map_attr.value_size = value_size;
+ map_attr.max_entries = max_entries;
+
+ return bpf_create_map_xattr(&map_attr);
+}
+
+int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
+ int key_size, int value_size, int max_entries,
+ __u32 map_flags)
+{
+ struct bpf_create_map_attr map_attr = {};
+
+ map_attr.name = name;
+ map_attr.map_type = map_type;
+ map_attr.map_flags = map_flags;
+ map_attr.key_size = key_size;
+ map_attr.value_size = value_size;
+ map_attr.max_entries = max_entries;
+
+ return bpf_create_map_xattr(&map_attr);
+}
+
+int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
+ int key_size, int inner_map_fd, int max_entries,
+ __u32 map_flags, int node)
+{
+ __u32 name_len = name ? strlen(name) : 0;
+ union bpf_attr attr;
+
+ memset(&attr, '\0', sizeof(attr));
+
+ attr.map_type = map_type;
+ attr.key_size = key_size;
+ attr.value_size = 4;
+ attr.inner_map_fd = inner_map_fd;
+ attr.max_entries = max_entries;
+ attr.map_flags = map_flags;
+ memcpy(attr.map_name, name, min(name_len, BPF_OBJ_NAME_LEN - 1));
+
+ if (node >= 0) {
+ attr.map_flags |= BPF_F_NUMA_NODE;
+ attr.numa_node = node;
+ }
+
+ return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
+}
+
+int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
+ int key_size, int inner_map_fd, int max_entries,
+ __u32 map_flags)
+{
+ return bpf_create_map_in_map_node(map_type, name, key_size,
+ inner_map_fd, max_entries, map_flags,
+ -1);
+}
+
+int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
+ char *log_buf, size_t log_buf_sz)
+{
+ union bpf_attr attr;
+ __u32 name_len;
+ int fd;
+
+ if (!load_attr)
+ return -EINVAL;
+
+ name_len = load_attr->name ? strlen(load_attr->name) : 0;
+
+ bzero(&attr, sizeof(attr));
+ attr.prog_type = load_attr->prog_type;
+ attr.expected_attach_type = load_attr->expected_attach_type;
+ attr.insn_cnt = (__u32)load_attr->insns_cnt;
+ attr.insns = ptr_to_u64(load_attr->insns);
+ attr.license = ptr_to_u64(load_attr->license);
+ attr.log_buf = ptr_to_u64(NULL);
+ attr.log_size = 0;
+ attr.log_level = 0;
+ attr.kern_version = load_attr->kern_version;
+ attr.prog_ifindex = load_attr->prog_ifindex;
+ memcpy(attr.prog_name, load_attr->name,
+ min(name_len, BPF_OBJ_NAME_LEN - 1));
+
+ fd = sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+ if (fd >= 0 || !log_buf || !log_buf_sz)
+ return fd;
+
+ /* Try again with log */
+ attr.log_buf = ptr_to_u64(log_buf);
+ attr.log_size = log_buf_sz;
+ attr.log_level = 1;
+ log_buf[0] = 0;
+ return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+}
+
+int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
+ size_t insns_cnt, const char *name, const char *license,
+ __u32 kern_version, char *log_buf,
+ size_t log_buf_sz)
+{
+ struct bpf_load_program_attr load_attr;
+
+ memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
+ load_attr.prog_type = type;
+ load_attr.expected_attach_type = 0;
+ load_attr.name = name;
+ load_attr.insns = insns;
+ load_attr.insns_cnt = insns_cnt;
+ load_attr.license = license;
+ load_attr.kern_version = kern_version;
+
+ return bpf_load_program_xattr(&load_attr, log_buf, log_buf_sz);
+}
+
+int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
+ size_t insns_cnt, int strict_alignment,
+ const char *license, __u32 kern_version,
+ char *log_buf, size_t log_buf_sz, int log_level)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.prog_type = type;
+ attr.insn_cnt = (__u32)insns_cnt;
+ attr.insns = ptr_to_u64(insns);
+ attr.license = ptr_to_u64(license);
+ attr.log_buf = ptr_to_u64(log_buf);
+ attr.log_size = log_buf_sz;
+ attr.log_level = log_level;
+ log_buf[0] = 0;
+ attr.kern_version = kern_version;
+ attr.prog_flags = strict_alignment ? BPF_F_STRICT_ALIGNMENT : 0;
+
+ return sys_bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
+}
+
+int bpf_map_update_elem(int fd, const void *key, const void *value,
+ __u64 flags)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+ attr.flags = flags;
+
+ return sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
+}
+
+int bpf_map_lookup_elem(int fd, const void *key, void *value)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.value = ptr_to_u64(value);
+
+ return sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
+}
+
+int bpf_map_delete_elem(int fd, const void *key)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+
+ return sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
+}
+
+int bpf_map_get_next_key(int fd, const void *key, void *next_key)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.map_fd = fd;
+ attr.key = ptr_to_u64(key);
+ attr.next_key = ptr_to_u64(next_key);
+
+ return sys_bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
+}
+
+int bpf_obj_pin(int fd, const char *pathname)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.pathname = ptr_to_u64((void *)pathname);
+ attr.bpf_fd = fd;
+
+ return sys_bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
+}
+
+int bpf_obj_get(const char *pathname)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.pathname = ptr_to_u64((void *)pathname);
+
+ return sys_bpf(BPF_OBJ_GET, &attr, sizeof(attr));
+}
+
+int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type,
+ unsigned int flags)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.target_fd = target_fd;
+ attr.attach_bpf_fd = prog_fd;
+ attr.attach_type = type;
+ attr.attach_flags = flags;
+
+ return sys_bpf(BPF_PROG_ATTACH, &attr, sizeof(attr));
+}
+
+int bpf_prog_detach(int target_fd, enum bpf_attach_type type)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.target_fd = target_fd;
+ attr.attach_type = type;
+
+ return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
+}
+
+int bpf_prog_detach2(int prog_fd, int target_fd, enum bpf_attach_type type)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.target_fd = target_fd;
+ attr.attach_bpf_fd = prog_fd;
+ attr.attach_type = type;
+
+ return sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
+}
+
+int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
+ __u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt)
+{
+ union bpf_attr attr;
+ int ret;
+
+ bzero(&attr, sizeof(attr));
+ attr.query.target_fd = target_fd;
+ attr.query.attach_type = type;
+ attr.query.query_flags = query_flags;
+ attr.query.prog_cnt = *prog_cnt;
+ attr.query.prog_ids = ptr_to_u64(prog_ids);
+
+ ret = sys_bpf(BPF_PROG_QUERY, &attr, sizeof(attr));
+ if (attach_flags)
+ *attach_flags = attr.query.attach_flags;
+ *prog_cnt = attr.query.prog_cnt;
+ return ret;
+}
+
+int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
+ void *data_out, __u32 *size_out, __u32 *retval,
+ __u32 *duration)
+{
+ union bpf_attr attr;
+ int ret;
+
+ bzero(&attr, sizeof(attr));
+ attr.test.prog_fd = prog_fd;
+ attr.test.data_in = ptr_to_u64(data);
+ attr.test.data_out = ptr_to_u64(data_out);
+ attr.test.data_size_in = size;
+ attr.test.repeat = repeat;
+
+ ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
+ if (size_out)
+ *size_out = attr.test.data_size_out;
+ if (retval)
+ *retval = attr.test.retval;
+ if (duration)
+ *duration = attr.test.duration;
+ return ret;
+}
+
+int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
+{
+ union bpf_attr attr;
+ int err;
+
+ bzero(&attr, sizeof(attr));
+ attr.start_id = start_id;
+
+ err = sys_bpf(BPF_PROG_GET_NEXT_ID, &attr, sizeof(attr));
+ if (!err)
+ *next_id = attr.next_id;
+
+ return err;
+}
+
+int bpf_map_get_next_id(__u32 start_id, __u32 *next_id)
+{
+ union bpf_attr attr;
+ int err;
+
+ bzero(&attr, sizeof(attr));
+ attr.start_id = start_id;
+
+ err = sys_bpf(BPF_MAP_GET_NEXT_ID, &attr, sizeof(attr));
+ if (!err)
+ *next_id = attr.next_id;
+
+ return err;
+}
+
+int bpf_prog_get_fd_by_id(__u32 id)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.prog_id = id;
+
+ return sys_bpf(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
+int bpf_map_get_fd_by_id(__u32 id)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.map_id = id;
+
+ return sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
+int bpf_btf_get_fd_by_id(__u32 id)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.btf_id = id;
+
+ return sys_bpf(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
+}
+
+int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len)
+{
+ union bpf_attr attr;
+ int err;
+
+ bzero(&attr, sizeof(attr));
+ attr.info.bpf_fd = prog_fd;
+ attr.info.info_len = *info_len;
+ attr.info.info = ptr_to_u64(info);
+
+ err = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
+ if (!err)
+ *info_len = attr.info.info_len;
+
+ return err;
+}
+
+int bpf_raw_tracepoint_open(const char *name, int prog_fd)
+{
+ union bpf_attr attr;
+
+ bzero(&attr, sizeof(attr));
+ attr.raw_tracepoint.name = ptr_to_u64(name);
+ attr.raw_tracepoint.prog_fd = prog_fd;
+
+ return sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr));
+}
+
+int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size,
+ bool do_log)
+{
+ union bpf_attr attr = {};
+ int fd;
+
+ attr.btf = ptr_to_u64(btf);
+ attr.btf_size = btf_size;
+
+retry:
+ if (do_log && log_buf && log_buf_size) {
+ attr.btf_log_level = 1;
+ attr.btf_log_size = log_buf_size;
+ attr.btf_log_buf = ptr_to_u64(log_buf);
+ }
+
+ fd = sys_bpf(BPF_BTF_LOAD, &attr, sizeof(attr));
+ if (fd == -1 && !do_log && log_buf && log_buf_size) {
+ do_log = true;
+ goto retry;
+ }
+
+ return fd;
+}
diff --git a/utils/keytable/bpf.h b/utils/keytable/bpf.h
new file mode 100644
index 00000000..fb3896c9
--- /dev/null
+++ b/utils/keytable/bpf.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * common eBPF ELF operations.
+ *
+ * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
+ * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
+ * Copyright (C) 2015 Huawei Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License (not later!)
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses>
+ */
+#ifndef __BPF_BPF_H
+#define __BPF_BPF_H
+
+#include <linux/bpf.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+struct bpf_create_map_attr {
+ const char *name;
+ enum bpf_map_type map_type;
+ __u32 map_flags;
+ __u32 key_size;
+ __u32 value_size;
+ __u32 max_entries;
+ __u32 numa_node;
+ __u32 btf_fd;
+ __u32 btf_key_type_id;
+ __u32 btf_value_type_id;
+ __u32 map_ifindex;
+};
+
+int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr);
+int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
+ int key_size, int value_size, int max_entries,
+ __u32 map_flags, int node);
+int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
+ int key_size, int value_size, int max_entries,
+ __u32 map_flags);
+int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
+ int max_entries, __u32 map_flags);
+int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
+ int key_size, int inner_map_fd, int max_entries,
+ __u32 map_flags, int node);
+int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
+ int key_size, int inner_map_fd, int max_entries,
+ __u32 map_flags);
+
+struct bpf_load_program_attr {
+ enum bpf_prog_type prog_type;
+ enum bpf_attach_type expected_attach_type;
+ const char *name;
+ const struct bpf_insn *insns;
+ size_t insns_cnt;
+ const char *license;
+ __u32 kern_version;
+ __u32 prog_ifindex;
+};
+
+/* Recommend log buffer size */
+#define BPF_LOG_BUF_SIZE (256 * 1024)
+int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
+ char *log_buf, size_t log_buf_sz);
+int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
+ size_t insns_cnt, const char *name, const char *license,
+ __u32 kern_version, char *log_buf,
+ size_t log_buf_sz);
+int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
+ size_t insns_cnt, int strict_alignment,
+ const char *license, __u32 kern_version,
+ char *log_buf, size_t log_buf_sz, int log_level);
+
+int bpf_map_update_elem(int fd, const void *key, const void *value,
+ __u64 flags);
+
+int bpf_map_lookup_elem(int fd, const void *key, void *value);
+int bpf_map_delete_elem(int fd, const void *key);
+int bpf_map_get_next_key(int fd, const void *key, void *next_key);
+int bpf_obj_pin(int fd, const char *pathname);
+int bpf_obj_get(const char *pathname);
+int bpf_prog_attach(int prog_fd, int attachable_fd, enum bpf_attach_type type,
+ unsigned int flags);
+int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type);
+int bpf_prog_detach2(int prog_fd, int attachable_fd, enum bpf_attach_type type);
+int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
+ void *data_out, __u32 *size_out, __u32 *retval,
+ __u32 *duration);
+int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
+int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
+int bpf_prog_get_fd_by_id(__u32 id);
+int bpf_map_get_fd_by_id(__u32 id);
+int bpf_btf_get_fd_by_id(__u32 id);
+int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len);
+int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
+ __u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt);
+int bpf_raw_tracepoint_open(const char *name, int prog_fd);
+int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size,
+ bool do_log);
+#endif
diff --git a/utils/keytable/bpf_load.c b/utils/keytable/bpf_load.c
new file mode 100644
index 00000000..5e9040a7
--- /dev/null
+++ b/utils/keytable/bpf_load.c
@@ -0,0 +1,455 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <linux/bpf.h>
+#include <assert.h>
+#include "bpf.h"
+#include "bpf_load.h"
+
+#ifdef ENABLE_NLS
+# define _(string) gettext(string)
+# include "gettext.h"
+# include <locale.h>
+# include <langinfo.h>
+# include <iconv.h>
+#else
+# define _(string) string
+#endif
+
+
+char bpf_log_buf[BPF_LOG_BUF_SIZE];
+extern int debug;
+
+struct bpf_file {
+ Elf *elf;
+ char license[128];
+ bool processed_sec[128];
+ int map_fd[MAX_MAPS];
+ struct bpf_map_data map_data[MAX_MAPS];
+ int nr_maps;
+ int maps_shidx;
+ int dataidx;
+ int bssidx;
+ Elf_Data *data;
+ int strtabidx;
+ Elf_Data *symbols;
+};
+
+static int load_and_attach(int lirc_fd, struct bpf_file *bpf_file, const char *name, struct bpf_insn *prog, int size)
+{
+ size_t insns_cnt = size / sizeof(struct bpf_insn);
+ int fd, err;
+
+ fd = bpf_load_program(BPF_PROG_TYPE_LIRC_MODE2, prog, insns_cnt,
+ name, bpf_file->license, 0,
+ bpf_log_buf, BPF_LOG_BUF_SIZE);
+ if (fd < 0) {
+ printf("bpf_load_program() err=%d\n%s", errno, bpf_log_buf);
+ return -1;
+ }
+
+ err = bpf_prog_attach(fd, lirc_fd, BPF_LIRC_MODE2, 0);
+ if (err) {
+ printf("bpf_prog_attach: err=%m\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int load_maps(struct bpf_file *bpf_file)
+{
+ struct bpf_map_data *maps = bpf_file->map_data;
+ int i, numa_node;
+
+ for (i = 0; i < bpf_file->nr_maps; i++) {
+ numa_node = maps[i].def.map_flags & BPF_F_NUMA_NODE ?
+ maps[i].def.numa_node : -1;
+
+ if (maps[i].def.type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
+ maps[i].def.type == BPF_MAP_TYPE_HASH_OF_MAPS) {
+ int inner_map_fd = bpf_file->map_fd[maps[i].def.inner_map_idx];
+
+ bpf_file->map_fd[i] = bpf_create_map_in_map_node(
+ maps[i].def.type,
+ maps[i].name,
+ maps[i].def.key_size,
+ inner_map_fd,
+ maps[i].def.max_entries,
+ maps[i].def.map_flags,
+ numa_node);
+ } else {
+ bpf_file->map_fd[i] = bpf_create_map_node(
+ maps[i].def.type,
+ maps[i].name,
+ maps[i].def.key_size,
+ maps[i].def.value_size,
+ maps[i].def.max_entries,
+ maps[i].def.map_flags,
+ numa_node);
+ }
+ if (bpf_file->map_fd[i] < 0) {
+ printf(_("failed to create a map: %d %s\n"),
+ errno, strerror(errno));
+ return 1;
+ }
+ maps[i].fd = bpf_file->map_fd[i];
+ }
+ return 0;
+}
+
+static int get_sec(Elf *elf, int i, GElf_Ehdr *ehdr, char **shname,
+ GElf_Shdr *shdr, Elf_Data **data)
+{
+ Elf_Scn *scn;
+
+ scn = elf_getscn(elf, i);
+ if (!scn)
+ return 1;
+
+ if (gelf_getshdr(scn, shdr) != shdr)
+ return 2;
+
+ *shname = elf_strptr(elf, ehdr->e_shstrndx, shdr->sh_name);
+ if (!*shname || !shdr->sh_size)
+ return 3;
+
+ *data = elf_getdata(scn, 0);
+ if (!*data || elf_getdata(scn, *data) != NULL)
+ return 4;
+
+ return 0;
+}
+
+static int parse_relo_and_apply(struct bpf_file *bpf_file, GElf_Shdr *shdr,
+ struct bpf_insn *insn, Elf_Data *data)
+{
+ int i, nrels;
+
+ nrels = shdr->sh_size / shdr->sh_entsize;
+
+ for (i = 0; i < nrels; i++) {
+ GElf_Sym sym;
+ GElf_Rel rel;
+ unsigned int insn_idx;
+ const char *sym_name;
+ bool match = false;
+ int map_idx;
+
+ gelf_getrel(data, i, &rel);
+
+ insn_idx = rel.r_offset / sizeof(struct bpf_insn);
+
+ gelf_getsym(bpf_file->symbols, GELF_R_SYM(rel.r_info), &sym);
+
+ sym_name = elf_strptr(bpf_file->elf, bpf_file->strtabidx, sym.st_name);
+
+ if (insn[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
+ printf(_("invalid relo for insn[%d].code 0x%x\n"),
+ insn_idx, insn[insn_idx].code);
+ return 1;
+ }
+
+ if (sym.st_shndx == bpf_file->maps_shidx) {
+ /* Match FD relocation against recorded map_data[] offset */
+ for (map_idx = 0; map_idx < bpf_file->nr_maps; map_idx++) {
+ if (bpf_file->map_data[map_idx].elf_offset == sym.st_value) {
+ match = true;
+ break;
+ }
+ }
+
+ if (match) {
+ insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
+ insn[insn_idx].imm = bpf_file->map_data[map_idx].fd;
+ continue;
+ }
+
+ printf(_("invalid relo for insn[%d] no map_data match\n"),
+ insn_idx);
+ return 1;
+ }
+ else if (sym.st_shndx == bpf_file->dataidx || sym.st_shndx == bpf_file->bssidx) {
+ const char *raw = NULL;
+ int value = 0;
+
+ if (!bpf_param(sym_name, &value)) {
+ // done
+ } else if (sym.st_shndx == bpf_file->dataidx) {
+ int32_t *p = (bpf_file->data->d_buf + sym.st_value);
+ value = *p;
+ }
+
+ if (debug)
+ printf(_("patching insn[%d] with immediate %d for symbol %s\n"), insn_idx, value, sym_name);
+
+ // patch ld to mov immediate
+ insn[insn_idx].imm = value;
+ } else {
+ printf(_("symbol %s has unknown section %d\n"), sym_name, sym.st_shndx);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int cmp_symbols(const void *l, const void *r)
+{
+ const GElf_Sym *lsym = (const GElf_Sym *)l;
+ const GElf_Sym *rsym = (const GElf_Sym *)r;
+
+ if (lsym->st_value < rsym->st_value)
+ return -1;
+ else if (lsym->st_value > rsym->st_value)
+ return 1;
+ else
+ return 0;
+}
+
+static int load_elf_maps_section(struct bpf_file *bpf_file)
+{
+ int map_sz_elf, map_sz_copy;
+ bool validate_zero = false;
+ Elf_Data *data_maps;
+ int i, nr_maps;
+ GElf_Sym *sym;
+ Elf_Scn *scn;
+
+ if (bpf_file->maps_shidx < 0)
+ return -EINVAL;
+ if (!bpf_file->symbols)
+ return -EINVAL;
+
+ /* Get data for maps section via elf index */
+ scn = elf_getscn(bpf_file->elf, bpf_file->maps_shidx);
+ if (scn)
+ data_maps = elf_getdata(scn, NULL);
+ if (!scn || !data_maps) {
+ printf(_("Failed to get Elf_Data from maps section %d\n"),
+ bpf_file->maps_shidx);
+ return -EINVAL;
+ }
+
+ /* For each map get corrosponding symbol table entry */
+ sym = calloc(MAX_MAPS+1, sizeof(GElf_Sym));
+ for (i = 0, nr_maps = 0; i < bpf_file->symbols->d_size / sizeof(GElf_Sym); i++) {
+ assert(nr_maps < MAX_MAPS+1);
+ if (!gelf_getsym(bpf_file->symbols, i, &sym[nr_maps]))
+ continue;
+ if (sym[nr_maps].st_shndx != bpf_file->maps_shidx)
+ continue;
+ /* Only increment iif maps section */
+ nr_maps++;
+ }
+
+ /* Align to map_fd[] order, via sort on offset in sym.st_value */
+ qsort(sym, nr_maps, sizeof(GElf_Sym), cmp_symbols);
+
+ /* Keeping compatible with ELF maps section changes
+ * ------------------------------------------------
+ * The program size of struct bpf_load_map_def is known by loader
+ * code, but struct stored in ELF file can be different.
+ *
+ * Unfortunately sym[i].st_size is zero. To calculate the
+ * struct size stored in the ELF file, assume all struct have
+ * the same size, and simply divide with number of map
+ * symbols.
+ */
+ map_sz_elf = data_maps->d_size / nr_maps;
+ map_sz_copy = sizeof(struct bpf_load_map_def);
+ if (map_sz_elf < map_sz_copy) {
+ /*
+ * Backward compat, loading older ELF file with
+ * smaller struct, keeping remaining bytes zero.
+ */
+ map_sz_copy = map_sz_elf;
+ } else if (map_sz_elf > map_sz_copy) {
+ /*
+ * Forward compat, loading newer ELF file with larger
+ * struct with unknown features. Assume zero means
+ * feature not used. Thus, validate rest of struct
+ * data is zero.
+ */
+ validate_zero = true;
+ }
+
+ /* Memcpy relevant part of ELF maps data to loader maps */
+ for (i = 0; i < nr_maps; i++) {
+ struct bpf_load_map_def *def;
+ unsigned char *addr, *end;
+ const char *map_name;
+ struct bpf_map_data *maps = bpf_file->map_data;
+ size_t offset;
+
+ map_name = elf_strptr(bpf_file->elf, bpf_file->strtabidx, sym[i].st_name);
+ maps[i].name = strdup(map_name);
+ if (!maps[i].name) {
+ printf(_("strdup(%s): %s(%d)\n"), map_name,
+ strerror(errno), errno);
+ free(sym);
+ return -errno;
+ }
+
+ /* Symbol value is offset into ELF maps section data area */
+ offset = sym[i].st_value;
+ def = (struct bpf_load_map_def *)(data_maps->d_buf + offset);
+ maps[i].elf_offset = offset;
+ memset(&maps[i].def, 0, sizeof(struct bpf_load_map_def));
+ memcpy(&maps[i].def, def, map_sz_copy);
+
+ /* Verify no newer features were requested */
+ if (validate_zero) {
+ addr = (unsigned char*) def + map_sz_copy;
+ end = (unsigned char*) def + map_sz_elf;
+ for (; addr < end; addr++) {
+ if (*addr != 0) {
+ free(sym);
+ return -EFBIG;
+ }
+ }
+ }
+ }
+
+ free(sym);
+ return nr_maps;
+}
+
+int load_bpf_file(const char *path, int lirc_fd)
+{
+ struct bpf_file bpf_file = {};
+ int fd, i, ret;
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr, shdr_prog;
+ Elf_Data *data, *data_prog, *data_map = NULL;
+ char *shname, *shname_prog;
+ int nr_maps = 0;
+
+ if (elf_version(EV_CURRENT) == EV_NONE)
+ return 1;
+
+ fd = open(path, O_RDONLY, 0);
+ if (fd < 0)
+ return 1;
+
+ elf = elf_begin(fd, ELF_C_READ, NULL);
+
+ if (!elf)
+ return 1;
+
+ if (gelf_getehdr(elf, &ehdr) != &ehdr)
+ return 1;
+
+ bpf_file.elf = elf;
+
+ /* scan over all elf sections to get license and map info */
+ for (i = 1; i < ehdr.e_shnum; i++) {
+
+ if (get_sec(elf, i, &ehdr, &shname, &shdr, &data))
+ continue;
+
+ if (debug)
+ printf(_("section %d:%s data %p size %zd link %d flags %d\n"),
+ i, shname, data->d_buf, data->d_size,
+ shdr.sh_link, (int) shdr.sh_flags);
+
+ if (strcmp(shname, "license") == 0) {
+ bpf_file.processed_sec[i] = true;
+ memcpy(bpf_file.license, data->d_buf, data->d_size);
+ } else if (strcmp(shname, "maps") == 0) {
+ int j;
+
+ bpf_file.maps_shidx = i;
+ data_map = data;
+ for (j = 0; j < MAX_MAPS; j++)
+ bpf_file.map_data[j].fd = -1;
+ } else if (strcmp(shname, ".data") == 0) {
+ bpf_file.dataidx = i;
+ bpf_file.data = data;
+ } else if (strcmp(shname, ".bss") == 0) {
+ bpf_file.bssidx = i;
+ } else if (shdr.sh_type == SHT_SYMTAB) {
+ bpf_file.strtabidx = shdr.sh_link;
+ bpf_file.symbols = data;
+ }
+ }
+
+ ret = 1;
+
+ if (!bpf_file.symbols) {
+ printf(_("missing SHT_SYMTAB section\n"));
+ goto done;
+ }
+
+ if (data_map) {
+ bpf_file.nr_maps = load_elf_maps_section(&bpf_file);
+ if (bpf_file.nr_maps < 0) {
+ printf(_("Error: Failed loading ELF maps (errno:%d):%s\n"),
+ nr_maps, strerror(-nr_maps));
+ goto done;
+ }
+ if (load_maps(&bpf_file))
+ goto done;
+
+ bpf_file.processed_sec[bpf_file.maps_shidx] = true;
+ }
+
+ /* process all relo sections, and rewrite bpf insns for maps */
+ for (i = 1; i < ehdr.e_shnum; i++) {
+ if (bpf_file.processed_sec[i])
+ continue;
+
+ if (get_sec(elf, i, &ehdr, &shname, &shdr, &data))
+ continue;
+
+ if (shdr.sh_type == SHT_REL) {
+ struct bpf_insn *insns;
+
+ /* locate prog sec that need map fixup (relocations) */
+ if (get_sec(elf, shdr.sh_info, &ehdr, &shname_prog,
+ &shdr_prog, &data_prog))
+ continue;
+
+ if (shdr_prog.sh_type != SHT_PROGBITS ||
+ !(shdr_prog.sh_flags & SHF_EXECINSTR))
+ continue;
+
+ insns = (struct bpf_insn *) data_prog->d_buf;
+ bpf_file.processed_sec[i] = true; /* relo section */
+
+ if (parse_relo_and_apply(&bpf_file, &shdr, insns, data))
+ continue;
+ }
+ }
+
+ /* load programs */
+ for (i = 1; i < ehdr.e_shnum; i++) {
+ if (bpf_file.processed_sec[i])
+ continue;
+
+ if (get_sec(elf, i, &ehdr, &shname, &shdr, &data))
+ continue;
+
+ if (shdr.sh_type != SHT_PROGBITS ||
+ !(shdr.sh_flags & SHF_EXECINSTR))
+ continue;
+
+ ret = load_and_attach(lirc_fd, &bpf_file, shname, data->d_buf,
+ data->d_size);
+ break;
+ }
+
+done:
+ close(fd);
+ return ret;
+}
diff --git a/utils/keytable/bpf_load.h b/utils/keytable/bpf_load.h
new file mode 100644
index 00000000..ec9763e4
--- /dev/null
+++ b/utils/keytable/bpf_load.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __BPF_LOAD_H
+#define __BPF_LOAD_H
+
+#define BPF_LOG_BUF_SIZE (256 * 1024)
+
+#define MAX_MAPS 32
+#define MAX_PROGS 64
+
+struct bpf_load_map_def {
+ unsigned int type;
+ unsigned int key_size;
+ unsigned int value_size;
+ unsigned int max_entries;
+ unsigned int map_flags;
+ unsigned int inner_map_idx;
+ unsigned int numa_node;
+};
+
+struct bpf_map_data {
+ int fd;
+ char *name;
+ size_t elf_offset;
+ struct bpf_load_map_def def;
+};
+
+/* parses elf file compiled by llvm .c->.o
+ * . parses 'maps' section and creates maps via BPF syscall
+ * . parses 'license' section and passes it to syscall
+ * . parses elf relocations for BPF maps and adjusts BPF_LD_IMM64 insns by
+ * storing map_fd into insn->imm and marking such insns as BPF_PSEUDO_MAP_FD
+ * . loads eBPF programs via BPF syscall
+ *
+ * One ELF file can contain multiple BPF programs which will be loaded
+ * and their FDs stored stored in prog_fd array
+ *
+ * returns zero on success
+ */
+int load_bpf_file(const char *path, int lirc_fd);
+
+int bpf_param(const char *name, int *val);
+
+#endif
diff --git a/utils/keytable/ir-keytable.1.in b/utils/keytable/ir-keytable.1.in
index db1843b3..0be8a5a7 100644
--- a/utils/keytable/ir-keytable.1.in
+++ b/utils/keytable/ir-keytable.1.in
@@ -35,8 +35,10 @@ Sets the delay before repeating a keystroke
Change scan/key pairs
.TP
\fB\-p\fR, \fB\-\-protocol\fR=\fIPROTOCOL\fR
-Protocol to enable (the other ones will be disabled). To enable more than one,
-use the option more than one time
+Comma seperated list of protocols to enabled (the other ones will be disabled). Alternatively, this is the path to a BPF protocol to be loaded.
+.TP
+\fB\-e\fR, \fB\-\-parameter\fR=\fIPARAMETER\fR
+Commma seperated list of parameters for the BPF protocol
.TP
\fB\-P\fR, \fB\-\-period\fR\=\fiPERIOD\fR
Sets the period to repeat a keystroke
@@ -78,7 +80,9 @@ a file wit a set of scancode=keycode value pairs
.IP \fISCANKEY\fR
a set of scancode1=keycode1,scancode2=keycode2.. value pairs
.IP \fIPROTOCOL\fR
-protocol name to be enabled (case insensitive). Supported protocols are: NEC, RC-5, RC-6, JVC, SONY, SANYO, LIRC, RC-5-SZ, SHARP, MCE-KBD, XMP, other, all.
+Comma separated list of protocols to be enabled (case insensitive). Supported kernel protocols are: NEC, RC-5, RC-6, JVC, SONY, SANYO, LIRC, RC-5-SZ, SHARP, MCE-KBD, XMP, IMON, other, all. There are also BPF protocols: manchester, rc_mm, pulse_distance, grundig. Alternatively, this can be the path to a BPF protocol file to be loaded.
+.IP \fIPARAMETERS\fR
+Comma seperated list of parameters for the BPF protocol being loaded. They have the format of name=value, where value is an number.
.IP \fIDELAY\fR
Delay before repeating a keystroke
.IP \fIPERIOD\fR
@@ -102,6 +106,10 @@ To append more codes to the existing table:
.PP
To read the current keytable, on the second remote controller:
\fBir\-keytable \-s rc1 \-r\fR
+.PP
+To enable NEC protocol and load a BPF protocol, with a parameter for the BPF protocol:
+.br
+ \fBir\-keytable \-p nec,pulse_distance \-e pulse_header=9000
.SH BUGS
Report bugs to \fBLinux Media Mailing List <linux-media@vger.kernel.org>\fR
.SH COPYRIGHT
diff --git a/utils/keytable/keytable.c b/utils/keytable/keytable.c
index 482fcf86..d058c856 100644
--- a/utils/keytable/keytable.c
+++ b/utils/keytable/keytable.c
@@ -33,6 +33,8 @@
#include "ir-encode.h"
#include "parse.h"
+#include "bpf.h"
+#include "bpf_load.h"
#ifdef ENABLE_NLS
# define _(string) gettext(string)
@@ -55,6 +57,10 @@ struct input_keymap_entry_v2 {
u_int8_t scancode[32];
};
+
+#define IR_PROTOCOLS_USER_DIR IR_KEYTABLE_USER_DIR "/protocols"
+#define IR_PROTOCOLS_SYSTEM_DIR IR_KEYTABLE_SYSTEM_DIR "/protocols"
+
#ifndef EVIOCSCLOCKID
#define EVIOCSCLOCKID _IOW('E', 0xa0, int)
#endif
@@ -218,15 +224,18 @@ static const char doc[] = N_(
"\nAllows get/set IR keycode/scancode tables\n"
"You need to have read permissions on /dev/input for the program to work\n"
"\nOn the options below, the arguments are:\n"
- " DEV - the /dev/input/event* device to control\n"
- " SYSDEV - the ir class as found at /sys/class/rc\n"
- " TABLE - a file with a set of scancode=keycode value pairs\n"
- " SCANKEY - a set of scancode1=keycode1,scancode2=keycode2.. value pairs\n"
- " PROTOCOL - protocol name (nec, rc-5, rc-6, jvc, sony, sanyo, rc-5-sz, lirc,\n"
- " sharp, mce_kbd, xmp, other, all) to be enabled\n"
- " DELAY - Delay before repeating a keystroke\n"
- " PERIOD - Period to repeat a keystroke\n"
- " CFGFILE - configuration file that associates a driver/table name with a keymap file\n"
+ " DEV - the /dev/input/event* device to control\n"
+ " SYSDEV - the ir class as found at /sys/class/rc\n"
+ " TABLE - a file with a set of scancode=keycode value pairs\n"
+ " SCANKEY - a set of scancode1=keycode1,scancode2=keycode2.. value pairs\n"
+ " PROTOCOL - protocol name (nec, rc-5, rc-6, jvc, sony, sanyo, rc-5-sz, lirc,\n"
+ " sharp, mce_kbd, xmp, imon, other, all) to be enabled,\n"
+ " or a bpf protocol name or file\n"
+ " DELAY - Delay before repeating a keystroke\n"
+ " PERIOD - Period to repeat a keystroke\n"
+ " PARAMETER - a set of name1=number1[,name2=number2]... for the BPF prototcol\n"
+ " CFGFILE - configuration file that associates a driver/table name with\n"
+ " a keymap file\n"
"\nOptions can be combined together.");
static const struct argp_option options[] = {
@@ -239,6 +248,7 @@ static const struct argp_option options[] = {
{"write", 'w', N_("TABLE"), 0, N_("write (adds) the scancodes to the device scancode/keycode table from an specified file"), 0},
{"set-key", 'k', N_("SCANKEY"), 0, N_("Change scan/key pairs"), 0},
{"protocol", 'p', N_("PROTOCOL"), 0, N_("Protocol to enable (the other ones will be disabled). To enable more than one, use the option more than one time"), 0},
+ {"parameter", 'e', N_("PARAMETER"), 0, N_("Set a parameter for the protocol decoder")},
{"delay", 'D', N_("DELAY"), 0, N_("Sets the delay before repeating a keystroke"), 0},
{"period", 'P', N_("PERIOD"), 0, N_("Sets the period to repeat a keystroke"), 0},
{"auto-load", 'a', N_("CFGFILE"), 0, N_("Auto-load a table, based on a configuration file. Only works with sysdev."), 0},
@@ -258,12 +268,26 @@ static char *devclass = NULL;
static char *devicename = NULL;
static int readtable = 0;
static int clear = 0;
-static int debug = 0;
+int debug = 0;
static int test = 0;
static int delay = -1;
static int period = -1;
static enum sysfs_protocols ch_proto = 0;
+struct bpf_protocol {
+ struct bpf_protocol *next;
+ char *name;
+};
+
+struct bpf_parameter {
+ struct bpf_parameter *next;
+ int value;
+ char name[0];
+};
+
+static struct bpf_protocol *bpf_protocol;
+static struct bpf_parameter *bpf_parameter;
+
struct cfgfile cfg = {
NULL, NULL, NULL, NULL
};
@@ -397,7 +421,6 @@ err_einval:
fprintf(stderr, _("Invalid parameter on line %d of %s\n"),
line, fname);
return EINVAL;
-
}
struct cfgfile *nextcfg = &cfg;
@@ -580,13 +603,61 @@ static error_t parse_opt(int k, char *arg, struct argp_state *state)
protocol = parse_sysfs_protocol(p, true);
if (protocol == SYSFS_INVALID) {
- argp_error(state, _("Unknown protocol: %s"), p);
+ struct bpf_protocol *b;
+
+ b = malloc(sizeof(*b));
+ b->name = strdup(p);
+ b->next = bpf_protocol;
+ bpf_protocol = b;
+ }
+ else {
+ ch_proto |= protocol;
+ }
+ }
+ break;
+ case 'e':
+ p = strtok(arg, ":=");
+ do {
+ struct bpf_parameter *param;
+
+ if (!param) {
+ argp_error(state, _("Missing parameter name: %s"), arg);
break;
}
- ch_proto |= protocol;
- }
+ param = calloc(1, sizeof(*param) + strlen(p) + 1);
+ if (!p) {
+ perror(_("No memory!\n"));
+ return ENOMEM;
+ }
+
+ strcpy(param->name, p);
+
+ p = strtok(NULL, ",;");
+ if (!p) {
+ free(param);
+ argp_error(state, _("Missing value"));
+ break;
+ }
+
+ param->value = strtol(p, NULL, 0);
+ if (errno) {
+ free(param);
+ argp_error(state, _("Unknown keycode: %s"), p);
+ break;
+ }
+
+ if (debug)
+ fprintf(stderr, _("parameter %s=%d\n"),
+ param->name, param->value);
+
+ param->next = bpf_parameter;
+ bpf_parameter = param;
+
+ p = strtok(NULL, ":=");
+ } while (p);
break;
+
case '?':
argp_state_help(state, state->out_stream,
ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG
@@ -1188,10 +1259,6 @@ static int set_proto(struct rc_device *rc_dev)
int rc = 0;
rc_dev->current &= rc_dev->supported;
- if (!rc_dev->current) {
- fprintf(stderr, _("Invalid protocols selected\n"));
- return EINVAL;
- }
if (rc_dev->version == VERSION_2) {
rc = v2_set_protocols(rc_dev);
@@ -1299,9 +1366,9 @@ static int add_keys(int fd)
static void display_proto(struct rc_device *rc_dev)
{
if (rc_dev->type == HARDWARE_DECODER)
- fprintf(stderr, _("Current protocols: "));
+ fprintf(stderr, _("Current kernel protocols: "));
else
- fprintf(stderr, _("Enabled protocols: "));
+ fprintf(stderr, _("Enabled kernel protocols: "));
write_sysfs_protocols(rc_dev->current, stderr, "%s ");
fprintf(stderr, "\n");
}
@@ -1571,6 +1638,147 @@ static void device_info(int fd, char *prepend)
perror ("EVIOCGID");
}
+#ifdef HAVE_LIBELF
+#define MAX_PROGS 64
+static void attach_bpf(const char *lirc_name, const char *bpf_prog)
+{
+ unsigned int features;
+ int fd;
+
+ fd = open(lirc_name, O_RDONLY);
+ if (fd == -1) {
+ perror(lirc_name);
+ return;
+ }
+
+ if (ioctl(fd, LIRC_GET_FEATURES, &features)) {
+ perror(lirc_name);
+ close(fd);
+ return;
+ }
+
+ if (!(features & LIRC_CAN_REC_MODE2)) {
+ fprintf(stderr, _("%s: not a raw IR receiver\n"), lirc_name);
+ close(fd);
+ return;
+ }
+
+ load_bpf_file(bpf_prog, fd);
+ close(fd);
+}
+
+static void show_bpf(const char *lirc_name)
+{
+ unsigned int prog_ids[MAX_PROGS], count = MAX_PROGS;
+ unsigned int features, i;
+ int ret, fd, prog_fd;
+
+ fd = open(lirc_name, O_RDONLY);
+ if (fd == -1)
+ goto error;
+
+ if (ioctl(fd, LIRC_GET_FEATURES, &features)) {
+ close(fd);
+ goto error;
+ }
+
+ if (!(features & LIRC_CAN_REC_MODE2)) {
+ // only support for mode2 type raw ir devices
+ close(fd);
+ return;
+ }
+
+ ret = bpf_prog_query(fd, BPF_LIRC_MODE2, 0, NULL, prog_ids, &count);
+ close(fd);
+ if (ret) {
+ if (errno == EINVAL)
+ errno = ENOTSUP;
+ goto error;
+ }
+
+ printf(_("\tAttached BPF protocols: "));
+ for (i=0; i<count; i++) {
+ if (i)
+ printf(" ");
+ prog_fd = bpf_prog_get_fd_by_id(prog_ids[i]);
+ if (prog_fd != -1) {
+ struct bpf_prog_info info = {};
+ __u32 info_len = sizeof(info);
+
+ ret = bpf_obj_get_info_by_fd(prog_fd, &info, &info_len);
+ close(prog_fd);
+ if (!ret && info.name[0]) {
+ printf("%s", info.name);
+ continue;
+ }
+ }
+ printf("%d", prog_ids[i]);
+ }
+ printf(_("\n"));
+ return;
+error:
+ printf(_("\tAttached BPF protocols: %m\n"));
+}
+
+static void clear_bpf(const char *lirc_name)
+{
+ unsigned int prog_ids[MAX_PROGS], count = MAX_PROGS;
+ unsigned int features, i;
+ int ret, prog_fd, fd;
+
+ fd = open(lirc_name, O_RDONLY);
+ if (fd == -1) {
+ perror(lirc_name);
+ return;
+ }
+
+ if (ioctl(fd, LIRC_GET_FEATURES, &features)) {
+ perror(lirc_name);
+ close(fd);
+ return;
+ }
+
+ if (!(features & LIRC_CAN_REC_MODE2)) {
+ // only support for mode2 type raw ir devices
+ close(fd);
+ return;
+ }
+
+ ret = bpf_prog_query(fd, BPF_LIRC_MODE2, 0, NULL, prog_ids, &count);
+ if (ret) {
+ close(fd);
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (debug)
+ fprintf(stderr, _("BPF protocol prog_id %d\n"),
+ prog_ids[i]);
+ prog_fd = bpf_prog_get_fd_by_id(prog_ids[i]);
+ if (prog_fd == -1) {
+ printf(_("Failed to get BPF prog id %u: %m\n"),
+ prog_ids[i]);
+ continue;
+ }
+ ret = bpf_prog_detach2(prog_fd, fd, BPF_LIRC_MODE2);
+ if (ret)
+ printf(("Failed to detach BPF prog id %u: %m\n"),
+ prog_ids[i]);
+ close(prog_fd);
+ }
+ close(fd);
+ if (debug)
+ fprintf(stderr, _("BPF protocols removed\n"));
+}
+#else
+static void attach_bpf(const char *lirc_name, const char *bpf_prog)
+{
+ fprintf(stderr, _("error: ir-keytable was compiled without BPF support\n"));
+}
+static void show_bpf(const char *lirc_name) {}
+static void clear_bpf(const char *lirc_name) {}
+#endif
+
static int show_sysfs_attribs(struct rc_device *rc_dev, char *name)
{
static struct sysfs_names *names, *cur;
@@ -1592,10 +1800,12 @@ static int show_sysfs_attribs(struct rc_device *rc_dev, char *name)
fprintf(stderr, _("\tDriver: %s, table: %s\n"),
rc_dev->drv_name,
rc_dev->keytable_name);
- if (rc_dev->lirc_name)
- fprintf(stderr, _("\tlirc device: %s\n"),
+ if (rc_dev->lirc_name) {
+ fprintf(stderr, _("\tLIRC device: %s\n"),
rc_dev->lirc_name);
- fprintf(stderr, _("\tSupported protocols: "));
+ show_bpf(rc_dev->lirc_name);
+ }
+ fprintf(stderr, _("\tSupported kernel protocols: "));
write_sysfs_protocols(rc_dev->supported, stderr, "%s ");
fprintf(stderr, "\n\t");
display_proto(rc_dev);
@@ -1614,6 +1824,44 @@ static int show_sysfs_attribs(struct rc_device *rc_dev, char *name)
return 0;
}
+static char *find_bpf_file(const char *name)
+{
+ struct stat st;
+ char *fname;
+
+ if (!stat(name, &st))
+ return strdup(name);
+
+ asprintf(&fname, IR_PROTOCOLS_USER_DIR "/%s.o", name);
+ if (stat(fname, &st)) {
+ free(fname);
+ asprintf(&fname, IR_PROTOCOLS_SYSTEM_DIR "/%s.o", name);
+
+ if (stat(fname, &st)) {
+ fprintf(stderr, _("Can't find %s bpf protocol in %s or %s\n"), name, IR_KEYTABLE_USER_DIR "/protocols", IR_KEYTABLE_SYSTEM_DIR "/protocols");
+ free(fname);
+ return NULL;
+ }
+ }
+
+ return fname;
+}
+
+int bpf_param(const char *name, int *val)
+{
+ struct bpf_parameter *param = bpf_parameter;
+
+ while (param) {
+ if (strcmp(name, param->name) == 0) {
+ *val = param->value;
+ return 0;
+ }
+ param = param->next;
+ }
+
+ return -ENOENT;
+}
+
int main(int argc, char *argv[])
{
int dev_from_class = 0, write_cnt;
@@ -1630,7 +1878,7 @@ int main(int argc, char *argv[])
argp_parse(&argp, argc, argv, ARGP_NO_HELP, 0, 0);
/* Just list all devices */
- if (!clear && !readtable && !keytable && !ch_proto && !cfg.next && !test && delay < 0 && period < 0) {
+ if (!clear && !readtable && !keytable && !ch_proto && !cfg.next && !test && delay < 0 && period < 0 && !bpf_protocol) {
if (devicename) {
fd = open(devicename, O_RDONLY);
if (fd < 0) {
@@ -1756,7 +2004,10 @@ int main(int argc, char *argv[])
/*
* Third step: change protocol
*/
- if (ch_proto) {
+ if (ch_proto || bpf_protocol) {
+ if (rc_dev.lirc_name)
+ clear_bpf(rc_dev.lirc_name);
+
rc_dev.current = ch_proto;
if (set_proto(&rc_dev))
fprintf(stderr, _("Couldn't change the IR protocols\n"));
@@ -1767,6 +2018,23 @@ int main(int argc, char *argv[])
}
}
+ if (bpf_protocol) {
+ struct bpf_protocol *b;
+
+ if (!rc_dev.lirc_name) {
+ fprintf(stderr, _("Error: unable to attach bpf program, lirc device name was not found\n"));
+ }
+
+ for (b = bpf_protocol; b && rc_dev.lirc_name; b = b->next) {
+ char *fname = find_bpf_file(b->name);
+
+ if (fname) {
+ attach_bpf(rc_dev.lirc_name, fname);
+ free(fname);
+ }
+ }
+ }
+
/*
* Fourth step: display current keytable
*/

Privacy Policy