164 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Dell AIO Serial Backlight board emulator for testing4 * 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 21int serial_fd;22int brightness = 50;23 24static 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 */35void signalhdlr(int signum)36{37}38 39int 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;158out_restore:159 tcsetattr(serial_fd, TCSANOW, &saved_tty);160out_close:161 close(serial_fd);162 return ret;163}164