xref: /linux/tools/arch/x86/dell-uart-backlight-emulator/dell-uart-backlight-emulator.c (revision ea5f6ad9ad9645733b72ab53a98e719b460d36a6)
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