Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: bpf: export hid_hw_output_report as a BPF kfunc

We currently only export hid_hw_raw_request() as a BPF kfunc.
However, some devices require an explicit write on the Output Report
instead of the use of the control channel.

So also export hid_hw_output_report to BPF

Link: https://lore.kernel.org/r/20240315-b4-hid-bpf-new-funcs-v4-2-079c282469d3@kernel.org
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>

+86 -30
+1 -1
Documentation/hid/hid-bpf.rst
··· 179 179 ----------------------------------------------------------- 180 180 181 181 .. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c 182 - :functions: hid_bpf_attach_prog hid_bpf_hw_request hid_bpf_allocate_context hid_bpf_release_context 182 + :functions: hid_bpf_attach_prog hid_bpf_hw_request hid_bpf_hw_output_report hid_bpf_allocate_context hid_bpf_release_context 183 183 184 184 General overview of a HID-BPF program 185 185 =====================================
+83 -29
drivers/hid/bpf/hid_bpf_dispatch.c
··· 376 376 put_device(&hid->dev); 377 377 } 378 378 379 + static int 380 + __hid_bpf_hw_check_params(struct hid_bpf_ctx *ctx, __u8 *buf, size_t *buf__sz, 381 + enum hid_report_type rtype) 382 + { 383 + struct hid_report_enum *report_enum; 384 + struct hid_report *report; 385 + struct hid_device *hdev; 386 + u32 report_len; 387 + 388 + /* check arguments */ 389 + if (!ctx || !hid_bpf_ops || !buf) 390 + return -EINVAL; 391 + 392 + switch (rtype) { 393 + case HID_INPUT_REPORT: 394 + case HID_OUTPUT_REPORT: 395 + case HID_FEATURE_REPORT: 396 + break; 397 + default: 398 + return -EINVAL; 399 + } 400 + 401 + if (*buf__sz < 1) 402 + return -EINVAL; 403 + 404 + hdev = (struct hid_device *)ctx->hid; /* discard const */ 405 + 406 + report_enum = hdev->report_enum + rtype; 407 + report = hid_bpf_ops->hid_get_report(report_enum, buf); 408 + if (!report) 409 + return -EINVAL; 410 + 411 + report_len = hid_report_len(report); 412 + 413 + if (*buf__sz > report_len) 414 + *buf__sz = report_len; 415 + 416 + return 0; 417 + } 418 + 379 419 /** 380 420 * hid_bpf_hw_request - Communicate with a HID device 381 421 * ··· 432 392 enum hid_report_type rtype, enum hid_class_request reqtype) 433 393 { 434 394 struct hid_device *hdev; 435 - struct hid_report *report; 436 - struct hid_report_enum *report_enum; 395 + size_t size = buf__sz; 437 396 u8 *dma_data; 438 - u32 report_len; 439 397 int ret; 440 398 441 399 /* check arguments */ 442 - if (!ctx || !hid_bpf_ops || !buf) 443 - return -EINVAL; 444 - 445 - switch (rtype) { 446 - case HID_INPUT_REPORT: 447 - case HID_OUTPUT_REPORT: 448 - case HID_FEATURE_REPORT: 449 - break; 450 - default: 451 - return -EINVAL; 452 - } 400 + ret = __hid_bpf_hw_check_params(ctx, buf, &size, rtype); 401 + if (ret) 402 + return ret; 453 403 454 404 switch (reqtype) { 455 405 case HID_REQ_GET_REPORT: ··· 453 423 return -EINVAL; 454 424 } 455 425 456 - if (buf__sz < 1) 457 - return -EINVAL; 458 - 459 426 hdev = (struct hid_device *)ctx->hid; /* discard const */ 460 427 461 - report_enum = hdev->report_enum + rtype; 462 - report = hid_bpf_ops->hid_get_report(report_enum, buf); 463 - if (!report) 464 - return -EINVAL; 465 - 466 - report_len = hid_report_len(report); 467 - 468 - if (buf__sz > report_len) 469 - buf__sz = report_len; 470 - 471 - dma_data = kmemdup(buf, buf__sz, GFP_KERNEL); 428 + dma_data = kmemdup(buf, size, GFP_KERNEL); 472 429 if (!dma_data) 473 430 return -ENOMEM; 474 431 475 432 ret = hid_bpf_ops->hid_hw_raw_request(hdev, 476 433 dma_data[0], 477 434 dma_data, 478 - buf__sz, 435 + size, 479 436 rtype, 480 437 reqtype); 481 438 482 439 if (ret > 0) 483 440 memcpy(buf, dma_data, ret); 441 + 442 + kfree(dma_data); 443 + return ret; 444 + } 445 + 446 + /** 447 + * hid_bpf_hw_output_report - Send an output report to a HID device 448 + * 449 + * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context() 450 + * @buf: a %PTR_TO_MEM buffer 451 + * @buf__sz: the size of the data to transfer 452 + * 453 + * Returns the number of bytes transferred on success, a negative error code otherwise. 454 + */ 455 + __bpf_kfunc int 456 + hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) 457 + { 458 + struct hid_device *hdev; 459 + size_t size = buf__sz; 460 + u8 *dma_data; 461 + int ret; 462 + 463 + /* check arguments */ 464 + ret = __hid_bpf_hw_check_params(ctx, buf, &size, HID_OUTPUT_REPORT); 465 + if (ret) 466 + return ret; 467 + 468 + hdev = (struct hid_device *)ctx->hid; /* discard const */ 469 + 470 + dma_data = kmemdup(buf, size, GFP_KERNEL); 471 + if (!dma_data) 472 + return -ENOMEM; 473 + 474 + ret = hid_bpf_ops->hid_hw_output_report(hdev, 475 + dma_data, 476 + size); 484 477 485 478 kfree(dma_data); 486 479 return ret; ··· 541 488 BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL) 542 489 BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE) 543 490 BTF_ID_FLAGS(func, hid_bpf_hw_request) 491 + BTF_ID_FLAGS(func, hid_bpf_hw_output_report) 544 492 BTF_KFUNCS_END(hid_bpf_syscall_kfunc_ids) 545 493 546 494 static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = {
+1
drivers/hid/hid-core.c
··· 2974 2974 static struct hid_bpf_ops hid_ops = { 2975 2975 .hid_get_report = hid_get_report, 2976 2976 .hid_hw_raw_request = hid_hw_raw_request, 2977 + .hid_hw_output_report = hid_hw_output_report, 2977 2978 .owner = THIS_MODULE, 2978 2979 .bus_type = &hid_bus_type, 2979 2980 };
+1
include/linux/hid_bpf.h
··· 103 103 unsigned char reportnum, __u8 *buf, 104 104 size_t len, enum hid_report_type rtype, 105 105 enum hid_class_request reqtype); 106 + int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len); 106 107 struct module *owner; 107 108 const struct bus_type *bus_type; 108 109 };