From e1a84d83b551db5c01ed6eaac0ca0c10b979dd72 Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Sat, 9 Dec 2023 11:30:51 +1300 Subject: [PATCH 1/5] 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/5] 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 -} From 4162208bf255b6a1a6ef7f70180b568468890138 Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Thu, 20 Mar 2025 11:34:52 +1300 Subject: [PATCH 3/5] rs485: Remove custom ioctl definition & use unix constant --- serial_unix.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/serial_unix.go b/serial_unix.go index 5743c09..56a4ad3 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -35,7 +35,6 @@ const ( rs485RTSOnSend = 1 << 1 rs485RTSAfterSend = 1 << 2 rs485RXDuringTX = 1 << 4 - rs485Tiocs = 0x542f ) // rs485_ioctl_opts is used to configure RS485 options in the driver @@ -511,5 +510,5 @@ func (port *unixPort) enableRS485(config *RS485Config) error { rs485.flags |= rs485RXDuringTX } - return unix.IoctlSetInt(port.handle, rs485Tiocs, int(uintptr(unsafe.Pointer(&rs485)))) + return unix.IoctlSetInt(port.handle, unix.TIOCSRS485, int(uintptr(unsafe.Pointer(&rs485)))) } From 073fb2554fd74dd24cd89be29a074309627a4717 Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Thu, 20 Mar 2025 11:37:40 +1300 Subject: [PATCH 4/5] Revert "rs485: Remove custom ioctl definition & use unix constant" This reverts commit 4162208bf255b6a1a6ef7f70180b568468890138. Looks like this constant wasn't introduced in the Go version we're on --- serial_unix.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/serial_unix.go b/serial_unix.go index 56a4ad3..5743c09 100644 --- a/serial_unix.go +++ b/serial_unix.go @@ -35,6 +35,7 @@ const ( rs485RTSOnSend = 1 << 1 rs485RTSAfterSend = 1 << 2 rs485RXDuringTX = 1 << 4 + rs485Tiocs = 0x542f ) // rs485_ioctl_opts is used to configure RS485 options in the driver @@ -510,5 +511,5 @@ func (port *unixPort) enableRS485(config *RS485Config) error { rs485.flags |= rs485RXDuringTX } - return unix.IoctlSetInt(port.handle, unix.TIOCSRS485, int(uintptr(unsafe.Pointer(&rs485)))) + return unix.IoctlSetInt(port.handle, rs485Tiocs, int(uintptr(unsafe.Pointer(&rs485)))) } From 0e1abfa14e94ae1d561d6a79068bae9e83b7cf08 Mon Sep 17 00:00:00 2001 From: Andre Renaud Date: Fri, 21 Mar 2025 13:37:07 +1300 Subject: [PATCH 5/5] Remove unncecessary new error codes --- serial.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/serial.go b/serial.go index bfa1383..497c449 100644 --- a/serial.go +++ b/serial.go @@ -182,12 +182,6 @@ 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 @@ -217,12 +211,6 @@ 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" }