Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: rc: self test for IR encoders and decoders

ir-loopback can transmit IR on one rc device and check the correct
scancode and protocol is decoded on a different rc device. This can be
used to check IR transmission between two rc devices. Using rc-loopback,
we use it to check the IR encoders and decoders themselves.

No hardware is required for this test.

Signed-off-by: Sean Young <sean@mess.org>
Cc: Shuah Khan <shuah@kernel.org>
Signed-off-by: Shuah Khan <shuah@kernel.org>

authored by

Sean Young and committed by
Shuah Khan
e55c884e 65102238

+226
+1
tools/testing/selftests/Makefile
··· 15 15 TARGETS += gpio 16 16 TARGETS += intel_pstate 17 17 TARGETS += ipc 18 + TARGETS += ir 18 19 TARGETS += kcmp 19 20 TARGETS += kvm 20 21 TARGETS += lib
+1
tools/testing/selftests/ir/.gitignore
··· 1 + ir_loopback
+5
tools/testing/selftests/ir/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + TEST_PROGS := ir_loopback.sh 3 + TEST_GEN_PROGS_EXTENDED := ir_loopback 4 + 5 + include ../lib.mk
+199
tools/testing/selftests/ir/ir_loopback.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // test ir decoder 3 + // 4 + // Copyright (C) 2018 Sean Young <sean@mess.org> 5 + 6 + // When sending LIRC_MODE_SCANCODE, the IR will be encoded. rc-loopback 7 + // will send this IR to the receiver side, where we try to read the decoded 8 + // IR. Decoding happens in a separate kernel thread, so we will need to 9 + // wait until that is scheduled, hence we use poll to check for read 10 + // readiness. 11 + 12 + #include <linux/lirc.h> 13 + #include <errno.h> 14 + #include <stdio.h> 15 + #include <stdlib.h> 16 + #include <stdbool.h> 17 + #include <string.h> 18 + #include <unistd.h> 19 + #include <poll.h> 20 + #include <time.h> 21 + #include <sys/types.h> 22 + #include <sys/ioctl.h> 23 + #include <dirent.h> 24 + #include <sys/stat.h> 25 + #include <fcntl.h> 26 + #include "../kselftest.h" 27 + 28 + #define TEST_SCANCODES 10 29 + #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 30 + 31 + static const struct { 32 + enum rc_proto proto; 33 + const char *name; 34 + unsigned int mask; 35 + const char *decoder; 36 + } protocols[] = { 37 + { RC_PROTO_RC5, "rc-5", 0x1f7f, "rc-5" }, 38 + { RC_PROTO_RC5X_20, "rc-5x-20", 0x1f7f3f, "rc-5" }, 39 + { RC_PROTO_RC5_SZ, "rc-5-sz", 0x2fff, "rc-5-sz" }, 40 + { RC_PROTO_JVC, "jvc", 0xffff, "jvc" }, 41 + { RC_PROTO_SONY12, "sony-12", 0x1f007f, "sony" }, 42 + { RC_PROTO_SONY15, "sony-15", 0xff007f, "sony" }, 43 + { RC_PROTO_SONY20, "sony-20", 0x1fff7f, "sony" }, 44 + { RC_PROTO_NEC, "nec", 0xffff, "nec" }, 45 + { RC_PROTO_NECX, "nec-x", 0xffffff, "nec" }, 46 + { RC_PROTO_NEC32, "nec-32", 0xffffffff, "nec" }, 47 + { RC_PROTO_SANYO, "sanyo", 0x1fffff, "sanyo" }, 48 + { RC_PROTO_RC6_0, "rc-6-0", 0xffff, "rc-6" }, 49 + { RC_PROTO_RC6_6A_20, "rc-6-6a-20", 0xfffff, "rc-6" }, 50 + { RC_PROTO_RC6_6A_24, "rc-6-6a-24", 0xffffff, "rc-6" }, 51 + { RC_PROTO_RC6_6A_32, "rc-6-6a-32", 0xffffffff, "rc-6" }, 52 + { RC_PROTO_RC6_MCE, "rc-6-mce", 0x00007fff, "rc-6" }, 53 + { RC_PROTO_SHARP, "sharp", 0x1fff, "sharp" }, 54 + }; 55 + 56 + int lirc_open(const char *rc) 57 + { 58 + struct dirent *dent; 59 + char buf[100]; 60 + DIR *d; 61 + int fd; 62 + 63 + snprintf(buf, sizeof(buf), "/sys/class/rc/%s", rc); 64 + 65 + d = opendir(buf); 66 + if (!d) 67 + ksft_exit_fail_msg("cannot open %s: %m\n", buf); 68 + 69 + while ((dent = readdir(d)) != NULL) { 70 + if (!strncmp(dent->d_name, "lirc", 4)) { 71 + snprintf(buf, sizeof(buf), "/dev/%s", dent->d_name); 72 + break; 73 + } 74 + } 75 + 76 + if (!dent) 77 + ksft_exit_fail_msg("cannot find lirc device for %s\n", rc); 78 + 79 + closedir(d); 80 + 81 + fd = open(buf, O_RDWR | O_NONBLOCK); 82 + if (fd == -1) 83 + ksft_exit_fail_msg("cannot open: %s: %m\n", buf); 84 + 85 + return fd; 86 + } 87 + 88 + int main(int argc, char **argv) 89 + { 90 + unsigned int mode; 91 + char buf[100]; 92 + int rlircfd, wlircfd, protocolfd, i, n; 93 + 94 + srand(time(NULL)); 95 + 96 + if (argc != 3) 97 + ksft_exit_fail_msg("Usage: %s <write rcN> <read rcN>\n", 98 + argv[0]); 99 + 100 + rlircfd = lirc_open(argv[2]); 101 + mode = LIRC_MODE_SCANCODE; 102 + if (ioctl(rlircfd, LIRC_SET_REC_MODE, &mode)) 103 + ksft_exit_fail_msg("failed to set scancode rec mode %s: %m\n", 104 + argv[2]); 105 + 106 + wlircfd = lirc_open(argv[1]); 107 + if (ioctl(wlircfd, LIRC_SET_SEND_MODE, &mode)) 108 + ksft_exit_fail_msg("failed to set scancode send mode %s: %m\n", 109 + argv[1]); 110 + 111 + snprintf(buf, sizeof(buf), "/sys/class/rc/%s/protocols", argv[2]); 112 + protocolfd = open(buf, O_WRONLY); 113 + if (protocolfd == -1) 114 + ksft_exit_fail_msg("failed to open %s: %m\n", buf); 115 + 116 + printf("Sending IR on %s and receiving IR on %s.\n", argv[1], argv[2]); 117 + 118 + for (i = 0; i < ARRAY_SIZE(protocols); i++) { 119 + if (write(protocolfd, protocols[i].decoder, 120 + strlen(protocols[i].decoder)) == -1) 121 + ksft_exit_fail_msg("failed to set write decoder\n"); 122 + 123 + printf("Testing protocol %s for decoder %s (%d/%d)...\n", 124 + protocols[i].name, protocols[i].decoder, 125 + i + 1, (int)ARRAY_SIZE(protocols)); 126 + 127 + for (n = 0; n < TEST_SCANCODES; n++) { 128 + unsigned int scancode = rand() & protocols[i].mask; 129 + unsigned int rc_proto = protocols[i].proto; 130 + 131 + if (rc_proto == RC_PROTO_RC6_MCE) 132 + scancode |= 0x800f0000; 133 + 134 + if (rc_proto == RC_PROTO_NECX && 135 + (((scancode >> 16) ^ ~(scancode >> 8)) & 0xff) == 0) 136 + continue; 137 + 138 + if (rc_proto == RC_PROTO_NEC32 && 139 + (((scancode >> 8) ^ ~scancode) & 0xff) == 0) 140 + continue; 141 + 142 + struct lirc_scancode lsc = { 143 + .rc_proto = rc_proto, 144 + .scancode = scancode 145 + }; 146 + 147 + printf("Testing scancode:%x\n", scancode); 148 + 149 + while (write(wlircfd, &lsc, sizeof(lsc)) < 0) { 150 + if (errno == EINTR) 151 + continue; 152 + 153 + ksft_exit_fail_msg("failed to send ir: %m\n"); 154 + } 155 + 156 + struct pollfd pfd = { .fd = rlircfd, .events = POLLIN }; 157 + struct lirc_scancode lsc2; 158 + 159 + poll(&pfd, 1, 1000); 160 + 161 + bool decoded = true; 162 + 163 + while (read(rlircfd, &lsc2, sizeof(lsc2)) < 0) { 164 + if (errno == EINTR) 165 + continue; 166 + 167 + ksft_test_result_error("no scancode decoded: %m\n"); 168 + decoded = false; 169 + break; 170 + } 171 + 172 + if (!decoded) 173 + continue; 174 + 175 + if (lsc.rc_proto != lsc2.rc_proto) 176 + ksft_test_result_error("decoded protocol is different: %d\n", 177 + lsc2.rc_proto); 178 + 179 + else if (lsc.scancode != lsc2.scancode) 180 + ksft_test_result_error("decoded scancode is different: %llx\n", 181 + lsc2.scancode); 182 + else 183 + ksft_inc_pass_cnt(); 184 + } 185 + 186 + printf("OK\n"); 187 + } 188 + 189 + close(rlircfd); 190 + close(wlircfd); 191 + close(protocolfd); 192 + 193 + if (ksft_get_fail_cnt() > 0) 194 + ksft_exit_fail(); 195 + else 196 + ksft_exit_pass(); 197 + 198 + return 0; 199 + }
+20
tools/testing/selftests/ir/ir_loopback.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + # Kselftest framework requirement - SKIP code is 4. 5 + ksft_skip=4 6 + 7 + if ! /sbin/modprobe -q -n rc-loopback; then 8 + echo "ir_loopback: module rc-loopback is not found [SKIP]" 9 + exit $ksft_skip 10 + fi 11 + 12 + /sbin/modprobe rc-loopback 13 + if [ $? -ne 0 ]; then 14 + exit 15 + fi 16 + 17 + RCDEV=$(grep -l DRV_NAME=rc-loopback /sys/class/rc/rc*/uevent | grep -o 'rc[0-9]\+') 18 + 19 + ./ir_loopback $RCDEV $RCDEV 20 + exit