1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2022 Red hat */
3 #include "hid_bpf_helpers.h"
4
5 char _license[] SEC("license") = "GPL";
6
7 struct attach_prog_args {
8 int prog_fd;
9 unsigned int hid;
10 int retval;
11 int insert_head;
12 };
13
14 __u64 callback_check = 52;
15 __u64 callback2_check = 52;
16 __u64 get_data_overflow_check;
17
18 SEC("?struct_ops/hid_device_event")
BPF_PROG(hid_first_event,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)19 int BPF_PROG(hid_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
20 {
21 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
22
23 if (!rw_data)
24 return 0; /* EPERM check */
25
26 callback_check = rw_data[1];
27
28 rw_data[2] = rw_data[1] + 5;
29
30 return hid_ctx->size;
31 }
32
33 SEC(".struct_ops.link")
34 struct hid_bpf_ops first_event = {
35 .hid_device_event = (void *)hid_first_event,
36 .hid_id = 2,
37 };
38
__hid_subprog_first_event(struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)39 int __hid_subprog_first_event(struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
40 {
41 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
42
43 if (!rw_data)
44 return 0; /* EPERM check */
45
46 rw_data[2] = rw_data[1] + 5;
47
48 return hid_ctx->size;
49 }
50
51 SEC("?struct_ops/hid_device_event")
BPF_PROG(hid_subprog_first_event,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)52 int BPF_PROG(hid_subprog_first_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
53 {
54 return __hid_subprog_first_event(hid_ctx, type);
55 }
56
57 SEC(".struct_ops.link")
58 struct hid_bpf_ops subprog_first_event = {
59 .hid_device_event = (void *)hid_subprog_first_event,
60 .hid_id = 2,
61 };
62
63 SEC("?struct_ops/hid_device_event")
BPF_PROG(hid_second_event,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)64 int BPF_PROG(hid_second_event, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
65 {
66 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
67
68 if (!rw_data)
69 return 0; /* EPERM check */
70
71 rw_data[3] = rw_data[2] + 5;
72
73 return hid_ctx->size;
74 }
75
76 SEC(".struct_ops.link")
77 struct hid_bpf_ops second_event = {
78 .hid_device_event = (void *)hid_second_event,
79 };
80
81 SEC("?struct_ops/hid_device_event")
BPF_PROG(hid_change_report_id,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)82 int BPF_PROG(hid_change_report_id, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
83 {
84 __u8 *rw_data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 3 /* size */);
85
86 if (!rw_data)
87 return 0; /* EPERM check */
88
89 rw_data[0] = 2;
90
91 return 9;
92 }
93
94 SEC(".struct_ops.link")
95 struct hid_bpf_ops change_report_id = {
96 .hid_device_event = (void *)hid_change_report_id,
97 };
98
99 struct hid_hw_request_syscall_args {
100 /* data needs to come at offset 0 so we can use it in calls */
101 __u8 data[10];
102 unsigned int hid;
103 int retval;
104 size_t size;
105 enum hid_report_type type;
106 __u8 request_type;
107 };
108
109 SEC("syscall")
hid_user_raw_request(struct hid_hw_request_syscall_args * args)110 int hid_user_raw_request(struct hid_hw_request_syscall_args *args)
111 {
112 struct hid_bpf_ctx *ctx;
113 const size_t size = args->size;
114 int i, ret = 0;
115
116 if (size > sizeof(args->data))
117 return -7; /* -E2BIG */
118
119 ctx = hid_bpf_allocate_context(args->hid);
120 if (!ctx)
121 return -1; /* EPERM check */
122
123 ret = hid_bpf_hw_request(ctx,
124 args->data,
125 size,
126 args->type,
127 args->request_type);
128 args->retval = ret;
129
130 hid_bpf_release_context(ctx);
131
132 return 0;
133 }
134
135 SEC("syscall")
hid_user_output_report(struct hid_hw_request_syscall_args * args)136 int hid_user_output_report(struct hid_hw_request_syscall_args *args)
137 {
138 struct hid_bpf_ctx *ctx;
139 const size_t size = args->size;
140 int i, ret = 0;
141
142 if (size > sizeof(args->data))
143 return -7; /* -E2BIG */
144
145 ctx = hid_bpf_allocate_context(args->hid);
146 if (!ctx)
147 return -1; /* EPERM check */
148
149 ret = hid_bpf_hw_output_report(ctx,
150 args->data,
151 size);
152 args->retval = ret;
153
154 hid_bpf_release_context(ctx);
155
156 return 0;
157 }
158
159 SEC("syscall")
hid_user_input_report(struct hid_hw_request_syscall_args * args)160 int hid_user_input_report(struct hid_hw_request_syscall_args *args)
161 {
162 struct hid_bpf_ctx *ctx;
163 const size_t size = args->size;
164 int i, ret = 0;
165
166 if (size > sizeof(args->data))
167 return -7; /* -E2BIG */
168
169 ctx = hid_bpf_allocate_context(args->hid);
170 if (!ctx)
171 return -1; /* EPERM check */
172
173 ret = hid_bpf_input_report(ctx, HID_INPUT_REPORT, args->data, size);
174 args->retval = ret;
175
176 hid_bpf_release_context(ctx);
177
178 return 0;
179 }
180
181 static const __u8 rdesc[] = {
182 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
183 0x09, 0x32, /* USAGE (Z) */
184 0x95, 0x01, /* REPORT_COUNT (1) */
185 0x81, 0x06, /* INPUT (Data,Var,Rel) */
186
187 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
188 0x19, 0x01, /* USAGE_MINIMUM (1) */
189 0x29, 0x03, /* USAGE_MAXIMUM (3) */
190 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
191 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
192 0x95, 0x03, /* REPORT_COUNT (3) */
193 0x75, 0x01, /* REPORT_SIZE (1) */
194 0x91, 0x02, /* Output (Data,Var,Abs) */
195 0x95, 0x01, /* REPORT_COUNT (1) */
196 0x75, 0x05, /* REPORT_SIZE (5) */
197 0x91, 0x01, /* Output (Cnst,Var,Abs) */
198
199 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
200 0x19, 0x06, /* USAGE_MINIMUM (6) */
201 0x29, 0x08, /* USAGE_MAXIMUM (8) */
202 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
203 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
204 0x95, 0x03, /* REPORT_COUNT (3) */
205 0x75, 0x01, /* REPORT_SIZE (1) */
206 0xb1, 0x02, /* Feature (Data,Var,Abs) */
207 0x95, 0x01, /* REPORT_COUNT (1) */
208 0x75, 0x05, /* REPORT_SIZE (5) */
209 0x91, 0x01, /* Output (Cnst,Var,Abs) */
210
211 0xc0, /* END_COLLECTION */
212 0xc0, /* END_COLLECTION */
213 };
214
215 /*
216 * the following program is marked as sleepable (struct_ops.s).
217 * This is not strictly mandatory but is a nice test for
218 * sleepable struct_ops
219 */
220 SEC("?struct_ops.s/hid_rdesc_fixup")
BPF_PROG(hid_rdesc_fixup,struct hid_bpf_ctx * hid_ctx)221 int BPF_PROG(hid_rdesc_fixup, struct hid_bpf_ctx *hid_ctx)
222 {
223 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4096 /* size */);
224
225 if (!data)
226 return 0; /* EPERM check */
227
228 callback2_check = data[4];
229
230 /* insert rdesc at offset 73 */
231 __builtin_memcpy(&data[73], rdesc, sizeof(rdesc));
232
233 /* Change Usage Vendor globally */
234 data[4] = 0x42;
235
236 return sizeof(rdesc) + 73;
237 }
238
239 SEC(".struct_ops.link")
240 struct hid_bpf_ops rdesc_fixup = {
241 .hid_rdesc_fixup = (void *)hid_rdesc_fixup,
242 };
243
244 SEC("?struct_ops.s/hid_rdesc_fixup")
BPF_PROG(hid_rdesc_fixup_get_data_overflow,struct hid_bpf_ctx * hid_ctx)245 int BPF_PROG(hid_rdesc_fixup_get_data_overflow, struct hid_bpf_ctx *hid_ctx)
246 {
247 if (!hid_bpf_get_data(hid_ctx, 2 /* offset */, ~0ULL /* size */))
248 get_data_overflow_check = 1;
249
250 return 0;
251 }
252
253 SEC(".struct_ops.link")
254 struct hid_bpf_ops rdesc_fixup_get_data_overflow = {
255 .hid_rdesc_fixup = (void *)hid_rdesc_fixup_get_data_overflow,
256 };
257
258 SEC("?struct_ops.s/hid_rdesc_fixup")
BPF_PROG(hid_rdesc_fixup_change_uniq_name_phys,struct hid_bpf_ctx * hid_ctx)259 int BPF_PROG(hid_rdesc_fixup_change_uniq_name_phys, struct hid_bpf_ctx *hid_ctx)
260 {
261 #define HID_BPF_MEMCPY(target, str) \
262 __builtin_memcpy(target, str, sizeof(str))
263
264 HID_BPF_MEMCPY(hid_ctx->hid->name, "name coming from bpf");
265 HID_BPF_MEMCPY(hid_ctx->hid->uniq, "uniq:coming:from:bpf");
266 /* hid_bpf relies on a phys being a rand % 1024 */
267 for (int i = 0; i < 5; i++) {
268 if (!hid_ctx->hid->phys[i]) {
269 HID_BPF_MEMCPY(hid_ctx->hid->phys + i, " phys:coming:from:bpf");
270 break;
271 }
272 }
273
274 #undef HID_BPF_MEMCPY
275
276 return 0;
277 }
278
279 SEC(".struct_ops.link")
280 struct hid_bpf_ops rdesc_fixup_change_uniq_name_phys = {
281 .hid_rdesc_fixup = (void *)hid_rdesc_fixup_change_uniq_name_phys,
282 };
283
284 SEC("?struct_ops/hid_device_event")
BPF_PROG(hid_test_insert1,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)285 int BPF_PROG(hid_test_insert1, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
286 {
287 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
288
289 if (!data)
290 return 0; /* EPERM check */
291
292 /* we need to be run first */
293 if (data[2] || data[3])
294 return -1;
295
296 data[1] = 1;
297
298 return 0;
299 }
300
301 SEC(".struct_ops.link")
302 struct hid_bpf_ops test_insert1 = {
303 .hid_device_event = (void *)hid_test_insert1,
304 .flags = BPF_F_BEFORE,
305 };
306
307 SEC("?struct_ops/hid_device_event")
BPF_PROG(hid_test_insert2,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)308 int BPF_PROG(hid_test_insert2, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
309 {
310 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
311
312 if (!data)
313 return 0; /* EPERM check */
314
315 /* after insert0 and before insert2 */
316 if (!data[1] || data[3])
317 return -1;
318
319 data[2] = 2;
320
321 return 0;
322 }
323
324 SEC(".struct_ops.link")
325 struct hid_bpf_ops test_insert2 = {
326 .hid_device_event = (void *)hid_test_insert2,
327 };
328
329 SEC("?struct_ops/hid_device_event")
BPF_PROG(hid_test_insert3,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)330 int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
331 {
332 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 4 /* size */);
333
334 if (!data)
335 return 0; /* EPERM check */
336
337 /* at the end */
338 if (!data[1] || !data[2])
339 return -1;
340
341 data[3] = 3;
342
343 return 0;
344 }
345
346 SEC(".struct_ops.link")
347 struct hid_bpf_ops test_insert3 = {
348 .hid_device_event = (void *)hid_test_insert3,
349 };
350
351 SEC("?struct_ops/hid_hw_request")
BPF_PROG(hid_test_filter_raw_request,struct hid_bpf_ctx * hctx,unsigned char reportnum,enum hid_report_type rtype,enum hid_class_request reqtype,__u64 source)352 int BPF_PROG(hid_test_filter_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
353 enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
354 {
355 return -20;
356 }
357
358 SEC(".struct_ops.link")
359 struct hid_bpf_ops test_filter_raw_request = {
360 .hid_hw_request = (void *)hid_test_filter_raw_request,
361 };
362
363 static struct file *current_file;
364
365 SEC("fentry/hidraw_open")
BPF_PROG(hidraw_open,struct inode * inode,struct file * file)366 int BPF_PROG(hidraw_open, struct inode *inode, struct file *file)
367 {
368 current_file = file;
369 return 0;
370 }
371
372 SEC("?struct_ops.s/hid_hw_request")
BPF_PROG(hid_test_hidraw_raw_request,struct hid_bpf_ctx * hctx,unsigned char reportnum,enum hid_report_type rtype,enum hid_class_request reqtype,__u64 source)373 int BPF_PROG(hid_test_hidraw_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
374 enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
375 {
376 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
377 int ret;
378
379 if (!data)
380 return 0; /* EPERM check */
381
382 /* check if the incoming request comes from our hidraw operation */
383 if (source == (__u64)current_file) {
384 data[0] = reportnum;
385
386 ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);
387 if (ret != 2)
388 return -1;
389 data[0] = reportnum + 1;
390 data[1] = reportnum + 2;
391 data[2] = reportnum + 3;
392 return 3;
393 }
394
395 return 0;
396 }
397
398 SEC(".struct_ops.link")
399 struct hid_bpf_ops test_hidraw_raw_request = {
400 .hid_hw_request = (void *)hid_test_hidraw_raw_request,
401 };
402
403 SEC("?struct_ops.s/hid_hw_request")
BPF_PROG(hid_test_infinite_loop_raw_request,struct hid_bpf_ctx * hctx,unsigned char reportnum,enum hid_report_type rtype,enum hid_class_request reqtype,__u64 source)404 int BPF_PROG(hid_test_infinite_loop_raw_request, struct hid_bpf_ctx *hctx, unsigned char reportnum,
405 enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
406 {
407 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
408 int ret;
409
410 if (!data)
411 return 0; /* EPERM check */
412
413 /* always forward the request as-is to the device, hid-bpf should prevent
414 * infinite loops.
415 */
416 data[0] = reportnum;
417
418 ret = hid_bpf_hw_request(hctx, data, 2, rtype, reqtype);
419 if (ret == 2)
420 return 3;
421
422 return 0;
423 }
424
425 SEC(".struct_ops.link")
426 struct hid_bpf_ops test_infinite_loop_raw_request = {
427 .hid_hw_request = (void *)hid_test_infinite_loop_raw_request,
428 };
429
430 SEC("?struct_ops/hid_hw_output_report")
BPF_PROG(hid_test_filter_output_report,struct hid_bpf_ctx * hctx,unsigned char reportnum,enum hid_report_type rtype,enum hid_class_request reqtype,__u64 source)431 int BPF_PROG(hid_test_filter_output_report, struct hid_bpf_ctx *hctx, unsigned char reportnum,
432 enum hid_report_type rtype, enum hid_class_request reqtype, __u64 source)
433 {
434 return -25;
435 }
436
437 SEC(".struct_ops.link")
438 struct hid_bpf_ops test_filter_output_report = {
439 .hid_hw_output_report = (void *)hid_test_filter_output_report,
440 };
441
442 SEC("?struct_ops.s/hid_hw_output_report")
BPF_PROG(hid_test_hidraw_output_report,struct hid_bpf_ctx * hctx,__u64 source)443 int BPF_PROG(hid_test_hidraw_output_report, struct hid_bpf_ctx *hctx, __u64 source)
444 {
445 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
446 int ret;
447
448 if (!data)
449 return 0; /* EPERM check */
450
451 /* check if the incoming request comes from our hidraw operation */
452 if (source == (__u64)current_file)
453 return hid_bpf_hw_output_report(hctx, data, 2);
454
455 return 0;
456 }
457
458 SEC(".struct_ops.link")
459 struct hid_bpf_ops test_hidraw_output_report = {
460 .hid_hw_output_report = (void *)hid_test_hidraw_output_report,
461 };
462
463 SEC("?struct_ops.s/hid_hw_output_report")
BPF_PROG(hid_test_infinite_loop_output_report,struct hid_bpf_ctx * hctx,__u64 source)464 int BPF_PROG(hid_test_infinite_loop_output_report, struct hid_bpf_ctx *hctx, __u64 source)
465 {
466 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 3 /* size */);
467 int ret;
468
469 if (!data)
470 return 0; /* EPERM check */
471
472 /* always forward the request as-is to the device, hid-bpf should prevent
473 * infinite loops.
474 */
475
476 ret = hid_bpf_hw_output_report(hctx, data, 2);
477 if (ret == 2)
478 return 2;
479
480 return 0;
481 }
482
483 SEC(".struct_ops.link")
484 struct hid_bpf_ops test_infinite_loop_output_report = {
485 .hid_hw_output_report = (void *)hid_test_infinite_loop_output_report,
486 };
487
488 struct elem {
489 struct bpf_wq work;
490 };
491
492 struct {
493 __uint(type, BPF_MAP_TYPE_HASH);
494 __uint(max_entries, 1);
495 __type(key, int);
496 __type(value, struct elem);
497 } hmap SEC(".maps");
498
wq_cb_sleepable(void * map,int * key,void * work)499 static int wq_cb_sleepable(void *map, int *key, void *work)
500 {
501 __u8 buf[9] = {2, 3, 4, 5, 6, 7, 8, 9, 10};
502 struct hid_bpf_ctx *hid_ctx;
503
504 hid_ctx = hid_bpf_allocate_context(*key);
505 if (!hid_ctx)
506 return 0; /* EPERM check */
507
508 hid_bpf_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));
509
510 hid_bpf_release_context(hid_ctx);
511
512 return 0;
513 }
514
test_inject_input_report_callback(int * key)515 static int test_inject_input_report_callback(int *key)
516 {
517 struct elem init = {}, *val;
518 struct bpf_wq *wq;
519
520 if (bpf_map_update_elem(&hmap, key, &init, 0))
521 return -1;
522
523 val = bpf_map_lookup_elem(&hmap, key);
524 if (!val)
525 return -2;
526
527 wq = &val->work;
528 if (bpf_wq_init(wq, &hmap, 0) != 0)
529 return -3;
530
531 if (bpf_wq_set_callback(wq, wq_cb_sleepable, 0))
532 return -4;
533
534 if (bpf_wq_start(wq, 0))
535 return -5;
536
537 return 0;
538 }
539
540 SEC("?struct_ops/hid_device_event")
BPF_PROG(hid_test_multiply_events_wq,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)541 int BPF_PROG(hid_test_multiply_events_wq, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
542 {
543 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);
544 int hid = hid_ctx->hid->id;
545 int ret;
546
547 if (!data)
548 return 0; /* EPERM check */
549
550 if (data[0] != 1)
551 return 0;
552
553 ret = test_inject_input_report_callback(&hid);
554 if (ret)
555 return ret;
556
557 data[1] += 5;
558
559 return 0;
560 }
561
562 SEC(".struct_ops.link")
563 struct hid_bpf_ops test_multiply_events_wq = {
564 .hid_device_event = (void *)hid_test_multiply_events_wq,
565 };
566
567 SEC("?struct_ops/hid_device_event")
BPF_PROG(hid_test_multiply_events,struct hid_bpf_ctx * hid_ctx,enum hid_report_type type)568 int BPF_PROG(hid_test_multiply_events, struct hid_bpf_ctx *hid_ctx, enum hid_report_type type)
569 {
570 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 9 /* size */);
571 __u8 buf[9];
572 int ret;
573
574 if (!data)
575 return 0; /* EPERM check */
576
577 if (data[0] != 1)
578 return 0;
579
580 /*
581 * we have to use an intermediate buffer as hid_bpf_input_report
582 * will memset data to \0
583 */
584 __builtin_memcpy(buf, data, sizeof(buf));
585
586 buf[0] = 2;
587 buf[1] += 5;
588 ret = hid_bpf_try_input_report(hid_ctx, HID_INPUT_REPORT, buf, sizeof(buf));
589 if (ret < 0)
590 return ret;
591
592 /*
593 * In real world we should reset the original buffer as data might be garbage now,
594 * but it actually now has the content of 'buf'
595 */
596 data[1] += 5;
597
598 return 9;
599 }
600
601 SEC(".struct_ops.link")
602 struct hid_bpf_ops test_multiply_events = {
603 .hid_device_event = (void *)hid_test_multiply_events,
604 };
605
606 SEC("?struct_ops/hid_device_event")
BPF_PROG(hid_test_infinite_loop_input_report,struct hid_bpf_ctx * hctx,enum hid_report_type report_type,__u64 source)607 int BPF_PROG(hid_test_infinite_loop_input_report, struct hid_bpf_ctx *hctx,
608 enum hid_report_type report_type, __u64 source)
609 {
610 __u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 6 /* size */);
611 __u8 buf[6];
612
613 if (!data)
614 return 0; /* EPERM check */
615
616 /*
617 * we have to use an intermediate buffer as hid_bpf_input_report
618 * will memset data to \0
619 */
620 __builtin_memcpy(buf, data, sizeof(buf));
621
622 /* always forward the request as-is to the device, hid-bpf should prevent
623 * infinite loops.
624 * the return value is ignored so the event is passing to userspace.
625 */
626
627 hid_bpf_try_input_report(hctx, report_type, buf, sizeof(buf));
628
629 /* each time we process the event, we increment by one data[1]:
630 * after each successful call to hid_bpf_try_input_report, buf
631 * has been memcopied into data by the kernel.
632 */
633 data[1] += 1;
634
635 return 0;
636 }
637
638 SEC(".struct_ops.link")
639 struct hid_bpf_ops test_infinite_loop_input_report = {
640 .hid_device_event = (void *)hid_test_infinite_loop_input_report,
641 };
642