1e55c884eSSean Young // SPDX-License-Identifier: GPL-2.0
2e55c884eSSean Young // test ir decoder
3e55c884eSSean Young //
4e55c884eSSean Young // Copyright (C) 2018 Sean Young <sean@mess.org>
5e55c884eSSean Young
6e55c884eSSean Young // When sending LIRC_MODE_SCANCODE, the IR will be encoded. rc-loopback
7e55c884eSSean Young // will send this IR to the receiver side, where we try to read the decoded
8e55c884eSSean Young // IR. Decoding happens in a separate kernel thread, so we will need to
9e55c884eSSean Young // wait until that is scheduled, hence we use poll to check for read
10e55c884eSSean Young // readiness.
11e55c884eSSean Young
12e55c884eSSean Young #include <linux/lirc.h>
13e55c884eSSean Young #include <errno.h>
14e55c884eSSean Young #include <stdio.h>
15e55c884eSSean Young #include <stdlib.h>
16e55c884eSSean Young #include <stdbool.h>
17e55c884eSSean Young #include <string.h>
18e55c884eSSean Young #include <unistd.h>
19e55c884eSSean Young #include <poll.h>
20e55c884eSSean Young #include <time.h>
21e55c884eSSean Young #include <sys/types.h>
22e55c884eSSean Young #include <sys/ioctl.h>
23e55c884eSSean Young #include <dirent.h>
24e55c884eSSean Young #include <sys/stat.h>
25e55c884eSSean Young #include <fcntl.h>
26e55c884eSSean Young #include "../kselftest.h"
27e55c884eSSean Young
28e55c884eSSean Young #define TEST_SCANCODES 10
29ed675ed9SShuah Khan #define SYSFS_PATH_MAX 256
30ed675ed9SShuah Khan #define DNAME_PATH_MAX 256
31e55c884eSSean Young
32*183f80fdSSean Young /*
33*183f80fdSSean Young * Support ancient lirc.h which does not have these values. Can be removed
34*183f80fdSSean Young * once RHEL 8 is no longer a relevant testing platform.
35*183f80fdSSean Young */
36*183f80fdSSean Young #if RC_PROTO_MAX < 26
37*183f80fdSSean Young #define RC_PROTO_RCMM12 24
38*183f80fdSSean Young #define RC_PROTO_RCMM24 25
39*183f80fdSSean Young #define RC_PROTO_RCMM32 26
40*183f80fdSSean Young #endif
41*183f80fdSSean Young
42e55c884eSSean Young static const struct {
43e55c884eSSean Young enum rc_proto proto;
44e55c884eSSean Young const char *name;
45e55c884eSSean Young unsigned int mask;
46e55c884eSSean Young const char *decoder;
47e55c884eSSean Young } protocols[] = {
48e55c884eSSean Young { RC_PROTO_RC5, "rc-5", 0x1f7f, "rc-5" },
49e55c884eSSean Young { RC_PROTO_RC5X_20, "rc-5x-20", 0x1f7f3f, "rc-5" },
50e55c884eSSean Young { RC_PROTO_RC5_SZ, "rc-5-sz", 0x2fff, "rc-5-sz" },
51e55c884eSSean Young { RC_PROTO_JVC, "jvc", 0xffff, "jvc" },
52e55c884eSSean Young { RC_PROTO_SONY12, "sony-12", 0x1f007f, "sony" },
53e55c884eSSean Young { RC_PROTO_SONY15, "sony-15", 0xff007f, "sony" },
54e55c884eSSean Young { RC_PROTO_SONY20, "sony-20", 0x1fff7f, "sony" },
55e55c884eSSean Young { RC_PROTO_NEC, "nec", 0xffff, "nec" },
56e55c884eSSean Young { RC_PROTO_NECX, "nec-x", 0xffffff, "nec" },
57e55c884eSSean Young { RC_PROTO_NEC32, "nec-32", 0xffffffff, "nec" },
58e55c884eSSean Young { RC_PROTO_SANYO, "sanyo", 0x1fffff, "sanyo" },
59e55c884eSSean Young { RC_PROTO_RC6_0, "rc-6-0", 0xffff, "rc-6" },
60e55c884eSSean Young { RC_PROTO_RC6_6A_20, "rc-6-6a-20", 0xfffff, "rc-6" },
61e55c884eSSean Young { RC_PROTO_RC6_6A_24, "rc-6-6a-24", 0xffffff, "rc-6" },
62e55c884eSSean Young { RC_PROTO_RC6_6A_32, "rc-6-6a-32", 0xffffffff, "rc-6" },
63e55c884eSSean Young { RC_PROTO_RC6_MCE, "rc-6-mce", 0x00007fff, "rc-6" },
64e55c884eSSean Young { RC_PROTO_SHARP, "sharp", 0x1fff, "sharp" },
65721074b0SPatrick Lerda { RC_PROTO_IMON, "imon", 0x7fffffff, "imon" },
66f1409116SSean Young { RC_PROTO_RCMM12, "rcmm-12", 0x00000fff, "rc-mm" },
67f1409116SSean Young { RC_PROTO_RCMM24, "rcmm-24", 0x00ffffff, "rc-mm" },
68f1409116SSean Young { RC_PROTO_RCMM32, "rcmm-32", 0xffffffff, "rc-mm" },
69e55c884eSSean Young };
70e55c884eSSean Young
lirc_open(const char * rc)71e55c884eSSean Young int lirc_open(const char *rc)
72e55c884eSSean Young {
73e55c884eSSean Young struct dirent *dent;
74ed675ed9SShuah Khan char buf[SYSFS_PATH_MAX + DNAME_PATH_MAX];
75e55c884eSSean Young DIR *d;
76e55c884eSSean Young int fd;
77e55c884eSSean Young
78e55c884eSSean Young snprintf(buf, sizeof(buf), "/sys/class/rc/%s", rc);
79e55c884eSSean Young
80e55c884eSSean Young d = opendir(buf);
81e55c884eSSean Young if (!d)
82e55c884eSSean Young ksft_exit_fail_msg("cannot open %s: %m\n", buf);
83e55c884eSSean Young
84e55c884eSSean Young while ((dent = readdir(d)) != NULL) {
85e55c884eSSean Young if (!strncmp(dent->d_name, "lirc", 4)) {
86e55c884eSSean Young snprintf(buf, sizeof(buf), "/dev/%s", dent->d_name);
87e55c884eSSean Young break;
88e55c884eSSean Young }
89e55c884eSSean Young }
90e55c884eSSean Young
91e55c884eSSean Young if (!dent)
92a5180977SShuah Khan ksft_exit_skip("cannot find lirc device for %s\n", rc);
93e55c884eSSean Young
94e55c884eSSean Young closedir(d);
95e55c884eSSean Young
96e55c884eSSean Young fd = open(buf, O_RDWR | O_NONBLOCK);
97e55c884eSSean Young if (fd == -1)
98e55c884eSSean Young ksft_exit_fail_msg("cannot open: %s: %m\n", buf);
99e55c884eSSean Young
100e55c884eSSean Young return fd;
101e55c884eSSean Young }
102e55c884eSSean Young
main(int argc,char ** argv)103e55c884eSSean Young int main(int argc, char **argv)
104e55c884eSSean Young {
105e55c884eSSean Young unsigned int mode;
106e55c884eSSean Young char buf[100];
107e55c884eSSean Young int rlircfd, wlircfd, protocolfd, i, n;
108e55c884eSSean Young
109e55c884eSSean Young srand(time(NULL));
110e55c884eSSean Young
111e55c884eSSean Young if (argc != 3)
112e55c884eSSean Young ksft_exit_fail_msg("Usage: %s <write rcN> <read rcN>\n",
113e55c884eSSean Young argv[0]);
114e55c884eSSean Young
115e55c884eSSean Young rlircfd = lirc_open(argv[2]);
116e55c884eSSean Young mode = LIRC_MODE_SCANCODE;
117e55c884eSSean Young if (ioctl(rlircfd, LIRC_SET_REC_MODE, &mode))
118e55c884eSSean Young ksft_exit_fail_msg("failed to set scancode rec mode %s: %m\n",
119e55c884eSSean Young argv[2]);
120e55c884eSSean Young
121e55c884eSSean Young wlircfd = lirc_open(argv[1]);
122e55c884eSSean Young if (ioctl(wlircfd, LIRC_SET_SEND_MODE, &mode))
123e55c884eSSean Young ksft_exit_fail_msg("failed to set scancode send mode %s: %m\n",
124e55c884eSSean Young argv[1]);
125e55c884eSSean Young
126e55c884eSSean Young snprintf(buf, sizeof(buf), "/sys/class/rc/%s/protocols", argv[2]);
127e55c884eSSean Young protocolfd = open(buf, O_WRONLY);
128e55c884eSSean Young if (protocolfd == -1)
129e55c884eSSean Young ksft_exit_fail_msg("failed to open %s: %m\n", buf);
130e55c884eSSean Young
131e55c884eSSean Young printf("Sending IR on %s and receiving IR on %s.\n", argv[1], argv[2]);
132e55c884eSSean Young
133e55c884eSSean Young for (i = 0; i < ARRAY_SIZE(protocols); i++) {
134e55c884eSSean Young if (write(protocolfd, protocols[i].decoder,
135e55c884eSSean Young strlen(protocols[i].decoder)) == -1)
136e55c884eSSean Young ksft_exit_fail_msg("failed to set write decoder\n");
137e55c884eSSean Young
138e55c884eSSean Young printf("Testing protocol %s for decoder %s (%d/%d)...\n",
139e55c884eSSean Young protocols[i].name, protocols[i].decoder,
140e55c884eSSean Young i + 1, (int)ARRAY_SIZE(protocols));
141e55c884eSSean Young
142e55c884eSSean Young for (n = 0; n < TEST_SCANCODES; n++) {
143e55c884eSSean Young unsigned int scancode = rand() & protocols[i].mask;
144e55c884eSSean Young unsigned int rc_proto = protocols[i].proto;
145e55c884eSSean Young
146e55c884eSSean Young if (rc_proto == RC_PROTO_RC6_MCE)
147e55c884eSSean Young scancode |= 0x800f0000;
148e55c884eSSean Young
149e55c884eSSean Young if (rc_proto == RC_PROTO_NECX &&
150e55c884eSSean Young (((scancode >> 16) ^ ~(scancode >> 8)) & 0xff) == 0)
151e55c884eSSean Young continue;
152e55c884eSSean Young
153e55c884eSSean Young if (rc_proto == RC_PROTO_NEC32 &&
154e55c884eSSean Young (((scancode >> 8) ^ ~scancode) & 0xff) == 0)
155e55c884eSSean Young continue;
156e55c884eSSean Young
157721074b0SPatrick Lerda if (rc_proto == RC_PROTO_RCMM32 &&
158721074b0SPatrick Lerda (scancode & 0x000c0000) != 0x000c0000 &&
159721074b0SPatrick Lerda scancode & 0x00008000)
160721074b0SPatrick Lerda continue;
161721074b0SPatrick Lerda
162e55c884eSSean Young struct lirc_scancode lsc = {
163e55c884eSSean Young .rc_proto = rc_proto,
164e55c884eSSean Young .scancode = scancode
165e55c884eSSean Young };
166e55c884eSSean Young
167e55c884eSSean Young printf("Testing scancode:%x\n", scancode);
168e55c884eSSean Young
169e55c884eSSean Young while (write(wlircfd, &lsc, sizeof(lsc)) < 0) {
170e55c884eSSean Young if (errno == EINTR)
171e55c884eSSean Young continue;
172e55c884eSSean Young
173e55c884eSSean Young ksft_exit_fail_msg("failed to send ir: %m\n");
174e55c884eSSean Young }
175e55c884eSSean Young
176e55c884eSSean Young struct pollfd pfd = { .fd = rlircfd, .events = POLLIN };
177e55c884eSSean Young struct lirc_scancode lsc2;
178e55c884eSSean Young
179e55c884eSSean Young poll(&pfd, 1, 1000);
180e55c884eSSean Young
181e55c884eSSean Young bool decoded = true;
182e55c884eSSean Young
183e55c884eSSean Young while (read(rlircfd, &lsc2, sizeof(lsc2)) < 0) {
184e55c884eSSean Young if (errno == EINTR)
185e55c884eSSean Young continue;
186e55c884eSSean Young
187e55c884eSSean Young ksft_test_result_error("no scancode decoded: %m\n");
188e55c884eSSean Young decoded = false;
189e55c884eSSean Young break;
190e55c884eSSean Young }
191e55c884eSSean Young
192e55c884eSSean Young if (!decoded)
193e55c884eSSean Young continue;
194e55c884eSSean Young
195e55c884eSSean Young if (lsc.rc_proto != lsc2.rc_proto)
196e55c884eSSean Young ksft_test_result_error("decoded protocol is different: %d\n",
197e55c884eSSean Young lsc2.rc_proto);
198e55c884eSSean Young
199e55c884eSSean Young else if (lsc.scancode != lsc2.scancode)
200e55c884eSSean Young ksft_test_result_error("decoded scancode is different: %llx\n",
201e55c884eSSean Young lsc2.scancode);
202e55c884eSSean Young else
203e55c884eSSean Young ksft_inc_pass_cnt();
204e55c884eSSean Young }
205e55c884eSSean Young
206e55c884eSSean Young printf("OK\n");
207e55c884eSSean Young }
208e55c884eSSean Young
209e55c884eSSean Young close(rlircfd);
210e55c884eSSean Young close(wlircfd);
211e55c884eSSean Young close(protocolfd);
212e55c884eSSean Young
213e55c884eSSean Young if (ksft_get_fail_cnt() > 0)
214e55c884eSSean Young ksft_exit_fail();
215e55c884eSSean Young else
216e55c884eSSean Young ksft_exit_pass();
217e55c884eSSean Young
218e55c884eSSean Young return 0;
219e55c884eSSean Young }
220