diff --git a/serial_windows.go b/serial_windows.go index 46803c6..6b6d0ee 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -18,13 +18,15 @@ package serial */ import ( - "syscall" "sync" + "syscall" + "time" ) type windowsPort struct { - mu sync.Mutex - handle syscall.Handle + mu sync.Mutex + handle syscall.Handle + readTimeoutCycles int64 } func nativeGetPortsList() ([]string, error) { @@ -80,6 +82,8 @@ func (port *windowsPort) Read(p []byte) (int, error) { return 0, err } defer syscall.CloseHandle(ev.HEvent) + + cycles := int64(0) for { err := syscall.ReadFile(port.handle, p, &readed, ev) if err == syscall.ERROR_IO_PENDING { @@ -103,6 +107,14 @@ func (port *windowsPort) Read(p []byte) (int, error) { return 0, err } + if port.readTimeoutCycles != -1 { + cycles++ + if cycles == port.readTimeoutCycles { + // Timeout + return 0, nil + } + } + // At the moment it seems that the only reliable way to check if // a serial port is alive in Windows is to check if the SetCommState // function fails. @@ -370,6 +382,31 @@ func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { }, nil } +func (port *windowsPort) SetReadTimeout(timeout time.Duration) error { + var cycles, cycleDuration int64 + if timeout == NoTimeout { + cycles = -1 + cycleDuration = 1000 + } else { + cycles = timeout.Milliseconds()/1000 + 1 + cycleDuration = timeout.Milliseconds() / cycles + } + + err := setCommTimeouts(port.handle, &commTimeouts{ + ReadIntervalTimeout: 0xFFFFFFFF, + ReadTotalTimeoutMultiplier: 0xFFFFFFFF, + ReadTotalTimeoutConstant: uint32(cycleDuration), + WriteTotalTimeoutConstant: 0, + WriteTotalTimeoutMultiplier: 0, + }) + if err != nil { + return &PortError{code: InvalidTimeoutValue, causedBy: err} + } + port.readTimeoutCycles = cycles + + return nil +} + func createOverlappedEvent() (*syscall.Overlapped, error) { h, err := createEvent(nil, true, false, nil) return &syscall.Overlapped{HEvent: h}, err @@ -435,18 +472,9 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { return nil, &PortError{code: InvalidSerialPort} } - // Set timeouts to 1 second - timeouts := &commTimeouts{ - ReadIntervalTimeout: 0xFFFFFFFF, - ReadTotalTimeoutMultiplier: 0xFFFFFFFF, - ReadTotalTimeoutConstant: 1000, // 1 sec - WriteTotalTimeoutConstant: 0, - WriteTotalTimeoutMultiplier: 0, - } - if setCommTimeouts(port.handle, timeouts) != nil { + if port.SetReadTimeout(NoTimeout) != nil { port.Close() return nil, &PortError{code: InvalidSerialPort} } - return port, nil }