162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (c) 2022 Benjamin Tissoires
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This is a pure HID-BPF example, and should be considered as such:
562306a36Sopenharmony_ci * on the Etekcity Scroll 6E, the X and Y axes will be swapped and
662306a36Sopenharmony_ci * inverted. On any other device... Not sure what this will do.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This C main file is generic though. To adapt the code and test, users
962306a36Sopenharmony_ci * must amend only the .bpf.c file, which this program will load any
1062306a36Sopenharmony_ci * eBPF program it finds.
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <assert.h>
1462306a36Sopenharmony_ci#include <errno.h>
1562306a36Sopenharmony_ci#include <fcntl.h>
1662306a36Sopenharmony_ci#include <libgen.h>
1762306a36Sopenharmony_ci#include <signal.h>
1862306a36Sopenharmony_ci#include <stdbool.h>
1962306a36Sopenharmony_ci#include <stdio.h>
2062306a36Sopenharmony_ci#include <stdlib.h>
2162306a36Sopenharmony_ci#include <string.h>
2262306a36Sopenharmony_ci#include <sys/resource.h>
2362306a36Sopenharmony_ci#include <unistd.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/bpf.h>
2662306a36Sopenharmony_ci#include <linux/errno.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <bpf/bpf.h>
2962306a36Sopenharmony_ci#include <bpf/libbpf.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "hid_mouse.skel.h"
3262306a36Sopenharmony_ci#include "hid_bpf_attach.h"
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic bool running = true;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic void int_exit(int sig)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	running = false;
3962306a36Sopenharmony_ci	exit(0);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void usage(const char *prog)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	fprintf(stderr,
4562306a36Sopenharmony_ci		"%s: %s /sys/bus/hid/devices/0BUS:0VID:0PID:00ID\n\n",
4662306a36Sopenharmony_ci		__func__, prog);
4762306a36Sopenharmony_ci	fprintf(stderr,
4862306a36Sopenharmony_ci		"This program will upload and attach a HID-BPF program to the given device.\n"
4962306a36Sopenharmony_ci		"On the Etekcity Scroll 6E, the X and Y axis will be inverted, but on any other\n"
5062306a36Sopenharmony_ci		"device, chances are high that the device will not be working anymore\n\n"
5162306a36Sopenharmony_ci		"consider this as a demo and adapt the eBPF program to your needs\n"
5262306a36Sopenharmony_ci		"Hit Ctrl-C to unbind the program and reset the device\n");
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic int get_hid_id(const char *path)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	const char *str_id, *dir;
5862306a36Sopenharmony_ci	char uevent[1024];
5962306a36Sopenharmony_ci	int fd;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	memset(uevent, 0, sizeof(uevent));
6262306a36Sopenharmony_ci	snprintf(uevent, sizeof(uevent) - 1, "%s/uevent", path);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	fd = open(uevent, O_RDONLY | O_NONBLOCK);
6562306a36Sopenharmony_ci	if (fd < 0)
6662306a36Sopenharmony_ci		return -ENOENT;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	close(fd);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	dir = basename((char *)path);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	str_id = dir + sizeof("0003:0001:0A37.");
7362306a36Sopenharmony_ci	return (int)strtol(str_id, NULL, 16);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciint main(int argc, char **argv)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct hid_mouse *skel;
7962306a36Sopenharmony_ci	struct bpf_program *prog;
8062306a36Sopenharmony_ci	int err;
8162306a36Sopenharmony_ci	const char *optstr = "";
8262306a36Sopenharmony_ci	const char *sysfs_path;
8362306a36Sopenharmony_ci	int opt, hid_id, attach_fd;
8462306a36Sopenharmony_ci	struct attach_prog_args args = {
8562306a36Sopenharmony_ci		.retval = -1,
8662306a36Sopenharmony_ci	};
8762306a36Sopenharmony_ci	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattr,
8862306a36Sopenharmony_ci			    .ctx_in = &args,
8962306a36Sopenharmony_ci			    .ctx_size_in = sizeof(args),
9062306a36Sopenharmony_ci	);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	while ((opt = getopt(argc, argv, optstr)) != -1) {
9362306a36Sopenharmony_ci		switch (opt) {
9462306a36Sopenharmony_ci		default:
9562306a36Sopenharmony_ci			usage(basename(argv[0]));
9662306a36Sopenharmony_ci			return 1;
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (optind == argc) {
10162306a36Sopenharmony_ci		usage(basename(argv[0]));
10262306a36Sopenharmony_ci		return 1;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	sysfs_path = argv[optind];
10662306a36Sopenharmony_ci	if (!sysfs_path) {
10762306a36Sopenharmony_ci		perror("sysfs");
10862306a36Sopenharmony_ci		return 1;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	skel = hid_mouse__open_and_load();
11262306a36Sopenharmony_ci	if (!skel) {
11362306a36Sopenharmony_ci		fprintf(stderr, "%s  %s:%d", __func__, __FILE__, __LINE__);
11462306a36Sopenharmony_ci		return -1;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	hid_id = get_hid_id(sysfs_path);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (hid_id < 0) {
12062306a36Sopenharmony_ci		fprintf(stderr, "can not open HID device: %m\n");
12162306a36Sopenharmony_ci		return 1;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	args.hid = hid_id;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	attach_fd = bpf_program__fd(skel->progs.attach_prog);
12662306a36Sopenharmony_ci	if (attach_fd < 0) {
12762306a36Sopenharmony_ci		fprintf(stderr, "can't locate attach prog: %m\n");
12862306a36Sopenharmony_ci		return 1;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	bpf_object__for_each_program(prog, *skel->skeleton->obj) {
13262306a36Sopenharmony_ci		/* ignore syscalls */
13362306a36Sopenharmony_ci		if (bpf_program__get_type(prog) != BPF_PROG_TYPE_TRACING)
13462306a36Sopenharmony_ci			continue;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		args.retval = -1;
13762306a36Sopenharmony_ci		args.prog_fd = bpf_program__fd(prog);
13862306a36Sopenharmony_ci		err = bpf_prog_test_run_opts(attach_fd, &tattr);
13962306a36Sopenharmony_ci		if (err) {
14062306a36Sopenharmony_ci			fprintf(stderr, "can't attach prog to hid device %d: %m (err: %d)\n",
14162306a36Sopenharmony_ci				hid_id, err);
14262306a36Sopenharmony_ci			return 1;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	signal(SIGINT, int_exit);
14762306a36Sopenharmony_ci	signal(SIGTERM, int_exit);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	while (running)
15062306a36Sopenharmony_ci		sleep(1);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	hid_mouse__destroy(skel);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
156