aboutsummaryrefslogtreecommitdiffstats
path: root/source/serial/src/impl/win.cc
diff options
context:
space:
mode:
Diffstat (limited to 'source/serial/src/impl/win.cc')
-rw-r--r--source/serial/src/impl/win.cc646
1 files changed, 646 insertions, 0 deletions
diff --git a/source/serial/src/impl/win.cc b/source/serial/src/impl/win.cc
new file mode 100644
index 0000000..786f4f6
--- /dev/null
+++ b/source/serial/src/impl/win.cc
@@ -0,0 +1,646 @@
+#if defined(_WIN32)
+
+/* Copyright 2012 William Woodall and John Harrison */
+
+#include <sstream>
+
+#include "serial/impl/win.h"
+
+using std::string;
+using std::wstring;
+using std::stringstream;
+using std::invalid_argument;
+using serial::Serial;
+using serial::Timeout;
+using serial::bytesize_t;
+using serial::parity_t;
+using serial::stopbits_t;
+using serial::flowcontrol_t;
+using serial::SerialException;
+using serial::PortNotOpenedException;
+using serial::IOException;
+
+inline wstring
+_prefix_port_if_needed(const wstring &input)
+{
+ static wstring windows_com_port_prefix = L"\\\\.\\";
+ if (input.compare(windows_com_port_prefix) != 0)
+ {
+ return windows_com_port_prefix + input;
+ }
+ return input;
+}
+
+Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate,
+ bytesize_t bytesize,
+ parity_t parity, stopbits_t stopbits,
+ 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)
+{
+ if (port_.empty () == false)
+ open ();
+ read_mutex = CreateMutex(NULL, false, NULL);
+ write_mutex = CreateMutex(NULL, false, NULL);
+}
+
+Serial::SerialImpl::~SerialImpl ()
+{
+ this->close();
+ CloseHandle(read_mutex);
+ CloseHandle(write_mutex);
+}
+
+void
+Serial::SerialImpl::open ()
+{
+ if (port_.empty ()) {
+ throw invalid_argument ("Empty port is invalid.");
+ }
+ if (is_open_ == true) {
+ throw SerialException ("Serial port already open.");
+ }
+
+ // See: https://github.com/wjwwood/serial/issues/84
+ wstring port_with_prefix = _prefix_port_if_needed(port_);
+ LPCWSTR lp_port = port_with_prefix.c_str();
+ fd_ = CreateFileW(lp_port,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ 0);
+
+ if (fd_ == INVALID_HANDLE_VALUE) {
+ DWORD create_file_err = GetLastError();
+ stringstream ss;
+ switch (create_file_err) {
+ case ERROR_FILE_NOT_FOUND:
+ // Use this->getPort to convert to a std::string
+ ss << "Specified port, " << this->getPort() << ", does not exist.";
+ THROW (IOException, ss.str().c_str());
+ default:
+ ss << "Unknown error opening the serial port: " << create_file_err;
+ THROW (IOException, ss.str().c_str());
+ }
+ }
+
+ reconfigurePort();
+ is_open_ = true;
+}
+
+void
+Serial::SerialImpl::reconfigurePort ()
+{
+ if (fd_ == INVALID_HANDLE_VALUE) {
+ // Can only operate on a valid file descriptor
+ THROW (IOException, "Invalid file descriptor, is the serial port open?");
+ }
+
+ DCB dcbSerialParams = {0};
+
+ dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
+
+ if (!GetCommState(fd_, &dcbSerialParams)) {
+ //error getting state
+ THROW (IOException, "Error getting the serial port state.");
+ }
+
+ // setup baud rate
+ switch (baudrate_) {
+#ifdef CBR_0
+ case 0: dcbSerialParams.BaudRate = CBR_0; break;
+#endif
+#ifdef CBR_50
+ case 50: dcbSerialParams.BaudRate = CBR_50; break;
+#endif
+#ifdef CBR_75
+ case 75: dcbSerialParams.BaudRate = CBR_75; break;
+#endif
+#ifdef CBR_110
+ case 110: dcbSerialParams.BaudRate = CBR_110; break;
+#endif
+#ifdef CBR_134
+ case 134: dcbSerialParams.BaudRate = CBR_134; break;
+#endif
+#ifdef CBR_150
+ case 150: dcbSerialParams.BaudRate = CBR_150; break;
+#endif
+#ifdef CBR_200
+ case 200: dcbSerialParams.BaudRate = CBR_200; break;
+#endif
+#ifdef CBR_300
+ case 300: dcbSerialParams.BaudRate = CBR_300; break;
+#endif
+#ifdef CBR_600
+ case 600: dcbSerialParams.BaudRate = CBR_600; break;
+#endif
+#ifdef CBR_1200
+ case 1200: dcbSerialParams.BaudRate = CBR_1200; break;
+#endif
+#ifdef CBR_1800
+ case 1800: dcbSerialParams.BaudRate = CBR_1800; break;
+#endif
+#ifdef CBR_2400
+ case 2400: dcbSerialParams.BaudRate = CBR_2400; break;
+#endif
+#ifdef CBR_4800
+ case 4800: dcbSerialParams.BaudRate = CBR_4800; break;
+#endif
+#ifdef CBR_7200
+ case 7200: dcbSerialParams.BaudRate = CBR_7200; break;
+#endif
+#ifdef CBR_9600
+ case 9600: dcbSerialParams.BaudRate = CBR_9600; break;
+#endif
+#ifdef CBR_14400
+ case 14400: dcbSerialParams.BaudRate = CBR_14400; break;
+#endif
+#ifdef CBR_19200
+ case 19200: dcbSerialParams.BaudRate = CBR_19200; break;
+#endif
+#ifdef CBR_28800
+ case 28800: dcbSerialParams.BaudRate = CBR_28800; break;
+#endif
+#ifdef CBR_57600
+ case 57600: dcbSerialParams.BaudRate = CBR_57600; break;
+#endif
+#ifdef CBR_76800
+ case 76800: dcbSerialParams.BaudRate = CBR_76800; break;
+#endif
+#ifdef CBR_38400
+ case 38400: dcbSerialParams.BaudRate = CBR_38400; break;
+#endif
+#ifdef CBR_115200
+ case 115200: dcbSerialParams.BaudRate = CBR_115200; break;
+#endif
+#ifdef CBR_128000
+ case 128000: dcbSerialParams.BaudRate = CBR_128000; break;
+#endif
+#ifdef CBR_153600
+ case 153600: dcbSerialParams.BaudRate = CBR_153600; break;
+#endif
+#ifdef CBR_230400
+ case 230400: dcbSerialParams.BaudRate = CBR_230400; break;
+#endif
+#ifdef CBR_256000
+ case 256000: dcbSerialParams.BaudRate = CBR_256000; break;
+#endif
+#ifdef CBR_460800
+ case 460800: dcbSerialParams.BaudRate = CBR_460800; break;
+#endif
+#ifdef CBR_921600
+ case 921600: dcbSerialParams.BaudRate = CBR_921600; break;
+#endif
+ default:
+ // Try to blindly assign it
+ dcbSerialParams.BaudRate = baudrate_;
+ }
+
+ // setup char len
+ if (bytesize_ == eightbits)
+ dcbSerialParams.ByteSize = 8;
+ else if (bytesize_ == sevenbits)
+ dcbSerialParams.ByteSize = 7;
+ else if (bytesize_ == sixbits)
+ dcbSerialParams.ByteSize = 6;
+ else if (bytesize_ == fivebits)
+ dcbSerialParams.ByteSize = 5;
+ else
+ throw invalid_argument ("invalid char len");
+
+ // setup stopbits
+ if (stopbits_ == stopbits_one)
+ dcbSerialParams.StopBits = ONESTOPBIT;
+ else if (stopbits_ == stopbits_one_point_five)
+ dcbSerialParams.StopBits = ONE5STOPBITS;
+ else if (stopbits_ == stopbits_two)
+ dcbSerialParams.StopBits = TWOSTOPBITS;
+ else
+ throw invalid_argument ("invalid stop bit");
+
+ // setup parity
+ if (parity_ == parity_none) {
+ dcbSerialParams.Parity = NOPARITY;
+ } else if (parity_ == parity_even) {
+ dcbSerialParams.Parity = EVENPARITY;
+ } else if (parity_ == parity_odd) {
+ dcbSerialParams.Parity = ODDPARITY;
+ } else if (parity_ == parity_mark) {
+ dcbSerialParams.Parity = MARKPARITY;
+ } else if (parity_ == parity_space) {
+ dcbSerialParams.Parity = SPACEPARITY;
+ } else {
+ throw invalid_argument ("invalid parity");
+ }
+
+ // setup flowcontrol
+ if (flowcontrol_ == flowcontrol_none) {
+ dcbSerialParams.fOutxCtsFlow = false;
+ dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
+ dcbSerialParams.fOutX = false;
+ dcbSerialParams.fInX = false;
+ }
+ if (flowcontrol_ == flowcontrol_software) {
+ dcbSerialParams.fOutxCtsFlow = false;
+ dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
+ dcbSerialParams.fOutX = true;
+ dcbSerialParams.fInX = true;
+ }
+ if (flowcontrol_ == flowcontrol_hardware) {
+ dcbSerialParams.fOutxCtsFlow = true;
+ dcbSerialParams.fRtsControl = RTS_CONTROL_HANDSHAKE;
+ dcbSerialParams.fOutX = false;
+ dcbSerialParams.fInX = false;
+ }
+
+ // activate settings
+ if (!SetCommState(fd_, &dcbSerialParams)){
+ CloseHandle(fd_);
+ THROW (IOException, "Error setting serial port settings.");
+ }
+
+ // Setup timeouts
+ COMMTIMEOUTS timeouts = {0};
+ timeouts.ReadIntervalTimeout = timeout_.inter_byte_timeout;
+ timeouts.ReadTotalTimeoutConstant = timeout_.read_timeout_constant;
+ timeouts.ReadTotalTimeoutMultiplier = timeout_.read_timeout_multiplier;
+ timeouts.WriteTotalTimeoutConstant = timeout_.write_timeout_constant;
+ timeouts.WriteTotalTimeoutMultiplier = timeout_.write_timeout_multiplier;
+ if (!SetCommTimeouts(fd_, &timeouts)) {
+ THROW (IOException, "Error setting timeouts.");
+ }
+}
+
+void
+Serial::SerialImpl::close ()
+{
+ if (is_open_ == true) {
+ if (fd_ != INVALID_HANDLE_VALUE) {
+ int ret;
+ ret = CloseHandle(fd_);
+ if (ret == 0) {
+ stringstream ss;
+ ss << "Error while closing serial port: " << GetLastError();
+ THROW (IOException, ss.str().c_str());
+ } else {
+ fd_ = INVALID_HANDLE_VALUE;
+ }
+ }
+ is_open_ = false;
+ }
+}
+
+bool
+Serial::SerialImpl::isOpen () const
+{
+ return is_open_;
+}
+
+size_t
+Serial::SerialImpl::available ()
+{
+ if (!is_open_) {
+ return 0;
+ }
+ COMSTAT cs;
+ if (!ClearCommError(fd_, NULL, &cs)) {
+ stringstream ss;
+ ss << "Error while checking status of the serial port: " << GetLastError();
+ THROW (IOException, ss.str().c_str());
+ }
+ return static_cast<size_t>(cs.cbInQue);
+}
+
+bool
+Serial::SerialImpl::waitReadable (uint32_t /*timeout*/)
+{
+ THROW (IOException, "waitReadable is not implemented on Windows.");
+ return false;
+}
+
+void
+Serial::SerialImpl::waitByteTimes (size_t /*count*/)
+{
+ THROW (IOException, "waitByteTimes is not implemented on Windows.");
+}
+
+size_t
+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<DWORD>(size), &bytes_read, NULL)) {
+ stringstream ss;
+ ss << "Error while reading from the serial port: " << GetLastError();
+ THROW (IOException, ss.str().c_str());
+ }
+ return (size_t) (bytes_read);
+}
+
+size_t
+Serial::SerialImpl::write (const uint8_t *data, size_t length)
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::write");
+ }
+ DWORD bytes_written;
+ if (!WriteFile(fd_, data, static_cast<DWORD>(length), &bytes_written, NULL)) {
+ stringstream ss;
+ ss << "Error while writing to the serial port: " << GetLastError();
+ THROW (IOException, ss.str().c_str());
+ }
+ return (size_t) (bytes_written);
+}
+
+void
+Serial::SerialImpl::setPort (const string &port)
+{
+ port_ = wstring(port.begin(), port.end());
+}
+
+string
+Serial::SerialImpl::getPort () const
+{
+ return string(port_.begin(), port_.end());
+}
+
+void
+Serial::SerialImpl::setTimeout (serial::Timeout &timeout)
+{
+ timeout_ = timeout;
+ if (is_open_) {
+ reconfigurePort ();
+ }
+}
+
+serial::Timeout
+Serial::SerialImpl::getTimeout () const
+{
+ return timeout_;
+}
+
+void
+Serial::SerialImpl::setBaudrate (unsigned long baudrate)
+{
+ baudrate_ = baudrate;
+ if (is_open_) {
+ reconfigurePort ();
+ }
+}
+
+unsigned long
+Serial::SerialImpl::getBaudrate () const
+{
+ return baudrate_;
+}
+
+void
+Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize)
+{
+ bytesize_ = bytesize;
+ if (is_open_) {
+ reconfigurePort ();
+ }
+}
+
+serial::bytesize_t
+Serial::SerialImpl::getBytesize () const
+{
+ return bytesize_;
+}
+
+void
+Serial::SerialImpl::setParity (serial::parity_t parity)
+{
+ parity_ = parity;
+ if (is_open_) {
+ reconfigurePort ();
+ }
+}
+
+serial::parity_t
+Serial::SerialImpl::getParity () const
+{
+ return parity_;
+}
+
+void
+Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits)
+{
+ stopbits_ = stopbits;
+ if (is_open_) {
+ reconfigurePort ();
+ }
+}
+
+serial::stopbits_t
+Serial::SerialImpl::getStopbits () const
+{
+ return stopbits_;
+}
+
+void
+Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol)
+{
+ flowcontrol_ = flowcontrol;
+ if (is_open_) {
+ reconfigurePort ();
+ }
+}
+
+serial::flowcontrol_t
+Serial::SerialImpl::getFlowcontrol () const
+{
+ return flowcontrol_;
+}
+
+void
+Serial::SerialImpl::flush ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::flush");
+ }
+ FlushFileBuffers (fd_);
+}
+
+void
+Serial::SerialImpl::flushInput ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException("Serial::flushInput");
+ }
+ PurgeComm(fd_, PURGE_RXCLEAR);
+}
+
+void
+Serial::SerialImpl::flushOutput ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException("Serial::flushOutput");
+ }
+ PurgeComm(fd_, PURGE_TXCLEAR);
+}
+
+void
+Serial::SerialImpl::sendBreak (int /*duration*/)
+{
+ THROW (IOException, "sendBreak is not supported on Windows.");
+}
+
+void
+Serial::SerialImpl::setBreak (bool level)
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::setBreak");
+ }
+ if (level) {
+ EscapeCommFunction (fd_, SETBREAK);
+ } else {
+ EscapeCommFunction (fd_, CLRBREAK);
+ }
+}
+
+void
+Serial::SerialImpl::setRTS (bool level)
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::setRTS");
+ }
+ if (level) {
+ EscapeCommFunction (fd_, SETRTS);
+ } else {
+ EscapeCommFunction (fd_, CLRRTS);
+ }
+}
+
+void
+Serial::SerialImpl::setDTR (bool level)
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::setDTR");
+ }
+ if (level) {
+ EscapeCommFunction (fd_, SETDTR);
+ } else {
+ EscapeCommFunction (fd_, CLRDTR);
+ }
+}
+
+bool
+Serial::SerialImpl::waitForChange ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::waitForChange");
+ }
+ DWORD dwCommEvent;
+
+ if (!SetCommMask(fd_, EV_CTS | EV_DSR | EV_RING | EV_RLSD)) {
+ // Error setting communications mask
+ return false;
+ }
+
+ if (!WaitCommEvent(fd_, &dwCommEvent, NULL)) {
+ // An error occurred waiting for the event.
+ return false;
+ } else {
+ // Event has occurred.
+ return true;
+ }
+}
+
+bool
+Serial::SerialImpl::getCTS ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::getCTS");
+ }
+ DWORD dwModemStatus;
+ if (!GetCommModemStatus(fd_, &dwModemStatus)) {
+ THROW (IOException, "Error getting the status of the CTS line.");
+ }
+
+ return (MS_CTS_ON & dwModemStatus) != 0;
+}
+
+bool
+Serial::SerialImpl::getDSR ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::getDSR");
+ }
+ DWORD dwModemStatus;
+ if (!GetCommModemStatus(fd_, &dwModemStatus)) {
+ THROW (IOException, "Error getting the status of the DSR line.");
+ }
+
+ return (MS_DSR_ON & dwModemStatus) != 0;
+}
+
+bool
+Serial::SerialImpl::getRI()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::getRI");
+ }
+ DWORD dwModemStatus;
+ if (!GetCommModemStatus(fd_, &dwModemStatus)) {
+ THROW (IOException, "Error getting the status of the RI line.");
+ }
+
+ return (MS_RING_ON & dwModemStatus) != 0;
+}
+
+bool
+Serial::SerialImpl::getCD()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::getCD");
+ }
+ DWORD dwModemStatus;
+ if (!GetCommModemStatus(fd_, &dwModemStatus)) {
+ // Error in GetCommModemStatus;
+ THROW (IOException, "Error getting the status of the CD line.");
+ }
+
+ return (MS_RLSD_ON & dwModemStatus) != 0;
+}
+
+void
+Serial::SerialImpl::readLock()
+{
+ if (WaitForSingleObject(read_mutex, INFINITE) != WAIT_OBJECT_0) {
+ THROW (IOException, "Error claiming read mutex.");
+ }
+}
+
+void
+Serial::SerialImpl::readUnlock()
+{
+ if (!ReleaseMutex(read_mutex)) {
+ THROW (IOException, "Error releasing read mutex.");
+ }
+}
+
+void
+Serial::SerialImpl::writeLock()
+{
+ if (WaitForSingleObject(write_mutex, INFINITE) != WAIT_OBJECT_0) {
+ THROW (IOException, "Error claiming write mutex.");
+ }
+}
+
+void
+Serial::SerialImpl::writeUnlock()
+{
+ if (!ReleaseMutex(write_mutex)) {
+ THROW (IOException, "Error releasing write mutex.");
+ }
+}
+
+#endif // #if defined(_WIN32)
+