186 lines
3.7 KiB
Go
186 lines
3.7 KiB
Go
/*
|
|
* ============================================================================
|
|
* Projekt.....: rs2322tcp
|
|
* Datei.......: serial.go
|
|
* Copyright (C) 2026 Dieter Lang
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* Beschreibung:
|
|
* Öffnet und verwaltet eine serielle Schnittstelle auf Basis der
|
|
* konfigurierten Geräteparameter.
|
|
* ============================================================================
|
|
*/
|
|
package serial
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"sync"
|
|
|
|
bugserial "go.bug.st/serial"
|
|
|
|
"git.lang-dieter.de/rs2322tcp/internal/config"
|
|
)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Connection
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Connection represents an opened serial connection.
|
|
type Connection struct {
|
|
mu sync.Mutex
|
|
port bugserial.Port
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Open
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Open opens the serial device described by the configuration.
|
|
func Open(device config.DeviceConfig) (*Connection, error) {
|
|
mode, err := createMode(device)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
port, err := bugserial.Open(device.SerialPort, mode)
|
|
if err != nil {
|
|
return nil, fmt.Errorf(
|
|
"open serial port %q: %w",
|
|
device.SerialPort,
|
|
err,
|
|
)
|
|
}
|
|
|
|
return &Connection{
|
|
port: port,
|
|
}, nil
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Mode
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
func createMode(device config.DeviceConfig) (*bugserial.Mode, error) {
|
|
mode := &bugserial.Mode{
|
|
BaudRate: device.BaudRate,
|
|
DataBits: device.DataBits,
|
|
Parity: bugserial.NoParity,
|
|
StopBits: bugserial.OneStopBit,
|
|
}
|
|
|
|
if device.BaudRate <= 0 {
|
|
return nil, fmt.Errorf(
|
|
"invalid baud rate: %d",
|
|
device.BaudRate,
|
|
)
|
|
}
|
|
|
|
switch device.DataBits {
|
|
case 5, 6, 7, 8:
|
|
// Supported by go.bug.st/serial.
|
|
|
|
default:
|
|
return nil, fmt.Errorf(
|
|
"unsupported data bits: %d",
|
|
device.DataBits,
|
|
)
|
|
}
|
|
|
|
switch device.Parity {
|
|
case "none":
|
|
mode.Parity = bugserial.NoParity
|
|
|
|
case "odd":
|
|
mode.Parity = bugserial.OddParity
|
|
|
|
case "even":
|
|
mode.Parity = bugserial.EvenParity
|
|
|
|
default:
|
|
return nil, fmt.Errorf(
|
|
"unsupported parity: %q",
|
|
device.Parity,
|
|
)
|
|
}
|
|
|
|
switch device.StopBits {
|
|
case 1:
|
|
mode.StopBits = bugserial.OneStopBit
|
|
|
|
case 2:
|
|
mode.StopBits = bugserial.TwoStopBits
|
|
|
|
default:
|
|
return nil, fmt.Errorf(
|
|
"unsupported stop bits: %d",
|
|
device.StopBits,
|
|
)
|
|
}
|
|
|
|
return mode, nil
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Read / Write
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Read reads data from the serial connection.
|
|
func (c *Connection) Read(p []byte) (int, error) {
|
|
if c == nil {
|
|
return 0, io.ErrClosedPipe
|
|
}
|
|
|
|
c.mu.Lock()
|
|
port := c.port
|
|
c.mu.Unlock()
|
|
|
|
if port == nil {
|
|
return 0, io.ErrClosedPipe
|
|
}
|
|
|
|
return port.Read(p)
|
|
}
|
|
|
|
// Write writes data to the serial connection.
|
|
func (c *Connection) Write(p []byte) (int, error) {
|
|
if c == nil {
|
|
return 0, io.ErrClosedPipe
|
|
}
|
|
|
|
c.mu.Lock()
|
|
port := c.port
|
|
c.mu.Unlock()
|
|
|
|
if port == nil {
|
|
return 0, io.ErrClosedPipe
|
|
}
|
|
|
|
return port.Write(p)
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Close
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Close closes the serial connection.
|
|
func (c *Connection) Close() error {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
|
|
c.mu.Lock()
|
|
|
|
if c.port == nil {
|
|
c.mu.Unlock()
|
|
return nil
|
|
}
|
|
|
|
port := c.port
|
|
c.port = nil
|
|
|
|
c.mu.Unlock()
|
|
|
|
return port.Close()
|
|
}
|