From d3bc369164494438627c9ac82c6ca9421bf08644 Mon Sep 17 00:00:00 2001 From: Hermes Date: Thu, 1 Oct 2026 15:26:24 +0200 Subject: [PATCH] fix: set 115200 baud via CBAUD bits and verify readback on UART open (#10) --- internal/serialport/serial_linux.go | 44 ++++++++++++++---- internal/serialport/serial_linux_test.go | 59 ++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 internal/serialport/serial_linux_test.go diff --git a/internal/serialport/serial_linux.go b/internal/serialport/serial_linux.go index ec6472c..3e407a2 100644 --- a/internal/serialport/serial_linux.go +++ b/internal/serialport/serial_linux.go @@ -19,19 +19,45 @@ func Open115200(path string) (*os.File, error) { return nil, err } - termios.Iflag = 0 - termios.Oflag = 0 - termios.Lflag = 0 - termios.Cflag &^= unix.CSIZE | unix.PARENB | unix.CSTOPB | unix.CRTSCTS - termios.Cflag |= unix.CS8 | unix.CREAD | unix.CLOCAL - termios.Ispeed = unix.B115200 - termios.Ospeed = unix.B115200 - termios.Cc[unix.VMIN] = 1 - termios.Cc[unix.VTIME] = 0 + configureRaw115200(termios) if err := unix.IoctlSetTermios(int(file.Fd()), unix.TCSETS, termios); err != nil { file.Close() return nil, fmt.Errorf("configure %s: %w", path, err) } + + effective, err := unix.IoctlGetTermios(int(file.Fd()), unix.TCGETS) + if err != nil { + file.Close() + return nil, fmt.Errorf("read back %s: %w", path, err) + } + if err := verifyBaud(*effective, unix.B115200); err != nil { + file.Close() + return nil, fmt.Errorf("verify %s: %w", path, err) + } return file, nil } + +// configureRaw115200 keeps the raw 115200 8N1 blocking-read configuration. +// On Linux TCSETS takes the line speed from the CBAUD bits in Cflag, so +// CBAUD must be cleared and B115200 selected there; Ispeed/Ospeed alone +// leave an inherited baud (for example 9600 from boot defaults) in effect. +func configureRaw115200(termios *unix.Termios) { + termios.Iflag = 0 + termios.Oflag = 0 + termios.Lflag = 0 + termios.Cflag &^= unix.CSIZE | unix.PARENB | unix.CSTOPB | unix.CRTSCTS | unix.CBAUD + termios.Cflag |= unix.B115200 + termios.Cflag |= unix.CS8 | unix.CREAD | unix.CLOCAL + termios.Ispeed = unix.B115200 + termios.Ospeed = unix.B115200 + termios.Cc[unix.VMIN] = 1 + termios.Cc[unix.VTIME] = 0 +} + +func verifyBaud(termios unix.Termios, baud uint32) error { + if got := termios.Cflag & unix.CBAUD; got != baud { + return fmt.Errorf("effective baud bits %#o, want %#o", got, baud) + } + return nil +} diff --git a/internal/serialport/serial_linux_test.go b/internal/serialport/serial_linux_test.go new file mode 100644 index 0000000..314bcfb --- /dev/null +++ b/internal/serialport/serial_linux_test.go @@ -0,0 +1,59 @@ +package serialport + +import ( + "testing" + + "golang.org/x/sys/unix" +) + +func TestConfigureRaw115200OverridesInheritedBaud(t *testing.T) { + var termios unix.Termios + termios.Iflag = unix.IGNBRK | unix.ICRNL + termios.Oflag = unix.OPOST + termios.Lflag = unix.ICANON | unix.ECHO + termios.Cflag = unix.B9600 | unix.CS8 | unix.CREAD | unix.CLOCAL | unix.HUPCL + termios.Ispeed = unix.B9600 + termios.Ospeed = unix.B9600 + + configureRaw115200(&termios) + + if got := termios.Cflag & unix.CBAUD; got != unix.B115200 { + t.Fatalf("effective baud bits=%#o want %#o", got, unix.B115200) + } + if got := termios.Cflag & unix.CSIZE; got != unix.CS8 { + t.Fatalf("character size bits=%#o want %#o", got, unix.CS8) + } + for _, flag := range []uint32{unix.CREAD, unix.CLOCAL} { + if termios.Cflag&flag == 0 { + t.Fatalf("flag %#o lost", flag) + } + } + for _, flag := range []uint32{unix.PARENB, unix.CSTOPB, unix.CRTSCTS} { + if termios.Cflag&flag != 0 { + t.Fatalf("flag %#o must be cleared", flag) + } + } + if termios.Iflag != 0 || termios.Oflag != 0 || termios.Lflag != 0 { + t.Fatal("raw mode must clear Iflag, Oflag and Lflag") + } + if termios.Ispeed != unix.B115200 || termios.Ospeed != unix.B115200 { + t.Fatalf("speed fields=%d/%d want 115200/115200", termios.Ispeed, termios.Ospeed) + } + if termios.Cc[unix.VMIN] != 1 || termios.Cc[unix.VTIME] != 0 { + t.Fatalf("VMIN=%d VTIME=%d want 1/0", termios.Cc[unix.VMIN], termios.Cc[unix.VTIME]) + } +} + +func TestVerifyBaudAcceptsOnlyConfiguredLineSpeed(t *testing.T) { + var inherited unix.Termios + inherited.Cflag = unix.B9600 | unix.CS8 + if err := verifyBaud(inherited, unix.B115200); err == nil { + t.Fatal("inherited 9600 line speed must fail verification") + } + + var configured unix.Termios + configured.Cflag = unix.B115200 | unix.CS8 | unix.CREAD + if err := verifyBaud(configured, unix.B115200); err != nil { + t.Fatalf("115200 line speed must verify: %v", err) + } +} -- 2.54.0