1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Dell AIO Serial Backlight board emulator for testing 4 * the Linux dell-uart-backlight driver. 5 * 6 * Copyright (C) 2024 Hans de Goede <hansg@kernel.org> 7 */ 8 #include <errno.h> 9 #include <fcntl.h> 10 #include <signal.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sys/ioctl.h> 15 #include <sys/stat.h> 16 #include <sys/types.h> 17 #include <sys/un.h> 18 #include <termios.h> 19 #include <unistd.h> 20 21 int serial_fd; 22 int brightness = 50; 23 24 static unsigned char dell_uart_checksum(unsigned char *buf, int len) 25 { 26 unsigned char val = 0; 27 28 while (len-- > 0) 29 val += buf[len]; 30 31 return val ^ 0xff; 32 } 33 34 /* read() will return -1 on SIGINT / SIGTERM causing the mainloop to cleanly exit */ 35 void signalhdlr(int signum) 36 { 37 } 38 39 int main(int argc, char *argv[]) 40 { 41 struct sigaction sigact = { .sa_handler = signalhdlr }; 42 unsigned char buf[4], csum, response[32]; 43 const char *version_str = "PHI23-V321"; 44 struct termios tty, saved_tty; 45 int ret, idx, len = 0; 46 47 if (argc != 2) { 48 fprintf(stderr, "Invalid or missing arguments\n"); 49 fprintf(stderr, "Usage: %s <serial-port>\n", argv[0]); 50 return 1; 51 } 52 53 serial_fd = open(argv[1], O_RDWR | O_NOCTTY); 54 if (serial_fd == -1) { 55 fprintf(stderr, "Error opening %s: %s\n", argv[1], strerror(errno)); 56 return 1; 57 } 58 59 ret = tcgetattr(serial_fd, &tty); 60 if (ret == -1) { 61 fprintf(stderr, "Error getting tcattr: %s\n", strerror(errno)); 62 goto out_close; 63 } 64 saved_tty = tty; 65 66 cfsetspeed(&tty, 9600); 67 cfmakeraw(&tty); 68 tty.c_cflag &= ~CSTOPB; 69 tty.c_cflag &= ~CRTSCTS; 70 tty.c_cflag |= CLOCAL | CREAD; 71 72 ret = tcsetattr(serial_fd, TCSANOW, &tty); 73 if (ret == -1) { 74 fprintf(stderr, "Error setting tcattr: %s\n", strerror(errno)); 75 goto out_restore; 76 } 77 78 sigaction(SIGINT, &sigact, 0); 79 sigaction(SIGTERM, &sigact, 0); 80 81 idx = 0; 82 while (read(serial_fd, &buf[idx], 1) == 1) { 83 if (idx == 0) { 84 switch (buf[0]) { 85 /* 3 MSB bits: cmd-len + 01010 SOF marker */ 86 case 0x6a: len = 3; break; 87 case 0x8a: len = 4; break; 88 default: 89 fprintf(stderr, "Error unexpected first byte: 0x%02x\n", buf[0]); 90 continue; /* Try to sync up with sender */ 91 } 92 } 93 94 /* Process msg when len bytes have been received */ 95 if (idx != (len - 1)) { 96 idx++; 97 continue; 98 } 99 100 /* Reset idx for next command */ 101 idx = 0; 102 103 csum = dell_uart_checksum(buf, len - 1); 104 if (buf[len - 1] != csum) { 105 fprintf(stderr, "Error checksum mismatch got 0x%02x expected 0x%02x\n", 106 buf[len - 1], csum); 107 continue; 108 } 109 110 switch ((buf[0] << 8) | buf[1]) { 111 case 0x6a06: /* cmd = 0x06, get version */ 112 len = strlen(version_str); 113 strcpy((char *)&response[2], version_str); 114 printf("Get version, reply: %s\n", version_str); 115 break; 116 case 0x8a0b: /* cmd = 0x0b, set brightness */ 117 if (buf[2] > 100) { 118 fprintf(stderr, "Error invalid brightness param: %d\n", buf[2]); 119 continue; 120 } 121 122 len = 0; 123 brightness = buf[2]; 124 printf("Set brightness %d\n", brightness); 125 break; 126 case 0x6a0c: /* cmd = 0x0c, get brightness */ 127 len = 1; 128 response[2] = brightness; 129 printf("Get brightness, reply: %d\n", brightness); 130 break; 131 case 0x8a0e: /* cmd = 0x0e, set backlight power */ 132 if (buf[2] != 0 && buf[2] != 1) { 133 fprintf(stderr, "Error invalid set power param: %d\n", buf[2]); 134 continue; 135 } 136 137 len = 0; 138 printf("Set power %d\n", buf[2]); 139 break; 140 default: 141 fprintf(stderr, "Error unknown cmd 0x%04x\n", 142 (buf[0] << 8) | buf[1]); 143 continue; 144 } 145 146 /* Respond with <total-len> <cmd> <data...> <csum> */ 147 response[0] = len + 3; /* response length in bytes */ 148 response[1] = buf[1]; /* ack cmd */ 149 csum = dell_uart_checksum(response, len + 2); 150 response[len + 2] = csum; 151 ret = write(serial_fd, response, response[0]); 152 if (ret != (response[0])) 153 fprintf(stderr, "Error writing %d bytes: %d\n", 154 response[0], ret); 155 } 156 157 ret = 0; 158 out_restore: 159 tcsetattr(serial_fd, TCSANOW, &saved_tty); 160 out_close: 161 close(serial_fd); 162 return ret; 163 } 164