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

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