xref: /openbmc/linux/tools/testing/selftests/bpf/test_lirc_mode2_user.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
16bdd533cSSean Young // SPDX-License-Identifier: GPL-2.0
26bdd533cSSean Young // test ir decoder
36bdd533cSSean Young //
46bdd533cSSean Young // Copyright (C) 2018 Sean Young <sean@mess.org>
56bdd533cSSean Young 
66bdd533cSSean Young // A lirc chardev is a device representing a consumer IR (cir) device which
76bdd533cSSean Young // can receive infrared signals from remote control and/or transmit IR.
86bdd533cSSean Young //
96bdd533cSSean Young // IR is sent as a series of pulses and space somewhat like morse code. The
106bdd533cSSean Young // BPF program can decode this into scancodes so that rc-core can translate
116bdd533cSSean Young // this into input key codes using the rc keymap.
126bdd533cSSean Young //
136bdd533cSSean Young // This test works by sending IR over rc-loopback, so the IR is processed by
146bdd533cSSean Young // BPF and then decoded into scancodes. The lirc chardev must be the one
156bdd533cSSean Young // associated with rc-loopback, see the output of ir-keytable(1).
166bdd533cSSean Young //
176bdd533cSSean Young // The following CONFIG options must be enabled for the test to succeed:
186bdd533cSSean Young // CONFIG_RC_CORE=y
196bdd533cSSean Young // CONFIG_BPF_RAWIR_EVENT=y
206bdd533cSSean Young // CONFIG_RC_LOOPBACK=y
216bdd533cSSean Young 
226bdd533cSSean Young // Steps:
236bdd533cSSean Young // 1. Open the /dev/lircN device for rc-loopback (given on command line)
246bdd533cSSean Young // 2. Attach bpf_lirc_mode2 program which decodes some IR.
256bdd533cSSean Young // 3. Send some IR to the same IR device; since it is loopback, this will
266bdd533cSSean Young //    end up in the bpf program
276bdd533cSSean Young // 4. bpf program should decode IR and report keycode
286bdd533cSSean Young // 5. We can read keycode from same /dev/lirc device
296bdd533cSSean Young 
306bdd533cSSean Young #include <linux/bpf.h>
3101d3240aSSean Young #include <linux/input.h>
326bdd533cSSean Young #include <errno.h>
336bdd533cSSean Young #include <stdio.h>
346bdd533cSSean Young #include <stdlib.h>
356bdd533cSSean Young #include <string.h>
366bdd533cSSean Young #include <unistd.h>
376bdd533cSSean Young #include <poll.h>
386bdd533cSSean Young #include <sys/types.h>
396bdd533cSSean Young #include <sys/ioctl.h>
406bdd533cSSean Young #include <sys/stat.h>
416bdd533cSSean Young #include <fcntl.h>
426bdd533cSSean Young 
436bdd533cSSean Young #include "bpf_util.h"
446bdd533cSSean Young #include <bpf/bpf.h>
456bdd533cSSean Young #include <bpf/libbpf.h>
466bdd533cSSean Young 
47cbdb1461SAndrii Nakryiko #include "testing_helpers.h"
48cbdb1461SAndrii Nakryiko 
main(int argc,char ** argv)496bdd533cSSean Young int main(int argc, char **argv)
506bdd533cSSean Young {
516bdd533cSSean Young 	struct bpf_object *obj;
5201d3240aSSean Young 	int ret, lircfd, progfd, inputfd;
5301d3240aSSean Young 	int testir1 = 0x1dead;
5401d3240aSSean Young 	int testir2 = 0x20101;
556bdd533cSSean Young 	u32 prog_ids[10], prog_flags[10], prog_cnt;
566bdd533cSSean Young 
5701d3240aSSean Young 	if (argc != 3) {
5801d3240aSSean Young 		printf("Usage: %s /dev/lircN /dev/input/eventM\n", argv[0]);
596bdd533cSSean Young 		return 2;
606bdd533cSSean Young 	}
616bdd533cSSean Young 
62*afef88e6SDaniel Müller 	ret = bpf_prog_test_load("test_lirc_mode2_kern.bpf.o",
636bdd533cSSean Young 				 BPF_PROG_TYPE_LIRC_MODE2, &obj, &progfd);
646bdd533cSSean Young 	if (ret) {
656bdd533cSSean Young 		printf("Failed to load bpf program\n");
666bdd533cSSean Young 		return 1;
676bdd533cSSean Young 	}
686bdd533cSSean Young 
696bdd533cSSean Young 	lircfd = open(argv[1], O_RDWR | O_NONBLOCK);
706bdd533cSSean Young 	if (lircfd == -1) {
716bdd533cSSean Young 		printf("failed to open lirc device %s: %m\n", argv[1]);
726bdd533cSSean Young 		return 1;
736bdd533cSSean Young 	}
746bdd533cSSean Young 
756bdd533cSSean Young 	/* Let's try detach it before it was ever attached */
766bdd533cSSean Young 	ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);
776bdd533cSSean Young 	if (ret != -1 || errno != ENOENT) {
786bdd533cSSean Young 		printf("bpf_prog_detach2 not attached should fail: %m\n");
796bdd533cSSean Young 		return 1;
806bdd533cSSean Young 	}
816bdd533cSSean Young 
8201d3240aSSean Young 	inputfd = open(argv[2], O_RDONLY | O_NONBLOCK);
8301d3240aSSean Young 	if (inputfd == -1) {
8401d3240aSSean Young 		printf("failed to open input device %s: %m\n", argv[1]);
856bdd533cSSean Young 		return 1;
866bdd533cSSean Young 	}
876bdd533cSSean Young 
886bdd533cSSean Young 	prog_cnt = 10;
896bdd533cSSean Young 	ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
906bdd533cSSean Young 			     &prog_cnt);
916bdd533cSSean Young 	if (ret) {
926bdd533cSSean Young 		printf("Failed to query bpf programs on lirc device: %m\n");
936bdd533cSSean Young 		return 1;
946bdd533cSSean Young 	}
956bdd533cSSean Young 
966bdd533cSSean Young 	if (prog_cnt != 0) {
976bdd533cSSean Young 		printf("Expected nothing to be attached\n");
986bdd533cSSean Young 		return 1;
996bdd533cSSean Young 	}
1006bdd533cSSean Young 
1016bdd533cSSean Young 	ret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 0);
1026bdd533cSSean Young 	if (ret) {
1036bdd533cSSean Young 		printf("Failed to attach bpf to lirc device: %m\n");
1046bdd533cSSean Young 		return 1;
1056bdd533cSSean Young 	}
1066bdd533cSSean Young 
1076bdd533cSSean Young 	/* Write raw IR */
10801d3240aSSean Young 	ret = write(lircfd, &testir1, sizeof(testir1));
10901d3240aSSean Young 	if (ret != sizeof(testir1)) {
1106bdd533cSSean Young 		printf("Failed to send test IR message: %m\n");
1116bdd533cSSean Young 		return 1;
1126bdd533cSSean Young 	}
1136bdd533cSSean Young 
11401d3240aSSean Young 	struct pollfd pfd = { .fd = inputfd, .events = POLLIN };
11501d3240aSSean Young 	struct input_event event;
1166bdd533cSSean Young 
11701d3240aSSean Young 	for (;;) {
1186bdd533cSSean Young 		poll(&pfd, 1, 100);
1196bdd533cSSean Young 
1206bdd533cSSean Young 		/* Read decoded IR */
12101d3240aSSean Young 		ret = read(inputfd, &event, sizeof(event));
12201d3240aSSean Young 		if (ret != sizeof(event)) {
1236bdd533cSSean Young 			printf("Failed to read decoded IR: %m\n");
1246bdd533cSSean Young 			return 1;
1256bdd533cSSean Young 		}
1266bdd533cSSean Young 
12701d3240aSSean Young 		if (event.type == EV_MSC && event.code == MSC_SCAN &&
12801d3240aSSean Young 		    event.value == 0xdead) {
12901d3240aSSean Young 			break;
13001d3240aSSean Young 		}
13101d3240aSSean Young 	}
13201d3240aSSean Young 
13301d3240aSSean Young 	/* Write raw IR */
13401d3240aSSean Young 	ret = write(lircfd, &testir2, sizeof(testir2));
13501d3240aSSean Young 	if (ret != sizeof(testir2)) {
13601d3240aSSean Young 		printf("Failed to send test IR message: %m\n");
1376bdd533cSSean Young 		return 1;
1386bdd533cSSean Young 	}
1396bdd533cSSean Young 
14001d3240aSSean Young 	for (;;) {
14101d3240aSSean Young 		poll(&pfd, 1, 100);
14201d3240aSSean Young 
14301d3240aSSean Young 		/* Read decoded IR */
14401d3240aSSean Young 		ret = read(inputfd, &event, sizeof(event));
14501d3240aSSean Young 		if (ret != sizeof(event)) {
14601d3240aSSean Young 			printf("Failed to read decoded IR: %m\n");
14701d3240aSSean Young 			return 1;
14801d3240aSSean Young 		}
14901d3240aSSean Young 
15001d3240aSSean Young 		if (event.type == EV_REL && event.code == REL_Y &&
15101d3240aSSean Young 		    event.value == 1 ) {
15201d3240aSSean Young 			break;
15301d3240aSSean Young 		}
15401d3240aSSean Young 	}
15501d3240aSSean Young 
1566bdd533cSSean Young 	prog_cnt = 10;
1576bdd533cSSean Young 	ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
1586bdd533cSSean Young 			     &prog_cnt);
1596bdd533cSSean Young 	if (ret) {
1606bdd533cSSean Young 		printf("Failed to query bpf programs on lirc device: %m\n");
1616bdd533cSSean Young 		return 1;
1626bdd533cSSean Young 	}
1636bdd533cSSean Young 
1646bdd533cSSean Young 	if (prog_cnt != 1) {
1656bdd533cSSean Young 		printf("Expected one program to be attached\n");
1666bdd533cSSean Young 		return 1;
1676bdd533cSSean Young 	}
1686bdd533cSSean Young 
1696bdd533cSSean Young 	/* Let's try detaching it now it is actually attached */
1706bdd533cSSean Young 	ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);
1716bdd533cSSean Young 	if (ret) {
1726bdd533cSSean Young 		printf("bpf_prog_detach2: returned %m\n");
1736bdd533cSSean Young 		return 1;
1746bdd533cSSean Young 	}
1756bdd533cSSean Young 
1766bdd533cSSean Young 	return 0;
1776bdd533cSSean Young }
178