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/input.h> |
32 | #include <errno.h> |
33 | #include <stdio.h> |
34 | #include <stdlib.h> |
35 | #include <string.h> |
36 | #include <unistd.h> |
37 | #include <poll.h> |
38 | #include <sys/types.h> |
39 | #include <sys/ioctl.h> |
40 | #include <sys/stat.h> |
41 | #include <fcntl.h> |
42 | |
43 | #include "bpf_util.h" |
44 | #include <bpf/bpf.h> |
45 | #include <bpf/libbpf.h> |
46 | |
47 | #include "testing_helpers.h" |
48 | |
49 | int main(int argc, char **argv) |
50 | { |
51 | struct bpf_object *obj; |
52 | int ret, lircfd, progfd, inputfd; |
53 | int testir1 = 0x1dead; |
54 | int testir2 = 0x20101; |
55 | u32 prog_ids[10], prog_flags[10], prog_cnt; |
56 | |
57 | if (argc != 3) { |
58 | printf("Usage: %s /dev/lircN /dev/input/eventM\n" , argv[0]); |
59 | return 2; |
60 | } |
61 | |
62 | ret = bpf_prog_test_load(file: "test_lirc_mode2_kern.bpf.o" , |
63 | type: BPF_PROG_TYPE_LIRC_MODE2, pobj: &obj, prog_fd: &progfd); |
64 | if (ret) { |
65 | printf("Failed to load bpf program\n" ); |
66 | return 1; |
67 | } |
68 | |
69 | lircfd = open(argv[1], O_RDWR | O_NONBLOCK); |
70 | if (lircfd == -1) { |
71 | printf("failed to open lirc device %s: %m\n" , argv[1]); |
72 | return 1; |
73 | } |
74 | |
75 | /* Let's try detach it before it was ever attached */ |
76 | ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2); |
77 | if (ret != -1 || errno != ENOENT) { |
78 | printf("bpf_prog_detach2 not attached should fail: %m\n" ); |
79 | return 1; |
80 | } |
81 | |
82 | inputfd = open(argv[2], O_RDONLY | O_NONBLOCK); |
83 | if (inputfd == -1) { |
84 | printf("failed to open input device %s: %m\n" , argv[1]); |
85 | return 1; |
86 | } |
87 | |
88 | prog_cnt = 10; |
89 | ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids, |
90 | &prog_cnt); |
91 | if (ret) { |
92 | printf("Failed to query bpf programs on lirc device: %m\n" ); |
93 | return 1; |
94 | } |
95 | |
96 | if (prog_cnt != 0) { |
97 | printf("Expected nothing to be attached\n" ); |
98 | return 1; |
99 | } |
100 | |
101 | ret = bpf_prog_attach(progfd, lircfd, BPF_LIRC_MODE2, 0); |
102 | if (ret) { |
103 | printf("Failed to attach bpf to lirc device: %m\n" ); |
104 | return 1; |
105 | } |
106 | |
107 | /* Write raw IR */ |
108 | ret = write(lircfd, &testir1, sizeof(testir1)); |
109 | if (ret != sizeof(testir1)) { |
110 | printf("Failed to send test IR message: %m\n" ); |
111 | return 1; |
112 | } |
113 | |
114 | struct pollfd pfd = { .fd = inputfd, .events = POLLIN }; |
115 | struct input_event event; |
116 | |
117 | for (;;) { |
118 | poll(&pfd, 1, 100); |
119 | |
120 | /* Read decoded IR */ |
121 | ret = read(inputfd, &event, sizeof(event)); |
122 | if (ret != sizeof(event)) { |
123 | printf("Failed to read decoded IR: %m\n" ); |
124 | return 1; |
125 | } |
126 | |
127 | if (event.type == EV_MSC && event.code == MSC_SCAN && |
128 | event.value == 0xdead) { |
129 | break; |
130 | } |
131 | } |
132 | |
133 | /* Write raw IR */ |
134 | ret = write(lircfd, &testir2, sizeof(testir2)); |
135 | if (ret != sizeof(testir2)) { |
136 | printf("Failed to send test IR message: %m\n" ); |
137 | return 1; |
138 | } |
139 | |
140 | for (;;) { |
141 | poll(&pfd, 1, 100); |
142 | |
143 | /* Read decoded IR */ |
144 | ret = read(inputfd, &event, sizeof(event)); |
145 | if (ret != sizeof(event)) { |
146 | printf("Failed to read decoded IR: %m\n" ); |
147 | return 1; |
148 | } |
149 | |
150 | if (event.type == EV_REL && event.code == REL_Y && |
151 | event.value == 1 ) { |
152 | break; |
153 | } |
154 | } |
155 | |
156 | prog_cnt = 10; |
157 | ret = bpf_prog_query(lircfd, BPF_LIRC_MODE2, 0, prog_flags, prog_ids, |
158 | &prog_cnt); |
159 | if (ret) { |
160 | printf("Failed to query bpf programs on lirc device: %m\n" ); |
161 | return 1; |
162 | } |
163 | |
164 | if (prog_cnt != 1) { |
165 | printf("Expected one program to be attached\n" ); |
166 | return 1; |
167 | } |
168 | |
169 | /* Let's try detaching it now it is actually attached */ |
170 | ret = bpf_prog_detach2(progfd, lircfd, BPF_LIRC_MODE2); |
171 | if (ret) { |
172 | printf("bpf_prog_detach2: returned %m\n" ); |
173 | return 1; |
174 | } |
175 | |
176 | return 0; |
177 | } |
178 | |