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
91 lines
2.6 KiB
|
2 months ago
|
//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
|
||
|
|
}
|