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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module go.bug.st/serial
go 1.17

require (
github.com/creack/goselect v0.1.2
github.com/creack/goselect v0.1.3
github.com/stretchr/testify v1.8.4
golang.org/x/sys v0.19.0
)
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/creack/goselect v0.1.2 h1:2DNy14+JPjRBgPzAd1thbQp4BSIihxcBf0IXhQXDRa0=
github.com/creack/goselect v0.1.2/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/creack/goselect v0.1.3 h1:MaGNMclRo7P2Jl21hBpR1Cn33ITSbKP6E49RtfblLKc=
github.com/creack/goselect v0.1.3/go.mod h1:a/NhLweNvqIYMuxcMOuWY516Cimucms3DglDzQP3hKY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
2 changes: 1 addition & 1 deletion serial_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func nativeOpen(portName string, mode *Mode) (*unixPort, error) {
// Explicitly disable RTS/CTS flow control
setTermSettingsCtsRts(false, settings)

if port.setTermSettings(settings) != nil {
if err = port.setTermSettings(settings); err != nil {
port.Close()
return nil, &PortError{code: InvalidSerialPort, causedBy: fmt.Errorf("error setting term settings: %w", err)}
}
Expand Down
88 changes: 63 additions & 25 deletions serial_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package serial

import (
"errors"
"strings"
"sync"
"syscall"
"time"
Expand All @@ -28,8 +29,9 @@ import (
)

type windowsPort struct {
mu sync.Mutex
handle windows.Handle
mu sync.Mutex
handle windows.Handle
hasTimeout bool
}

func nativeGetPortsList() ([]string, error) {
Expand All @@ -44,12 +46,22 @@ func nativeGetPortsList() ([]string, error) {
}
defer key.Close()

list, err := key.ReadValueNames(0)
names, err := key.ReadValueNames(0)
if err != nil {
return nil, &PortError{code: ErrorEnumeratingPorts, causedBy: err}
}

return list, nil
var values []string
for _, n := range names {
v, _, err := key.GetStringValue(n)
if err != nil || v == "" {
continue
}

values = append(values, v)
}

return values, nil
}

func (port *windowsPort) Close() error {
Expand All @@ -72,26 +84,33 @@ func (port *windowsPort) Read(p []byte) (int, error) {
}
defer windows.CloseHandle(ev.HEvent)

err = windows.ReadFile(port.handle, p, &readed, ev)
if err == windows.ERROR_IO_PENDING {
err = windows.GetOverlappedResult(port.handle, ev, &readed, true)
}
switch err {
case nil:
// operation completed successfully
case windows.ERROR_OPERATION_ABORTED:
// port may have been closed
return int(readed), &PortError{code: PortClosed, causedBy: err}
default:
// error happened
return int(readed), err
}
if readed > 0 {
return int(readed), nil
}
for {
err = windows.ReadFile(port.handle, p, &readed, ev)
if err == windows.ERROR_IO_PENDING {
err = windows.GetOverlappedResult(port.handle, ev, &readed, true)
}
switch err {
case nil:
// operation completed successfully
case windows.ERROR_OPERATION_ABORTED:
// port may have been closed
return int(readed), &PortError{code: PortClosed, causedBy: err}
default:
// error happened
return int(readed), err
}
if readed > 0 {
return int(readed), nil
}

// Timeout
return 0, nil
// Timeout
port.mu.Lock()
hasTimeout := port.hasTimeout
port.mu.Unlock()
if hasTimeout {
return 0, nil
}
}
}

func (port *windowsPort) Write(p []byte) (int, error) {
Expand Down Expand Up @@ -275,10 +294,19 @@ func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) {
}

func (port *windowsPort) SetReadTimeout(timeout time.Duration) error {
// This is a brutal hack to make the CH340 chipset work properly.
// Normally this value should be 0xFFFFFFFE but, after a lot of
// tinkering, I discovered that any value with the highest
// bit set will make the CH340 driver behave like the timeout is 0,
// in the best cases leading to a spinning loop...
// (could this be a wrong signed vs unsigned conversion in the driver?)
// https://github.com/arduino/serial-monitor/issues/112
const MaxReadTotalTimeoutConstant = 0x7FFFFFFE

commTimeouts := &windows.CommTimeouts{
ReadIntervalTimeout: 0xFFFFFFFF,
ReadTotalTimeoutMultiplier: 0xFFFFFFFF,
ReadTotalTimeoutConstant: 0xFFFFFFFE,
ReadTotalTimeoutConstant: MaxReadTotalTimeoutConstant,
WriteTotalTimeoutConstant: 0,
WriteTotalTimeoutMultiplier: 0,
}
Expand All @@ -287,12 +315,20 @@ func (port *windowsPort) SetReadTimeout(timeout time.Duration) error {
if ms > 0xFFFFFFFE || ms < 0 {
return &PortError{code: InvalidTimeoutValue}
}

if ms > MaxReadTotalTimeoutConstant {
ms = MaxReadTotalTimeoutConstant
}

commTimeouts.ReadTotalTimeoutConstant = uint32(ms)
}

port.mu.Lock()
defer port.mu.Unlock()
if err := windows.SetCommTimeouts(port.handle, commTimeouts); err != nil {
return &PortError{code: InvalidTimeoutValue, causedBy: err}
}
port.hasTimeout = (timeout != NoTimeout)

return nil
}
Expand All @@ -317,7 +353,9 @@ func createOverlappedEvent() (*windows.Overlapped, error) {
}

func nativeOpen(portName string, mode *Mode) (*windowsPort, error) {
portName = "\\\\.\\" + portName
if !strings.HasPrefix(portName, `\\.\`) {
portName = `\\.\` + portName
}
path, err := windows.UTF16PtrFromString(portName)
if err != nil {
return nil, err
Expand Down