You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

91 lines
2.6 KiB

//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
}