Hi, Again thanks for a thoughtful review. This will definitely will improve the code. On Thu, May 17, 2018 at 10:02:52AM -0700, Y Song wrote: > On Wed, May 16, 2018 at 2:04 PM, Sean Young <sean@xxxxxxxx> wrote: > > Add support for BPF_PROG_RAWIR_EVENT. 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. > > > > The bpf program can be attached to using the bpf(BPF_PROG_ATTACH) syscall; > > the target_fd must be the /dev/lircN device. > > > > Signed-off-by: Sean Young <sean@xxxxxxxx> > > --- > > drivers/media/rc/Kconfig | 13 ++ > > drivers/media/rc/Makefile | 1 + > > drivers/media/rc/bpf-rawir-event.c | 363 +++++++++++++++++++++++++++++ > > drivers/media/rc/lirc_dev.c | 24 ++ > > drivers/media/rc/rc-core-priv.h | 24 ++ > > drivers/media/rc/rc-ir-raw.c | 14 +- > > include/linux/bpf_rcdev.h | 30 +++ > > include/linux/bpf_types.h | 3 + > > include/uapi/linux/bpf.h | 55 ++++- > > kernel/bpf/syscall.c | 7 + > > 10 files changed, 531 insertions(+), 3 deletions(-) > > create mode 100644 drivers/media/rc/bpf-rawir-event.c > > create mode 100644 include/linux/bpf_rcdev.h > > > > diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig > > index eb2c3b6eca7f..2172d65b0213 100644 > > --- a/drivers/media/rc/Kconfig > > +++ b/drivers/media/rc/Kconfig > > @@ -25,6 +25,19 @@ config LIRC > > passes raw IR to and from userspace, which is needed for > > IR transmitting (aka "blasting") and for the lirc daemon. > > > > +config BPF_RAWIR_EVENT > > + bool "Support for eBPF programs attached to lirc devices" > > + depends on BPF_SYSCALL > > + depends on RC_CORE=y > > + depends on LIRC > > + help > > + Allow attaching eBPF programs to a lirc device using the bpf(2) > > + syscall command BPF_PROG_ATTACH. This is supported for raw IR > > + receivers. > > + > > + These eBPF programs can be used to decode IR into scancodes, for > > + IR protocols not supported by the kernel decoders. > > + > > menuconfig RC_DECODERS > > bool "Remote controller decoders" > > depends on RC_CORE > > diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile > > index 2e1c87066f6c..74907823bef8 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_BPF_RAWIR_EVENT) += bpf-rawir-event.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/bpf-rawir-event.c b/drivers/media/rc/bpf-rawir-event.c > > new file mode 100644 > > index 000000000000..7cb48b8d87b5 > > --- /dev/null > > +++ b/drivers/media/rc/bpf-rawir-event.c > > @@ -0,0 +1,363 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +// bpf-rawir-event.c - handles bpf > > +// > > +// Copyright (C) 2018 Sean Young <sean@xxxxxxxx> > > + > > +#include <linux/bpf.h> > > +#include <linux/filter.h> > > +#include <linux/bpf_rcdev.h> > > +#include "rc-core-priv.h" > > + > > +/* > > + * BPF interface for raw IR > > + */ > > +const struct bpf_prog_ops rawir_event_prog_ops = { > > +}; > > + > > +BPF_CALL_1(bpf_rc_repeat, struct bpf_rawir_event*, event) > > +{ > > + struct ir_raw_event_ctrl *ctrl; > > + > > + ctrl = container_of(event, struct ir_raw_event_ctrl, bpf_rawir_event); > > + > > + 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_INTEGER, > > + .arg1_type = ARG_PTR_TO_CTX, > > +}; > > + > > +BPF_CALL_4(bpf_rc_keydown, struct bpf_rawir_event*, event, u32, protocol, > > + u32, scancode, u32, toggle) > > +{ > > + struct ir_raw_event_ctrl *ctrl; > > + > > + ctrl = container_of(event, struct ir_raw_event_ctrl, bpf_rawir_event); > > + > > + 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_INTEGER, > > + .arg1_type = ARG_PTR_TO_CTX, > > + .arg2_type = ARG_ANYTHING, > > + .arg3_type = ARG_ANYTHING, > > + .arg4_type = ARG_ANYTHING, > > +}; > > + > > +static const struct bpf_func_proto * > > +rawir_event_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; > > + case BPF_FUNC_trace_printk: > > + if (capable(CAP_SYS_ADMIN)) > > + return bpf_get_trace_printk_proto(); > > + /* fall through */ > > + default: > > + return NULL; > > + } > > +} > > + > > +static bool rawir_event_is_valid_access(int off, int size, > > + enum bpf_access_type type, > > + const struct bpf_prog *prog, > > + struct bpf_insn_access_aux *info) > > +{ > > + /* struct bpf_rawir_event has two u32 fields */ > > + if (type == BPF_WRITE) > > + return false; > > + > > + if (size != sizeof(__u32)) > > + return false; > > + > > + if (!(off == offsetof(struct bpf_rawir_event, duration) || > > + off == offsetof(struct bpf_rawir_event, type))) > > + return false; > > + > > + return true; > > +} > > + > > +const struct bpf_verifier_ops rawir_event_verifier_ops = { > > + .get_func_proto = rawir_event_func_proto, > > + .is_valid_access = rawir_event_is_valid_access > > +}; > > + > > +#define BPF_MAX_PROGS 64 > > + > > +static int rc_dev_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog) > > +{ > > + struct ir_raw_event_ctrl *raw; > > + struct bpf_prog_list *pl; > > + int ret, size; > > + > > + if (rcdev->driver_type != RC_DRIVER_IR_RAW) > > + return -EINVAL; > > + > > + ret = mutex_lock_interruptible(&ir_raw_handler_lock); > > + if (ret) > > + return ret; > > + > > + raw = rcdev->raw; > > + if (!raw) { > > + ret = -ENODEV; > > + goto out; > > + } > > + > > + size = 0; > > + list_for_each_entry(pl, &raw->progs, node) { > > + if (pl->prog == prog) { > > + ret = -EEXIST; > > + goto out; > > + } > > + > > + size++; > > + } > > + > > + if (size >= BPF_MAX_PROGS) { > > + ret = -E2BIG; > > + goto out; > > + } > > + > > + pl = kmalloc(sizeof(*pl), GFP_KERNEL); > > + if (!pl) { > > + ret = -ENOMEM; > > + goto out; > > + } > > + > > + pl->prog = prog; > > + list_add(&pl->node, &raw->progs); > > +out: > > + mutex_unlock(&ir_raw_handler_lock); > > + return ret; > > +} > > + > > +static int rc_dev_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog) > > +{ > > + struct ir_raw_event_ctrl *raw; > > + struct bpf_prog_list *pl, *tmp; > > + int ret; > > + > > + if (rcdev->driver_type != RC_DRIVER_IR_RAW) > > + return -EINVAL; > > + > > + ret = mutex_lock_interruptible(&ir_raw_handler_lock); > > + if (ret) > > + return ret; > > + > > + raw = rcdev->raw; > > + if (!raw) { > > + ret = -ENODEV; > > + goto out; > > + } > > + > > + ret = -ENOENT; > > + > > + list_for_each_entry_safe(pl, tmp, &raw->progs, node) { > > + if (pl->prog == prog) { > > + list_del(&pl->node); > > + kfree(pl); > > + bpf_prog_put(prog); > > + ret = 0; > > + goto out; > > + } > > + } > > +out: > > + mutex_unlock(&ir_raw_handler_lock); > > + return ret; > > +} > > + > > +void rc_dev_bpf_init(struct rc_dev *rcdev) > > +{ > > + INIT_LIST_HEAD(&rcdev->raw->progs); > > +} > > + > > +void rc_dev_bpf_run(struct rc_dev *rcdev, struct ir_raw_event ev) > > +{ > > + struct ir_raw_event_ctrl *raw = rcdev->raw; > > + struct bpf_prog_list *pl; > > + > > + if (list_empty(&raw->progs)) > > + return; > > + > > + if (unlikely(ev.carrier_report)) { > > + raw->bpf_rawir_event.carrier = ev.carrier; > > + raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_CARRIER; > > + } else { > > + raw->bpf_rawir_event.duration = ev.duration; > > + > > + if (ev.pulse) > > + raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_PULSE; > > + else if (ev.timeout) > > + raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_TIMEOUT; > > + else if (ev.reset) > > + raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_RESET; > > + else > > + raw->bpf_rawir_event.type = BPF_RAWIR_EVENT_SPACE; > > + } > > + > > + list_for_each_entry(pl, &raw->progs, node) > > + BPF_PROG_RUN(pl->prog, &raw->bpf_rawir_event); > > Is the raw->progs protected by locks? It is possible that attaching/detaching > could manipulate raw->progs at the same time? In perf/cgroup prog array > case, the prog array run is protected by rcu and the dummy prog idea is > to avoid the prog is skipped by reshuffling. Yes, it is. The caller takes the appropriate lock. These functions could do with some comments. > Also, the original idea about using prog array is the least overhead since you > want to BPF programs to execute as soon as possible. Now, for our purposes the we're not bothered by a few milliseconds delay, so I would pick whatever uses less cpu/memory overall. There is some overhead in using rcu but the array is nice since it uses less memory than the double-linked list, and less cache misses. So I wanted bpf_prog_detach() to return -ENOENT if the prog was not in the list, however bpf_prog_array_copy() and bpf_prog_array_delete_safe() do not return anything useful for that. Maybe I should look at adding a count-delta return to bpf_prog_array_copy(), and a bool return to bpf_prog_array_delete_safe(). Or I could scan the array an extra time to see if it is present. Any other ideas? Also, BPF_F_OVERRIDE is not relevant for this but possibly BPF_F_ALLOW_MULTI could invoke the same behaviour as for cgroups. What do you think? > > +} > > + > > +void rc_dev_bpf_free(struct rc_dev *rcdev) > > +{ > > + struct bpf_prog_list *pl, *tmp; > > + > > + list_for_each_entry_safe(pl, tmp, &rcdev->raw->progs, node) { > > + list_del(&pl->node); > > + bpf_prog_put(pl->prog); > > + kfree(pl); > > + } > > +} > > + > > +int rc_dev_prog_attach(const union bpf_attr *attr) > > +{ > > + struct bpf_prog *prog; > > + struct rc_dev *rcdev; > > + int ret; > > + > > + if (attr->attach_flags) > > + return -EINVAL; > > + > > + prog = bpf_prog_get_type(attr->attach_bpf_fd, > > + BPF_PROG_TYPE_RAWIR_EVENT); > > + 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); > > + 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) > > + return -EINVAL; > > + > > + prog = bpf_prog_get_type(attr->attach_bpf_fd, > > + BPF_PROG_TYPE_RAWIR_EVENT); > > + 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); > > + > > + 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 ir_raw_event_ctrl *raw; > > + struct bpf_prog_list *pl; > > + struct rc_dev *rcdev; > > + u32 cnt, flags = 0, *ids, i; > > + 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; > > mutex_lock_interruptible() has not been called. You can just return here. Yep. > > + } > > + > > + ret = mutex_lock_interruptible(&ir_raw_handler_lock); > > + if (ret) > > + goto out; > > Maybe you can rename label "out" to "unlock" since it > is really an unlock and out? Good point. > > + > > + raw = rcdev->raw; > > + if (!raw) { > > + ret = -ENODEV; > > + goto out; > > + } > > + > > + cnt = 0; > > + list_for_each_entry(pl, &raw->progs, node) > > + cnt++; > > + > > + 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) { > > + if (attr->query.prog_cnt < cnt) > > + cnt = attr->query.prog_cnt; > > + > > + ids = kmalloc_array(cnt, sizeof(u32), GFP_KERNEL); > > + if (!ids) { > > + ret = -ENOMEM; > > + goto out; > > + } > > + > > + i = 0; > > + list_for_each_entry(pl, &raw->progs, node) { > > + ids[i++] = pl->prog->aux->id; > > + if (i == cnt) > > + break; > > + } > > + > > + ret = copy_to_user(prog_ids, ids, cnt * sizeof(u32)); > > Do you want to give user a chance to know that the "cnt" is not big enough > by return -ENOSPC if cnt is smaller than the number of progs in the array? True. > > + } > > +out: > > + mutex_unlock(&ir_raw_handler_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..193540ded626 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> > > @@ -816,4 +817,27 @@ 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 fd f = fdget(fd); > > + struct lirc_fh *fh; > > + struct rc_dev *dev; > > + > > + if (!f.file) > > + return ERR_PTR(-EBADF); > > + > > + if (f.file->f_op != &lirc_fops) { > > + fdput(f); > > + return ERR_PTR(-EINVAL); > > + } > > + > > + fh = f.file->private_data; > > + dev = fh->rc; > > + > > + get_device(&dev->dev); > > + fdput(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..148db73cfa0c 100644 > > --- a/drivers/media/rc/rc-core-priv.h > > +++ b/drivers/media/rc/rc-core-priv.h > > @@ -13,6 +13,7 @@ > > #define MAX_IR_EVENT_SIZE 512 > > > > #include <linux/slab.h> > > +#include <uapi/linux/bpf.h> > > #include <media/rc-core.h> > > > > /** > > @@ -57,6 +58,11 @@ struct ir_raw_event_ctrl { > > /* raw decoder state follows */ > > struct ir_raw_event prev_ev; > > struct ir_raw_event this_ev; > > + > > +#ifdef CONFIG_BPF_RAWIR_EVENT > > + struct bpf_rawir_event bpf_rawir_event; > > + struct list_head progs; > > +#endif > > struct nec_dec { > > int state; > > unsigned count; > > @@ -126,6 +132,9 @@ struct ir_raw_event_ctrl { > > } imon; > > }; > > > > +/* Mutex for locking raw IR processing and handler change */ > > +extern struct mutex ir_raw_handler_lock; > > + > > /* macros for IR decoders */ > > static inline bool geq_margin(unsigned d1, unsigned d2, unsigned margin) > > { > > @@ -288,6 +297,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 +309,18 @@ 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_BPF_RAWIR_EVENT > > +void rc_dev_bpf_init(struct rc_dev *dev); > > +void rc_dev_bpf_free(struct rc_dev *dev); > > +void rc_dev_bpf_run(struct rc_dev *dev, struct ir_raw_event ev); > > +#else > > +static inline void rc_dev_bpf_init(struct rc_dev *dev) { } > > +static inline void rc_dev_bpf_free(struct rc_dev *dev) { } > > +static inline void rc_dev_bpf_run(struct rc_dev *dev, struct ir_raw_event ev) > > +{ } > > +#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..e68cdd4fbf5d 100644 > > --- a/drivers/media/rc/rc-ir-raw.c > > +++ b/drivers/media/rc/rc-ir-raw.c > > @@ -14,7 +14,7 @@ > > static LIST_HEAD(ir_raw_client_list); > > > > /* Used to handle IR raw handler extensions */ > > -static DEFINE_MUTEX(ir_raw_handler_lock); > > +DEFINE_MUTEX(ir_raw_handler_lock); > > static LIST_HEAD(ir_raw_handler_list); > > static atomic64_t available_protocols = ATOMIC64_INIT(0); > > > > @@ -32,6 +32,7 @@ static int ir_raw_event_thread(void *data) > > handler->protocols || !handler->protocols) > > handler->decode(raw->dev, ev); > > ir_lirc_raw_event(raw->dev, ev); > > + rc_dev_bpf_run(raw->dev, ev); > > raw->prev_ev = ev; > > } > > mutex_unlock(&ir_raw_handler_lock); > > @@ -572,6 +573,7 @@ int ir_raw_event_prepare(struct rc_dev *dev) > > spin_lock_init(&dev->raw->edge_spinlock); > > timer_setup(&dev->raw->edge_handle, ir_raw_edge_handle, 0); > > INIT_KFIFO(dev->raw->kfifo); > > + rc_dev_bpf_init(dev); > > > > return 0; > > } > > @@ -621,9 +623,17 @@ void ir_raw_event_unregister(struct rc_dev *dev) > > list_for_each_entry(handler, &ir_raw_handler_list, list) > > if (handler->raw_unregister) > > handler->raw_unregister(dev); > > - mutex_unlock(&ir_raw_handler_lock); > > + > > + rc_dev_bpf_free(dev); > > > > ir_raw_event_free(dev); > > + > > + /* > > + * A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so > > + * ensure that the raw member is null on unlock; this is how > > + * "device gone" is checked. > > + */ > > + mutex_unlock(&ir_raw_handler_lock); > > } > > > > /* > > diff --git a/include/linux/bpf_rcdev.h b/include/linux/bpf_rcdev.h > > new file mode 100644 > > index 000000000000..17a30f30436a > > --- /dev/null > > +++ b/include/linux/bpf_rcdev.h > > @@ -0,0 +1,30 @@ > > +/* SPDX-License-Identifier: GPL-2.0 */ > > +#ifndef _BPF_RCDEV_H > > +#define _BPF_RCDEV_H > > + > > +#include <linux/bpf.h> > > +#include <uapi/linux/bpf.h> > > + > > +#ifdef CONFIG_BPF_RAWIR_EVENT > > +int rc_dev_prog_attach(const union bpf_attr *attr); > > +int rc_dev_prog_detach(const union bpf_attr *attr); > > +int rc_dev_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr); > > +#else > > +static inline int rc_dev_prog_attach(const union bpf_attr *attr) > > +{ > > + return -EINVAL; > > +} > > + > > +static inline int rc_dev_prog_detach(const union bpf_attr *attr) > > +{ > > + return -EINVAL; > > +} > > + > > +static inline int rc_dev_prog_query(const union bpf_attr *attr, > > + union bpf_attr __user *uattr) > > +{ > > + return -EINVAL; > > +} > > +#endif > > + > > +#endif /* _BPF_RCDEV_H */ > > diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h > > index b67f8793de0d..e2b1b12474d4 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_BPF_RAWIR_EVENT > > +BPF_PROG_TYPE(BPF_PROG_TYPE_RAWIR_EVENT, rawir_event) > > +#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 d94d333a8225..243e141e8a5b 100644 > > --- a/include/uapi/linux/bpf.h > > +++ b/include/uapi/linux/bpf.h > > @@ -141,6 +141,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_EVENT, > > }; > > > > enum bpf_attach_type { > > @@ -158,6 +159,7 @@ enum bpf_attach_type { > > BPF_CGROUP_INET6_CONNECT, > > BPF_CGROUP_INET4_POST_BIND, > > BPF_CGROUP_INET6_POST_BIND, > > + BPF_RAWIR_EVENT, > > __MAX_BPF_ATTACH_TYPE > > }; > > > > @@ -1902,6 +1904,35 @@ union bpf_attr { > > * egress otherwise). This is the only flag supported for now. > > * Return > > * **SK_PASS** on success, or **SK_DROP** on error. > > + * > > + * int bpf_rc_keydown(void *ctx, u32 protocol, u32 scancode, u32 toggle) > > + * Description > > + * Report decoded scancode with toggle value. For use in > > + * BPF_PROG_TYPE_RAWIR_EVENT, to report a successfully > > + * decoded scancode. This is will generate a keydown event, > > + * and a keyup event once the scancode is no longer repeated. > > + * > > + * *ctx* pointer to bpf_rawir_event, *protocol* is decoded > > + * protocol (see RC_PROTO_* enum). > > + * > > + * Some protocols include a toggle bit, in case the button > > + * was released and pressed again between consecutive scancodes, > > + * copy this bit into *toggle* if it exists, else set to 0. > > + * > > + * Return > > + * Always return 0 (for now) > > + * > > + * int bpf_rc_repeat(void *ctx) > > + * Description > > + * Repeat the last decoded scancode; some IR protocols like > > + * NEC have a special IR message for repeat last button, > > + * in case user is holding a button down; the scancode is > > + * not repeated. > > + * > > + * *ctx* pointer to bpf_rawir_event. > > + * > > + * Return > > + * Always return 0 (for now) > > */ > > #define __BPF_FUNC_MAPPER(FN) \ > > FN(unspec), \ > > @@ -1976,7 +2007,9 @@ union bpf_attr { > > FN(fib_lookup), \ > > FN(sock_hash_update), \ > > FN(msg_redirect_hash), \ > > - FN(sk_redirect_hash), > > + FN(sk_redirect_hash), \ > > + FN(rc_repeat), \ > > + FN(rc_keydown), > > > > /* integer value in 'imm' field of BPF_CALL instruction selects which helper > > * function eBPF program intends to call > > @@ -2043,6 +2076,26 @@ enum bpf_hdr_start_off { > > BPF_HDR_START_NET, > > }; > > > > +/* > > + * user accessible mirror of in-kernel ir_raw_event > > + */ > > +#define BPF_RAWIR_EVENT_SPACE 0 > > +#define BPF_RAWIR_EVENT_PULSE 1 > > +#define BPF_RAWIR_EVENT_TIMEOUT 2 > > +#define BPF_RAWIR_EVENT_RESET 3 > > +#define BPF_RAWIR_EVENT_CARRIER 4 > > +#define BPF_RAWIR_EVENT_DUTY_CYCLE 5 > > + > > +struct bpf_rawir_event { > > + union { > > + __u32 duration; > > + __u32 carrier; > > + __u32 duty_cycle; > > + }; > > + > > + __u32 type; > > +}; > > + > > /* user accessible mirror of in-kernel sk_buff. > > * new fields can only be added to the end of this structure > > */ > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c > > index e2aeb5e89f44..75c089f407c8 100644 > > --- a/kernel/bpf/syscall.c > > +++ b/kernel/bpf/syscall.c > > @@ -11,6 +11,7 @@ > > */ > > #include <linux/bpf.h> > > #include <linux/bpf_trace.h> > > +#include <linux/bpf_rcdev.h> > > #include <linux/btf.h> > > #include <linux/syscalls.h> > > #include <linux/slab.h> > > @@ -1567,6 +1568,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_EVENT: > > + return rc_dev_prog_attach(attr); > > default: > > return -EINVAL; > > } > > @@ -1637,6 +1640,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_EVENT: > > + return rc_dev_prog_detach(attr); > > default: > > return -EINVAL; > > } > > @@ -1684,6 +1689,8 @@ static int bpf_prog_query(const union bpf_attr *attr, > > case BPF_CGROUP_SOCK_OPS: > > case BPF_CGROUP_DEVICE: > > break; > > + case BPF_RAWIR_EVENT: > > + return rc_dev_prog_query(attr, uattr); > > default: > > return -EINVAL; > > } > > -- > > 2.17.0 > >