[PATCH bpf-next v2 18/28] selftests/bpf: hid: rely on uhid event to know if a test device is ready

From: Benjamin Tissoires
Date: Fri Mar 04 2022 - 12:34:34 EST


We need this for 2 reasons:
- first we remove the ugly sleeps
- then when we try to communicate with the device, we need to have another
thread that handles that communication and simulate a real device

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@xxxxxxxxxx>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
tools/testing/selftests/bpf/prog_tests/hid.c | 126 ++++++++++++++++++-
1 file changed, 120 insertions(+), 6 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c
index eb4c0d0a4666..cc01601c1168 100644
--- a/tools/testing/selftests/bpf/prog_tests/hid.c
+++ b/tools/testing/selftests/bpf/prog_tests/hid.c
@@ -67,6 +67,12 @@ static unsigned char rdesc[] = {
0xc0, /* END_COLLECTION */
};

+static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;
+
+/* no need to protect uhid_stopped, only one thread accesses it */
+static bool uhid_stopped;
+
static int uhid_write(int fd, const struct uhid_event *ev)
{
ssize_t ret;
@@ -118,6 +124,104 @@ static void destroy(int fd)
uhid_write(fd, &ev);
}

+static int event(int fd)
+{
+ struct uhid_event ev;
+ ssize_t ret;
+
+ memset(&ev, 0, sizeof(ev));
+ ret = read(fd, &ev, sizeof(ev));
+ if (ret == 0) {
+ fprintf(stderr, "Read HUP on uhid-cdev\n");
+ return -EFAULT;
+ } else if (ret < 0) {
+ fprintf(stderr, "Cannot read uhid-cdev: %m\n");
+ return -errno;
+ } else if (ret != sizeof(ev)) {
+ fprintf(stderr, "Invalid size read from uhid-dev: %zd != %zu\n",
+ ret, sizeof(ev));
+ return -EFAULT;
+ }
+
+ switch (ev.type) {
+ case UHID_START:
+ pthread_mutex_lock(&uhid_started_mtx);
+ pthread_cond_signal(&uhid_started);
+ pthread_mutex_unlock(&uhid_started_mtx);
+
+ fprintf(stderr, "UHID_START from uhid-dev\n");
+ break;
+ case UHID_STOP:
+ uhid_stopped = true;
+
+ fprintf(stderr, "UHID_STOP from uhid-dev\n");
+ break;
+ case UHID_OPEN:
+ fprintf(stderr, "UHID_OPEN from uhid-dev\n");
+ break;
+ case UHID_CLOSE:
+ fprintf(stderr, "UHID_CLOSE from uhid-dev\n");
+ break;
+ case UHID_OUTPUT:
+ fprintf(stderr, "UHID_OUTPUT from uhid-dev\n");
+ break;
+ case UHID_GET_REPORT:
+ fprintf(stderr, "UHID_GET_REPORT from uhid-dev\n");
+ break;
+ case UHID_SET_REPORT:
+ fprintf(stderr, "UHID_SET_REPORT from uhid-dev\n");
+ break;
+ default:
+ fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type);
+ }
+
+ return 0;
+}
+
+static void *read_uhid_events_thread(void *arg)
+{
+ int fd = *(int *)arg;
+ struct pollfd pfds[1];
+ int ret = 0;
+
+ pfds[0].fd = fd;
+ pfds[0].events = POLLIN;
+
+ uhid_stopped = false;
+
+ while (!uhid_stopped) {
+ ret = poll(pfds, 1, 100);
+ if (ret < 0) {
+ fprintf(stderr, "Cannot poll for fds: %m\n");
+ break;
+ }
+ if (pfds[0].revents & POLLIN) {
+ ret = event(fd);
+ if (ret)
+ break;
+ }
+ }
+
+ return (void *)(long)ret;
+}
+
+static int uhid_start_listener(pthread_t *tid, int uhid_fd)
+{
+ int fd = uhid_fd;
+
+ pthread_mutex_lock(&uhid_started_mtx);
+ if (CHECK_FAIL(pthread_create(tid, NULL, read_uhid_events_thread,
+ (void *)&fd))) {
+ pthread_mutex_unlock(&uhid_started_mtx);
+ close(fd);
+ return -EIO;
+ }
+ pthread_cond_wait(&uhid_started, &uhid_started_mtx);
+ pthread_mutex_unlock(&uhid_started_mtx);
+
+ return 0;
+}
+
static int send_event(int fd, u8 *buf, size_t size)
{
struct uhid_event ev;
@@ -422,7 +526,9 @@ static int test_rdesc_fixup(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
struct hidraw_report_descriptor rpt_desc = {0};
int err, desc_size, hidraw_ino, hidraw_fd = -1;
char hidraw_path[64] = {0};
+ void *uhid_err;
int ret = -1;
+ pthread_t tid;

/* attach the program */
hid_skel->links.hid_rdesc_fixup =
@@ -431,9 +537,8 @@ static int test_rdesc_fixup(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
"attach_hid(hid_rdesc_fixup)"))
return PTR_ERR(hid_skel->links.hid_rdesc_fixup);

- /* give a little bit of time for the device to appear */
- /* TODO: check on uhid events */
- usleep(1000);
+ err = uhid_start_listener(&tid, uhid_fd);
+ ASSERT_OK(err, "uhid_start_listener");

hidraw_ino = get_hidraw(hid_skel->links.hid_rdesc_fixup);
if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw"))
@@ -474,6 +579,10 @@ static int test_rdesc_fixup(struct hid *hid_skel, int uhid_fd, int sysfs_fd)

hid__detach(hid_skel);

+ pthread_join(tid, &uhid_err);
+ err = (int)(long)uhid_err;
+ CHECK_FAIL(err);
+
return ret;
}

@@ -481,7 +590,9 @@ void serial_test_hid_bpf(void)
{
struct hid *hid_skel = NULL;
int err, uhid_fd, sysfs_fd;
+ void *uhid_err;
time_t t;
+ pthread_t tid;
int rand_nb;

/* initialize random number generator */
@@ -493,9 +604,8 @@ void serial_test_hid_bpf(void)
if (!ASSERT_GE(uhid_fd, 0, "setup uhid"))
return;

- /* give a little bit of time for the device to appear */
- /* TODO: check on uhid events */
- usleep(1000);
+ err = uhid_start_listener(&tid, uhid_fd);
+ ASSERT_OK(err, "uhid_start_listener");

/* locate the uevent file of the created device */
sysfs_fd = get_sysfs_fd(rand_nb);
@@ -522,4 +632,8 @@ void serial_test_hid_bpf(void)
cleanup:
hid__destroy(hid_skel);
destroy(uhid_fd);
+
+ pthread_join(tid, &uhid_err);
+ err = (int)(long)uhid_err;
+ CHECK_FAIL(err);
}
--
2.35.1