162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// test ir decoder 362306a36Sopenharmony_ci// 462306a36Sopenharmony_ci// Copyright (C) 2018 Sean Young <sean@mess.org> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci// When sending LIRC_MODE_SCANCODE, the IR will be encoded. rc-loopback 762306a36Sopenharmony_ci// will send this IR to the receiver side, where we try to read the decoded 862306a36Sopenharmony_ci// IR. Decoding happens in a separate kernel thread, so we will need to 962306a36Sopenharmony_ci// wait until that is scheduled, hence we use poll to check for read 1062306a36Sopenharmony_ci// readiness. 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/lirc.h> 1362306a36Sopenharmony_ci#include <errno.h> 1462306a36Sopenharmony_ci#include <stdio.h> 1562306a36Sopenharmony_ci#include <stdlib.h> 1662306a36Sopenharmony_ci#include <stdbool.h> 1762306a36Sopenharmony_ci#include <string.h> 1862306a36Sopenharmony_ci#include <unistd.h> 1962306a36Sopenharmony_ci#include <poll.h> 2062306a36Sopenharmony_ci#include <time.h> 2162306a36Sopenharmony_ci#include <sys/types.h> 2262306a36Sopenharmony_ci#include <sys/ioctl.h> 2362306a36Sopenharmony_ci#include <dirent.h> 2462306a36Sopenharmony_ci#include <sys/stat.h> 2562306a36Sopenharmony_ci#include <fcntl.h> 2662306a36Sopenharmony_ci#include "../kselftest.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define TEST_SCANCODES 10 2962306a36Sopenharmony_ci#define SYSFS_PATH_MAX 256 3062306a36Sopenharmony_ci#define DNAME_PATH_MAX 256 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* 3362306a36Sopenharmony_ci * Support ancient lirc.h which does not have these values. Can be removed 3462306a36Sopenharmony_ci * once RHEL 8 is no longer a relevant testing platform. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci#if RC_PROTO_MAX < 26 3762306a36Sopenharmony_ci#define RC_PROTO_RCMM12 24 3862306a36Sopenharmony_ci#define RC_PROTO_RCMM24 25 3962306a36Sopenharmony_ci#define RC_PROTO_RCMM32 26 4062306a36Sopenharmony_ci#endif 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic const struct { 4362306a36Sopenharmony_ci enum rc_proto proto; 4462306a36Sopenharmony_ci const char *name; 4562306a36Sopenharmony_ci unsigned int mask; 4662306a36Sopenharmony_ci const char *decoder; 4762306a36Sopenharmony_ci} protocols[] = { 4862306a36Sopenharmony_ci { RC_PROTO_RC5, "rc-5", 0x1f7f, "rc-5" }, 4962306a36Sopenharmony_ci { RC_PROTO_RC5X_20, "rc-5x-20", 0x1f7f3f, "rc-5" }, 5062306a36Sopenharmony_ci { RC_PROTO_RC5_SZ, "rc-5-sz", 0x2fff, "rc-5-sz" }, 5162306a36Sopenharmony_ci { RC_PROTO_JVC, "jvc", 0xffff, "jvc" }, 5262306a36Sopenharmony_ci { RC_PROTO_SONY12, "sony-12", 0x1f007f, "sony" }, 5362306a36Sopenharmony_ci { RC_PROTO_SONY15, "sony-15", 0xff007f, "sony" }, 5462306a36Sopenharmony_ci { RC_PROTO_SONY20, "sony-20", 0x1fff7f, "sony" }, 5562306a36Sopenharmony_ci { RC_PROTO_NEC, "nec", 0xffff, "nec" }, 5662306a36Sopenharmony_ci { RC_PROTO_NECX, "nec-x", 0xffffff, "nec" }, 5762306a36Sopenharmony_ci { RC_PROTO_NEC32, "nec-32", 0xffffffff, "nec" }, 5862306a36Sopenharmony_ci { RC_PROTO_SANYO, "sanyo", 0x1fffff, "sanyo" }, 5962306a36Sopenharmony_ci { RC_PROTO_RC6_0, "rc-6-0", 0xffff, "rc-6" }, 6062306a36Sopenharmony_ci { RC_PROTO_RC6_6A_20, "rc-6-6a-20", 0xfffff, "rc-6" }, 6162306a36Sopenharmony_ci { RC_PROTO_RC6_6A_24, "rc-6-6a-24", 0xffffff, "rc-6" }, 6262306a36Sopenharmony_ci { RC_PROTO_RC6_6A_32, "rc-6-6a-32", 0xffffffff, "rc-6" }, 6362306a36Sopenharmony_ci { RC_PROTO_RC6_MCE, "rc-6-mce", 0x00007fff, "rc-6" }, 6462306a36Sopenharmony_ci { RC_PROTO_SHARP, "sharp", 0x1fff, "sharp" }, 6562306a36Sopenharmony_ci { RC_PROTO_IMON, "imon", 0x7fffffff, "imon" }, 6662306a36Sopenharmony_ci { RC_PROTO_RCMM12, "rcmm-12", 0x00000fff, "rc-mm" }, 6762306a36Sopenharmony_ci { RC_PROTO_RCMM24, "rcmm-24", 0x00ffffff, "rc-mm" }, 6862306a36Sopenharmony_ci { RC_PROTO_RCMM32, "rcmm-32", 0xffffffff, "rc-mm" }, 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciint lirc_open(const char *rc) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct dirent *dent; 7462306a36Sopenharmony_ci char buf[SYSFS_PATH_MAX + DNAME_PATH_MAX]; 7562306a36Sopenharmony_ci DIR *d; 7662306a36Sopenharmony_ci int fd; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "/sys/class/rc/%s", rc); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci d = opendir(buf); 8162306a36Sopenharmony_ci if (!d) 8262306a36Sopenharmony_ci ksft_exit_fail_msg("cannot open %s: %m\n", buf); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci while ((dent = readdir(d)) != NULL) { 8562306a36Sopenharmony_ci if (!strncmp(dent->d_name, "lirc", 4)) { 8662306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "/dev/%s", dent->d_name); 8762306a36Sopenharmony_ci break; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (!dent) 9262306a36Sopenharmony_ci ksft_exit_skip("cannot find lirc device for %s\n", rc); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci closedir(d); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci fd = open(buf, O_RDWR | O_NONBLOCK); 9762306a36Sopenharmony_ci if (fd == -1) 9862306a36Sopenharmony_ci ksft_exit_fail_msg("cannot open: %s: %m\n", buf); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci return fd; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ciint main(int argc, char **argv) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci unsigned int mode; 10662306a36Sopenharmony_ci char buf[100]; 10762306a36Sopenharmony_ci int rlircfd, wlircfd, protocolfd, i, n; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci srand(time(NULL)); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (argc != 3) 11262306a36Sopenharmony_ci ksft_exit_fail_msg("Usage: %s <write rcN> <read rcN>\n", 11362306a36Sopenharmony_ci argv[0]); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci rlircfd = lirc_open(argv[2]); 11662306a36Sopenharmony_ci mode = LIRC_MODE_SCANCODE; 11762306a36Sopenharmony_ci if (ioctl(rlircfd, LIRC_SET_REC_MODE, &mode)) 11862306a36Sopenharmony_ci ksft_exit_fail_msg("failed to set scancode rec mode %s: %m\n", 11962306a36Sopenharmony_ci argv[2]); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci wlircfd = lirc_open(argv[1]); 12262306a36Sopenharmony_ci if (ioctl(wlircfd, LIRC_SET_SEND_MODE, &mode)) 12362306a36Sopenharmony_ci ksft_exit_fail_msg("failed to set scancode send mode %s: %m\n", 12462306a36Sopenharmony_ci argv[1]); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "/sys/class/rc/%s/protocols", argv[2]); 12762306a36Sopenharmony_ci protocolfd = open(buf, O_WRONLY); 12862306a36Sopenharmony_ci if (protocolfd == -1) 12962306a36Sopenharmony_ci ksft_exit_fail_msg("failed to open %s: %m\n", buf); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci printf("Sending IR on %s and receiving IR on %s.\n", argv[1], argv[2]); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(protocols); i++) { 13462306a36Sopenharmony_ci if (write(protocolfd, protocols[i].decoder, 13562306a36Sopenharmony_ci strlen(protocols[i].decoder)) == -1) 13662306a36Sopenharmony_ci ksft_exit_fail_msg("failed to set write decoder\n"); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci printf("Testing protocol %s for decoder %s (%d/%d)...\n", 13962306a36Sopenharmony_ci protocols[i].name, protocols[i].decoder, 14062306a36Sopenharmony_ci i + 1, (int)ARRAY_SIZE(protocols)); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci for (n = 0; n < TEST_SCANCODES; n++) { 14362306a36Sopenharmony_ci unsigned int scancode = rand() & protocols[i].mask; 14462306a36Sopenharmony_ci unsigned int rc_proto = protocols[i].proto; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (rc_proto == RC_PROTO_RC6_MCE) 14762306a36Sopenharmony_ci scancode |= 0x800f0000; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (rc_proto == RC_PROTO_NECX && 15062306a36Sopenharmony_ci (((scancode >> 16) ^ ~(scancode >> 8)) & 0xff) == 0) 15162306a36Sopenharmony_ci continue; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (rc_proto == RC_PROTO_NEC32 && 15462306a36Sopenharmony_ci (((scancode >> 8) ^ ~scancode) & 0xff) == 0) 15562306a36Sopenharmony_ci continue; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (rc_proto == RC_PROTO_RCMM32 && 15862306a36Sopenharmony_ci (scancode & 0x000c0000) != 0x000c0000 && 15962306a36Sopenharmony_ci scancode & 0x00008000) 16062306a36Sopenharmony_ci continue; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci struct lirc_scancode lsc = { 16362306a36Sopenharmony_ci .rc_proto = rc_proto, 16462306a36Sopenharmony_ci .scancode = scancode 16562306a36Sopenharmony_ci }; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci printf("Testing scancode:%x\n", scancode); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci while (write(wlircfd, &lsc, sizeof(lsc)) < 0) { 17062306a36Sopenharmony_ci if (errno == EINTR) 17162306a36Sopenharmony_ci continue; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ksft_exit_fail_msg("failed to send ir: %m\n"); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci struct pollfd pfd = { .fd = rlircfd, .events = POLLIN }; 17762306a36Sopenharmony_ci struct lirc_scancode lsc2; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci poll(&pfd, 1, 1000); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci bool decoded = true; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci while (read(rlircfd, &lsc2, sizeof(lsc2)) < 0) { 18462306a36Sopenharmony_ci if (errno == EINTR) 18562306a36Sopenharmony_ci continue; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci ksft_test_result_error("no scancode decoded: %m\n"); 18862306a36Sopenharmony_ci decoded = false; 18962306a36Sopenharmony_ci break; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!decoded) 19362306a36Sopenharmony_ci continue; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (lsc.rc_proto != lsc2.rc_proto) 19662306a36Sopenharmony_ci ksft_test_result_error("decoded protocol is different: %d\n", 19762306a36Sopenharmony_ci lsc2.rc_proto); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci else if (lsc.scancode != lsc2.scancode) 20062306a36Sopenharmony_ci ksft_test_result_error("decoded scancode is different: %llx\n", 20162306a36Sopenharmony_ci lsc2.scancode); 20262306a36Sopenharmony_ci else 20362306a36Sopenharmony_ci ksft_inc_pass_cnt(); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci printf("OK\n"); 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci close(rlircfd); 21062306a36Sopenharmony_ci close(wlircfd); 21162306a36Sopenharmony_ci close(protocolfd); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (ksft_get_fail_cnt() > 0) 21462306a36Sopenharmony_ci ksft_exit_fail(); 21562306a36Sopenharmony_ci else 21662306a36Sopenharmony_ci ksft_exit_pass(); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 220