1 // SPDX-License-Identifier: GPL-2.0
2 // test ir decoder
3 //
4 // Copyright (C) 2018 Sean Young <sean@mess.org>
5 
6 // A lirc chardev is a device representing a consumer IR (cir) device which
7 // can receive infrared signals from remote control and/or transmit IR.
8 //
9 // IR is sent as a series of pulses and space somewhat like morse code. The
10 // BPF program can decode this into scancodes so that rc-core can translate
11 // this into input key codes using the rc keymap.
12 //
13 // This test works by sending IR over rc-loopback, so the IR is processed by
14 // BPF and then decoded into scancodes. The lirc chardev must be the one
15 // associated with rc-loopback, see the output of ir-keytable(1).
16 //
17 // The following CONFIG options must be enabled for the test to succeed:
18 // CONFIG_RC_CORE=y
19 // CONFIG_BPF_RAWIR_EVENT=y
20 // CONFIG_RC_LOOPBACK=y
21 
22 // Steps:
23 // 1. Open the /dev/lircN device for rc-loopback (given on command line)
24 // 2. Attach bpf_lirc_mode2 program which decodes some IR.
25 // 3. Send some IR to the same IR device; since it is loopback, this will
26 //    end up in the bpf program
27 // 4. bpf program should decode IR and report keycode
28 // 5. We can read keycode from same /dev/lirc device
29 
30 #include <linux/bpf.h>
31 #include <linux/lirc.h>
32 #include <linux/input.h>
33 #include <errno.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <poll.h>
39 #include <sys/types.h>
40 #include <sys/ioctl.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 
44 #include "bpf_util.h"
45 #include <bpf/bpf.h>
46 #include <bpf/libbpf.h>
47 
48 #include "testing_helpers.h"
49 
50 int main(int argc, char **argv)
51 {
52 	struct bpf_object *obj;
53 	int ret, lircfd, progfd, inputfd;
54 	int testir1 = 0x1dead;
55 	int testir2 = 0x20101;
56 	u32 prog_ids[10], prog_flags[10], prog_cnt;
57 
58 	if (argc != 3) {
59 		printf("Usage: %s /dev/lircN /dev/input/eventM\n", argv[0]);
60 		return 2;
61 	}
62 
63 	ret = bpf_prog_test_load("test_lirc_mode2_kern.o",
64 				 BPF_PROG_TYPE_LIRC_MODE2, &obj, &progfd);
65 	if (ret) {
66 		printf("Failed to load bpf program\n");
67 		return 1;
68 	}
69 
70 	lircfd = open(argv[1], O_RDWR | O_NONBLOCK);
71 	if (lircfd == -1) {
72 		printf("failed to open lirc device %s: %m\n", argv[1]);
73 		return 1;
74 	}
75 
76 	/* Let's try detach it before it was ever attached */
77 	ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);
78 	if (ret != -1 || errno != ENOENT) {
79 		printf("bpf_prog_detach2 not attached should fail: %m\n");
80 		return 1;
81 	}
82 
83 	inputfd = open(argv[2], O_RDONLY | O_NONBLOCK);
84 	if (inputfd == -1) {
85 		printf("failed to open input device %s: %m\n", argv[1]);
86 		return 1;
87 	}
88 
89 	prog_cnt = 10;
90 	ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
91 			     &prog_cnt);
92 	if (ret) {
93 		printf("Failed to query bpf programs on lirc device: %m\n");
94 		return 1;
95 	}
96 
97 	if (prog_cnt != 0) {
98 		printf("Expected nothing to be attached\n");
99 		return 1;
100 	}
101 
102 	ret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 0);
103 	if (ret) {
104 		printf("Failed to attach bpf to lirc device: %m\n");
105 		return 1;
106 	}
107 
108 	/* Write raw IR */
109 	ret = write(lircfd, &testir1, sizeof(testir1));
110 	if (ret != sizeof(testir1)) {
111 		printf("Failed to send test IR message: %m\n");
112 		return 1;
113 	}
114 
115 	struct pollfd pfd = { .fd = inputfd, .events = POLLIN };
116 	struct input_event event;
117 
118 	for (;;) {
119 		poll(&pfd, 1, 100);
120 
121 		/* Read decoded IR */
122 		ret = read(inputfd, &event, sizeof(event));
123 		if (ret != sizeof(event)) {
124 			printf("Failed to read decoded IR: %m\n");
125 			return 1;
126 		}
127 
128 		if (event.type == EV_MSC && event.code == MSC_SCAN &&
129 		    event.value == 0xdead) {
130 			break;
131 		}
132 	}
133 
134 	/* Write raw IR */
135 	ret = write(lircfd, &testir2, sizeof(testir2));
136 	if (ret != sizeof(testir2)) {
137 		printf("Failed to send test IR message: %m\n");
138 		return 1;
139 	}
140 
141 	for (;;) {
142 		poll(&pfd, 1, 100);
143 
144 		/* Read decoded IR */
145 		ret = read(inputfd, &event, sizeof(event));
146 		if (ret != sizeof(event)) {
147 			printf("Failed to read decoded IR: %m\n");
148 			return 1;
149 		}
150 
151 		if (event.type == EV_REL && event.code == REL_Y &&
152 		    event.value == 1 ) {
153 			break;
154 		}
155 	}
156 
157 	prog_cnt = 10;
158 	ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids,
159 			     &prog_cnt);
160 	if (ret) {
161 		printf("Failed to query bpf programs on lirc device: %m\n");
162 		return 1;
163 	}
164 
165 	if (prog_cnt != 1) {
166 		printf("Expected one program to be attached\n");
167 		return 1;
168 	}
169 
170 	/* Let's try detaching it now it is actually attached */
171 	ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2);
172 	if (ret) {
173 		printf("bpf_prog_detach2: returned %m\n");
174 		return 1;
175 	}
176 
177 	return 0;
178 }
179