From e1a84d83b551db5c01ed6eaac0ca0c10b979dd72 Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Sat, 9 Dec 2023 11:30:51 +1300 Subject: [PATCH 1/2] Merge of RS485 --- serial.go | 29 ++++++++++++++++++++++++++++ serial_unix.go | 48 +++++++++++++++++++++++++++++++++++++++++++++++ serial_windows.go | 9 +++++++++ 3 files changed, 86 insertions(+) diff --git a/serial.go b/serial.go index abfd7f9..e06c698 100644 --- a/serial.go +++ b/serial.go @@ -101,6 +101,7 @@ type Mode struct { Parity Parity // Parity (see Parity type for more info) StopBits StopBits // Stop bits (see StopBits type for more info) InitialStatusBits *ModemOutputBits // Initial output modem bits status (if nil defaults to DTR=true and RTS=true) + RS485 RS485Config // RS485 configuration } // Parity describes a serial port parity setting @@ -131,6 +132,22 @@ const ( TwoStopBits ) +// RS485Config -- platform independent RS485 config. Thie structure is ignored unless Enable is true. +type RS485Config struct { + // Enable RS485 support + Enabled bool + // Delay RTS prior to send + DelayRtsBeforeSend time.Duration + // Delay RTS after send + DelayRtsAfterSend time.Duration + // Set RTS high during send + RtsHighDuringSend bool + // Set RTS high after send + RtsHighAfterSend bool + // Rx during Tx + RxDuringTx bool +} + // PortError is a platform independent error type for serial ports type PortError struct { code PortErrorCode @@ -165,6 +182,12 @@ const ( PortClosed // FunctionNotImplemented the requested function is not implemented FunctionNotImplemented + // ReadFailed indicates the read failed + ReadFailed + // ConfigureRS485Error indicates an error configuring RS485 on the platform + ConfigureRS485Error + // NoPlatformSupportForRS485 indicates no platform support for RS485 + NoPlatformSupportForRS485 ) // EncodedErrorString returns a string explaining the error code @@ -194,6 +217,12 @@ func (e PortError) EncodedErrorString() string { return "Port has been closed" case FunctionNotImplemented: return "Function not implemented" + case ReadFailed: + return "Read failed" + case ConfigureRS485Error: + return "Error configuring RS485 on the platform" + case NoPlatformSupportForRS485: + return "Platform does not support RS485" default: return "Other error" } diff --git a/serial_unix.go b/serial_unix.go index f6ec896..7b7304b 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -15,6 +15,7 @@ import ( "sync" "sync/atomic" "time" + "unsafe" "go.bug.st/serial/unixutils" "golang.org/x/sys/unix" @@ -29,6 +30,22 @@ type unixPort struct { opened uint32 } +const ( + rs485Enabled = 1 << 0 + rs485RTSOnSend = 1 << 1 + rs485RTSAfterSend = 1 << 2 + rs485RXDuringTX = 1 << 4 + rs485Tiocs = 0x542f +) + +// rs485_ioctl_opts is used to configure RS485 options in the driver +type rs485IoctlOpts struct { + flags uint32 + delayRtsBeforeSend uint32 + delayRtsAfterSend uint32 + padding [5]uint32 +} + func (port *unixPort) Close() error { if !atomic.CompareAndSwapUint32(&port.opened, 1, 0) { return nil @@ -277,6 +294,12 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) { port.acquireExclusiveAccess() + // Enable RS485, if requested + if err = port.enableRS485(&mode.RS485); err != nil { + port.Close() + return nil, err + } + // This pipe is used as a signal to cancel blocking Read pipe := &unixutils.Pipe{} if err := pipe.Open(); err != nil { @@ -467,3 +490,28 @@ func (port *unixPort) acquireExclusiveAccess() error { func (port *unixPort) releaseExclusiveAccess() error { return unix.IoctlSetInt(port.handle, unix.TIOCNXCL, 0) } + +// enableRS485 enables RS485 functionality of driver via an ioctl if the config says so +func (port *unixPort) enableRS485(config *RS485Config) error { + if !config.Enabled { + return nil + } + rs485 := rs485IoctlOpts{ + rs485Enabled, + uint32(config.DelayRtsBeforeSend / time.Millisecond), + uint32(config.DelayRtsAfterSend / time.Millisecond), + [5]uint32{0, 0, 0, 0, 0}, + } + + if config.RtsHighDuringSend { + rs485.flags |= rs485RTSOnSend + } + if config.RtsHighAfterSend { + rs485.flags |= rs485RTSAfterSend + } + if config.RxDuringTx { + rs485.flags |= rs485RXDuringTX + } + + return unix.IoctlSetInt(port.handle, rs485Tiocs, int(uintptr(unsafe.Pointer(&rs485)))) +} diff --git a/serial_windows.go b/serial_windows.go index 287856a..2e8a4cc 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -479,3 +479,12 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { } return port, nil } + +// enableRS485 enables RS485 functionality of driver via an ioctl if the config says so +func (port *windowsPort) enableRS485(config *RS485Config) error { + if !config.Enabled { + return &PortError{code: NoPlatformSupportForRS485, causedBy: nil} + } + + return nil +} From bdfda6f5029033547b64c2b9d98e0a7ca690b2ff Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Tue, 28 May 2024 07:28:21 +1200 Subject: [PATCH 2/2] Remove windows call - it is no longer used in this PR --- serial_windows.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/serial_windows.go b/serial_windows.go index 0aca838..e350f86 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -479,12 +479,3 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { } return port, nil } - -// enableRS485 enables RS485 functionality of driver via an ioctl if the config says so -func (port *windowsPort) enableRS485(config *RS485Config) error { - if !config.Enabled { - return &PortError{code: NoPlatformSupportForRS485, causedBy: nil} - } - - return nil -}