From a7e90c0f846a85d9f738aa4e838751037e922a82 Mon Sep 17 00:00:00 2001 From: chinaheyu <810130242@qq.com> Date: Mon, 21 Nov 2022 16:53:44 +0800 Subject: [PATCH 1/3] Full duplex support on Windows. --- include/serial/impl/win.h | 3 ++ src/impl/win.cc | 65 ++++++++++++++++++++++++++++++++------- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/include/serial/impl/win.h b/include/serial/impl/win.h index 2c0c6cde..d5441900 100644 --- a/include/serial/impl/win.h +++ b/include/serial/impl/win.h @@ -198,6 +198,9 @@ class serial::Serial::SerialImpl { HANDLE read_mutex; // Mutex used to lock the write functions 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..2096f433 100644 --- a/src/impl/win.cc +++ b/src/impl/win.cc @@ -43,6 +43,8 @@ Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate, open (); 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 () @@ -50,6 +52,8 @@ Serial::SerialImpl::~SerialImpl () this->close(); CloseHandle(read_mutex); CloseHandle(write_mutex); + CloseHandle(ov_read.hEvent); + CloseHandle(ov_write.hEvent); } void @@ -70,7 +74,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 +281,23 @@ Serial::SerialImpl::reconfigurePort () void Serial::SerialImpl::close () { - if (is_open_ == true) { + if (is_open_) { + // Cancel a blocking operation. + DWORD rc; + WINBOOL 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_); @@ -333,13 +353,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; + WINBOOL 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()); + } + } + WINBOOL 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 +387,17 @@ 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)) { + int success = WriteFile(fd_, data, static_cast(length), &bytes_written, &ov_write); + DWORD error = success ? ERROR_SUCCESS : GetLastError(); + 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 << "Error while writing to the serial port: " << GetLastError(); + ss << "WriteFile failed: " << error; THROW (IOException, ss.str().c_str()); } - return (size_t) (bytes_written); } void From 310b473868bce4814e23d7574412f9e04cbc0faf Mon Sep 17 00:00:00 2001 From: chinaheyu <810130242@qq.com> Date: Mon, 21 Nov 2022 22:05:33 +0800 Subject: [PATCH 2/3] Implement waitReadable and waitByteTime --- src/impl/win.cc | 81 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/src/impl/win.cc b/src/impl/win.cc index 2096f433..d4ae9921 100644 --- a/src/impl/win.cc +++ b/src/impl/win.cc @@ -335,16 +335,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 @@ -389,14 +427,35 @@ Serial::SerialImpl::write (const uint8_t *data, size_t length) DWORD bytes_written; int success = WriteFile(fd_, data, static_cast(length), &bytes_written, &ov_write); DWORD error = success ? ERROR_SUCCESS : GetLastError(); - 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); + 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 { - stringstream ss; - ss << "WriteFile failed: " << error; - THROW (IOException, ss.str().c_str()); + 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()); + } } } From a196d9fd68425e2b6fc57f4ee99aa7a29f36161f Mon Sep 17 00:00:00 2001 From: chinaheyu <810130242@qq.com> Date: Tue, 5 Sep 2023 09:36:57 +0800 Subject: [PATCH 3/3] Fix `ERROR_INVALID_PARAMETER` and `undefined WINBOOL` --- include/serial/impl/win.h | 8 +++--- src/impl/win.cc | 55 ++++++++++++++++++++------------------- 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/include/serial/impl/win.h b/include/serial/impl/win.h index d5441900..fca54a5d 100644 --- a/include/serial/impl/win.h +++ b/include/serial/impl/win.h @@ -195,12 +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 + OVERLAPPED ov_read_; // OVERLAPPED read + OVERLAPPED ov_write_; // OVERLAPPED write }; } diff --git a/src/impl/win.cc b/src/impl/win.cc index d4ae9921..f36a986d 100644 --- a/src/impl/win.cc +++ b/src/impl/win.cc @@ -37,23 +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); - ov_read.hEvent = CreateEvent(NULL, 1, 0, NULL); - ov_write.hEvent = CreateEvent(NULL, 0, 0, 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(ov_read.hEvent); - CloseHandle(ov_write.hEvent); + CloseHandle(read_mutex_); + CloseHandle(write_mutex_); + CloseHandle(ov_read_.hEvent); + CloseHandle(ov_write_.hEvent); } void @@ -284,18 +285,18 @@ Serial::SerialImpl::close () if (is_open_) { // Cancel a blocking operation. DWORD rc; - WINBOOL err; - err = GetOverlappedResult(fd_, &ov_read, &rc, FALSE); + 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); + CancelIoEx(fd_, &ov_read_); } - err = GetOverlappedResult(fd_, &ov_write, &rc, FALSE); + err = GetOverlappedResult(fd_, &ov_write_, &rc, FALSE); if (!err) { rc = GetLastError(); if (rc == ERROR_IO_PENDING || rc == ERROR_IO_INCOMPLETE) - CancelIoEx(fd_, &ov_write); + CancelIoEx(fd_, &ov_write_); } if (fd_ != INVALID_HANDLE_VALUE) { @@ -350,14 +351,14 @@ Serial::SerialImpl::waitReadable (uint32_t timeout) } msk = 0; SetCommMask(fd_, EV_RXCHAR | EV_ERR); - if (!WaitCommEvent(fd_, &msk, &ov_read)) { + if (!WaitCommEvent(fd_, &msk, &ov_read_)) { if (GetLastError() == ERROR_IO_PENDING) { - if (WaitForSingleObject(ov_read.hEvent, (DWORD)timeout) == WAIT_TIMEOUT) { + 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); + GetOverlappedResult(fd_, &ov_read_, &length, TRUE); + ResetEvent(ov_read_.hEvent); } else { ClearCommError(fd_, &error, &cs); SetCommMask(fd_, old_msk); @@ -392,9 +393,9 @@ Serial::SerialImpl::read (uint8_t *buf, size_t size) throw PortNotOpenedException ("Serial::read"); } if (size > 0) { - ResetEvent(ov_read.hEvent); + ResetEvent(ov_read_.hEvent); DWORD bytes_read; - WINBOOL read_ok = ReadFile(fd_, buf, static_cast(size), &bytes_read, &ov_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)) { @@ -403,7 +404,7 @@ Serial::SerialImpl::read (uint8_t *buf, size_t size) THROW (IOException, ss.str().c_str()); } } - WINBOOL result_ok = GetOverlappedResult(fd_, &ov_read, &bytes_read, TRUE); + BOOL result_ok = GetOverlappedResult(fd_, &ov_read_, &bytes_read, TRUE); if (!result_ok) { DWORD error = GetLastError(); if (error != ERROR_OPERATION_ABORTED) { @@ -425,7 +426,7 @@ Serial::SerialImpl::write (const uint8_t *data, size_t length) throw PortNotOpenedException ("Serial::write"); } DWORD bytes_written; - int success = WriteFile(fd_, data, static_cast(length), &bytes_written, &ov_write); + 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)) { @@ -433,7 +434,7 @@ Serial::SerialImpl::write (const uint8_t *data, size_t length) ss << "WriteFile failed: " << error; THROW (IOException, ss.str().c_str()); } - GetOverlappedResult(fd_, &ov_write, &bytes_written, TRUE); + GetOverlappedResult(fd_, &ov_write_, &bytes_written, TRUE); error = GetLastError(); if (error == ERROR_OPERATION_ABORTED) { return bytes_written; @@ -715,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."); } } @@ -723,7 +724,7 @@ Serial::SerialImpl::readLock() void Serial::SerialImpl::readUnlock() { - if (!ReleaseMutex(read_mutex)) { + if (!ReleaseMutex(read_mutex_)) { THROW (IOException, "Error releasing read mutex."); } } @@ -731,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."); } } @@ -739,7 +740,7 @@ Serial::SerialImpl::writeLock() void Serial::SerialImpl::writeUnlock() { - if (!ReleaseMutex(write_mutex)) { + if (!ReleaseMutex(write_mutex_)) { THROW (IOException, "Error releasing write mutex."); } }