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
127 lines
3.8 KiB
|
2 months ago
|
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
|
||
|
|
}
|
||
|
|
}
|