The kernel IR decoders support the most widely used IR protocols, but there
are many protocols which are not supported[1]. For example, the
lirc-remotes[2] repo has over 2700 remotes, many of which are not supported
by rc-core. There is a "long tail" of unsupported IR protocols.
IR encoding is done in such a way that some simple circuit can decode it;
therefore, bpf is ideal.
In order to support all these protocols, here we have bpf based IR decoding.
The idea is that user-space can define a decoder in bpf, attach it to
the rc device through the lirc chardev.
Separate work is underway to extend ir-keytable to have an extensive library
of bpf-based decoders, and a much expanded library of rc keymaps.
Another future application would be to compile IRP[3] to a IR BPF program, and
so support virtually every remote without having to write a decoder for each.
Thanks,
Sean Young
[1] http://www.hifi-remote.com/wiki/index.php?title=DecodeIR
[2] https://sourceforge.net/p/lirc-remotes/code/ci/master/tree/remotes/
[3] http://www.hifi-remote.com/wiki/index.php?title=IRP_Notation
Sean Young (4):
media: rc: introduce BPF_PROG_IR_DECODER
media: bpf: allow raw IR decoder bpf programs to be used
media: rc bpf: move ir_raw_event to uapi
samples/bpf: an example of a raw IR decoder
drivers/media/rc/Kconfig | 8 +
drivers/media/rc/Makefile | 1 +
drivers/media/rc/ir-bpf-decoder.c | 284 ++++++++++++++++++++++
drivers/media/rc/lirc_dev.c | 30 +++
drivers/media/rc/rc-core-priv.h | 15 ++
drivers/media/rc/rc-ir-raw.c | 5 +
include/linux/bpf_types.h | 3 +
include/media/rc-core.h | 19 +-
include/uapi/linux/bpf.h | 17 +-
include/uapi/linux/bpf_rcdev.h | 24 ++
kernel/bpf/syscall.c | 7 +
samples/bpf/Makefile | 4 +
samples/bpf/bpf_load.c | 9 +-
samples/bpf/grundig_decoder_kern.c | 112 +++++++++
samples/bpf/grundig_decoder_user.c | 54 ++++
tools/bpf/bpftool/prog.c | 1 +
tools/include/uapi/linux/bpf.h | 17 +-
tools/testing/selftests/bpf/bpf_helpers.h | 6 +
18 files changed, 594 insertions(+), 22 deletions(-)
create mode 100644 drivers/media/rc/ir-bpf-decoder.c
create mode 100644 include/uapi/linux/bpf_rcdev.h
create mode 100644 samples/bpf/grundig_decoder_kern.c
create mode 100644 samples/bpf/grundig_decoder_user.c
--
2.17.0
This implements attaching, detaching, querying and execution. The target
fd has to be the /dev/lircN device.
Signed-off-by: Sean Young <[email protected]>
---
drivers/media/rc/ir-bpf-decoder.c | 191 ++++++++++++++++++++++++++++++
drivers/media/rc/lirc_dev.c | 30 +++++
drivers/media/rc/rc-core-priv.h | 15 +++
drivers/media/rc/rc-ir-raw.c | 5 +
include/uapi/linux/bpf.h | 1 +
kernel/bpf/syscall.c | 7 ++
6 files changed, 249 insertions(+)
diff --git a/drivers/media/rc/ir-bpf-decoder.c b/drivers/media/rc/ir-bpf-decoder.c
index aaa5e208b1a5..651590a14772 100644
--- a/drivers/media/rc/ir-bpf-decoder.c
+++ b/drivers/media/rc/ir-bpf-decoder.c
@@ -91,3 +91,194 @@ const struct bpf_verifier_ops ir_decoder_verifier_ops = {
.get_func_proto = ir_decoder_func_proto,
.is_valid_access = ir_decoder_is_valid_access
};
+
+#define BPF_MAX_PROGS 64
+
+int rc_dev_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog, u32 flags)
+{
+ struct ir_raw_event_ctrl *raw;
+ struct bpf_prog_array __rcu *old_array;
+ struct bpf_prog_array *new_array;
+ int ret;
+
+ if (rcdev->driver_type != RC_DRIVER_IR_RAW)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&rcdev->lock);
+ if (ret)
+ return ret;
+
+ raw = rcdev->raw;
+
+ if (raw->progs && bpf_prog_array_length(raw->progs) >= BPF_MAX_PROGS) {
+ ret = -E2BIG;
+ goto out;
+ }
+
+ old_array = raw->progs;
+ ret = bpf_prog_array_copy(old_array, NULL, prog, &new_array);
+ if (ret < 0)
+ goto out;
+
+ rcu_assign_pointer(raw->progs, new_array);
+ bpf_prog_array_free(old_array);
+out:
+ mutex_unlock(&rcdev->lock);
+ return ret;
+}
+
+int rc_dev_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog, u32 flags)
+{
+ struct ir_raw_event_ctrl *raw;
+ struct bpf_prog_array __rcu *old_array;
+ struct bpf_prog_array *new_array;
+ int ret;
+
+ if (rcdev->driver_type != RC_DRIVER_IR_RAW)
+ return -EINVAL;
+
+ ret = mutex_lock_interruptible(&rcdev->lock);
+ if (ret)
+ return ret;
+
+ raw = rcdev->raw;
+
+ old_array = raw->progs;
+ ret = bpf_prog_array_copy(old_array, prog, NULL, &new_array);
+ if (ret < 0) {
+ bpf_prog_array_delete_safe(old_array, prog);
+ } else {
+ rcu_assign_pointer(raw->progs, new_array);
+ bpf_prog_array_free(old_array);
+ }
+
+ bpf_prog_put(prog);
+ mutex_unlock(&rcdev->lock);
+ return 0;
+}
+
+void rc_dev_bpf_run(struct rc_dev *rcdev)
+{
+ struct ir_raw_event_ctrl *raw = rcdev->raw;
+
+ if (raw->progs)
+ BPF_PROG_RUN_ARRAY(raw->progs, &raw->prev_ev, BPF_PROG_RUN);
+}
+
+void rc_dev_bpf_put(struct rc_dev *rcdev)
+{
+ struct bpf_prog_array *progs = rcdev->raw->progs;
+ int i, size;
+
+ if (!progs)
+ return;
+
+ size = bpf_prog_array_length(progs);
+ for (i = 0; i < size; i++)
+ bpf_prog_put(progs->progs[i]);
+
+ bpf_prog_array_free(rcdev->raw->progs);
+}
+
+int rc_dev_prog_attach(const union bpf_attr *attr)
+{
+ struct bpf_prog *prog;
+ struct rc_dev *rcdev;
+ int ret;
+
+ if (attr->attach_flags & BPF_F_ALLOW_OVERRIDE)
+ return -EINVAL;
+
+ prog = bpf_prog_get_type(attr->attach_bpf_fd,
+ BPF_PROG_TYPE_RAWIR_DECODER);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ rcdev = rc_dev_get_from_fd(attr->target_fd);
+ if (IS_ERR(rcdev)) {
+ bpf_prog_put(prog);
+ return PTR_ERR(rcdev);
+ }
+
+ ret = rc_dev_bpf_attach(rcdev, prog, attr->attach_flags);
+ if (ret)
+ bpf_prog_put(prog);
+
+ put_device(&rcdev->dev);
+
+ return ret;
+}
+
+int rc_dev_prog_detach(const union bpf_attr *attr)
+{
+ struct bpf_prog *prog;
+ struct rc_dev *rcdev;
+ int ret;
+
+ if (attr->attach_flags & BPF_F_ALLOW_OVERRIDE)
+ return -EINVAL;
+
+ prog = bpf_prog_get_type(attr->attach_bpf_fd,
+ BPF_PROG_TYPE_RAWIR_DECODER);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ rcdev = rc_dev_get_from_fd(attr->target_fd);
+ if (IS_ERR(rcdev)) {
+ bpf_prog_put(prog);
+ return PTR_ERR(rcdev);
+ }
+
+ ret = rc_dev_bpf_detach(rcdev, prog, attr->attach_flags);
+
+ bpf_prog_put(prog);
+ put_device(&rcdev->dev);
+
+ return ret;
+}
+
+int rc_dev_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr)
+{
+ __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
+ struct bpf_prog_array *progs;
+ struct rc_dev *rcdev;
+ u32 cnt, flags = 0;
+ int ret;
+
+ if (attr->query.query_flags)
+ return -EINVAL;
+
+ rcdev = rc_dev_get_from_fd(attr->query.target_fd);
+ if (IS_ERR(rcdev))
+ return PTR_ERR(rcdev);
+
+ if (rcdev->driver_type != RC_DRIVER_IR_RAW) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = mutex_lock_interruptible(&rcdev->lock);
+ if (ret)
+ goto out;
+
+ progs = rcdev->raw->progs;
+ cnt = progs ? bpf_prog_array_length(progs) : 0;
+
+ if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (attr->query.prog_cnt != 0 && prog_ids && cnt)
+ ret = bpf_prog_array_copy_to_user(progs, prog_ids, cnt);
+
+out:
+ mutex_unlock(&rcdev->lock);
+ put_device(&rcdev->dev);
+
+ return ret;
+}
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 24e9fbb80e81..65319f2ccc13 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -20,6 +20,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/device.h>
+#include <linux/file.h>
#include <linux/idr.h>
#include <linux/poll.h>
#include <linux/sched.h>
@@ -28,6 +29,8 @@
#include "rc-core-priv.h"
#include <uapi/linux/lirc.h>
+#include <linux/bpf-rcdev.h>
+
#define LIRCBUF_SIZE 256
static dev_t lirc_base_dev;
@@ -816,4 +819,31 @@ void __exit lirc_dev_exit(void)
unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
}
+struct rc_dev *rc_dev_get_from_fd(int fd)
+{
+ struct rc_dev *dev;
+ struct file *f;
+
+ f = fget_raw(fd);
+ if (!f)
+ return ERR_PTR(-EBADF);
+
+ if (!S_ISCHR(f->f_inode->i_mode) ||
+ imajor(f->f_inode) != MAJOR(lirc_base_dev)) {
+ fput(f);
+ return ERR_PTR(-EBADF);
+ }
+
+ dev = container_of(f->f_inode->i_cdev, struct rc_dev, lirc_cdev);
+ if (!dev->registered) {
+ fput(f);
+ return ERR_PTR(-ENODEV);
+ }
+
+ get_device(&dev->dev);
+ fput(f);
+
+ return dev;
+}
+
MODULE_ALIAS("lirc_dev");
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index e0e6a17460f6..b6f24f369657 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -57,6 +57,9 @@ struct ir_raw_event_ctrl {
/* raw decoder state follows */
struct ir_raw_event prev_ev;
struct ir_raw_event this_ev;
+#ifdef CONFIG_IR_BPF_DECODER
+ struct bpf_prog_array *progs;
+#endif
struct nec_dec {
int state;
unsigned count;
@@ -288,6 +291,7 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev);
void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc);
int ir_lirc_register(struct rc_dev *dev);
void ir_lirc_unregister(struct rc_dev *dev);
+struct rc_dev *rc_dev_get_from_fd(int fd);
#else
static inline int lirc_dev_init(void) { return 0; }
static inline void lirc_dev_exit(void) {}
@@ -299,4 +303,15 @@ static inline int ir_lirc_register(struct rc_dev *dev) { return 0; }
static inline void ir_lirc_unregister(struct rc_dev *dev) { }
#endif
+/*
+ * bpf interface
+ */
+#ifdef CONFIG_IR_BPF_DECODER
+void rc_dev_bpf_put(struct rc_dev *dev);
+void rc_dev_bpf_run(struct rc_dev *dev);
+#else
+void rc_dev_bpf_put(struct rc_dev *dev) {}
+void rc_dev_bpf_run(struct rc_dev *dev) {}
+#endif
+
#endif /* _RC_CORE_PRIV */
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index 374f83105a23..efddd9c44466 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -8,6 +8,8 @@
#include <linux/mutex.h>
#include <linux/kmod.h>
#include <linux/sched.h>
+#include <linux/filter.h>
+#include <linux/bpf.h>
#include "rc-core-priv.h"
/* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */
@@ -33,6 +35,7 @@ static int ir_raw_event_thread(void *data)
handler->decode(raw->dev, ev);
ir_lirc_raw_event(raw->dev, ev);
raw->prev_ev = ev;
+ rc_dev_bpf_run(raw->dev);
}
mutex_unlock(&ir_raw_handler_lock);
@@ -623,6 +626,8 @@ void ir_raw_event_unregister(struct rc_dev *dev)
handler->raw_unregister(dev);
mutex_unlock(&ir_raw_handler_lock);
+ rc_dev_bpf_put(dev);
+
ir_raw_event_free(dev);
}
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 6ad053e831c0..d9740599adf6 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -155,6 +155,7 @@ enum bpf_attach_type {
BPF_CGROUP_INET6_CONNECT,
BPF_CGROUP_INET4_POST_BIND,
BPF_CGROUP_INET6_POST_BIND,
+ BPF_RAWIR_DECODER,
__MAX_BPF_ATTACH_TYPE
};
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 016ef9025827..63ecc1f2e1e3 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -27,6 +27,7 @@
#include <linux/timekeeping.h>
#include <linux/ctype.h>
#include <linux/nospec.h>
+#include <linux/bpf-rcdev.h>
#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \
(map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
@@ -1556,6 +1557,8 @@ static int bpf_prog_attach(const union bpf_attr *attr)
case BPF_SK_SKB_STREAM_PARSER:
case BPF_SK_SKB_STREAM_VERDICT:
return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, true);
+ case BPF_RAWIR_DECODER:
+ return rc_dev_prog_attach(attr);
default:
return -EINVAL;
}
@@ -1626,6 +1629,8 @@ static int bpf_prog_detach(const union bpf_attr *attr)
case BPF_SK_SKB_STREAM_PARSER:
case BPF_SK_SKB_STREAM_VERDICT:
return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, false);
+ case BPF_RAWIR_DECODER:
+ return rc_dev_prog_detach(attr);
default:
return -EINVAL;
}
@@ -1673,6 +1678,8 @@ static int bpf_prog_query(const union bpf_attr *attr,
case BPF_CGROUP_SOCK_OPS:
case BPF_CGROUP_DEVICE:
break;
+ case BPF_RAWIR_DECODER:
+ return rc_dev_prog_query(attr, uattr);
default:
return -EINVAL;
}
--
2.17.0
This implements the grundig-16 IR protocol.
Signed-off-by: Sean Young <[email protected]>
---
samples/bpf/Makefile | 4 +
samples/bpf/bpf_load.c | 9 +-
samples/bpf/grundig_decoder_kern.c | 112 ++++++++++++++++++++++
samples/bpf/grundig_decoder_user.c | 54 +++++++++++
tools/bpf/bpftool/prog.c | 1 +
tools/include/uapi/linux/bpf.h | 17 +++-
tools/testing/selftests/bpf/bpf_helpers.h | 6 ++
7 files changed, 200 insertions(+), 3 deletions(-)
create mode 100644 samples/bpf/grundig_decoder_kern.c
create mode 100644 samples/bpf/grundig_decoder_user.c
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 4d6a6edd4bf6..c6fa111f103a 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -44,6 +44,7 @@ hostprogs-y += xdp_monitor
hostprogs-y += xdp_rxq_info
hostprogs-y += syscall_tp
hostprogs-y += cpustat
+hostprogs-y += grundig_decoder
# Libbpf dependencies
LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o
@@ -95,6 +96,7 @@ xdp_monitor-objs := bpf_load.o $(LIBBPF) xdp_monitor_user.o
xdp_rxq_info-objs := bpf_load.o $(LIBBPF) xdp_rxq_info_user.o
syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o
cpustat-objs := bpf_load.o $(LIBBPF) cpustat_user.o
+grundig_decoder-objs := bpf_load.o $(LIBBPF) grundig_decoder_user.o
# Tell kbuild to always build the programs
always := $(hostprogs-y)
@@ -148,6 +150,7 @@ always += xdp_rxq_info_kern.o
always += xdp2skb_meta_kern.o
always += syscall_tp_kern.o
always += cpustat_kern.o
+always += grundig_decoder_kern.o
HOSTCFLAGS += -I$(objtree)/usr/include
HOSTCFLAGS += -I$(srctree)/tools/lib/
@@ -193,6 +196,7 @@ HOSTLOADLIBES_xdp_monitor += -lelf
HOSTLOADLIBES_xdp_rxq_info += -lelf
HOSTLOADLIBES_syscall_tp += -lelf
HOSTLOADLIBES_cpustat += -lelf
+HOSTLOADLIBES_grundig_decoder += -lelf
# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
# make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index bebe4188b4b3..0fd389e95bb9 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -69,6 +69,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
bool is_sockops = strncmp(event, "sockops", 7) == 0;
bool is_sk_skb = strncmp(event, "sk_skb", 6) == 0;
bool is_sk_msg = strncmp(event, "sk_msg", 6) == 0;
+ bool is_ir_decoder = strncmp(event, "ir_decoder", 10) == 0;
size_t insns_cnt = size / sizeof(struct bpf_insn);
enum bpf_prog_type prog_type;
char buf[256];
@@ -102,6 +103,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
prog_type = BPF_PROG_TYPE_SK_SKB;
} else if (is_sk_msg) {
prog_type = BPF_PROG_TYPE_SK_MSG;
+ } else if (is_ir_decoder) {
+ prog_type = BPF_PROG_TYPE_RAWIR_DECODER;
} else {
printf("Unknown event '%s'\n", event);
return -1;
@@ -116,7 +119,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
prog_fd[prog_cnt++] = fd;
- if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
+ if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk ||
+ is_ir_decoder)
return 0;
if (is_socket || is_sockops || is_sk_skb || is_sk_msg) {
@@ -607,7 +611,8 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
memcmp(shname, "cgroup/", 7) == 0 ||
memcmp(shname, "sockops", 7) == 0 ||
memcmp(shname, "sk_skb", 6) == 0 ||
- memcmp(shname, "sk_msg", 6) == 0) {
+ memcmp(shname, "sk_msg", 6) == 0 ||
+ memcmp(shname, "ir_decoder", 10) == 0) {
ret = load_and_attach(shname, data->d_buf,
data->d_size);
if (ret != 0)
diff --git a/samples/bpf/grundig_decoder_kern.c b/samples/bpf/grundig_decoder_kern.c
new file mode 100644
index 000000000000..c80f2c9cc69a
--- /dev/null
+++ b/samples/bpf/grundig_decoder_kern.c
@@ -0,0 +1,112 @@
+
+#include <uapi/linux/bpf.h>
+#include <uapi/linux/bpf_rcdev.h>
+#include "bpf_helpers.h"
+#include <linux/version.h>
+
+enum grundig_state {
+ STATE_INACTIVE,
+ STATE_HEADER_SPACE,
+ STATE_LEADING_PULSE,
+ STATE_BITS_SPACE,
+ STATE_BITS_PULSE,
+};
+
+struct decoder_state {
+ u32 bits;
+ enum grundig_state state;
+ u32 count;
+ u32 last_space;
+};
+
+struct bpf_map_def SEC("maps") decoder_state_map = {
+ .type = BPF_MAP_TYPE_ARRAY,
+ .key_size = sizeof(u32),
+ .value_size = sizeof(struct decoder_state),
+ .max_entries = 1,
+};
+
+#define US_TO_NS(t) 1000*(t)
+static inline bool eq_margin(unsigned d1, unsigned d2, unsigned margin)
+{
+ return ((d1 > (d2 - margin)) && (d1 < (d2 + margin)));
+}
+
+SEC("ir_decoder/grundig_decoder")
+int bpf_decoder(struct ir_raw_event *raw)
+{
+ u32 key = 0;
+ struct decoder_state init = {};
+
+ struct decoder_state *s = bpf_map_lookup_elem(&decoder_state_map, &key);
+
+ if (!s)
+ s = &init;
+
+ if (raw->carrier_report) {
+ // ignore
+ } else if (raw->reset) {
+ s->state = STATE_INACTIVE;
+ } else if (s->state == STATE_INACTIVE) {
+ if (raw->pulse && eq_margin(US_TO_NS(900), raw->duration, US_TO_NS(100))) {
+ s->bits = 0;
+ s->state = STATE_HEADER_SPACE;
+ s->count = 0;
+ }
+ } else if (s->state == STATE_HEADER_SPACE) {
+ if (!raw->pulse && eq_margin(US_TO_NS(2900), raw->duration, US_TO_NS(200)))
+ s->state = STATE_LEADING_PULSE;
+ else
+ s->state = STATE_INACTIVE;
+ } else if (s->state == STATE_LEADING_PULSE) {
+ if (raw->pulse && eq_margin(US_TO_NS(1300), raw->duration, US_TO_NS(100)))
+ s->state = STATE_BITS_SPACE;
+ else
+ s->state = STATE_INACTIVE;
+ } else if (s->state == STATE_BITS_SPACE) {
+ s->last_space = raw->duration;
+ s->state = STATE_BITS_PULSE;
+ } else if (s->state == STATE_BITS_PULSE) {
+ int t = -1;
+ if (eq_margin(s->last_space, US_TO_NS(472), US_TO_NS(150)) &&
+ eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
+ t = 0;
+ if (eq_margin(s->last_space, US_TO_NS(1139), US_TO_NS(150)) &&
+ eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
+ t = 1;
+ if (eq_margin(s->last_space, US_TO_NS(1806), US_TO_NS(150)) &&
+ eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
+ t = 2;
+ if (eq_margin(s->last_space, US_TO_NS(2200), US_TO_NS(150)) &&
+ eq_margin(raw->duration, US_TO_NS(1139), US_TO_NS(150)))
+ t = 3;
+ if (t < 0) {
+ s->state = STATE_INACTIVE;
+ } else {
+ s->bits <<= 2;
+ switch (t) {
+ case 3: s->bits |= 0; break;
+ case 2: s->bits |= 3; break;
+ case 1: s->bits |= 2; break;
+ case 0: s->bits |= 1; break;
+ }
+ s->count += 2;
+ if (s->count == 16) {
+ bpf_rc_keydown(raw, 0x40, s->bits, 0);
+ s->state = STATE_INACTIVE;
+ } else {
+ s->state = STATE_BITS_SPACE;
+ }
+ }
+ }
+
+ if (s == &init)
+ bpf_map_update_elem(&decoder_state_map, &key, s, BPF_NOEXIST);
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+
+u32 _version SEC("version") = LINUX_VERSION_CODE;
+
diff --git a/samples/bpf/grundig_decoder_user.c b/samples/bpf/grundig_decoder_user.c
new file mode 100644
index 000000000000..61e8ee5f73ee
--- /dev/null
+++ b/samples/bpf/grundig_decoder_user.c
@@ -0,0 +1,54 @@
+
+#include <linux/bpf.h>
+#include <assert.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "bpf_load.h"
+#include "bpf_util.h"
+#include "libbpf.h"
+
+int main(int argc, char **argv)
+{
+ char filename[256];
+ int ret, lircfd;
+
+ if (argc != 2) {
+ printf("Usage: %s /dev/lircN\n", argv[0]);
+ return 2;
+ }
+
+ snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+
+ if (load_bpf_file(filename)) {
+ printf("%s", bpf_log_buf);
+ return 1;
+ }
+
+ lircfd = open(argv[1], O_RDWR);
+ if (lircfd == -1) {
+ printf("failed to open lirc device %s: %m\n", argv[1]);
+ return 1;
+ }
+
+ ret = bpf_prog_attach(prog_fd[0], lircfd, BPF_RAWIR_DECODER, 0);
+ if (ret) {
+ printf("Failed to attach bpf to lirc device: %m\n");
+ return 1;
+ }
+
+ printf("Grundig IR decoder loaded and attached. Hit any key to stop\n");
+ getchar();
+
+ return 0;
+}
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index f7a810897eac..ae1c26df212d 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -68,6 +68,7 @@ static const char * const prog_type_name[] = {
[BPF_PROG_TYPE_SOCK_OPS] = "sock_ops",
[BPF_PROG_TYPE_SK_SKB] = "sk_skb",
[BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device",
+ [BPF_PROG_TYPE_RAWIR_DECODER] = "ir_decoder",
};
static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index c5ec89732a8d..d9740599adf6 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -137,6 +137,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_SK_MSG,
BPF_PROG_TYPE_RAW_TRACEPOINT,
BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_PROG_TYPE_RAWIR_DECODER,
};
enum bpf_attach_type {
@@ -154,6 +155,7 @@ enum bpf_attach_type {
BPF_CGROUP_INET6_CONNECT,
BPF_CGROUP_INET4_POST_BIND,
BPF_CGROUP_INET6_POST_BIND,
+ BPF_RAWIR_DECODER,
__MAX_BPF_ATTACH_TYPE
};
@@ -755,6 +757,17 @@ union bpf_attr {
* @addr: pointer to struct sockaddr to bind socket to
* @addr_len: length of sockaddr structure
* Return: 0 on success or negative error code
+ *
+ * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
+ * Report decoded scancode with toggle value
+ * @ctx: pointer to ctx
+ * @protocol: decoded protocol
+ * @scancode: decoded scancode
+ * @toggle: set to 1 if button was toggled, else 0
+ *
+ * int bpf_rc_repeat(ctx)
+ * Repeat the last decoded scancode
+ * @ctx: pointer to ctx
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -821,7 +834,9 @@ union bpf_attr {
FN(msg_apply_bytes), \
FN(msg_cork_bytes), \
FN(msg_pull_data), \
- FN(bind),
+ FN(bind), \
+ FN(rc_repeat), \
+ FN(rc_keydown),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
index d8223d99f96d..4bf23d3dfc33 100644
--- a/tools/testing/selftests/bpf/bpf_helpers.h
+++ b/tools/testing/selftests/bpf/bpf_helpers.h
@@ -96,6 +96,12 @@ static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) =
(void *) BPF_FUNC_msg_pull_data;
static int (*bpf_bind)(void *ctx, void *addr, int addr_len) =
(void *) BPF_FUNC_bind;
+static int (*bpf_rc_repeat)(void *ctx) =
+ (void *) BPF_FUNC_rc_repeat;
+static int (*bpf_rc_keydown)(void *ctx, unsigned protocol, unsigned scancode,
+ unsigned toggle) =
+ (void *) BPF_FUNC_rc_keydown;
+
/* llvm builtin functions that eBPF C program may use to
* emit BPF_LD_ABS and BPF_LD_IND instructions
--
2.17.0
The context provided to a BPF_PROG_RAWIR_DECODER is a struct ir_raw_event;
ensure user space has a a definition.
Signed-off-by: Sean Young <[email protected]>
---
include/media/rc-core.h | 19 +------------------
include/uapi/linux/bpf_rcdev.h | 24 ++++++++++++++++++++++++
2 files changed, 25 insertions(+), 18 deletions(-)
create mode 100644 include/uapi/linux/bpf_rcdev.h
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index 6742fd86ff65..5d31e31d8ade 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -21,6 +21,7 @@
#include <linux/kfifo.h>
#include <linux/time.h>
#include <linux/timer.h>
+#include <uapi/linux/bpf_rcdev.h>
#include <media/rc-map.h>
/**
@@ -299,24 +300,6 @@ void rc_keydown_notimeout(struct rc_dev *dev, enum rc_proto protocol,
void rc_keyup(struct rc_dev *dev);
u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode);
-/*
- * From rc-raw.c
- * The Raw interface is specific to InfraRed. It may be a good idea to
- * split it later into a separate header.
- */
-struct ir_raw_event {
- union {
- u32 duration;
- u32 carrier;
- };
- u8 duty_cycle;
-
- unsigned pulse:1;
- unsigned reset:1;
- unsigned timeout:1;
- unsigned carrier_report:1;
-};
-
#define DEFINE_IR_RAW_EVENT(event) struct ir_raw_event event = {}
static inline void init_ir_raw_event(struct ir_raw_event *ev)
diff --git a/include/uapi/linux/bpf_rcdev.h b/include/uapi/linux/bpf_rcdev.h
new file mode 100644
index 000000000000..d8629ff2b960
--- /dev/null
+++ b/include/uapi/linux/bpf_rcdev.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* Copyright (c) 2018 Sean Young <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#ifndef _UAPI__LINUX_BPF_RCDEV_H__
+#define _UAPI__LINUX_BPF_RCDEV_H__
+
+struct ir_raw_event {
+ union {
+ __u32 duration;
+ __u32 carrier;
+ };
+ __u8 duty_cycle;
+
+ unsigned pulse:1;
+ unsigned reset:1;
+ unsigned timeout:1;
+ unsigned carrier_report:1;
+};
+
+#endif /* _UAPI__LINUX_BPF_RCDEV_H__ */
--
2.17.0
Add support for BPF_PROG_IR_DECODER. This type of BPF program can call
rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
that the last key should be repeated.
Signed-off-by: Sean Young <[email protected]>
---
drivers/media/rc/Kconfig | 8 +++
drivers/media/rc/Makefile | 1 +
drivers/media/rc/ir-bpf-decoder.c | 93 +++++++++++++++++++++++++++++++
include/linux/bpf_types.h | 3 +
include/uapi/linux/bpf.h | 16 +++++-
5 files changed, 120 insertions(+), 1 deletion(-)
create mode 100644 drivers/media/rc/ir-bpf-decoder.c
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index eb2c3b6eca7f..10ad6167d87c 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -120,6 +120,14 @@ config IR_IMON_DECODER
remote control and you would like to use it with a raw IR
receiver, or if you wish to use an encoder to transmit this IR.
+config IR_BPF_DECODER
+ bool "Enable IR raw decoder using BPF"
+ depends on BPF_SYSCALL
+ depends on RC_CORE=y
+ help
+ Enable this option to make it possible to load custom IR
+ decoders written in BPF.
+
endif #RC_DECODERS
menuconfig RC_DEVICES
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index 2e1c87066f6c..12e1118430d0 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -5,6 +5,7 @@ obj-y += keymaps/
obj-$(CONFIG_RC_CORE) += rc-core.o
rc-core-y := rc-main.o rc-ir-raw.o
rc-core-$(CONFIG_LIRC) += lirc_dev.o
+rc-core-$(CONFIG_IR_BPF_DECODER) += ir-bpf-decoder.o
obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
diff --git a/drivers/media/rc/ir-bpf-decoder.c b/drivers/media/rc/ir-bpf-decoder.c
new file mode 100644
index 000000000000..aaa5e208b1a5
--- /dev/null
+++ b/drivers/media/rc/ir-bpf-decoder.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+// ir-bpf-decoder.c - handles bpf decoders
+//
+// Copyright (C) 2018 Sean Young <[email protected]>
+
+#include <linux/bpf.h>
+#include <linux/filter.h>
+#include "rc-core-priv.h"
+
+/*
+ * BPF interface for raw IR decoder
+ */
+const struct bpf_prog_ops ir_decoder_prog_ops = {
+};
+
+BPF_CALL_1(bpf_rc_repeat, struct ir_raw_event*, event)
+{
+ struct ir_raw_event_ctrl *ctrl;
+
+ ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
+
+ rc_repeat(ctrl->dev);
+ return 0;
+}
+
+static const struct bpf_func_proto rc_repeat_proto = {
+ .func = bpf_rc_repeat,
+ .gpl_only = true, // rc_repeat is EXPORT_SYMBOL_GPL
+ .ret_type = RET_VOID,
+ .arg1_type = ARG_PTR_TO_CTX,
+};
+
+BPF_CALL_4(bpf_rc_keydown, struct ir_raw_event*, event, u32, protocol,
+ u32, scancode, u32, toggle)
+{
+ struct ir_raw_event_ctrl *ctrl;
+
+ ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
+ rc_keydown(ctrl->dev, protocol, scancode, toggle != 0);
+ return 0;
+}
+
+static const struct bpf_func_proto rc_keydown_proto = {
+ .func = bpf_rc_keydown,
+ .gpl_only = true, // rc_keydown is EXPORT_SYMBOL_GPL
+ .ret_type = RET_VOID,
+ .arg1_type = ARG_PTR_TO_CTX,
+ .arg2_type = ARG_ANYTHING,
+ .arg3_type = ARG_ANYTHING,
+ .arg4_type = ARG_ANYTHING,
+};
+
+static const struct bpf_func_proto *ir_decoder_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+{
+ switch (func_id) {
+ case BPF_FUNC_rc_repeat:
+ return &rc_repeat_proto;
+ case BPF_FUNC_rc_keydown:
+ return &rc_keydown_proto;
+ case BPF_FUNC_map_lookup_elem:
+ return &bpf_map_lookup_elem_proto;
+ case BPF_FUNC_map_update_elem:
+ return &bpf_map_update_elem_proto;
+ case BPF_FUNC_map_delete_elem:
+ return &bpf_map_delete_elem_proto;
+ case BPF_FUNC_ktime_get_ns:
+ return &bpf_ktime_get_ns_proto;
+ case BPF_FUNC_tail_call:
+ return &bpf_tail_call_proto;
+ case BPF_FUNC_get_prandom_u32:
+ return &bpf_get_prandom_u32_proto;
+ default:
+ return NULL;
+ }
+}
+
+static bool ir_decoder_is_valid_access(int off, int size,
+ enum bpf_access_type type,
+ const struct bpf_prog *prog,
+ struct bpf_insn_access_aux *info)
+{
+ if (type == BPF_WRITE)
+ return false;
+ if (off < 0 || off + size > sizeof(struct ir_raw_event))
+ return false;
+
+ return true;
+}
+
+const struct bpf_verifier_ops ir_decoder_verifier_ops = {
+ .get_func_proto = ir_decoder_func_proto,
+ .is_valid_access = ir_decoder_is_valid_access
+};
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 2b28fcf6f6ae..ee5355715ee0 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -25,6 +25,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT, raw_tracepoint)
#ifdef CONFIG_CGROUP_BPF
BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
#endif
+#ifdef CONFIG_IR_BPF_DECODER
+BPF_PROG_TYPE(BPF_PROG_TYPE_RAWIR_DECODER, ir_decoder)
+#endif
BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index c5ec89732a8d..6ad053e831c0 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -137,6 +137,7 @@ enum bpf_prog_type {
BPF_PROG_TYPE_SK_MSG,
BPF_PROG_TYPE_RAW_TRACEPOINT,
BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
+ BPF_PROG_TYPE_RAWIR_DECODER,
};
enum bpf_attach_type {
@@ -755,6 +756,17 @@ union bpf_attr {
* @addr: pointer to struct sockaddr to bind socket to
* @addr_len: length of sockaddr structure
* Return: 0 on success or negative error code
+ *
+ * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
+ * Report decoded scancode with toggle value
+ * @ctx: pointer to ctx
+ * @protocol: decoded protocol
+ * @scancode: decoded scancode
+ * @toggle: set to 1 if button was toggled, else 0
+ *
+ * int bpf_rc_repeat(ctx)
+ * Repeat the last decoded scancode
+ * @ctx: pointer to ctx
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -821,7 +833,9 @@ union bpf_attr {
FN(msg_apply_bytes), \
FN(msg_cork_bytes), \
FN(msg_pull_data), \
- FN(bind),
+ FN(bind), \
+ FN(rc_repeat), \
+ FN(rc_keydown),
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
* function eBPF program intends to call
--
2.17.0
On 05/14/2018 02:10 PM, Sean Young wrote:
> Add support for BPF_PROG_IR_DECODER. This type of BPF program can call
Kconfig file below uses IR_BPF_DECODER instead of the symbol name above.
and then patch 3 says a third choice:
The context provided to a BPF_PROG_RAWIR_DECODER is a struct ir_raw_event;
> rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
> that the last key should be repeated.
>
> Signed-off-by: Sean Young <[email protected]>
> ---
> drivers/media/rc/Kconfig | 8 +++
> drivers/media/rc/Makefile | 1 +
> drivers/media/rc/ir-bpf-decoder.c | 93 +++++++++++++++++++++++++++++++
> include/linux/bpf_types.h | 3 +
> include/uapi/linux/bpf.h | 16 +++++-
> 5 files changed, 120 insertions(+), 1 deletion(-)
> create mode 100644 drivers/media/rc/ir-bpf-decoder.c
>
> diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> index eb2c3b6eca7f..10ad6167d87c 100644
> --- a/drivers/media/rc/Kconfig
> +++ b/drivers/media/rc/Kconfig
> @@ -120,6 +120,14 @@ config IR_IMON_DECODER
> remote control and you would like to use it with a raw IR
> receiver, or if you wish to use an encoder to transmit this IR.
>
> +config IR_BPF_DECODER
> + bool "Enable IR raw decoder using BPF"
> + depends on BPF_SYSCALL
> + depends on RC_CORE=y
> + help
> + Enable this option to make it possible to load custom IR
> + decoders written in BPF.
> +
> endif #RC_DECODERS
>
> menuconfig RC_DEVICES
> diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> index 2e1c87066f6c..12e1118430d0 100644
> --- a/drivers/media/rc/Makefile
> +++ b/drivers/media/rc/Makefile
> @@ -5,6 +5,7 @@ obj-y += keymaps/
> obj-$(CONFIG_RC_CORE) += rc-core.o
> rc-core-y := rc-main.o rc-ir-raw.o
> rc-core-$(CONFIG_LIRC) += lirc_dev.o
> +rc-core-$(CONFIG_IR_BPF_DECODER) += ir-bpf-decoder.o
--
~Randy
Hi Sean,
I love your patch! Perhaps something to improve:
[auto build test WARNING on linus/master]
[also build test WARNING on v4.17-rc5]
[cannot apply to next-20180514]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Sean-Young/media-rc-introduce-BPF_PROG_IR_DECODER/20180515-093234
config: i386-tinyconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All warnings (new ones prefixed by >>):
>> ./usr/include/linux/bpf_rcdev.h:13: found __[us]{8,16,32,64} type without #include <linux/types.h>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Sean,
I love your patch! Yet something to improve:
[auto build test ERROR on linus/master]
[also build test ERROR on v4.17-rc5]
[cannot apply to next-20180514]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Sean-Young/media-rc-introduce-BPF_PROG_IR_DECODER/20180515-093234
config: i386-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-16) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
>> drivers/media/rc/lirc_dev.c:32:10: fatal error: linux/bpf-rcdev.h: No such file or directory
#include <linux/bpf-rcdev.h>
^~~~~~~~~~~~~~~~~~~
compilation terminated.
--
>> kernel/bpf/syscall.c:30:10: fatal error: linux/bpf-rcdev.h: No such file or directory
#include <linux/bpf-rcdev.h>
^~~~~~~~~~~~~~~~~~~
compilation terminated.
vim +32 drivers/media/rc/lirc_dev.c
31
> 32 #include <linux/bpf-rcdev.h>
33
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Mon, May 14, 2018 at 2:10 PM, Sean Young <[email protected]> wrote:
> Add support for BPF_PROG_IR_DECODER. This type of BPF program can call
> rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
> that the last key should be repeated.
>
> Signed-off-by: Sean Young <[email protected]>
> ---
> drivers/media/rc/Kconfig | 8 +++
> drivers/media/rc/Makefile | 1 +
> drivers/media/rc/ir-bpf-decoder.c | 93 +++++++++++++++++++++++++++++++
> include/linux/bpf_types.h | 3 +
> include/uapi/linux/bpf.h | 16 +++++-
> 5 files changed, 120 insertions(+), 1 deletion(-)
> create mode 100644 drivers/media/rc/ir-bpf-decoder.c
>
> diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> index eb2c3b6eca7f..10ad6167d87c 100644
> --- a/drivers/media/rc/Kconfig
> +++ b/drivers/media/rc/Kconfig
> @@ -120,6 +120,14 @@ config IR_IMON_DECODER
> remote control and you would like to use it with a raw IR
> receiver, or if you wish to use an encoder to transmit this IR.
>
> +config IR_BPF_DECODER
> + bool "Enable IR raw decoder using BPF"
> + depends on BPF_SYSCALL
> + depends on RC_CORE=y
> + help
> + Enable this option to make it possible to load custom IR
> + decoders written in BPF.
> +
> endif #RC_DECODERS
>
> menuconfig RC_DEVICES
> diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> index 2e1c87066f6c..12e1118430d0 100644
> --- a/drivers/media/rc/Makefile
> +++ b/drivers/media/rc/Makefile
> @@ -5,6 +5,7 @@ obj-y += keymaps/
> obj-$(CONFIG_RC_CORE) += rc-core.o
> rc-core-y := rc-main.o rc-ir-raw.o
> rc-core-$(CONFIG_LIRC) += lirc_dev.o
> +rc-core-$(CONFIG_IR_BPF_DECODER) += ir-bpf-decoder.o
> obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
> obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
> obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
> diff --git a/drivers/media/rc/ir-bpf-decoder.c b/drivers/media/rc/ir-bpf-decoder.c
> new file mode 100644
> index 000000000000..aaa5e208b1a5
> --- /dev/null
> +++ b/drivers/media/rc/ir-bpf-decoder.c
> @@ -0,0 +1,93 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// ir-bpf-decoder.c - handles bpf decoders
> +//
> +// Copyright (C) 2018 Sean Young <[email protected]>
> +
> +#include <linux/bpf.h>
> +#include <linux/filter.h>
> +#include "rc-core-priv.h"
> +
> +/*
> + * BPF interface for raw IR decoder
> + */
> +const struct bpf_prog_ops ir_decoder_prog_ops = {
> +};
> +
> +BPF_CALL_1(bpf_rc_repeat, struct ir_raw_event*, event)
> +{
> + struct ir_raw_event_ctrl *ctrl;
> +
> + ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
> +
> + rc_repeat(ctrl->dev);
> + return 0;
> +}
> +
> +static const struct bpf_func_proto rc_repeat_proto = {
> + .func = bpf_rc_repeat,
> + .gpl_only = true, // rc_repeat is EXPORT_SYMBOL_GPL
> + .ret_type = RET_VOID,
I suggest using RET_INTEGER here since we do return an integer 0.
RET_INTEGER will also make it easy to extend if you want to return
a non-zero value for error code or other reasons.
> + .arg1_type = ARG_PTR_TO_CTX,
> +};
> +
> +BPF_CALL_4(bpf_rc_keydown, struct ir_raw_event*, event, u32, protocol,
> + u32, scancode, u32, toggle)
> +{
> + struct ir_raw_event_ctrl *ctrl;
> +
> + ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
> + rc_keydown(ctrl->dev, protocol, scancode, toggle != 0);
> + return 0;
> +}
> +
> +static const struct bpf_func_proto rc_keydown_proto = {
> + .func = bpf_rc_keydown,
> + .gpl_only = true, // rc_keydown is EXPORT_SYMBOL_GPL
> + .ret_type = RET_VOID,
ditto. RET_INTEGER is preferable.
> + .arg1_type = ARG_PTR_TO_CTX,
> + .arg2_type = ARG_ANYTHING,
> + .arg3_type = ARG_ANYTHING,
> + .arg4_type = ARG_ANYTHING,
> +};
> +
> +static const struct bpf_func_proto *ir_decoder_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
> +{
> + switch (func_id) {
> + case BPF_FUNC_rc_repeat:
> + return &rc_repeat_proto;
> + case BPF_FUNC_rc_keydown:
> + return &rc_keydown_proto;
> + case BPF_FUNC_map_lookup_elem:
> + return &bpf_map_lookup_elem_proto;
> + case BPF_FUNC_map_update_elem:
> + return &bpf_map_update_elem_proto;
> + case BPF_FUNC_map_delete_elem:
> + return &bpf_map_delete_elem_proto;
> + case BPF_FUNC_ktime_get_ns:
> + return &bpf_ktime_get_ns_proto;
> + case BPF_FUNC_tail_call:
> + return &bpf_tail_call_proto;
> + case BPF_FUNC_get_prandom_u32:
> + return &bpf_get_prandom_u32_proto;
> + default:
> + return NULL;
> + }
> +}
> +
> +static bool ir_decoder_is_valid_access(int off, int size,
> + enum bpf_access_type type,
> + const struct bpf_prog *prog,
> + struct bpf_insn_access_aux *info)
> +{
> + if (type == BPF_WRITE)
> + return false;
> + if (off < 0 || off + size > sizeof(struct ir_raw_event))
> + return false;
You probably need more than just checking the boundary.
From patch #3, the structure is:
struct ir_raw_event {
union {
__u32 duration;
__u32 carrier;
};
__u8 duty_cycle;
unsigned pulse:1;
unsigned reset:1;
unsigned timeout:1;
unsigned carrier_report:1;
};
You would like the memory access to be aligned,
so accessing duration/carrier with 4-byte alignment, and
pulse/reset/timeout/carrier_report 4-byte alignment as well.
You could only allow __u32 access to duration/carrier.
But if you want bpf program to access duration/carrier with
code like (__u16)(ctx->duration), then the compiler may
translate the load to a 2-byte load. You may need to handle
endianness here. You can check net/core/filter.c function
bpf_skb_is_valid_access for some examples.
> +
> + return true;
> +}
> +
> +const struct bpf_verifier_ops ir_decoder_verifier_ops = {
> + .get_func_proto = ir_decoder_func_proto,
> + .is_valid_access = ir_decoder_is_valid_access
> +};
> diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
> index 2b28fcf6f6ae..ee5355715ee0 100644
> --- a/include/linux/bpf_types.h
> +++ b/include/linux/bpf_types.h
> @@ -25,6 +25,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT, raw_tracepoint)
> #ifdef CONFIG_CGROUP_BPF
> BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
> #endif
> +#ifdef CONFIG_IR_BPF_DECODER
> +BPF_PROG_TYPE(BPF_PROG_TYPE_RAWIR_DECODER, ir_decoder)
> +#endif
>
> BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
> BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index c5ec89732a8d..6ad053e831c0 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -137,6 +137,7 @@ enum bpf_prog_type {
> BPF_PROG_TYPE_SK_MSG,
> BPF_PROG_TYPE_RAW_TRACEPOINT,
> BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
> + BPF_PROG_TYPE_RAWIR_DECODER,
> };
>
> enum bpf_attach_type {
> @@ -755,6 +756,17 @@ union bpf_attr {
> * @addr: pointer to struct sockaddr to bind socket to
> * @addr_len: length of sockaddr structure
> * Return: 0 on success or negative error code
> + *
> + * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
> + * Report decoded scancode with toggle value
> + * @ctx: pointer to ctx
> + * @protocol: decoded protocol
> + * @scancode: decoded scancode
> + * @toggle: set to 1 if button was toggled, else 0
> + *
> + * int bpf_rc_repeat(ctx)
> + * Repeat the last decoded scancode
> + * @ctx: pointer to ctx
The comment format has changed dramatically for
documentation reason. Could you rebase your change
on top of bpf-next tree? You will need to rewrite the above
helper description so tools can generate proper documentation
for them.
> */
> #define __BPF_FUNC_MAPPER(FN) \
> FN(unspec), \
> @@ -821,7 +833,9 @@ union bpf_attr {
> FN(msg_apply_bytes), \
> FN(msg_cork_bytes), \
> FN(msg_pull_data), \
> - FN(bind),
> + FN(bind), \
> + FN(rc_repeat), \
> + FN(rc_keydown),
>
> /* integer value in 'imm' field of BPF_CALL instruction selects which helper
> * function eBPF program intends to call
> --
> 2.17.0
>
On Mon, May 14, 2018 at 2:10 PM, Sean Young <[email protected]> wrote:
> This implements attaching, detaching, querying and execution. The target
> fd has to be the /dev/lircN device.
>
> Signed-off-by: Sean Young <[email protected]>
> ---
> drivers/media/rc/ir-bpf-decoder.c | 191 ++++++++++++++++++++++++++++++
> drivers/media/rc/lirc_dev.c | 30 +++++
> drivers/media/rc/rc-core-priv.h | 15 +++
> drivers/media/rc/rc-ir-raw.c | 5 +
> include/uapi/linux/bpf.h | 1 +
> kernel/bpf/syscall.c | 7 ++
> 6 files changed, 249 insertions(+)
>
> diff --git a/drivers/media/rc/ir-bpf-decoder.c b/drivers/media/rc/ir-bpf-decoder.c
> index aaa5e208b1a5..651590a14772 100644
> --- a/drivers/media/rc/ir-bpf-decoder.c
> +++ b/drivers/media/rc/ir-bpf-decoder.c
> @@ -91,3 +91,194 @@ const struct bpf_verifier_ops ir_decoder_verifier_ops = {
> .get_func_proto = ir_decoder_func_proto,
> .is_valid_access = ir_decoder_is_valid_access
> };
> +
> +#define BPF_MAX_PROGS 64
> +
> +int rc_dev_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog, u32 flags)
flags is not used in this function.
> +{
> + struct ir_raw_event_ctrl *raw;
> + struct bpf_prog_array __rcu *old_array;
> + struct bpf_prog_array *new_array;
> + int ret;
> +
> + if (rcdev->driver_type != RC_DRIVER_IR_RAW)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&rcdev->lock);
> + if (ret)
> + return ret;
> +
> + raw = rcdev->raw;
> +
> + if (raw->progs && bpf_prog_array_length(raw->progs) >= BPF_MAX_PROGS) {
> + ret = -E2BIG;
> + goto out;
> + }
> +
> + old_array = raw->progs;
> + ret = bpf_prog_array_copy(old_array, NULL, prog, &new_array);
> + if (ret < 0)
> + goto out;
> +
> + rcu_assign_pointer(raw->progs, new_array);
> + bpf_prog_array_free(old_array);
> +out:
> + mutex_unlock(&rcdev->lock);
> + return ret;
> +}
> +
> +int rc_dev_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog, u32 flags)
flags is not used in this function.
> +{
> + struct ir_raw_event_ctrl *raw;
> + struct bpf_prog_array __rcu *old_array;
> + struct bpf_prog_array *new_array;
> + int ret;
> +
> + if (rcdev->driver_type != RC_DRIVER_IR_RAW)
> + return -EINVAL;
> +
> + ret = mutex_lock_interruptible(&rcdev->lock);
> + if (ret)
> + return ret;
> +
> + raw = rcdev->raw;
> +
> + old_array = raw->progs;
> + ret = bpf_prog_array_copy(old_array, prog, NULL, &new_array);
> + if (ret < 0) {
> + bpf_prog_array_delete_safe(old_array, prog);
> + } else {
> + rcu_assign_pointer(raw->progs, new_array);
> + bpf_prog_array_free(old_array);
> + }
> +
> + bpf_prog_put(prog);
> + mutex_unlock(&rcdev->lock);
> + return 0;
> +}
> +
> +void rc_dev_bpf_run(struct rc_dev *rcdev)
> +{
> + struct ir_raw_event_ctrl *raw = rcdev->raw;
> +
> + if (raw->progs)
> + BPF_PROG_RUN_ARRAY(raw->progs, &raw->prev_ev, BPF_PROG_RUN);
> +}
> +
> +void rc_dev_bpf_put(struct rc_dev *rcdev)
> +{
> + struct bpf_prog_array *progs = rcdev->raw->progs;
> + int i, size;
> +
> + if (!progs)
> + return;
> +
> + size = bpf_prog_array_length(progs);
> + for (i = 0; i < size; i++)
> + bpf_prog_put(progs->progs[i]);
> +
> + bpf_prog_array_free(rcdev->raw->progs);
> +}
> +
> +int rc_dev_prog_attach(const union bpf_attr *attr)
> +{
> + struct bpf_prog *prog;
> + struct rc_dev *rcdev;
> + int ret;
> +
> + if (attr->attach_flags & BPF_F_ALLOW_OVERRIDE)
> + return -EINVAL;
Looks like you really did not use flags except here.
BPF_F_ALLOW_OVERRIDE is originally used for
cgroup type of attachment and the comment explicits
saying so.
In the query below, the flags value "0" is copied to userspace.
In your case, I think you can just disallow any value, i.g.,
attr->attach_flags must be 0, and then you further down
check that if the prog is already in the array, you just return an error.
> +
> + prog = bpf_prog_get_type(attr->attach_bpf_fd,
> + BPF_PROG_TYPE_RAWIR_DECODER);
> + if (IS_ERR(prog))
> + return PTR_ERR(prog);
> +
> + rcdev = rc_dev_get_from_fd(attr->target_fd);
> + if (IS_ERR(rcdev)) {
> + bpf_prog_put(prog);
> + return PTR_ERR(rcdev);
> + }
> +
> + ret = rc_dev_bpf_attach(rcdev, prog, attr->attach_flags);
> + if (ret)
> + bpf_prog_put(prog);
> +
> + put_device(&rcdev->dev);
> +
> + return ret;
> +}
> +
> +int rc_dev_prog_detach(const union bpf_attr *attr)
> +{
> + struct bpf_prog *prog;
> + struct rc_dev *rcdev;
> + int ret;
> +
> + if (attr->attach_flags & BPF_F_ALLOW_OVERRIDE)
> + return -EINVAL;
> +
> + prog = bpf_prog_get_type(attr->attach_bpf_fd,
> + BPF_PROG_TYPE_RAWIR_DECODER);
> + if (IS_ERR(prog))
> + return PTR_ERR(prog);
> +
> + rcdev = rc_dev_get_from_fd(attr->target_fd);
> + if (IS_ERR(rcdev)) {
> + bpf_prog_put(prog);
> + return PTR_ERR(rcdev);
> + }
> +
> + ret = rc_dev_bpf_detach(rcdev, prog, attr->attach_flags);
> +
> + bpf_prog_put(prog);
> + put_device(&rcdev->dev);
> +
> + return ret;
> +}
> +
> +int rc_dev_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr)
> +{
> + __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
> + struct bpf_prog_array *progs;
> + struct rc_dev *rcdev;
> + u32 cnt, flags = 0;
> + int ret;
> +
> + if (attr->query.query_flags)
> + return -EINVAL;
> +
> + rcdev = rc_dev_get_from_fd(attr->query.target_fd);
> + if (IS_ERR(rcdev))
> + return PTR_ERR(rcdev);
> +
> + if (rcdev->driver_type != RC_DRIVER_IR_RAW) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + ret = mutex_lock_interruptible(&rcdev->lock);
> + if (ret)
> + goto out;
> +
> + progs = rcdev->raw->progs;
> + cnt = progs ? bpf_prog_array_length(progs) : 0;
> +
> + if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) {
> + ret = -EFAULT;
> + goto out;
> + }
> + if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) {
> + ret = -EFAULT;
> + goto out;
> + }
> +
> + if (attr->query.prog_cnt != 0 && prog_ids && cnt)
> + ret = bpf_prog_array_copy_to_user(progs, prog_ids, cnt);
> +
> +out:
> + mutex_unlock(&rcdev->lock);
> + put_device(&rcdev->dev);
> +
> + return ret;
> +}
> diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
> index 24e9fbb80e81..65319f2ccc13 100644
> --- a/drivers/media/rc/lirc_dev.c
> +++ b/drivers/media/rc/lirc_dev.c
> @@ -20,6 +20,7 @@
> #include <linux/module.h>
> #include <linux/mutex.h>
> #include <linux/device.h>
> +#include <linux/file.h>
> #include <linux/idr.h>
> #include <linux/poll.h>
> #include <linux/sched.h>
> @@ -28,6 +29,8 @@
> #include "rc-core-priv.h"
> #include <uapi/linux/lirc.h>
>
> +#include <linux/bpf-rcdev.h>
> +
> #define LIRCBUF_SIZE 256
>
> static dev_t lirc_base_dev;
> @@ -816,4 +819,31 @@ void __exit lirc_dev_exit(void)
> unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
> }
>
> +struct rc_dev *rc_dev_get_from_fd(int fd)
> +{
> + struct rc_dev *dev;
> + struct file *f;
> +
> + f = fget_raw(fd);
> + if (!f)
> + return ERR_PTR(-EBADF);
> +
> + if (!S_ISCHR(f->f_inode->i_mode) ||
> + imajor(f->f_inode) != MAJOR(lirc_base_dev)) {
> + fput(f);
> + return ERR_PTR(-EBADF);
> + }
> +
> + dev = container_of(f->f_inode->i_cdev, struct rc_dev, lirc_cdev);
> + if (!dev->registered) {
> + fput(f);
> + return ERR_PTR(-ENODEV);
> + }
> +
> + get_device(&dev->dev);
> + fput(f);
> +
> + return dev;
> +}
> +
> MODULE_ALIAS("lirc_dev");
> diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
> index e0e6a17460f6..b6f24f369657 100644
> --- a/drivers/media/rc/rc-core-priv.h
> +++ b/drivers/media/rc/rc-core-priv.h
> @@ -57,6 +57,9 @@ struct ir_raw_event_ctrl {
> /* raw decoder state follows */
> struct ir_raw_event prev_ev;
> struct ir_raw_event this_ev;
> +#ifdef CONFIG_IR_BPF_DECODER
> + struct bpf_prog_array *progs;
> +#endif
> struct nec_dec {
> int state;
> unsigned count;
> @@ -288,6 +291,7 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev);
> void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc);
> int ir_lirc_register(struct rc_dev *dev);
> void ir_lirc_unregister(struct rc_dev *dev);
> +struct rc_dev *rc_dev_get_from_fd(int fd);
> #else
> static inline int lirc_dev_init(void) { return 0; }
> static inline void lirc_dev_exit(void) {}
> @@ -299,4 +303,15 @@ static inline int ir_lirc_register(struct rc_dev *dev) { return 0; }
> static inline void ir_lirc_unregister(struct rc_dev *dev) { }
> #endif
>
> +/*
> + * bpf interface
> + */
> +#ifdef CONFIG_IR_BPF_DECODER
> +void rc_dev_bpf_put(struct rc_dev *dev);
> +void rc_dev_bpf_run(struct rc_dev *dev);
> +#else
> +void rc_dev_bpf_put(struct rc_dev *dev) {}
> +void rc_dev_bpf_run(struct rc_dev *dev) {}
> +#endif
> +
> #endif /* _RC_CORE_PRIV */
> diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
> index 374f83105a23..efddd9c44466 100644
> --- a/drivers/media/rc/rc-ir-raw.c
> +++ b/drivers/media/rc/rc-ir-raw.c
> @@ -8,6 +8,8 @@
> #include <linux/mutex.h>
> #include <linux/kmod.h>
> #include <linux/sched.h>
> +#include <linux/filter.h>
> +#include <linux/bpf.h>
> #include "rc-core-priv.h"
>
> /* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */
> @@ -33,6 +35,7 @@ static int ir_raw_event_thread(void *data)
> handler->decode(raw->dev, ev);
> ir_lirc_raw_event(raw->dev, ev);
> raw->prev_ev = ev;
> + rc_dev_bpf_run(raw->dev);
> }
> mutex_unlock(&ir_raw_handler_lock);
>
> @@ -623,6 +626,8 @@ void ir_raw_event_unregister(struct rc_dev *dev)
> handler->raw_unregister(dev);
> mutex_unlock(&ir_raw_handler_lock);
>
> + rc_dev_bpf_put(dev);
> +
> ir_raw_event_free(dev);
> }
>
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 6ad053e831c0..d9740599adf6 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -155,6 +155,7 @@ enum bpf_attach_type {
> BPF_CGROUP_INET6_CONNECT,
> BPF_CGROUP_INET4_POST_BIND,
> BPF_CGROUP_INET6_POST_BIND,
> + BPF_RAWIR_DECODER,
> __MAX_BPF_ATTACH_TYPE
> };
>
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index 016ef9025827..63ecc1f2e1e3 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -27,6 +27,7 @@
> #include <linux/timekeeping.h>
> #include <linux/ctype.h>
> #include <linux/nospec.h>
> +#include <linux/bpf-rcdev.h>
>
> #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \
> (map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
> @@ -1556,6 +1557,8 @@ static int bpf_prog_attach(const union bpf_attr *attr)
> case BPF_SK_SKB_STREAM_PARSER:
> case BPF_SK_SKB_STREAM_VERDICT:
> return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, true);
> + case BPF_RAWIR_DECODER:
> + return rc_dev_prog_attach(attr);
> default:
> return -EINVAL;
> }
> @@ -1626,6 +1629,8 @@ static int bpf_prog_detach(const union bpf_attr *attr)
> case BPF_SK_SKB_STREAM_PARSER:
> case BPF_SK_SKB_STREAM_VERDICT:
> return sockmap_get_from_fd(attr, BPF_PROG_TYPE_SK_SKB, false);
> + case BPF_RAWIR_DECODER:
> + return rc_dev_prog_detach(attr);
> default:
> return -EINVAL;
> }
> @@ -1673,6 +1678,8 @@ static int bpf_prog_query(const union bpf_attr *attr,
> case BPF_CGROUP_SOCK_OPS:
> case BPF_CGROUP_DEVICE:
> break;
> + case BPF_RAWIR_DECODER:
> + return rc_dev_prog_query(attr, uattr);
> default:
> return -EINVAL;
> }
> --
> 2.17.0
>
On Mon, May 14, 2018 at 2:11 PM, Sean Young <[email protected]> wrote:
> The context provided to a BPF_PROG_RAWIR_DECODER is a struct ir_raw_event;
> ensure user space has a a definition.
>
> Signed-off-by: Sean Young <[email protected]>
> ---
> include/media/rc-core.h | 19 +------------------
> include/uapi/linux/bpf_rcdev.h | 24 ++++++++++++++++++++++++
Patch #2 already referenced this file. So if Patches #1 and #2
applied, there will be
a compilation error. Could you re-arrange your patchset so that after
sequentially
applying each patch, there is no compilation error?
> 2 files changed, 25 insertions(+), 18 deletions(-)
> create mode 100644 include/uapi/linux/bpf_rcdev.h
>
> diff --git a/include/media/rc-core.h b/include/media/rc-core.h
> index 6742fd86ff65..5d31e31d8ade 100644
> --- a/include/media/rc-core.h
> +++ b/include/media/rc-core.h
> @@ -21,6 +21,7 @@
> #include <linux/kfifo.h>
> #include <linux/time.h>
> #include <linux/timer.h>
> +#include <uapi/linux/bpf_rcdev.h>
> #include <media/rc-map.h>
>
> /**
> @@ -299,24 +300,6 @@ void rc_keydown_notimeout(struct rc_dev *dev, enum rc_proto protocol,
> void rc_keyup(struct rc_dev *dev);
> u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode);
>
> -/*
> - * From rc-raw.c
> - * The Raw interface is specific to InfraRed. It may be a good idea to
> - * split it later into a separate header.
> - */
> -struct ir_raw_event {
> - union {
> - u32 duration;
> - u32 carrier;
> - };
> - u8 duty_cycle;
> -
> - unsigned pulse:1;
> - unsigned reset:1;
> - unsigned timeout:1;
> - unsigned carrier_report:1;
> -};
> -
> #define DEFINE_IR_RAW_EVENT(event) struct ir_raw_event event = {}
>
> static inline void init_ir_raw_event(struct ir_raw_event *ev)
> diff --git a/include/uapi/linux/bpf_rcdev.h b/include/uapi/linux/bpf_rcdev.h
> new file mode 100644
> index 000000000000..d8629ff2b960
> --- /dev/null
> +++ b/include/uapi/linux/bpf_rcdev.h
> @@ -0,0 +1,24 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/* Copyright (c) 2018 Sean Young <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of version 2 of the GNU General Public
> + * License as published by the Free Software Foundation.
> + */
> +#ifndef _UAPI__LINUX_BPF_RCDEV_H__
> +#define _UAPI__LINUX_BPF_RCDEV_H__
> +
> +struct ir_raw_event {
> + union {
> + __u32 duration;
> + __u32 carrier;
> + };
> + __u8 duty_cycle;
> +
> + unsigned pulse:1;
> + unsigned reset:1;
> + unsigned timeout:1;
> + unsigned carrier_report:1;
> +};
> +
> +#endif /* _UAPI__LINUX_BPF_RCDEV_H__ */
> --
> 2.17.0
>
On Mon, May 14, 2018 at 2:11 PM, Sean Young <[email protected]> wrote:
> This implements the grundig-16 IR protocol.
>
> Signed-off-by: Sean Young <[email protected]>
> ---
> samples/bpf/Makefile | 4 +
> samples/bpf/bpf_load.c | 9 +-
> samples/bpf/grundig_decoder_kern.c | 112 ++++++++++++++++++++++
> samples/bpf/grundig_decoder_user.c | 54 +++++++++++
> tools/bpf/bpftool/prog.c | 1 +
> tools/include/uapi/linux/bpf.h | 17 +++-
> tools/testing/selftests/bpf/bpf_helpers.h | 6 ++
> 7 files changed, 200 insertions(+), 3 deletions(-)
> create mode 100644 samples/bpf/grundig_decoder_kern.c
> create mode 100644 samples/bpf/grundig_decoder_user.c
>
> diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
> index 4d6a6edd4bf6..c6fa111f103a 100644
> --- a/samples/bpf/Makefile
> +++ b/samples/bpf/Makefile
> @@ -44,6 +44,7 @@ hostprogs-y += xdp_monitor
> hostprogs-y += xdp_rxq_info
> hostprogs-y += syscall_tp
> hostprogs-y += cpustat
> +hostprogs-y += grundig_decoder
>
> # Libbpf dependencies
> LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o
> @@ -95,6 +96,7 @@ xdp_monitor-objs := bpf_load.o $(LIBBPF) xdp_monitor_user.o
> xdp_rxq_info-objs := bpf_load.o $(LIBBPF) xdp_rxq_info_user.o
> syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o
> cpustat-objs := bpf_load.o $(LIBBPF) cpustat_user.o
> +grundig_decoder-objs := bpf_load.o $(LIBBPF) grundig_decoder_user.o
>
> # Tell kbuild to always build the programs
> always := $(hostprogs-y)
> @@ -148,6 +150,7 @@ always += xdp_rxq_info_kern.o
> always += xdp2skb_meta_kern.o
> always += syscall_tp_kern.o
> always += cpustat_kern.o
> +always += grundig_decoder_kern.o
>
> HOSTCFLAGS += -I$(objtree)/usr/include
> HOSTCFLAGS += -I$(srctree)/tools/lib/
> @@ -193,6 +196,7 @@ HOSTLOADLIBES_xdp_monitor += -lelf
> HOSTLOADLIBES_xdp_rxq_info += -lelf
> HOSTLOADLIBES_syscall_tp += -lelf
> HOSTLOADLIBES_cpustat += -lelf
> +HOSTLOADLIBES_grundig_decoder += -lelf
>
> # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
> # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
> diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
> index bebe4188b4b3..0fd389e95bb9 100644
> --- a/samples/bpf/bpf_load.c
> +++ b/samples/bpf/bpf_load.c
> @@ -69,6 +69,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
> bool is_sockops = strncmp(event, "sockops", 7) == 0;
> bool is_sk_skb = strncmp(event, "sk_skb", 6) == 0;
> bool is_sk_msg = strncmp(event, "sk_msg", 6) == 0;
> + bool is_ir_decoder = strncmp(event, "ir_decoder", 10) == 0;
> size_t insns_cnt = size / sizeof(struct bpf_insn);
> enum bpf_prog_type prog_type;
> char buf[256];
> @@ -102,6 +103,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
> prog_type = BPF_PROG_TYPE_SK_SKB;
> } else if (is_sk_msg) {
> prog_type = BPF_PROG_TYPE_SK_MSG;
> + } else if (is_ir_decoder) {
> + prog_type = BPF_PROG_TYPE_RAWIR_DECODER;
> } else {
> printf("Unknown event '%s'\n", event);
> return -1;
> @@ -116,7 +119,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
>
> prog_fd[prog_cnt++] = fd;
>
> - if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
> + if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk ||
> + is_ir_decoder)
> return 0;
>
> if (is_socket || is_sockops || is_sk_skb || is_sk_msg) {
> @@ -607,7 +611,8 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
> memcmp(shname, "cgroup/", 7) == 0 ||
> memcmp(shname, "sockops", 7) == 0 ||
> memcmp(shname, "sk_skb", 6) == 0 ||
> - memcmp(shname, "sk_msg", 6) == 0) {
> + memcmp(shname, "sk_msg", 6) == 0 ||
> + memcmp(shname, "ir_decoder", 10) == 0) {
> ret = load_and_attach(shname, data->d_buf,
> data->d_size);
> if (ret != 0)
> diff --git a/samples/bpf/grundig_decoder_kern.c b/samples/bpf/grundig_decoder_kern.c
> new file mode 100644
> index 000000000000..c80f2c9cc69a
> --- /dev/null
> +++ b/samples/bpf/grundig_decoder_kern.c
> @@ -0,0 +1,112 @@
> +
> +#include <uapi/linux/bpf.h>
> +#include <uapi/linux/bpf_rcdev.h>
> +#include "bpf_helpers.h"
> +#include <linux/version.h>
> +
> +enum grundig_state {
> + STATE_INACTIVE,
> + STATE_HEADER_SPACE,
> + STATE_LEADING_PULSE,
> + STATE_BITS_SPACE,
> + STATE_BITS_PULSE,
> +};
> +
> +struct decoder_state {
> + u32 bits;
> + enum grundig_state state;
> + u32 count;
> + u32 last_space;
> +};
> +
> +struct bpf_map_def SEC("maps") decoder_state_map = {
> + .type = BPF_MAP_TYPE_ARRAY,
> + .key_size = sizeof(u32),
> + .value_size = sizeof(struct decoder_state),
> + .max_entries = 1,
> +};
> +
> +#define US_TO_NS(t) 1000*(t)
> +static inline bool eq_margin(unsigned d1, unsigned d2, unsigned margin)
> +{
> + return ((d1 > (d2 - margin)) && (d1 < (d2 + margin)));
> +}
> +
> +SEC("ir_decoder/grundig_decoder")
> +int bpf_decoder(struct ir_raw_event *raw)
> +{
> + u32 key = 0;
> + struct decoder_state init = {};
> +
> + struct decoder_state *s = bpf_map_lookup_elem(&decoder_state_map, &key);
> +
> + if (!s)
> + s = &init;
> +
> + if (raw->carrier_report) {
> + // ignore
> + } else if (raw->reset) {
> + s->state = STATE_INACTIVE;
> + } else if (s->state == STATE_INACTIVE) {
> + if (raw->pulse && eq_margin(US_TO_NS(900), raw->duration, US_TO_NS(100))) {
> + s->bits = 0;
> + s->state = STATE_HEADER_SPACE;
> + s->count = 0;
> + }
> + } else if (s->state == STATE_HEADER_SPACE) {
> + if (!raw->pulse && eq_margin(US_TO_NS(2900), raw->duration, US_TO_NS(200)))
> + s->state = STATE_LEADING_PULSE;
> + else
> + s->state = STATE_INACTIVE;
> + } else if (s->state == STATE_LEADING_PULSE) {
> + if (raw->pulse && eq_margin(US_TO_NS(1300), raw->duration, US_TO_NS(100)))
> + s->state = STATE_BITS_SPACE;
> + else
> + s->state = STATE_INACTIVE;
> + } else if (s->state == STATE_BITS_SPACE) {
> + s->last_space = raw->duration;
> + s->state = STATE_BITS_PULSE;
> + } else if (s->state == STATE_BITS_PULSE) {
> + int t = -1;
> + if (eq_margin(s->last_space, US_TO_NS(472), US_TO_NS(150)) &&
> + eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> + t = 0;
> + if (eq_margin(s->last_space, US_TO_NS(1139), US_TO_NS(150)) &&
> + eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> + t = 1;
> + if (eq_margin(s->last_space, US_TO_NS(1806), US_TO_NS(150)) &&
> + eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> + t = 2;
> + if (eq_margin(s->last_space, US_TO_NS(2200), US_TO_NS(150)) &&
> + eq_margin(raw->duration, US_TO_NS(1139), US_TO_NS(150)))
> + t = 3;
> + if (t < 0) {
> + s->state = STATE_INACTIVE;
> + } else {
> + s->bits <<= 2;
> + switch (t) {
> + case 3: s->bits |= 0; break;
> + case 2: s->bits |= 3; break;
> + case 1: s->bits |= 2; break;
> + case 0: s->bits |= 1; break;
> + }
> + s->count += 2;
> + if (s->count == 16) {
> + bpf_rc_keydown(raw, 0x40, s->bits, 0);
> + s->state = STATE_INACTIVE;
> + } else {
> + s->state = STATE_BITS_SPACE;
> + }
> + }
> + }
> +
> + if (s == &init)
> + bpf_map_update_elem(&decoder_state_map, &key, s, BPF_NOEXIST);
> +
> + return 0;
> +}
> +
> +char _license[] SEC("license") = "GPL";
> +
> +u32 _version SEC("version") = LINUX_VERSION_CODE;
> +
> diff --git a/samples/bpf/grundig_decoder_user.c b/samples/bpf/grundig_decoder_user.c
> new file mode 100644
> index 000000000000..61e8ee5f73ee
> --- /dev/null
> +++ b/samples/bpf/grundig_decoder_user.c
> @@ -0,0 +1,54 @@
> +
> +#include <linux/bpf.h>
> +#include <assert.h>
> +#include <errno.h>
> +#include <signal.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <libgen.h>
> +#include <sys/resource.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +
> +#include "bpf_load.h"
> +#include "bpf_util.h"
> +#include "libbpf.h"
> +
> +int main(int argc, char **argv)
> +{
> + char filename[256];
> + int ret, lircfd;
> +
> + if (argc != 2) {
> + printf("Usage: %s /dev/lircN\n", argv[0]);
Looks like the test requires /dev/lircN device. Is there any easy way
to test it?
Also, looks like the program does not depend on any kernel headers,
maybe it can be
moved to tools/testing/selftests/bpf/? There are testbot to run those
tests regularly.
> + return 2;
> + }
> +
> + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
> +
> + if (load_bpf_file(filename)) {
> + printf("%s", bpf_log_buf);
> + return 1;
> + }
> +
> + lircfd = open(argv[1], O_RDWR);
> + if (lircfd == -1) {
> + printf("failed to open lirc device %s: %m\n", argv[1]);
> + return 1;
> + }
> +
> + ret = bpf_prog_attach(prog_fd[0], lircfd, BPF_RAWIR_DECODER, 0);
> + if (ret) {
> + printf("Failed to attach bpf to lirc device: %m\n");
> + return 1;
> + }
> +
> + printf("Grundig IR decoder loaded and attached. Hit any key to stop\n");
> + getchar();
> +
> + return 0;
> +}
> diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
> index f7a810897eac..ae1c26df212d 100644
> --- a/tools/bpf/bpftool/prog.c
> +++ b/tools/bpf/bpftool/prog.c
> @@ -68,6 +68,7 @@ static const char * const prog_type_name[] = {
> [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops",
> [BPF_PROG_TYPE_SK_SKB] = "sk_skb",
> [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device",
> + [BPF_PROG_TYPE_RAWIR_DECODER] = "ir_decoder",
> };
>
> static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
> diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> index c5ec89732a8d..d9740599adf6 100644
> --- a/tools/include/uapi/linux/bpf.h
> +++ b/tools/include/uapi/linux/bpf.h
> @@ -137,6 +137,7 @@ enum bpf_prog_type {
> BPF_PROG_TYPE_SK_MSG,
> BPF_PROG_TYPE_RAW_TRACEPOINT,
> BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
> + BPF_PROG_TYPE_RAWIR_DECODER,
> };
>
> enum bpf_attach_type {
> @@ -154,6 +155,7 @@ enum bpf_attach_type {
> BPF_CGROUP_INET6_CONNECT,
> BPF_CGROUP_INET4_POST_BIND,
> BPF_CGROUP_INET6_POST_BIND,
> + BPF_RAWIR_DECODER,
> __MAX_BPF_ATTACH_TYPE
> };
>
> @@ -755,6 +757,17 @@ union bpf_attr {
> * @addr: pointer to struct sockaddr to bind socket to
> * @addr_len: length of sockaddr structure
> * Return: 0 on success or negative error code
> + *
> + * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
> + * Report decoded scancode with toggle value
> + * @ctx: pointer to ctx
> + * @protocol: decoded protocol
> + * @scancode: decoded scancode
> + * @toggle: set to 1 if button was toggled, else 0
> + *
> + * int bpf_rc_repeat(ctx)
> + * Repeat the last decoded scancode
> + * @ctx: pointer to ctx
> */
> #define __BPF_FUNC_MAPPER(FN) \
> FN(unspec), \
> @@ -821,7 +834,9 @@ union bpf_attr {
> FN(msg_apply_bytes), \
> FN(msg_cork_bytes), \
> FN(msg_pull_data), \
> - FN(bind),
> + FN(bind), \
> + FN(rc_repeat), \
> + FN(rc_keydown),
>
> /* integer value in 'imm' field of BPF_CALL instruction selects which helper
> * function eBPF program intends to call
> diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
> index d8223d99f96d..4bf23d3dfc33 100644
> --- a/tools/testing/selftests/bpf/bpf_helpers.h
> +++ b/tools/testing/selftests/bpf/bpf_helpers.h
> @@ -96,6 +96,12 @@ static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) =
> (void *) BPF_FUNC_msg_pull_data;
> static int (*bpf_bind)(void *ctx, void *addr, int addr_len) =
> (void *) BPF_FUNC_bind;
> +static int (*bpf_rc_repeat)(void *ctx) =
> + (void *) BPF_FUNC_rc_repeat;
> +static int (*bpf_rc_keydown)(void *ctx, unsigned protocol, unsigned scancode,
> + unsigned toggle) =
> + (void *) BPF_FUNC_rc_keydown;
> +
>
> /* llvm builtin functions that eBPF C program may use to
> * emit BPF_LD_ABS and BPF_LD_IND instructions
> --
> 2.17.0
>
On Mon, May 14, 2018 at 09:48:05PM -0700, Y Song wrote:
> On Mon, May 14, 2018 at 2:10 PM, Sean Young <[email protected]> wrote:
> > Add support for BPF_PROG_IR_DECODER. This type of BPF program can call
> > rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
> > that the last key should be repeated.
> >
> > Signed-off-by: Sean Young <[email protected]>
> > ---
> > drivers/media/rc/Kconfig | 8 +++
> > drivers/media/rc/Makefile | 1 +
> > drivers/media/rc/ir-bpf-decoder.c | 93 +++++++++++++++++++++++++++++++
> > include/linux/bpf_types.h | 3 +
> > include/uapi/linux/bpf.h | 16 +++++-
> > 5 files changed, 120 insertions(+), 1 deletion(-)
> > create mode 100644 drivers/media/rc/ir-bpf-decoder.c
> >
> > diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> > index eb2c3b6eca7f..10ad6167d87c 100644
> > --- a/drivers/media/rc/Kconfig
> > +++ b/drivers/media/rc/Kconfig
> > @@ -120,6 +120,14 @@ config IR_IMON_DECODER
> > remote control and you would like to use it with a raw IR
> > receiver, or if you wish to use an encoder to transmit this IR.
> >
> > +config IR_BPF_DECODER
> > + bool "Enable IR raw decoder using BPF"
> > + depends on BPF_SYSCALL
> > + depends on RC_CORE=y
> > + help
> > + Enable this option to make it possible to load custom IR
> > + decoders written in BPF.
> > +
> > endif #RC_DECODERS
> >
> > menuconfig RC_DEVICES
> > diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> > index 2e1c87066f6c..12e1118430d0 100644
> > --- a/drivers/media/rc/Makefile
> > +++ b/drivers/media/rc/Makefile
> > @@ -5,6 +5,7 @@ obj-y += keymaps/
> > obj-$(CONFIG_RC_CORE) += rc-core.o
> > rc-core-y := rc-main.o rc-ir-raw.o
> > rc-core-$(CONFIG_LIRC) += lirc_dev.o
> > +rc-core-$(CONFIG_IR_BPF_DECODER) += ir-bpf-decoder.o
> > obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
> > obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
> > obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
> > diff --git a/drivers/media/rc/ir-bpf-decoder.c b/drivers/media/rc/ir-bpf-decoder.c
> > new file mode 100644
> > index 000000000000..aaa5e208b1a5
> > --- /dev/null
> > +++ b/drivers/media/rc/ir-bpf-decoder.c
> > @@ -0,0 +1,93 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +// ir-bpf-decoder.c - handles bpf decoders
> > +//
> > +// Copyright (C) 2018 Sean Young <[email protected]>
> > +
> > +#include <linux/bpf.h>
> > +#include <linux/filter.h>
> > +#include "rc-core-priv.h"
> > +
> > +/*
> > + * BPF interface for raw IR decoder
> > + */
> > +const struct bpf_prog_ops ir_decoder_prog_ops = {
> > +};
> > +
> > +BPF_CALL_1(bpf_rc_repeat, struct ir_raw_event*, event)
> > +{
> > + struct ir_raw_event_ctrl *ctrl;
> > +
> > + ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
> > +
> > + rc_repeat(ctrl->dev);
> > + return 0;
> > +}
> > +
> > +static const struct bpf_func_proto rc_repeat_proto = {
> > + .func = bpf_rc_repeat,
> > + .gpl_only = true, // rc_repeat is EXPORT_SYMBOL_GPL
> > + .ret_type = RET_VOID,
>
> I suggest using RET_INTEGER here since we do return an integer 0.
> RET_INTEGER will also make it easy to extend if you want to return
> a non-zero value for error code or other reasons.
Ok.
> > + .arg1_type = ARG_PTR_TO_CTX,
> > +};
> > +
> > +BPF_CALL_4(bpf_rc_keydown, struct ir_raw_event*, event, u32, protocol,
> > + u32, scancode, u32, toggle)
> > +{
> > + struct ir_raw_event_ctrl *ctrl;
> > +
> > + ctrl = container_of(event, struct ir_raw_event_ctrl, prev_ev);
> > + rc_keydown(ctrl->dev, protocol, scancode, toggle != 0);
> > + return 0;
> > +}
> > +
> > +static const struct bpf_func_proto rc_keydown_proto = {
> > + .func = bpf_rc_keydown,
> > + .gpl_only = true, // rc_keydown is EXPORT_SYMBOL_GPL
> > + .ret_type = RET_VOID,
>
> ditto. RET_INTEGER is preferable.
Ok.
> > + .arg1_type = ARG_PTR_TO_CTX,
> > + .arg2_type = ARG_ANYTHING,
> > + .arg3_type = ARG_ANYTHING,
> > + .arg4_type = ARG_ANYTHING,
> > +};
> > +
> > +static const struct bpf_func_proto *ir_decoder_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
> > +{
> > + switch (func_id) {
> > + case BPF_FUNC_rc_repeat:
> > + return &rc_repeat_proto;
> > + case BPF_FUNC_rc_keydown:
> > + return &rc_keydown_proto;
> > + case BPF_FUNC_map_lookup_elem:
> > + return &bpf_map_lookup_elem_proto;
> > + case BPF_FUNC_map_update_elem:
> > + return &bpf_map_update_elem_proto;
> > + case BPF_FUNC_map_delete_elem:
> > + return &bpf_map_delete_elem_proto;
> > + case BPF_FUNC_ktime_get_ns:
> > + return &bpf_ktime_get_ns_proto;
> > + case BPF_FUNC_tail_call:
> > + return &bpf_tail_call_proto;
> > + case BPF_FUNC_get_prandom_u32:
> > + return &bpf_get_prandom_u32_proto;
> > + default:
> > + return NULL;
> > + }
> > +}
> > +
> > +static bool ir_decoder_is_valid_access(int off, int size,
> > + enum bpf_access_type type,
> > + const struct bpf_prog *prog,
> > + struct bpf_insn_access_aux *info)
> > +{
> > + if (type == BPF_WRITE)
> > + return false;
> > + if (off < 0 || off + size > sizeof(struct ir_raw_event))
> > + return false;
>
> You probably need more than just checking the boundary.
> >From patch #3, the structure is:
> struct ir_raw_event {
> union {
> __u32 duration;
> __u32 carrier;
> };
> __u8 duty_cycle;
>
> unsigned pulse:1;
> unsigned reset:1;
> unsigned timeout:1;
> unsigned carrier_report:1;
> };
>
> You would like the memory access to be aligned,
> so accessing duration/carrier with 4-byte alignment, and
> pulse/reset/timeout/carrier_report 4-byte alignment as well.
>
> You could only allow __u32 access to duration/carrier.
> But if you want bpf program to access duration/carrier with
> code like (__u16)(ctx->duration), then the compiler may
> translate the load to a 2-byte load. You may need to handle
> endianness here. You can check net/core/filter.c function
> bpf_skb_is_valid_access for some examples.
Thanks, yes that makes sense. Actually exposing a struct with bit fields
isn't great. I think I can rework into something simpler (two u32 fields).
> > +
> > + return true;
> > +}
> > +
> > +const struct bpf_verifier_ops ir_decoder_verifier_ops = {
> > + .get_func_proto = ir_decoder_func_proto,
> > + .is_valid_access = ir_decoder_is_valid_access
> > +};
> > diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
> > index 2b28fcf6f6ae..ee5355715ee0 100644
> > --- a/include/linux/bpf_types.h
> > +++ b/include/linux/bpf_types.h
> > @@ -25,6 +25,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_RAW_TRACEPOINT, raw_tracepoint)
> > #ifdef CONFIG_CGROUP_BPF
> > BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
> > #endif
> > +#ifdef CONFIG_IR_BPF_DECODER
> > +BPF_PROG_TYPE(BPF_PROG_TYPE_RAWIR_DECODER, ir_decoder)
> > +#endif
> >
> > BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops)
> > BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops)
> > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > index c5ec89732a8d..6ad053e831c0 100644
> > --- a/include/uapi/linux/bpf.h
> > +++ b/include/uapi/linux/bpf.h
> > @@ -137,6 +137,7 @@ enum bpf_prog_type {
> > BPF_PROG_TYPE_SK_MSG,
> > BPF_PROG_TYPE_RAW_TRACEPOINT,
> > BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
> > + BPF_PROG_TYPE_RAWIR_DECODER,
> > };
> >
> > enum bpf_attach_type {
> > @@ -755,6 +756,17 @@ union bpf_attr {
> > * @addr: pointer to struct sockaddr to bind socket to
> > * @addr_len: length of sockaddr structure
> > * Return: 0 on success or negative error code
> > + *
> > + * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
> > + * Report decoded scancode with toggle value
> > + * @ctx: pointer to ctx
> > + * @protocol: decoded protocol
> > + * @scancode: decoded scancode
> > + * @toggle: set to 1 if button was toggled, else 0
> > + *
> > + * int bpf_rc_repeat(ctx)
> > + * Repeat the last decoded scancode
> > + * @ctx: pointer to ctx
>
> The comment format has changed dramatically for
> documentation reason. Could you rebase your change
> on top of bpf-next tree? You will need to rewrite the above
> helper description so tools can generate proper documentation
> for them.
Ah, I need to rebase on top of bpf-next.
Thanks!
> > */
> > #define __BPF_FUNC_MAPPER(FN) \
> > FN(unspec), \
> > @@ -821,7 +833,9 @@ union bpf_attr {
> > FN(msg_apply_bytes), \
> > FN(msg_cork_bytes), \
> > FN(msg_pull_data), \
> > - FN(bind),
> > + FN(bind), \
> > + FN(rc_repeat), \
> > + FN(rc_keydown),
> >
> > /* integer value in 'imm' field of BPF_CALL instruction selects which helper
> > * function eBPF program intends to call
> > --
> > 2.17.0
> >
On Mon, May 14, 2018 at 10:34:57PM -0700, Y Song wrote:
> On Mon, May 14, 2018 at 2:11 PM, Sean Young <[email protected]> wrote:
> > This implements the grundig-16 IR protocol.
> >
> > Signed-off-by: Sean Young <[email protected]>
> > ---
> > samples/bpf/Makefile | 4 +
> > samples/bpf/bpf_load.c | 9 +-
> > samples/bpf/grundig_decoder_kern.c | 112 ++++++++++++++++++++++
> > samples/bpf/grundig_decoder_user.c | 54 +++++++++++
> > tools/bpf/bpftool/prog.c | 1 +
> > tools/include/uapi/linux/bpf.h | 17 +++-
> > tools/testing/selftests/bpf/bpf_helpers.h | 6 ++
> > 7 files changed, 200 insertions(+), 3 deletions(-)
> > create mode 100644 samples/bpf/grundig_decoder_kern.c
> > create mode 100644 samples/bpf/grundig_decoder_user.c
> >
> > diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
> > index 4d6a6edd4bf6..c6fa111f103a 100644
> > --- a/samples/bpf/Makefile
> > +++ b/samples/bpf/Makefile
> > @@ -44,6 +44,7 @@ hostprogs-y += xdp_monitor
> > hostprogs-y += xdp_rxq_info
> > hostprogs-y += syscall_tp
> > hostprogs-y += cpustat
> > +hostprogs-y += grundig_decoder
> >
> > # Libbpf dependencies
> > LIBBPF := ../../tools/lib/bpf/bpf.o ../../tools/lib/bpf/nlattr.o
> > @@ -95,6 +96,7 @@ xdp_monitor-objs := bpf_load.o $(LIBBPF) xdp_monitor_user.o
> > xdp_rxq_info-objs := bpf_load.o $(LIBBPF) xdp_rxq_info_user.o
> > syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o
> > cpustat-objs := bpf_load.o $(LIBBPF) cpustat_user.o
> > +grundig_decoder-objs := bpf_load.o $(LIBBPF) grundig_decoder_user.o
> >
> > # Tell kbuild to always build the programs
> > always := $(hostprogs-y)
> > @@ -148,6 +150,7 @@ always += xdp_rxq_info_kern.o
> > always += xdp2skb_meta_kern.o
> > always += syscall_tp_kern.o
> > always += cpustat_kern.o
> > +always += grundig_decoder_kern.o
> >
> > HOSTCFLAGS += -I$(objtree)/usr/include
> > HOSTCFLAGS += -I$(srctree)/tools/lib/
> > @@ -193,6 +196,7 @@ HOSTLOADLIBES_xdp_monitor += -lelf
> > HOSTLOADLIBES_xdp_rxq_info += -lelf
> > HOSTLOADLIBES_syscall_tp += -lelf
> > HOSTLOADLIBES_cpustat += -lelf
> > +HOSTLOADLIBES_grundig_decoder += -lelf
> >
> > # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
> > # make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
> > diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
> > index bebe4188b4b3..0fd389e95bb9 100644
> > --- a/samples/bpf/bpf_load.c
> > +++ b/samples/bpf/bpf_load.c
> > @@ -69,6 +69,7 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
> > bool is_sockops = strncmp(event, "sockops", 7) == 0;
> > bool is_sk_skb = strncmp(event, "sk_skb", 6) == 0;
> > bool is_sk_msg = strncmp(event, "sk_msg", 6) == 0;
> > + bool is_ir_decoder = strncmp(event, "ir_decoder", 10) == 0;
> > size_t insns_cnt = size / sizeof(struct bpf_insn);
> > enum bpf_prog_type prog_type;
> > char buf[256];
> > @@ -102,6 +103,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
> > prog_type = BPF_PROG_TYPE_SK_SKB;
> > } else if (is_sk_msg) {
> > prog_type = BPF_PROG_TYPE_SK_MSG;
> > + } else if (is_ir_decoder) {
> > + prog_type = BPF_PROG_TYPE_RAWIR_DECODER;
> > } else {
> > printf("Unknown event '%s'\n", event);
> > return -1;
> > @@ -116,7 +119,8 @@ static int load_and_attach(const char *event, struct bpf_insn *prog, int size)
> >
> > prog_fd[prog_cnt++] = fd;
> >
> > - if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk)
> > + if (is_xdp || is_perf_event || is_cgroup_skb || is_cgroup_sk ||
> > + is_ir_decoder)
> > return 0;
> >
> > if (is_socket || is_sockops || is_sk_skb || is_sk_msg) {
> > @@ -607,7 +611,8 @@ static int do_load_bpf_file(const char *path, fixup_map_cb fixup_map)
> > memcmp(shname, "cgroup/", 7) == 0 ||
> > memcmp(shname, "sockops", 7) == 0 ||
> > memcmp(shname, "sk_skb", 6) == 0 ||
> > - memcmp(shname, "sk_msg", 6) == 0) {
> > + memcmp(shname, "sk_msg", 6) == 0 ||
> > + memcmp(shname, "ir_decoder", 10) == 0) {
> > ret = load_and_attach(shname, data->d_buf,
> > data->d_size);
> > if (ret != 0)
> > diff --git a/samples/bpf/grundig_decoder_kern.c b/samples/bpf/grundig_decoder_kern.c
> > new file mode 100644
> > index 000000000000..c80f2c9cc69a
> > --- /dev/null
> > +++ b/samples/bpf/grundig_decoder_kern.c
> > @@ -0,0 +1,112 @@
> > +
> > +#include <uapi/linux/bpf.h>
> > +#include <uapi/linux/bpf_rcdev.h>
> > +#include "bpf_helpers.h"
> > +#include <linux/version.h>
> > +
> > +enum grundig_state {
> > + STATE_INACTIVE,
> > + STATE_HEADER_SPACE,
> > + STATE_LEADING_PULSE,
> > + STATE_BITS_SPACE,
> > + STATE_BITS_PULSE,
> > +};
> > +
> > +struct decoder_state {
> > + u32 bits;
> > + enum grundig_state state;
> > + u32 count;
> > + u32 last_space;
> > +};
> > +
> > +struct bpf_map_def SEC("maps") decoder_state_map = {
> > + .type = BPF_MAP_TYPE_ARRAY,
> > + .key_size = sizeof(u32),
> > + .value_size = sizeof(struct decoder_state),
> > + .max_entries = 1,
> > +};
> > +
> > +#define US_TO_NS(t) 1000*(t)
> > +static inline bool eq_margin(unsigned d1, unsigned d2, unsigned margin)
> > +{
> > + return ((d1 > (d2 - margin)) && (d1 < (d2 + margin)));
> > +}
> > +
> > +SEC("ir_decoder/grundig_decoder")
> > +int bpf_decoder(struct ir_raw_event *raw)
> > +{
> > + u32 key = 0;
> > + struct decoder_state init = {};
> > +
> > + struct decoder_state *s = bpf_map_lookup_elem(&decoder_state_map, &key);
> > +
> > + if (!s)
> > + s = &init;
> > +
> > + if (raw->carrier_report) {
> > + // ignore
> > + } else if (raw->reset) {
> > + s->state = STATE_INACTIVE;
> > + } else if (s->state == STATE_INACTIVE) {
> > + if (raw->pulse && eq_margin(US_TO_NS(900), raw->duration, US_TO_NS(100))) {
> > + s->bits = 0;
> > + s->state = STATE_HEADER_SPACE;
> > + s->count = 0;
> > + }
> > + } else if (s->state == STATE_HEADER_SPACE) {
> > + if (!raw->pulse && eq_margin(US_TO_NS(2900), raw->duration, US_TO_NS(200)))
> > + s->state = STATE_LEADING_PULSE;
> > + else
> > + s->state = STATE_INACTIVE;
> > + } else if (s->state == STATE_LEADING_PULSE) {
> > + if (raw->pulse && eq_margin(US_TO_NS(1300), raw->duration, US_TO_NS(100)))
> > + s->state = STATE_BITS_SPACE;
> > + else
> > + s->state = STATE_INACTIVE;
> > + } else if (s->state == STATE_BITS_SPACE) {
> > + s->last_space = raw->duration;
> > + s->state = STATE_BITS_PULSE;
> > + } else if (s->state == STATE_BITS_PULSE) {
> > + int t = -1;
> > + if (eq_margin(s->last_space, US_TO_NS(472), US_TO_NS(150)) &&
> > + eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> > + t = 0;
> > + if (eq_margin(s->last_space, US_TO_NS(1139), US_TO_NS(150)) &&
> > + eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> > + t = 1;
> > + if (eq_margin(s->last_space, US_TO_NS(1806), US_TO_NS(150)) &&
> > + eq_margin(raw->duration, US_TO_NS(583), US_TO_NS(150)))
> > + t = 2;
> > + if (eq_margin(s->last_space, US_TO_NS(2200), US_TO_NS(150)) &&
> > + eq_margin(raw->duration, US_TO_NS(1139), US_TO_NS(150)))
> > + t = 3;
> > + if (t < 0) {
> > + s->state = STATE_INACTIVE;
> > + } else {
> > + s->bits <<= 2;
> > + switch (t) {
> > + case 3: s->bits |= 0; break;
> > + case 2: s->bits |= 3; break;
> > + case 1: s->bits |= 2; break;
> > + case 0: s->bits |= 1; break;
> > + }
> > + s->count += 2;
> > + if (s->count == 16) {
> > + bpf_rc_keydown(raw, 0x40, s->bits, 0);
> > + s->state = STATE_INACTIVE;
> > + } else {
> > + s->state = STATE_BITS_SPACE;
> > + }
> > + }
> > + }
> > +
> > + if (s == &init)
> > + bpf_map_update_elem(&decoder_state_map, &key, s, BPF_NOEXIST);
> > +
> > + return 0;
> > +}
> > +
> > +char _license[] SEC("license") = "GPL";
> > +
> > +u32 _version SEC("version") = LINUX_VERSION_CODE;
> > +
> > diff --git a/samples/bpf/grundig_decoder_user.c b/samples/bpf/grundig_decoder_user.c
> > new file mode 100644
> > index 000000000000..61e8ee5f73ee
> > --- /dev/null
> > +++ b/samples/bpf/grundig_decoder_user.c
> > @@ -0,0 +1,54 @@
> > +
> > +#include <linux/bpf.h>
> > +#include <assert.h>
> > +#include <errno.h>
> > +#include <signal.h>
> > +#include <stdio.h>
> > +#include <stdlib.h>
> > +#include <stdbool.h>
> > +#include <string.h>
> > +#include <unistd.h>
> > +#include <libgen.h>
> > +#include <sys/resource.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <fcntl.h>
> > +
> > +#include "bpf_load.h"
> > +#include "bpf_util.h"
> > +#include "libbpf.h"
> > +
> > +int main(int argc, char **argv)
> > +{
> > + char filename[256];
> > + int ret, lircfd;
> > +
> > + if (argc != 2) {
> > + printf("Usage: %s /dev/lircN\n", argv[0]);
>
> Looks like the test requires /dev/lircN device. Is there any easy way
> to test it?
It can be tested using rc-loopback.
> Also, looks like the program does not depend on any kernel headers,
> maybe it can be
> moved to tools/testing/selftests/bpf/? There are testbot to run those
> tests regularly.
That's a good idea. Let me see what I can do.
Thanks for all review comments, I agree with all them (so I won't have
to reply to every message).
I'll re-roll a v2 of this series in the next couple of days.
Thanks
Sean
>
> > + return 2;
> > + }
> > +
> > + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
> > +
> > + if (load_bpf_file(filename)) {
> > + printf("%s", bpf_log_buf);
> > + return 1;
> > + }
> > +
> > + lircfd = open(argv[1], O_RDWR);
> > + if (lircfd == -1) {
> > + printf("failed to open lirc device %s: %m\n", argv[1]);
> > + return 1;
> > + }
> > +
> > + ret = bpf_prog_attach(prog_fd[0], lircfd, BPF_RAWIR_DECODER, 0);
> > + if (ret) {
> > + printf("Failed to attach bpf to lirc device: %m\n");
> > + return 1;
> > + }
> > +
> > + printf("Grundig IR decoder loaded and attached. Hit any key to stop\n");
> > + getchar();
> > +
> > + return 0;
> > +}
> > diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
> > index f7a810897eac..ae1c26df212d 100644
> > --- a/tools/bpf/bpftool/prog.c
> > +++ b/tools/bpf/bpftool/prog.c
> > @@ -68,6 +68,7 @@ static const char * const prog_type_name[] = {
> > [BPF_PROG_TYPE_SOCK_OPS] = "sock_ops",
> > [BPF_PROG_TYPE_SK_SKB] = "sk_skb",
> > [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device",
> > + [BPF_PROG_TYPE_RAWIR_DECODER] = "ir_decoder",
> > };
> >
> > static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
> > diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
> > index c5ec89732a8d..d9740599adf6 100644
> > --- a/tools/include/uapi/linux/bpf.h
> > +++ b/tools/include/uapi/linux/bpf.h
> > @@ -137,6 +137,7 @@ enum bpf_prog_type {
> > BPF_PROG_TYPE_SK_MSG,
> > BPF_PROG_TYPE_RAW_TRACEPOINT,
> > BPF_PROG_TYPE_CGROUP_SOCK_ADDR,
> > + BPF_PROG_TYPE_RAWIR_DECODER,
> > };
> >
> > enum bpf_attach_type {
> > @@ -154,6 +155,7 @@ enum bpf_attach_type {
> > BPF_CGROUP_INET6_CONNECT,
> > BPF_CGROUP_INET4_POST_BIND,
> > BPF_CGROUP_INET6_POST_BIND,
> > + BPF_RAWIR_DECODER,
> > __MAX_BPF_ATTACH_TYPE
> > };
> >
> > @@ -755,6 +757,17 @@ union bpf_attr {
> > * @addr: pointer to struct sockaddr to bind socket to
> > * @addr_len: length of sockaddr structure
> > * Return: 0 on success or negative error code
> > + *
> > + * int bpf_rc_keydown(ctx, protocol, scancode, toggle)
> > + * Report decoded scancode with toggle value
> > + * @ctx: pointer to ctx
> > + * @protocol: decoded protocol
> > + * @scancode: decoded scancode
> > + * @toggle: set to 1 if button was toggled, else 0
> > + *
> > + * int bpf_rc_repeat(ctx)
> > + * Repeat the last decoded scancode
> > + * @ctx: pointer to ctx
> > */
> > #define __BPF_FUNC_MAPPER(FN) \
> > FN(unspec), \
> > @@ -821,7 +834,9 @@ union bpf_attr {
> > FN(msg_apply_bytes), \
> > FN(msg_cork_bytes), \
> > FN(msg_pull_data), \
> > - FN(bind),
> > + FN(bind), \
> > + FN(rc_repeat), \
> > + FN(rc_keydown),
> >
> > /* integer value in 'imm' field of BPF_CALL instruction selects which helper
> > * function eBPF program intends to call
> > diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h
> > index d8223d99f96d..4bf23d3dfc33 100644
> > --- a/tools/testing/selftests/bpf/bpf_helpers.h
> > +++ b/tools/testing/selftests/bpf/bpf_helpers.h
> > @@ -96,6 +96,12 @@ static int (*bpf_msg_pull_data)(void *ctx, int start, int end, int flags) =
> > (void *) BPF_FUNC_msg_pull_data;
> > static int (*bpf_bind)(void *ctx, void *addr, int addr_len) =
> > (void *) BPF_FUNC_bind;
> > +static int (*bpf_rc_repeat)(void *ctx) =
> > + (void *) BPF_FUNC_rc_repeat;
> > +static int (*bpf_rc_keydown)(void *ctx, unsigned protocol, unsigned scancode,
> > + unsigned toggle) =
> > + (void *) BPF_FUNC_rc_keydown;
> > +
> >
> > /* llvm builtin functions that eBPF C program may use to
> > * emit BPF_LD_ABS and BPF_LD_IND instructions
> > --
> > 2.17.0
> >
On Mon, May 14, 2018 at 04:27:19PM -0700, Randy Dunlap wrote:
> On 05/14/2018 02:10 PM, Sean Young wrote:
> > Add support for BPF_PROG_IR_DECODER. This type of BPF program can call
>
> Kconfig file below uses IR_BPF_DECODER instead of the symbol name above.
>
> and then patch 3 says a third choice:
> The context provided to a BPF_PROG_RAWIR_DECODER is a struct ir_raw_event;
Yes, you're right. I guess the source/trigger is raw IR events; decoding
is something you're likely to do, but not necessarily. So:
bpf type: BPF_PROG_TYPE_RAWIR_EVENT, has context struct bpf_rawir_event.
Then we can call the Kconfig option CONFIG_BPF_RAW_IR_EVENT
Sean
>
> > rc_keydown() to reported decoded IR scancodes, or rc_repeat() to report
> > that the last key should be repeated.
> >
> > Signed-off-by: Sean Young <[email protected]>
> > ---
> > drivers/media/rc/Kconfig | 8 +++
> > drivers/media/rc/Makefile | 1 +
> > drivers/media/rc/ir-bpf-decoder.c | 93 +++++++++++++++++++++++++++++++
> > include/linux/bpf_types.h | 3 +
> > include/uapi/linux/bpf.h | 16 +++++-
> > 5 files changed, 120 insertions(+), 1 deletion(-)
> > create mode 100644 drivers/media/rc/ir-bpf-decoder.c
> >
> > diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
> > index eb2c3b6eca7f..10ad6167d87c 100644
> > --- a/drivers/media/rc/Kconfig
> > +++ b/drivers/media/rc/Kconfig
> > @@ -120,6 +120,14 @@ config IR_IMON_DECODER
> > remote control and you would like to use it with a raw IR
> > receiver, or if you wish to use an encoder to transmit this IR.
> >
> > +config IR_BPF_DECODER
> > + bool "Enable IR raw decoder using BPF"
> > + depends on BPF_SYSCALL
> > + depends on RC_CORE=y
> > + help
> > + Enable this option to make it possible to load custom IR
> > + decoders written in BPF.
> > +
> > endif #RC_DECODERS
> >
> > menuconfig RC_DEVICES
> > diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
> > index 2e1c87066f6c..12e1118430d0 100644
> > --- a/drivers/media/rc/Makefile
> > +++ b/drivers/media/rc/Makefile
> > @@ -5,6 +5,7 @@ obj-y += keymaps/
> > obj-$(CONFIG_RC_CORE) += rc-core.o
> > rc-core-y := rc-main.o rc-ir-raw.o
> > rc-core-$(CONFIG_LIRC) += lirc_dev.o
> > +rc-core-$(CONFIG_IR_BPF_DECODER) += ir-bpf-decoder.o
>
>
> --
> ~Randy