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