diff --git a/include/serial/impl/win.h b/include/serial/impl/win.h index 2c0c6cde..fca54a5d 100644 --- a/include/serial/impl/win.h +++ b/include/serial/impl/win.h @@ -195,9 +195,12 @@ class serial::Serial::SerialImpl { flowcontrol_t flowcontrol_; // Flow Control // Mutex used to lock the read functions - HANDLE read_mutex; + HANDLE read_mutex_; // Mutex used to lock the write functions - HANDLE write_mutex; + HANDLE write_mutex_; + + OVERLAPPED ov_read_; // OVERLAPPED read + OVERLAPPED ov_write_; // OVERLAPPED write }; } diff --git a/src/impl/win.cc b/src/impl/win.cc index 889e06f1..f36a986d 100644 --- a/src/impl/win.cc +++ b/src/impl/win.cc @@ -37,19 +37,24 @@ Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate, flowcontrol_t flowcontrol) : port_ (port.begin(), port.end()), fd_ (INVALID_HANDLE_VALUE), is_open_ (false), baudrate_ (baudrate), parity_ (parity), - bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol) + bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol), + ov_read_{}, ov_write_{} { if (port_.empty () == false) open (); - read_mutex = CreateMutex(NULL, false, NULL); - write_mutex = CreateMutex(NULL, false, NULL); + read_mutex_ = CreateMutex(NULL, false, NULL); + write_mutex_ = CreateMutex(NULL, false, NULL); + ov_read_.hEvent = CreateEvent(NULL, 1, 0, NULL); + ov_write_.hEvent = CreateEvent(NULL, 0, 0, NULL); } Serial::SerialImpl::~SerialImpl () { this->close(); - CloseHandle(read_mutex); - CloseHandle(write_mutex); + CloseHandle(read_mutex_); + CloseHandle(write_mutex_); + CloseHandle(ov_read_.hEvent); + CloseHandle(ov_write_.hEvent); } void @@ -70,7 +75,7 @@ Serial::SerialImpl::open () 0, 0, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0); if (fd_ == INVALID_HANDLE_VALUE) { @@ -277,7 +282,23 @@ Serial::SerialImpl::reconfigurePort () void Serial::SerialImpl::close () { - if (is_open_ == true) { + if (is_open_) { + // Cancel a blocking operation. + DWORD rc; + BOOL err; + err = GetOverlappedResult(fd_, &ov_read_, &rc, FALSE); + if (!err) { + rc = GetLastError(); + if (rc == ERROR_IO_PENDING || rc == ERROR_IO_INCOMPLETE) + CancelIoEx(fd_, &ov_read_); + } + err = GetOverlappedResult(fd_, &ov_write_, &rc, FALSE); + if (!err) { + rc = GetLastError(); + if (rc == ERROR_IO_PENDING || rc == ERROR_IO_INCOMPLETE) + CancelIoEx(fd_, &ov_write_); + } + if (fd_ != INVALID_HANDLE_VALUE) { int ret; ret = CloseHandle(fd_); @@ -315,16 +336,54 @@ Serial::SerialImpl::available () } bool -Serial::SerialImpl::waitReadable (uint32_t /*timeout*/) +Serial::SerialImpl::waitReadable (uint32_t timeout) { - THROW (IOException, "waitReadable is not implemented on Windows."); + COMSTAT cs; + DWORD error; + DWORD old_msk, msk, length; + if (!isOpen()) { + return false; + } + if (!GetCommMask(fd_, &old_msk)) { + stringstream ss; + ss << "Error while get mask of the serial port: " << GetLastError(); + THROW(IOException, ss.str().c_str()); + } + msk = 0; + SetCommMask(fd_, EV_RXCHAR | EV_ERR); + if (!WaitCommEvent(fd_, &msk, &ov_read_)) { + if (GetLastError() == ERROR_IO_PENDING) { + if (WaitForSingleObject(ov_read_.hEvent, (DWORD)timeout) == WAIT_TIMEOUT) { + SetCommMask(fd_, old_msk); + return false; + } + GetOverlappedResult(fd_, &ov_read_, &length, TRUE); + ResetEvent(ov_read_.hEvent); + } else { + ClearCommError(fd_, &error, &cs); + SetCommMask(fd_, old_msk); + return cs.cbInQue > 0; + } + } + SetCommMask(fd_, old_msk); + if (msk & EV_ERR) { + ClearCommError(fd_, &error, &cs); + return cs.cbInQue > 0; + } + if (msk & EV_RXCHAR) { + return true; + } return false; } void -Serial::SerialImpl::waitByteTimes (size_t /*count*/) +Serial::SerialImpl::waitByteTimes (size_t count) { - THROW (IOException, "waitByteTimes is not implemented on Windows."); + DWORD wait_time = timeout_.inter_byte_timeout * count; + HANDLE wait_event = CreateEvent(NULL, FALSE, FALSE, NULL); + ResetEvent(wait_event); + WaitForSingleObject(wait_event, wait_time); + CloseHandle(wait_event); } size_t @@ -333,13 +392,31 @@ Serial::SerialImpl::read (uint8_t *buf, size_t size) if (!is_open_) { throw PortNotOpenedException ("Serial::read"); } - DWORD bytes_read; - if (!ReadFile(fd_, buf, static_cast(size), &bytes_read, NULL)) { - stringstream ss; - ss << "Error while reading from the serial port: " << GetLastError(); - THROW (IOException, ss.str().c_str()); + if (size > 0) { + ResetEvent(ov_read_.hEvent); + DWORD bytes_read; + BOOL read_ok = ReadFile(fd_, buf, static_cast(size), &bytes_read, &ov_read_); + if (!read_ok) { + DWORD error = GetLastError(); + if ((error != ERROR_SUCCESS) && (error != ERROR_IO_PENDING)) { + stringstream ss; + ss << "Error while reading from the serial port: " << error; + THROW (IOException, ss.str().c_str()); + } + } + BOOL result_ok = GetOverlappedResult(fd_, &ov_read_, &bytes_read, TRUE); + if (!result_ok) { + DWORD error = GetLastError(); + if (error != ERROR_OPERATION_ABORTED) { + stringstream ss; + ss << "GetOverlappedResult failed: " << error; + THROW (IOException, ss.str().c_str()); + } + } + return (size_t) (bytes_read); + } else { + return 0; } - return (size_t) (bytes_read); } size_t @@ -349,12 +426,38 @@ Serial::SerialImpl::write (const uint8_t *data, size_t length) throw PortNotOpenedException ("Serial::write"); } DWORD bytes_written; - if (!WriteFile(fd_, data, static_cast(length), &bytes_written, NULL)) { - stringstream ss; - ss << "Error while writing to the serial port: " << GetLastError(); - THROW (IOException, ss.str().c_str()); + int success = WriteFile(fd_, data, static_cast(length), &bytes_written, &ov_write_); + DWORD error = success ? ERROR_SUCCESS : GetLastError(); + if (timeout_.write_timeout_constant != 0) { + if ((error != ERROR_SUCCESS) && (error != ERROR_IO_PENDING)) { + stringstream ss; + ss << "WriteFile failed: " << error; + THROW (IOException, ss.str().c_str()); + } + GetOverlappedResult(fd_, &ov_write_, &bytes_written, TRUE); + error = GetLastError(); + if (error == ERROR_OPERATION_ABORTED) { + return bytes_written; + } + if (bytes_written != length) { + stringstream ss; + ss << "Write timeout."; + THROW (IOException, ss.str().c_str()); + } + return bytes_written; + } else { + if ((error == ERROR_INVALID_USER_BUFFER) || + (error == ERROR_NOT_ENOUGH_MEMORY) || + (error == ERROR_OPERATION_ABORTED)) { + return 0; + } else if ((error == ERROR_SUCCESS) || (error == ERROR_IO_PENDING)) { + return (size_t) (bytes_written); + } else { + stringstream ss; + ss << "WriteFile failed: " << error; + THROW (IOException, ss.str().c_str()); + } } - return (size_t) (bytes_written); } void @@ -613,7 +716,7 @@ Serial::SerialImpl::getCD() void Serial::SerialImpl::readLock() { - if (WaitForSingleObject(read_mutex, INFINITE) != WAIT_OBJECT_0) { + if (WaitForSingleObject(read_mutex_, INFINITE) != WAIT_OBJECT_0) { THROW (IOException, "Error claiming read mutex."); } } @@ -621,7 +724,7 @@ Serial::SerialImpl::readLock() void Serial::SerialImpl::readUnlock() { - if (!ReleaseMutex(read_mutex)) { + if (!ReleaseMutex(read_mutex_)) { THROW (IOException, "Error releasing read mutex."); } } @@ -629,7 +732,7 @@ Serial::SerialImpl::readUnlock() void Serial::SerialImpl::writeLock() { - if (WaitForSingleObject(write_mutex, INFINITE) != WAIT_OBJECT_0) { + if (WaitForSingleObject(write_mutex_, INFINITE) != WAIT_OBJECT_0) { THROW (IOException, "Error claiming write mutex."); } } @@ -637,7 +740,7 @@ Serial::SerialImpl::writeLock() void Serial::SerialImpl::writeUnlock() { - if (!ReleaseMutex(write_mutex)) { + if (!ReleaseMutex(write_mutex_)) { THROW (IOException, "Error releasing write mutex."); } }