Merge tag 'platform-drivers-x86-v6.10-1' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-block.git] / tools / arch / x86 / dell-uart-backlight-emulator / dell-uart-backlight-emulator.c
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 }