Skip to content

Feature RS485 #170

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

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
17 changes: 17 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
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))))
}