@@ -875,6 +875,63 @@ TEST_F(hid_bpf, test_hid_user_raw_request_call)
ASSERT_EQ(args.data[1], 2);
}
+static __u8 workload_data;
+
+static int handle_event(void *ctx, void *data, size_t data_sz)
+{
+ const __u8 *e = data;
+
+ workload_data = *e;
+
+ return 0;
+}
+
+/*
+ * Call hid_bpf_schedule_delayed_work against the given uhid device,
+ * check that the program is called and does the expected.
+ */
+TEST_F(hid_bpf, test_hid_schedule_work)
+{
+ const struct test_program progs[] = {
+ { .name = "hid_defer_event" },
+ { .name = "hid_offload_notify" },
+ };
+ struct ring_buffer *rb = NULL;
+ __u8 buf[10] = {0};
+ __u32* delay;
+ int err;
+
+ LOAD_PROGRAMS(progs);
+
+ /* Set up ring buffer polling */
+ rb = ring_buffer__new(bpf_map__fd(self->skel->maps.rb), handle_event, NULL, NULL);
+ ASSERT_OK_PTR(rb) TH_LOG("Failed to create ring buffer");
+ ASSERT_EQ(workload_data, 0);
+
+ delay = (__u32 *)&buf[2];
+
+ /* inject one event */
+ buf[0] = 1;
+ buf[1] = 42; /* this will be placed in the ring buffer */
+ *delay = 0;
+ uhid_send_event(_metadata, self->uhid_fd, buf, 6);
+
+ err = ring_buffer__poll(rb, 100 /* timeout, ms */);
+ ASSERT_EQ(err, 1) TH_LOG("error while calling ring_buffer__poll");
+
+ ASSERT_EQ(workload_data, 42);
+
+ /* inject one another */
+ buf[0] = 1;
+ buf[1] = 53; /* this will be placed in the ring buffer */
+ *delay = 100;
+ uhid_send_event(_metadata, self->uhid_fd, buf, 6);
+
+ err = ring_buffer__poll(rb, 1000 /* timeout, ms */);
+ ASSERT_EQ(err, 1) TH_LOG("error while calling ring_buffer__poll");
+ ASSERT_EQ(workload_data, 53);
+}
+
/*
* Attach hid_insert{0,1,2} to the given uhid device,
* retrieve and open the matching hidraw node,
@@ -250,3 +250,58 @@ int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx)
return 0;
}
+
+struct test_report {
+ __u8 data[6];
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_QUEUE);
+ __uint(max_entries, 8);
+ __type(value, struct test_report);
+} queue SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+ __uint(max_entries, 8);
+} rb SEC(".maps");
+
+SEC("?fmod_ret.s/hid_bpf_offload")
+int BPF_PROG(hid_offload_notify, struct hid_bpf_ctx *hid_ctx)
+{
+ struct test_report buf;
+ __u8 *rb_elem;
+
+ if (bpf_map_pop_elem(&queue, &buf))
+ return 0;
+
+ rb_elem = bpf_ringbuf_reserve(&rb, sizeof(*rb_elem), 0);
+ if (!rb_elem)
+ return 0;
+
+ *rb_elem = buf.data[1];
+
+ bpf_ringbuf_submit(rb_elem, 0);
+
+ return 0;
+}
+
+SEC("?fmod_ret/hid_bpf_device_event")
+int BPF_PROG(hid_defer_event, struct hid_bpf_ctx *hctx)
+{
+ __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 6 /* size */);
+ struct test_report buf;
+ __u32 delay;
+
+ if (!data)
+ return 0; /* EPERM check */
+
+ __builtin_memcpy(&buf.data, data, 6);
+
+ delay = *(__u32 *)&data[2];
+
+ bpf_map_push_elem(&queue, &buf, BPF_ANY);
+ hid_bpf_schedule_delayed_work(hctx, delay);
+
+ return -1; /* discard the event */
+}
@@ -100,5 +100,7 @@ extern int hid_bpf_input_report(struct hid_bpf_ctx *ctx,
enum hid_report_type type,
__u8 *data,
size_t buf__sz) __ksym;
+extern bool hid_bpf_schedule_delayed_work(struct hid_bpf_ctx *ctx,
+ unsigned int delay_ms) __ksym;
#endif /* __HID_BPF_HELPERS_H */