Skip to content
Merged
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
69 changes: 47 additions & 22 deletions serial_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ import (
)

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

func nativeGetPortsList() ([]string, error) {
Expand Down Expand Up @@ -72,26 +73,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 +283,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 +304,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 Down