Skip to content
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
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/jacobsa/go-serial

go 1.17

require golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7 h1:c20P3CcPbopVp2f7099WLOqSNKURf30Z0uq66HpijZY=
golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
37 changes: 31 additions & 6 deletions serial/open_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ package serial

import (
"errors"
"io"
"fmt"
"os"
"syscall"
"unsafe"

"golang.org/x/sys/unix"
)
import "os"
import "syscall"
import "unsafe"

// termios types
type cc_t byte
Expand Down Expand Up @@ -196,7 +198,9 @@ func convertOptions(options OpenOptions) (*termios, error) {
return &result, nil
}

func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {
type port struct{ *os.File }

func openInternal(options OpenOptions) (*port, error) {
// Open the serial port in non-blocking mode, since otherwise the OS will
// wait for the CARRIER line to be asserted.
file, err :=
Expand Down Expand Up @@ -254,5 +258,26 @@ func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {
}

// We're done.
return file, nil
return &port{file}, nil
}

func (p *port) Flush(in, out bool) error {
var what int
if in && out {
what = 0x03
} else if in {
what = 0x01
} else if out {
what = 0x02
} else {
return nil
}
if err := unix.IoctlSetPointerInt(int(p.Fd()), unix.TIOCFLUSH, what); err != nil {
return fmt.Errorf("ioctl(TIOCFLUSH) failed: %w", err)
}
return nil
}

func (p *port) PortName() string {
return p.Name()
}
2 changes: 1 addition & 1 deletion serial/open_freebsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ package serial

import "io"

func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {
func openInternal(options OpenOptions) (Port, error) {
return nil, "Not implemented on this OS."
}
30 changes: 27 additions & 3 deletions serial/open_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package serial

import (
"errors"
"io"
"fmt"
"os"
"syscall"
"unsafe"
Expand Down Expand Up @@ -138,7 +138,9 @@ func makeTermios2(options OpenOptions) (*termios2, error) {
return t2, nil
}

func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {
type port struct{ *os.File }

func openInternal(options OpenOptions) (*port, error) {

file, openErr :=
os.OpenFile(
Expand Down Expand Up @@ -205,5 +207,27 @@ func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {
}
}

return file, nil
return &port{file}, nil
}

func (p *port) Flush(in, out bool) error {
var queueSel int
if in && out {
queueSel = unix.TCIOFLUSH
} else if in {
queueSel = unix.TCIFLUSH
} else if out {
queueSel = unix.TCOFLUSH
} else {
return nil
}
// TCFLSH = 0x540B aka tcflush()
if err := unix.IoctlSetInt(int(p.Fd()), 0x540B, queueSel); err != nil {
return fmt.Errorf("tcflush failed: %w", err)
}
return nil
}

func (p *port) PortName() string {
return p.Name()
}
35 changes: 32 additions & 3 deletions serial/open_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package serial

import (
"fmt"
"io"
"os"
"sync"
"syscall"
Expand Down Expand Up @@ -49,7 +48,7 @@ type structTimeouts struct {
WriteTotalTimeoutConstant uint32
}

func openInternal(options OpenOptions) (io.ReadWriteCloser, error) {
func openInternal(options OpenOptions) (*serialPort, error) {
if len(options.PortName) > 0 && options.PortName[0] != '\\' {
options.PortName = "\\\\.\\" + options.PortName
}
Expand Down Expand Up @@ -139,14 +138,39 @@ func (p *serialPort) Read(buf []byte) (int, error) {
return getOverlappedResult(p.fd, p.ro)
}

func (p *serialPort) Fd() uintptr {
return p.f.Fd()
}

func (p *serialPort) Flush(in, out bool) error {
var flags uintptr
if in {
flags |= 0x0008
}
if out {
flags |= 0x0004
}
if flags == 0 {
return nil
}

// BOOL PurgeComm(HANDLE hFile, DWORD dwFlags)
r, _, err := syscall.Syscall(nPurgeComm, 2, p.Fd(), flags, 0)
if r == 0 {
return fmt.Errorf("PurgeComm failed: %w", err)
}
return nil
}

var (
nSetCommState,
nSetCommTimeouts,
nSetCommMask,
nSetupComm,
nGetOverlappedResult,
nCreateEvent,
nResetEvent uintptr
nResetEvent,
nPurgeComm uintptr
)

func init() {
Expand All @@ -163,6 +187,7 @@ func init() {
nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult")
nCreateEvent = getProcAddr(k32, "CreateEventW")
nResetEvent = getProcAddr(k32, "ResetEvent")
nPurgeComm = getProcAddr(k32, "PurgeComm")
}

func getProcAddr(lib syscall.Handle, name string) uintptr {
Expand Down Expand Up @@ -320,3 +345,7 @@ func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int,

return n, nil
}

func (p *serialPort) PortName() string {
return p.f.Name()
}
12 changes: 10 additions & 2 deletions serial/serial.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,16 @@ type OpenOptions struct {
Rs485DelayRtsAfterSend int
}

// Open creates an io.ReadWriteCloser based on the supplied options struct.
func Open(options OpenOptions) (io.ReadWriteCloser, error) {
// Port defines the serial port.
type Port interface {
io.ReadWriteCloser
Fd() uintptr
Flush(in, out bool) error
PortName() string
}

// Open creates a Port based on the supplied options struct.
func Open(options OpenOptions) (Port, error) {
// Redirect to the OS-specific function.
return openInternal(options)
}
Expand Down