Skip to content

Commit d9bab77

Browse files
committed
tools arch x86: Add dell-uart-backlight-emulator
Dell All In One (AIO) models released after 2017 use a backlight controller board connected to an UART. Add a small emulator to allow development and testing of the drivers/platform/x86/dell/dell-uart-backlight.c driver for this board, without requiring access to an actual Dell All In One. Signed-off-by: Hans de Goede <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 484bae9 commit d9bab77

File tree

4 files changed

+229
-0
lines changed

4 files changed

+229
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dell-uart-backlight-emulator
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
# Makefile for Intel Software Defined Silicon provisioning tool
3+
4+
dell-uart-backlight-emulator: dell-uart-backlight-emulator.c
5+
6+
BINDIR ?= /usr/bin
7+
8+
override CFLAGS += -O2 -Wall
9+
10+
%: %.c
11+
$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
12+
13+
.PHONY : clean
14+
clean :
15+
@rm -f dell-uart-backlight-emulator
16+
17+
install : dell-uart-backlight-emulator
18+
install -d $(DESTDIR)$(BINDIR)
19+
install -m 755 -p dell-uart-backlight-emulator $(DESTDIR)$(BINDIR)/dell-uart-backlight-emulator
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Emulator for DELL0501 UART attached backlight controller
2+
--------------------------------------------------------
3+
4+
Dell All In One (AIO) models released after 2017 use a backlight controller
5+
board connected to an UART.
6+
7+
In DSDT this uart port will be defined as:
8+
9+
Name (_HID, "DELL0501")
10+
Name (_CID, EisaId ("PNP0501")
11+
12+
With the DELL0501 indicating that we are dealing with an UART with
13+
the backlight controller board attached.
14+
15+
This small emulator allows testing
16+
the drivers/platform/x86/dell/dell-uart-backlight.c driver without access
17+
to an actual Dell All In One.
18+
19+
This requires:
20+
1. A (desktop) PC with a 16550 UART on the motherboard and a standard DB9
21+
connector connected to this UART.
22+
2. A DB9 NULL modem cable.
23+
3. A second DB9 serial port, this can e.g. be a USB to serial converter
24+
with a DB9 connector plugged into the same desktop PC.
25+
4. A DSDT overlay for the desktop PC replacing the _HID of the 16550 UART
26+
ACPI Device() with "DELL0501" and adding a _CID of "PNP0501", see
27+
DSDT.patch for an example of the necessary DSDT changes.
28+
29+
With everything setup and the NULL modem cable connected between
30+
the 2 serial ports run:
31+
32+
./dell-uart-backlight-emulator <path-to-/dev/tty*S#-for-second-port>
33+
34+
For example when using an USB to serial converter for the second port:
35+
36+
./dell-uart-backlight-emulator /dev/ttyUSB0
37+
38+
And then (re)load the dell-uart-backlight driver:
39+
40+
sudo rmmod dell-uart-backlight; sudo modprobe dell-uart-backlight dyndbg
41+
42+
After this check "dmesg" to see if the driver correctly received
43+
the firmware version string from the emulator. If this works there
44+
should be a /sys/class/backlight/dell_uart_backlight/ directory now
45+
and writes to the brightness or bl_power files should be reflected
46+
by matching output from the emulator.
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
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 <[email protected]>
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+
}

0 commit comments

Comments
 (0)