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
dell_uart_checksum(unsigned char * buf,int len)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 */
signalhdlr(int signum)35 void signalhdlr(int signum)
36 {
37 }
38
main(int argc,char * argv[])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