xref: /openbmc/linux/tools/testing/selftests/ir/ir_loopback.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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