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.

127 lines
3.8 KiB

package serial
import (
"errors"
"fmt"
"strings"
)
type LineConfig struct {
Device string
BaudRate int
DataBits int
Parity string
StopBits int
StopBitsMode string
FlowControl string
RTSControl string
DTRControl string
IgnoreJammed bool
}
var (
ErrUnsupportedPlatform = errors.New("serial termios is unsupported on this platform")
ErrInvalidLineConfig = errors.New("invalid serial line config")
)
func NormalizeLineConfig(config LineConfig) (LineConfig, error) {
normalized := LineConfig{
Device: strings.TrimSpace(config.Device),
BaudRate: config.BaudRate,
DataBits: config.DataBits,
Parity: strings.ToLower(strings.TrimSpace(config.Parity)),
StopBits: config.StopBits,
StopBitsMode: strings.TrimSpace(config.StopBitsMode),
FlowControl: strings.ToLower(strings.TrimSpace(config.FlowControl)),
RTSControl: strings.ToLower(strings.TrimSpace(config.RTSControl)),
DTRControl: strings.ToLower(strings.TrimSpace(config.DTRControl)),
IgnoreJammed: config.IgnoreJammed,
}
if normalized.BaudRate == 0 {
normalized.BaudRate = 9600
}
if normalized.DataBits == 0 {
normalized.DataBits = 8
}
if normalized.Parity == "" {
normalized.Parity = "none"
}
if normalized.StopBits == 0 {
normalized.StopBits = 1
}
if normalized.StopBitsMode == "" {
normalized.StopBitsMode = fmt.Sprintf("%d", normalized.StopBits)
}
if normalized.FlowControl == "" {
normalized.FlowControl = "none"
}
if normalized.RTSControl == "" {
normalized.RTSControl = "auto"
}
if normalized.DTRControl == "" {
normalized.DTRControl = "auto"
}
if normalized.Device == "" {
return LineConfig{}, invalidLineConfig("device must not be empty")
}
if !isAllowedSerialDevice(normalized.Device) {
return LineConfig{}, invalidLineConfig("device must be under /dev/tty* or /dev/serial/by-path")
}
if !isAllowedBaudRate(normalized.BaudRate) {
return LineConfig{}, invalidLineConfig("baud rate is not supported")
}
if normalized.DataBits < 5 || normalized.DataBits > 8 {
return LineConfig{}, invalidLineConfig("data bits must be 5 through 8")
}
switch normalized.Parity {
case "none", "even", "odd", "mark", "space":
default:
return LineConfig{}, invalidLineConfig("parity must be none, even, odd, mark, or space")
}
if normalized.StopBitsMode != "1" && normalized.StopBitsMode != "1.5" && normalized.StopBitsMode != "2" {
return LineConfig{}, invalidLineConfig("stop bits must be 1, 1.5, or 2")
}
if normalized.StopBitsMode == "1.5" && normalized.DataBits != 5 {
return LineConfig{}, invalidLineConfig("1.5 stop bits requires 5 data bits")
}
if normalized.StopBitsMode == "2" {
normalized.StopBits = 2
} else {
normalized.StopBits = 1
}
switch normalized.FlowControl {
case "none", "rtscts", "xonxoff":
default:
return LineConfig{}, invalidLineConfig("flow control must be none, rtscts, or xonxoff")
}
if normalized.FlowControl == "rtscts" && normalized.RTSControl != "auto" {
return LineConfig{}, invalidLineConfig("manual RTS control requires flow control other than rtscts")
}
for name, value := range map[string]string{"rts control": normalized.RTSControl, "dtr control": normalized.DTRControl} {
if value != "auto" && value != "on" && value != "off" {
return LineConfig{}, invalidLineConfig(name + " must be auto, on, or off")
}
}
return normalized, nil
}
func invalidLineConfig(message string) error {
return fmt.Errorf("%w: %s", ErrInvalidLineConfig, message)
}
func isAllowedSerialDevice(device string) bool {
return strings.HasPrefix(device, "/dev/tty") ||
strings.HasPrefix(device, "/dev/serial/by-path/")
}
func isAllowedBaudRate(baudRate int) bool {
switch baudRate {
case 110, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800:
return true
default:
return false
}
}