Skip to content

Commit f4364dc

Browse files
seanyoungborkmann
authored andcommitted
media: rc: introduce BPF_PROG_LIRC_MODE2
Add support for BPF_PROG_LIRC_MODE2. 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. Acked-by: Yonghong Song <[email protected]> Signed-off-by: Sean Young <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]>
1 parent 170a7e3 commit f4364dc

File tree

10 files changed

+479
-3
lines changed

10 files changed

+479
-3
lines changed

Diff for: drivers/media/rc/Kconfig

+13
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@ config LIRC
2525
passes raw IR to and from userspace, which is needed for
2626
IR transmitting (aka "blasting") and for the lirc daemon.
2727

28+
config BPF_LIRC_MODE2
29+
bool "Support for eBPF programs attached to lirc devices"
30+
depends on BPF_SYSCALL
31+
depends on RC_CORE=y
32+
depends on LIRC
33+
help
34+
Allow attaching eBPF programs to a lirc device using the bpf(2)
35+
syscall command BPF_PROG_ATTACH. This is supported for raw IR
36+
receivers.
37+
38+
These eBPF programs can be used to decode IR into scancodes, for
39+
IR protocols not supported by the kernel decoders.
40+
2841
menuconfig RC_DECODERS
2942
bool "Remote controller decoders"
3043
depends on RC_CORE

Diff for: drivers/media/rc/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ obj-y += keymaps/
55
obj-$(CONFIG_RC_CORE) += rc-core.o
66
rc-core-y := rc-main.o rc-ir-raw.o
77
rc-core-$(CONFIG_LIRC) += lirc_dev.o
8+
rc-core-$(CONFIG_BPF_LIRC_MODE2) += bpf-lirc.o
89
obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
910
obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
1011
obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o

Diff for: drivers/media/rc/bpf-lirc.c

+313
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// bpf-lirc.c - handles bpf
3+
//
4+
// Copyright (C) 2018 Sean Young <[email protected]>
5+
6+
#include <linux/bpf.h>
7+
#include <linux/filter.h>
8+
#include <linux/bpf_lirc.h>
9+
#include "rc-core-priv.h"
10+
11+
/*
12+
* BPF interface for raw IR
13+
*/
14+
const struct bpf_prog_ops lirc_mode2_prog_ops = {
15+
};
16+
17+
BPF_CALL_1(bpf_rc_repeat, u32*, sample)
18+
{
19+
struct ir_raw_event_ctrl *ctrl;
20+
21+
ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample);
22+
23+
rc_repeat(ctrl->dev);
24+
25+
return 0;
26+
}
27+
28+
static const struct bpf_func_proto rc_repeat_proto = {
29+
.func = bpf_rc_repeat,
30+
.gpl_only = true, /* rc_repeat is EXPORT_SYMBOL_GPL */
31+
.ret_type = RET_INTEGER,
32+
.arg1_type = ARG_PTR_TO_CTX,
33+
};
34+
35+
/*
36+
* Currently rc-core does not support 64-bit scancodes, but there are many
37+
* known protocols with more than 32 bits. So, define the interface as u64
38+
* as a future-proof.
39+
*/
40+
BPF_CALL_4(bpf_rc_keydown, u32*, sample, u32, protocol, u64, scancode,
41+
u32, toggle)
42+
{
43+
struct ir_raw_event_ctrl *ctrl;
44+
45+
ctrl = container_of(sample, struct ir_raw_event_ctrl, bpf_sample);
46+
47+
rc_keydown(ctrl->dev, protocol, scancode, toggle != 0);
48+
49+
return 0;
50+
}
51+
52+
static const struct bpf_func_proto rc_keydown_proto = {
53+
.func = bpf_rc_keydown,
54+
.gpl_only = true, /* rc_keydown is EXPORT_SYMBOL_GPL */
55+
.ret_type = RET_INTEGER,
56+
.arg1_type = ARG_PTR_TO_CTX,
57+
.arg2_type = ARG_ANYTHING,
58+
.arg3_type = ARG_ANYTHING,
59+
.arg4_type = ARG_ANYTHING,
60+
};
61+
62+
static const struct bpf_func_proto *
63+
lirc_mode2_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
64+
{
65+
switch (func_id) {
66+
case BPF_FUNC_rc_repeat:
67+
return &rc_repeat_proto;
68+
case BPF_FUNC_rc_keydown:
69+
return &rc_keydown_proto;
70+
case BPF_FUNC_map_lookup_elem:
71+
return &bpf_map_lookup_elem_proto;
72+
case BPF_FUNC_map_update_elem:
73+
return &bpf_map_update_elem_proto;
74+
case BPF_FUNC_map_delete_elem:
75+
return &bpf_map_delete_elem_proto;
76+
case BPF_FUNC_ktime_get_ns:
77+
return &bpf_ktime_get_ns_proto;
78+
case BPF_FUNC_tail_call:
79+
return &bpf_tail_call_proto;
80+
case BPF_FUNC_get_prandom_u32:
81+
return &bpf_get_prandom_u32_proto;
82+
case BPF_FUNC_trace_printk:
83+
if (capable(CAP_SYS_ADMIN))
84+
return bpf_get_trace_printk_proto();
85+
/* fall through */
86+
default:
87+
return NULL;
88+
}
89+
}
90+
91+
static bool lirc_mode2_is_valid_access(int off, int size,
92+
enum bpf_access_type type,
93+
const struct bpf_prog *prog,
94+
struct bpf_insn_access_aux *info)
95+
{
96+
/* We have one field of u32 */
97+
return type == BPF_READ && off == 0 && size == sizeof(u32);
98+
}
99+
100+
const struct bpf_verifier_ops lirc_mode2_verifier_ops = {
101+
.get_func_proto = lirc_mode2_func_proto,
102+
.is_valid_access = lirc_mode2_is_valid_access
103+
};
104+
105+
#define BPF_MAX_PROGS 64
106+
107+
static int lirc_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog)
108+
{
109+
struct bpf_prog_array __rcu *old_array;
110+
struct bpf_prog_array *new_array;
111+
struct ir_raw_event_ctrl *raw;
112+
int ret;
113+
114+
if (rcdev->driver_type != RC_DRIVER_IR_RAW)
115+
return -EINVAL;
116+
117+
ret = mutex_lock_interruptible(&ir_raw_handler_lock);
118+
if (ret)
119+
return ret;
120+
121+
raw = rcdev->raw;
122+
if (!raw) {
123+
ret = -ENODEV;
124+
goto unlock;
125+
}
126+
127+
if (raw->progs && bpf_prog_array_length(raw->progs) >= BPF_MAX_PROGS) {
128+
ret = -E2BIG;
129+
goto unlock;
130+
}
131+
132+
old_array = raw->progs;
133+
ret = bpf_prog_array_copy(old_array, NULL, prog, &new_array);
134+
if (ret < 0)
135+
goto unlock;
136+
137+
rcu_assign_pointer(raw->progs, new_array);
138+
bpf_prog_array_free(old_array);
139+
140+
unlock:
141+
mutex_unlock(&ir_raw_handler_lock);
142+
return ret;
143+
}
144+
145+
static int lirc_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog)
146+
{
147+
struct bpf_prog_array __rcu *old_array;
148+
struct bpf_prog_array *new_array;
149+
struct ir_raw_event_ctrl *raw;
150+
int ret;
151+
152+
if (rcdev->driver_type != RC_DRIVER_IR_RAW)
153+
return -EINVAL;
154+
155+
ret = mutex_lock_interruptible(&ir_raw_handler_lock);
156+
if (ret)
157+
return ret;
158+
159+
raw = rcdev->raw;
160+
if (!raw) {
161+
ret = -ENODEV;
162+
goto unlock;
163+
}
164+
165+
old_array = raw->progs;
166+
ret = bpf_prog_array_copy(old_array, prog, NULL, &new_array);
167+
/*
168+
* Do not use bpf_prog_array_delete_safe() as we would end up
169+
* with a dummy entry in the array, and the we would free the
170+
* dummy in lirc_bpf_free()
171+
*/
172+
if (ret)
173+
goto unlock;
174+
175+
rcu_assign_pointer(raw->progs, new_array);
176+
bpf_prog_array_free(old_array);
177+
unlock:
178+
mutex_unlock(&ir_raw_handler_lock);
179+
return ret;
180+
}
181+
182+
void lirc_bpf_run(struct rc_dev *rcdev, u32 sample)
183+
{
184+
struct ir_raw_event_ctrl *raw = rcdev->raw;
185+
186+
raw->bpf_sample = sample;
187+
188+
if (raw->progs)
189+
BPF_PROG_RUN_ARRAY(raw->progs, &raw->bpf_sample, BPF_PROG_RUN);
190+
}
191+
192+
/*
193+
* This should be called once the rc thread has been stopped, so there can be
194+
* no concurrent bpf execution.
195+
*/
196+
void lirc_bpf_free(struct rc_dev *rcdev)
197+
{
198+
struct bpf_prog **progs;
199+
200+
if (!rcdev->raw->progs)
201+
return;
202+
203+
progs = rcu_dereference(rcdev->raw->progs)->progs;
204+
while (*progs)
205+
bpf_prog_put(*progs++);
206+
207+
bpf_prog_array_free(rcdev->raw->progs);
208+
}
209+
210+
int lirc_prog_attach(const union bpf_attr *attr)
211+
{
212+
struct bpf_prog *prog;
213+
struct rc_dev *rcdev;
214+
int ret;
215+
216+
if (attr->attach_flags)
217+
return -EINVAL;
218+
219+
prog = bpf_prog_get_type(attr->attach_bpf_fd,
220+
BPF_PROG_TYPE_LIRC_MODE2);
221+
if (IS_ERR(prog))
222+
return PTR_ERR(prog);
223+
224+
rcdev = rc_dev_get_from_fd(attr->target_fd);
225+
if (IS_ERR(rcdev)) {
226+
bpf_prog_put(prog);
227+
return PTR_ERR(rcdev);
228+
}
229+
230+
ret = lirc_bpf_attach(rcdev, prog);
231+
if (ret)
232+
bpf_prog_put(prog);
233+
234+
put_device(&rcdev->dev);
235+
236+
return ret;
237+
}
238+
239+
int lirc_prog_detach(const union bpf_attr *attr)
240+
{
241+
struct bpf_prog *prog;
242+
struct rc_dev *rcdev;
243+
int ret;
244+
245+
if (attr->attach_flags)
246+
return -EINVAL;
247+
248+
prog = bpf_prog_get_type(attr->attach_bpf_fd,
249+
BPF_PROG_TYPE_LIRC_MODE2);
250+
if (IS_ERR(prog))
251+
return PTR_ERR(prog);
252+
253+
rcdev = rc_dev_get_from_fd(attr->target_fd);
254+
if (IS_ERR(rcdev)) {
255+
bpf_prog_put(prog);
256+
return PTR_ERR(rcdev);
257+
}
258+
259+
ret = lirc_bpf_detach(rcdev, prog);
260+
261+
bpf_prog_put(prog);
262+
put_device(&rcdev->dev);
263+
264+
return ret;
265+
}
266+
267+
int lirc_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr)
268+
{
269+
__u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
270+
struct bpf_prog_array __rcu *progs;
271+
struct rc_dev *rcdev;
272+
u32 cnt, flags = 0;
273+
int ret;
274+
275+
if (attr->query.query_flags)
276+
return -EINVAL;
277+
278+
rcdev = rc_dev_get_from_fd(attr->query.target_fd);
279+
if (IS_ERR(rcdev))
280+
return PTR_ERR(rcdev);
281+
282+
if (rcdev->driver_type != RC_DRIVER_IR_RAW) {
283+
ret = -EINVAL;
284+
goto put;
285+
}
286+
287+
ret = mutex_lock_interruptible(&ir_raw_handler_lock);
288+
if (ret)
289+
goto put;
290+
291+
progs = rcdev->raw->progs;
292+
cnt = progs ? bpf_prog_array_length(progs) : 0;
293+
294+
if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) {
295+
ret = -EFAULT;
296+
goto unlock;
297+
}
298+
299+
if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) {
300+
ret = -EFAULT;
301+
goto unlock;
302+
}
303+
304+
if (attr->query.prog_cnt != 0 && prog_ids && cnt)
305+
ret = bpf_prog_array_copy_to_user(progs, prog_ids, cnt);
306+
307+
unlock:
308+
mutex_unlock(&ir_raw_handler_lock);
309+
put:
310+
put_device(&rcdev->dev);
311+
312+
return ret;
313+
}

Diff for: drivers/media/rc/lirc_dev.c

+30
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/module.h>
2121
#include <linux/mutex.h>
2222
#include <linux/device.h>
23+
#include <linux/file.h>
2324
#include <linux/idr.h>
2425
#include <linux/poll.h>
2526
#include <linux/sched.h>
@@ -104,6 +105,12 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
104105
TO_US(ev.duration), TO_STR(ev.pulse));
105106
}
106107

108+
/*
109+
* bpf does not care about the gap generated above; that exists
110+
* for backwards compatibility
111+
*/
112+
lirc_bpf_run(dev, sample);
113+
107114
spin_lock_irqsave(&dev->lirc_fh_lock, flags);
108115
list_for_each_entry(fh, &dev->lirc_fh, list) {
109116
if (LIRC_IS_TIMEOUT(sample) && !fh->send_timeout_reports)
@@ -816,4 +823,27 @@ void __exit lirc_dev_exit(void)
816823
unregister_chrdev_region(lirc_base_dev, RC_DEV_MAX);
817824
}
818825

826+
struct rc_dev *rc_dev_get_from_fd(int fd)
827+
{
828+
struct fd f = fdget(fd);
829+
struct lirc_fh *fh;
830+
struct rc_dev *dev;
831+
832+
if (!f.file)
833+
return ERR_PTR(-EBADF);
834+
835+
if (f.file->f_op != &lirc_fops) {
836+
fdput(f);
837+
return ERR_PTR(-EINVAL);
838+
}
839+
840+
fh = f.file->private_data;
841+
dev = fh->rc;
842+
843+
get_device(&dev->dev);
844+
fdput(f);
845+
846+
return dev;
847+
}
848+
819849
MODULE_ALIAS("lirc_dev");

0 commit comments

Comments
 (0)