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