From 99469f6654496851abbca086000c017b09e5d78d Mon Sep 17 00:00:00 2001 From: Sven Schwermer Date: Thu, 23 Sep 2021 09:30:20 +0200 Subject: [PATCH 1/4] Convert to go module --- go.mod | 5 +++++ go.sum | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..956f20b --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/jacobsa/go-serial + +go 1.17 + +require golang.org/x/sys v0.0.0-20210923061019-b8560ed6a9b7 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d570d24 --- /dev/null +++ b/go.sum @@ -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= From a69b991317900c497cb79d025aa5455a993c1d3c Mon Sep 17 00:00:00 2001 From: Sven Schwermer Date: Thu, 23 Sep 2021 09:30:55 +0200 Subject: [PATCH 2/4] Make port's file descriptor available --- serial/open_darwin.go | 9 ++++----- serial/open_freebsd.go | 2 +- serial/open_linux.go | 3 +-- serial/open_windows.go | 7 +++++-- serial/serial.go | 10 ++++++++-- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/serial/open_darwin.go b/serial/open_darwin.go index 6e50629..b826a91 100644 --- a/serial/open_darwin.go +++ b/serial/open_darwin.go @@ -27,11 +27,10 @@ package serial import ( "errors" - "io" + "os" + "syscall" + "unsafe" ) -import "os" -import "syscall" -import "unsafe" // termios types type cc_t byte @@ -196,7 +195,7 @@ func convertOptions(options OpenOptions) (*termios, error) { return &result, nil } -func openInternal(options OpenOptions) (io.ReadWriteCloser, error) { +func openInternal(options OpenOptions) (*os.File, error) { // Open the serial port in non-blocking mode, since otherwise the OS will // wait for the CARRIER line to be asserted. file, err := diff --git a/serial/open_freebsd.go b/serial/open_freebsd.go index f1e8990..2901347 100644 --- a/serial/open_freebsd.go +++ b/serial/open_freebsd.go @@ -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." } diff --git a/serial/open_linux.go b/serial/open_linux.go index ccce12c..c445adf 100644 --- a/serial/open_linux.go +++ b/serial/open_linux.go @@ -2,7 +2,6 @@ package serial import ( "errors" - "io" "os" "syscall" "unsafe" @@ -138,7 +137,7 @@ func makeTermios2(options OpenOptions) (*termios2, error) { return t2, nil } -func openInternal(options OpenOptions) (io.ReadWriteCloser, error) { +func openInternal(options OpenOptions) (*os.File, error) { file, openErr := os.OpenFile( diff --git a/serial/open_windows.go b/serial/open_windows.go index fb398f1..b4dab31 100644 --- a/serial/open_windows.go +++ b/serial/open_windows.go @@ -16,7 +16,6 @@ package serial import ( "fmt" - "io" "os" "sync" "syscall" @@ -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 } @@ -139,6 +138,10 @@ func (p *serialPort) Read(buf []byte) (int, error) { return getOverlappedResult(p.fd, p.ro) } +func (p *serialPort) Fd() uintptr { + return p.f.Fd() +} + var ( nSetCommState, nSetCommTimeouts, diff --git a/serial/serial.go b/serial/serial.go index 9d9f26b..bb77e56 100644 --- a/serial/serial.go +++ b/serial/serial.go @@ -159,8 +159,14 @@ 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 +} + +// 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) } From ba49fa6f702b398d7d8d1b58566df3d47eac864a Mon Sep 17 00:00:00 2001 From: Sven Schwermer Date: Wed, 29 Sep 2021 09:14:53 +0200 Subject: [PATCH 3/4] Add flush function --- serial/open_darwin.go | 26 ++++++++++++++++++++++++-- serial/open_linux.go | 25 +++++++++++++++++++++++-- serial/open_windows.go | 24 +++++++++++++++++++++++- serial/serial.go | 1 + 4 files changed, 71 insertions(+), 5 deletions(-) diff --git a/serial/open_darwin.go b/serial/open_darwin.go index b826a91..7867ac2 100644 --- a/serial/open_darwin.go +++ b/serial/open_darwin.go @@ -27,9 +27,12 @@ package serial import ( "errors" + "fmt" "os" "syscall" "unsafe" + + "golang.org/x/sys/unix" ) // termios types @@ -195,7 +198,9 @@ func convertOptions(options OpenOptions) (*termios, error) { return &result, nil } -func openInternal(options OpenOptions) (*os.File, 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 := @@ -253,5 +258,22 @@ func openInternal(options OpenOptions) (*os.File, 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 } diff --git a/serial/open_linux.go b/serial/open_linux.go index c445adf..c83436c 100644 --- a/serial/open_linux.go +++ b/serial/open_linux.go @@ -2,6 +2,7 @@ package serial import ( "errors" + "fmt" "os" "syscall" "unsafe" @@ -137,7 +138,9 @@ func makeTermios2(options OpenOptions) (*termios2, error) { return t2, nil } -func openInternal(options OpenOptions) (*os.File, error) { +type port struct{ *os.File } + +func openInternal(options OpenOptions) (*port, error) { file, openErr := os.OpenFile( @@ -204,5 +207,23 @@ func openInternal(options OpenOptions) (*os.File, 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 } diff --git a/serial/open_windows.go b/serial/open_windows.go index b4dab31..be1cf15 100644 --- a/serial/open_windows.go +++ b/serial/open_windows.go @@ -142,6 +142,26 @@ 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, @@ -149,7 +169,8 @@ var ( nSetupComm, nGetOverlappedResult, nCreateEvent, - nResetEvent uintptr + nResetEvent, + nPurgeComm uintptr ) func init() { @@ -166,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 { diff --git a/serial/serial.go b/serial/serial.go index bb77e56..8b085d6 100644 --- a/serial/serial.go +++ b/serial/serial.go @@ -163,6 +163,7 @@ type OpenOptions struct { type Port interface { io.ReadWriteCloser Fd() uintptr + Flush(in, out bool) error } // Open creates a Port based on the supplied options struct. From 2d2ac6486f20b6312b95cc3b4322712d771d3cbb Mon Sep 17 00:00:00 2001 From: Sven Schwermer Date: Wed, 29 Sep 2021 16:28:06 +0200 Subject: [PATCH 4/4] Add PortName() method --- serial/open_darwin.go | 4 ++++ serial/open_linux.go | 4 ++++ serial/open_windows.go | 4 ++++ serial/serial.go | 1 + 4 files changed, 13 insertions(+) diff --git a/serial/open_darwin.go b/serial/open_darwin.go index 7867ac2..1137200 100644 --- a/serial/open_darwin.go +++ b/serial/open_darwin.go @@ -277,3 +277,7 @@ func (p *port) Flush(in, out bool) error { } return nil } + +func (p *port) PortName() string { + return p.Name() +} diff --git a/serial/open_linux.go b/serial/open_linux.go index c83436c..6283fe1 100644 --- a/serial/open_linux.go +++ b/serial/open_linux.go @@ -227,3 +227,7 @@ func (p *port) Flush(in, out bool) error { } return nil } + +func (p *port) PortName() string { + return p.Name() +} diff --git a/serial/open_windows.go b/serial/open_windows.go index be1cf15..7e4f00d 100644 --- a/serial/open_windows.go +++ b/serial/open_windows.go @@ -345,3 +345,7 @@ func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int, return n, nil } + +func (p *serialPort) PortName() string { + return p.f.Name() +} diff --git a/serial/serial.go b/serial/serial.go index 8b085d6..652da28 100644 --- a/serial/serial.go +++ b/serial/serial.go @@ -164,6 +164,7 @@ type Port interface { io.ReadWriteCloser Fd() uintptr Flush(in, out bool) error + PortName() string } // Open creates a Port based on the supplied options struct.