//go:build linux package serial import ( "errors" "fmt" "reflect" "golang.org/x/sys/unix" ) type platformLineOperations struct{} type platformLineSnapshot struct { termios unix.Termios modemOutputs int } func (platformLineOperations) Open(path string) (lineDevice, error) { return openSerialDevice(path) } func (platformLineOperations) Snapshot(device lineDevice) (any, error) { termios, err := unix.IoctlGetTermios(int(device.Fd()), unix.TCGETS) if err != nil { return nil, fmt.Errorf("read serial termios snapshot: %w", err) } bits, err := unix.IoctlGetInt(int(device.Fd()), unix.TIOCMGET) if err != nil { return nil, fmt.Errorf("read serial modem-output snapshot: %w", err) } return platformLineSnapshot{ termios: *termios, modemOutputs: bits & (unix.TIOCM_DTR | unix.TIOCM_RTS), }, nil } func (platformLineOperations) Apply(device lineDevice, config LineConfig) error { return ApplyLineConfig(device.Fd(), config) } func (platformLineOperations) Read(device lineDevice, path string) (LineConfig, error) { return ReadLineConfig(device.Fd(), path) } func (platformLineOperations) Restore(device lineDevice, snapshot any) error { state, ok := snapshot.(platformLineSnapshot) if !ok { return errors.New("invalid serial line snapshot") } if err := unix.IoctlSetTermios(int(device.Fd()), unix.TCSETS, &state.termios); err != nil { return fmt.Errorf("restore serial termios: %w", err) } readback, err := unix.IoctlGetTermios(int(device.Fd()), unix.TCGETS) if err != nil { return fmt.Errorf("read restored serial termios: %w", err) } if !reflect.DeepEqual(*readback, state.termios) { return errors.New("restored serial termios does not match snapshot") } if err := restoreModemOutputs(int(device.Fd()), state.modemOutputs); err != nil { return err } return nil } func restoreModemOutputs(fd, outputs int) error { const outputMask = unix.TIOCM_DTR | unix.TIOCM_RTS setMask := outputs & outputMask clearMask := outputMask &^ outputs if setMask != 0 { if err := unix.IoctlSetPointerInt(fd, unix.TIOCMBIS, setMask); err != nil { return fmt.Errorf("restore serial modem outputs on: %w", err) } } if clearMask != 0 { if err := unix.IoctlSetPointerInt(fd, unix.TIOCMBIC, clearMask); err != nil { return fmt.Errorf("restore serial modem outputs off: %w", err) } } readback, err := unix.IoctlGetInt(fd, unix.TIOCMGET) if err != nil { return fmt.Errorf("read restored serial modem outputs: %w", err) } if readback&outputMask != outputs&outputMask { return errors.New("restored serial modem outputs do not match snapshot") } return nil }