diff --git a/serial_windows.go b/serial_windows.go index ed04e6b..68f7f76 100644 --- a/serial_windows.go +++ b/serial_windows.go @@ -65,13 +65,32 @@ func (port *windowsPort) Close() error { func (port *windowsPort) Read(p []byte) (int, error) { var readed uint32 params := &dcb{} + ev, err := createOverlappedEvent() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(ev.HEvent) for { - if err := syscall.ReadFile(port.handle, p, &readed, nil); err != nil { + err := syscall.ReadFile(port.handle, p, &readed, ev) + switch err { + case nil: + // operation completed successfully + case syscall.ERROR_IO_PENDING: + // wait for overlapped I/O to complete + if err := getOverlappedResult(port.handle, ev, &readed, true); err != nil { + return int(readed), err + } + default: + // error happened return int(readed), err } + if readed > 0 { return int(readed), nil } + if err := resetEvent(ev.HEvent); err != nil { + return 0, err + } // 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 @@ -87,7 +106,16 @@ func (port *windowsPort) Read(p []byte) (int, error) { func (port *windowsPort) Write(p []byte) (int, error) { var writed uint32 - err := syscall.WriteFile(port.handle, p, &writed, nil) + ev, err := createOverlappedEvent() + if err != nil { + return 0, err + } + defer syscall.CloseHandle(ev.HEvent) + err = syscall.WriteFile(port.handle, p, &writed, ev) + if err == syscall.ERROR_IO_PENDING { + // wait for write to complete + err = getOverlappedResult(port.handle, ev, &writed, true) + } return int(writed), err } @@ -295,6 +323,15 @@ func (port *windowsPort) GetModemStatusBits() (*ModemStatusBits, error) { }, nil } +//sys createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) = CreateEventW +//sys resetEvent(handle syscall.Handle) (err error) = ResetEvent +//sys getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) = GetOverlappedResult + +func createOverlappedEvent() (*syscall.Overlapped, error) { + h, err := createEvent(nil, true, false, nil) + return &syscall.Overlapped{HEvent: h}, err +} + func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { portName = "\\\\.\\" + portName path, err := syscall.UTF16PtrFromString(portName) @@ -306,7 +343,7 @@ func nativeOpen(portName string, mode *Mode) (*windowsPort, error) { syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, - 0, //syscall.FILE_FLAG_OVERLAPPED, + syscall.FILE_FLAG_OVERLAPPED, 0) if err != nil { switch err { diff --git a/syscall_windows.go b/syscall_windows.go index dfa3f84..a72df76 100644 --- a/syscall_windows.go +++ b/syscall_windows.go @@ -15,12 +15,15 @@ var ( modadvapi32 = windows.NewLazySystemDLL("advapi32.dll") modkernel32 = windows.NewLazySystemDLL("kernel32.dll") - procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") - procGetCommState = modkernel32.NewProc("GetCommState") - procSetCommState = modkernel32.NewProc("SetCommState") - procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") - procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") - procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") + procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW") + procGetCommState = modkernel32.NewProc("GetCommState") + procSetCommState = modkernel32.NewProc("SetCommState") + procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") + procEscapeCommFunction = modkernel32.NewProc("EscapeCommFunction") + procGetCommModemStatus = modkernel32.NewProc("GetCommModemStatus") + procCreateEventW = modkernel32.NewProc("CreateEventW") + procResetEvent = modkernel32.NewProc("ResetEvent") + procGetOverlappedResult = modkernel32.NewProc("GetOverlappedResult") ) func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, class *uint16, value *uint16, valueLen *uint32) (regerrno error) { @@ -78,3 +81,58 @@ func getCommModemStatus(handle syscall.Handle, bits *uint32) (res bool) { res = r0 != 0 return } + +func createEvent(eventAttributes *uint32, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) { + var _p0 uint32 + if manualReset { + _p0 = 1 + } else { + _p0 = 0 + } + var _p1 uint32 + if initialState { + _p1 = 1 + } else { + _p1 = 0 + } + r0, _, e1 := syscall.Syscall6(procCreateEventW.Addr(), 4, uintptr(unsafe.Pointer(eventAttributes)), uintptr(_p0), uintptr(_p1), uintptr(unsafe.Pointer(name)), 0, 0) + handle = syscall.Handle(r0) + if handle == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func resetEvent(handle syscall.Handle) (err error) { + r1, _, e1 := syscall.Syscall(procResetEvent.Addr(), 1, uintptr(handle), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func getOverlappedResult(handle syscall.Handle, overlapEvent *syscall.Overlapped, n *uint32, wait bool) (err error) { + var _p0 uint32 + if wait { + _p0 = 1 + } else { + _p0 = 0 + } + r1, _, e1 := syscall.Syscall6(procGetOverlappedResult.Addr(), 4, uintptr(handle), uintptr(unsafe.Pointer(overlapEvent)), uintptr(unsafe.Pointer(n)), uintptr(_p0), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = error(e1) + } else { + err = syscall.EINVAL + } + } + return +}