Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature RS485 #170

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions serial.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"
}
Expand Down
48 changes: 48 additions & 0 deletions serial_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"sync"
"sync/atomic"
"time"
"unsafe"

"go.bug.st/serial/unixutils"
"golang.org/x/sys/unix"
Expand All @@ -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
Expand Down Expand Up @@ -279,6 +296,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 {
Expand Down Expand Up @@ -465,3 +488,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))))
}