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.
241 lines
5.6 KiB
241 lines
5.6 KiB
//go:build linux
|
|
|
|
package serial
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func ApplyLineConfig(fd uintptr, config LineConfig) error {
|
|
normalized, err := NormalizeLineConfig(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
termios, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
|
|
if err != nil {
|
|
return fmt.Errorf("read serial termios: %w", err)
|
|
}
|
|
applyTermiosConfig(termios, normalized)
|
|
if err := unix.IoctlSetTermios(int(fd), unix.TCSETS, termios); err != nil {
|
|
return fmt.Errorf("apply serial termios: %w", err)
|
|
}
|
|
if err := applyModemOutputs(int(fd), normalized); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// applyModemOutputs makes the public rts_control/dtr_control settings affect
|
|
// the physical RS-232 output pins. "auto" deliberately leaves ownership to
|
|
// the tty driver (for example CRTSCTS), while on/off are applied and verified.
|
|
func applyModemOutputs(fd int, config LineConfig) error {
|
|
if err := applyModemOutput(fd, "DTR", config.DTRControl, unix.TIOCM_DTR); err != nil {
|
|
return err
|
|
}
|
|
if err := applyModemOutput(fd, "RTS", config.RTSControl, unix.TIOCM_RTS); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func applyModemOutput(fd int, name, control string, mask int) error {
|
|
if control == "auto" {
|
|
return nil
|
|
}
|
|
request := uint(unix.TIOCMBIC)
|
|
wantOn := control == "on"
|
|
if wantOn {
|
|
request = uint(unix.TIOCMBIS)
|
|
}
|
|
if err := unix.IoctlSetPointerInt(fd, request, mask); err != nil {
|
|
return fmt.Errorf("set serial %s output %s: %w", name, control, err)
|
|
}
|
|
bits, err := unix.IoctlGetInt(fd, unix.TIOCMGET)
|
|
if err != nil {
|
|
return fmt.Errorf("verify serial %s output %s: %w", name, control, err)
|
|
}
|
|
if (bits&mask != 0) != wantOn {
|
|
return fmt.Errorf("verify serial %s output: requested %s but hardware readback did not match", name, control)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ReadLineConfig(fd uintptr, device string) (LineConfig, error) {
|
|
termios, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
|
|
if err != nil {
|
|
return LineConfig{}, fmt.Errorf("read serial termios: %w", err)
|
|
}
|
|
baudRate, ok := baudRateFromConstant(termios.Cflag & unix.CBAUD)
|
|
if !ok {
|
|
return LineConfig{}, fmt.Errorf("read serial termios: unsupported baud encoding %#x", termios.Cflag&unix.CBAUD)
|
|
}
|
|
dataBits, ok := dataBitsFromConstant(termios.Cflag & unix.CSIZE)
|
|
if !ok {
|
|
return LineConfig{}, fmt.Errorf("read serial termios: unsupported data-bit encoding %#x", termios.Cflag&unix.CSIZE)
|
|
}
|
|
parity := "none"
|
|
if termios.Cflag&unix.PARENB != 0 {
|
|
if termios.Cflag&unix.CMSPAR != 0 {
|
|
parity = "space"
|
|
if termios.Cflag&unix.PARODD != 0 {
|
|
parity = "mark"
|
|
}
|
|
} else {
|
|
parity = "even"
|
|
if termios.Cflag&unix.PARODD != 0 {
|
|
parity = "odd"
|
|
}
|
|
}
|
|
}
|
|
stopBits := 1
|
|
stopBitsMode := "1"
|
|
if termios.Cflag&unix.CSTOPB != 0 {
|
|
if dataBits == 5 {
|
|
stopBitsMode = "1.5"
|
|
} else {
|
|
stopBits = 2
|
|
stopBitsMode = "2"
|
|
}
|
|
}
|
|
flowControl := "none"
|
|
if termios.Cflag&unix.CRTSCTS != 0 {
|
|
flowControl = "rtscts"
|
|
} else if termios.Iflag&(unix.IXON|unix.IXOFF) != 0 {
|
|
flowControl = "xonxoff"
|
|
}
|
|
return LineConfig{
|
|
Device: device, BaudRate: baudRate, DataBits: dataBits, Parity: parity,
|
|
StopBits: stopBits, StopBitsMode: stopBitsMode, FlowControl: flowControl,
|
|
RTSControl: "auto", DTRControl: "auto",
|
|
}, nil
|
|
}
|
|
|
|
func applyTermiosConfig(termios *unix.Termios, config LineConfig) {
|
|
speed := baudConstant(config.BaudRate)
|
|
termios.Iflag = unix.IGNPAR
|
|
termios.Oflag = 0
|
|
termios.Lflag = 0
|
|
termios.Cflag = unix.CLOCAL | unix.CREAD | speed | dataBitsConstant(config.DataBits)
|
|
|
|
switch config.Parity {
|
|
case "even":
|
|
termios.Cflag |= unix.PARENB
|
|
case "odd":
|
|
termios.Cflag |= unix.PARENB | unix.PARODD
|
|
case "mark":
|
|
termios.Cflag |= unix.PARENB | unix.PARODD | unix.CMSPAR
|
|
case "space":
|
|
termios.Cflag |= unix.PARENB | unix.CMSPAR
|
|
}
|
|
if config.StopBitsMode == "1.5" || config.StopBitsMode == "2" {
|
|
termios.Cflag |= unix.CSTOPB
|
|
}
|
|
if config.FlowControl == "rtscts" {
|
|
termios.Cflag |= unix.CRTSCTS
|
|
} else if config.FlowControl == "xonxoff" {
|
|
termios.Iflag |= unix.IXON | unix.IXOFF
|
|
}
|
|
termios.Cc[unix.VMIN] = 1
|
|
termios.Cc[unix.VTIME] = 0
|
|
}
|
|
|
|
func baudConstant(baudRate int) uint32 {
|
|
switch baudRate {
|
|
case 110:
|
|
return unix.B110
|
|
case 300:
|
|
return unix.B300
|
|
case 600:
|
|
return unix.B600
|
|
case 1200:
|
|
return unix.B1200
|
|
case 1800:
|
|
return unix.B1800
|
|
case 2400:
|
|
return unix.B2400
|
|
case 4800:
|
|
return unix.B4800
|
|
case 9600:
|
|
return unix.B9600
|
|
case 19200:
|
|
return unix.B19200
|
|
case 38400:
|
|
return unix.B38400
|
|
case 57600:
|
|
return unix.B57600
|
|
case 115200:
|
|
return unix.B115200
|
|
case 230400:
|
|
return unix.B230400
|
|
case 460800:
|
|
return unix.B460800
|
|
default:
|
|
return unix.B9600
|
|
}
|
|
}
|
|
|
|
func dataBitsConstant(dataBits int) uint32 {
|
|
switch dataBits {
|
|
case 5:
|
|
return unix.CS5
|
|
case 6:
|
|
return unix.CS6
|
|
case 7:
|
|
return unix.CS7
|
|
default:
|
|
return unix.CS8
|
|
}
|
|
}
|
|
|
|
func baudRateFromConstant(value uint32) (int, bool) {
|
|
switch value {
|
|
case unix.B110:
|
|
return 110, true
|
|
case unix.B300:
|
|
return 300, true
|
|
case unix.B600:
|
|
return 600, true
|
|
case unix.B1200:
|
|
return 1200, true
|
|
case unix.B1800:
|
|
return 1800, true
|
|
case unix.B2400:
|
|
return 2400, true
|
|
case unix.B4800:
|
|
return 4800, true
|
|
case unix.B9600:
|
|
return 9600, true
|
|
case unix.B19200:
|
|
return 19200, true
|
|
case unix.B38400:
|
|
return 38400, true
|
|
case unix.B57600:
|
|
return 57600, true
|
|
case unix.B115200:
|
|
return 115200, true
|
|
case unix.B230400:
|
|
return 230400, true
|
|
case unix.B460800:
|
|
return 460800, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|
|
func dataBitsFromConstant(value uint32) (int, bool) {
|
|
switch value {
|
|
case unix.CS5:
|
|
return 5, true
|
|
case unix.CS6:
|
|
return 6, true
|
|
case unix.CS7:
|
|
return 7, true
|
|
case unix.CS8:
|
|
return 8, true
|
|
default:
|
|
return 0, false
|
|
}
|
|
}
|
|
|