diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0fc39d0..191f21c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,43 +12,42 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
add_compile_options(-Os -O3)
+add_subdirectory("serial")
set(QT_VERSION Qt6)
find_package(${QT_VERSION} REQUIRED COMPONENTS Widgets Network PrintSupport)
find_package(Eigen3 REQUIRED)
-# For #include "..."
include_directories(.)
-# 库文件收集
file(
- GLOB_RECURSE PROJECT_SOURCE
- CONFIGURE_DEPENDS
+ GLOB_RECURSE PROJECT_SOURCE
+ CONFIGURE_DEPENDS
# Project source
"modern-qt/*.cc"
# Custom signals
"modern-qt/widget/sliders.hh"
)
add_library(
- modern-qt SHARED
- ${PROJECT_SOURCE}
+ modern-qt SHARED
+ ${PROJECT_SOURCE}
modern-qt/widget/select.hh
modern-qt/widget/select.impl.hh
)
target_link_libraries(
- modern-qt PUBLIC
- ${QT_VERSION}::Widgets
- ${QT_VERSION}::Network
+ modern-qt PUBLIC
+ ${QT_VERSION}::Widgets
+ ${QT_VERSION}::Network
)
file(
- GLOB_RECURSE WIDGETS_CC
+ GLOB_RECURSE WIDGETS_CC
CONFIGURE_DEPENDS "components/*.cc"
)
file(
- GLOB_RECURSE QCUSTOMPLOT_SOURCE
- CONFIGURE_DEPENDS
+ GLOB_RECURSE QCUSTOMPLOT_SOURCE
+ CONFIGURE_DEPENDS
"qcustomplot/*.cpp"
"qcustomplot/*.h"
)
@@ -57,34 +56,32 @@ add_library(
${QCUSTOMPLOT_SOURCE}
)
target_link_libraries(
- qcustomplot PUBLIC
- ${QT_VERSION}::Core
- ${QT_VERSION}::Gui
- ${QT_VERSION}::PrintSupport
+ qcustomplot PUBLIC
+ ${QT_VERSION}::Core
+ ${QT_VERSION}::Gui
+ ${QT_VERSION}::PrintSupport
)
qt_standard_project_setup()
add_executable(
- ${PROJECT_NAME}
- ${WIDGETS_CC}
- main.cc
- component.hh
- resources.qrc
- components/view.cc
- modern-qt/widget/select.cc
+ ${PROJECT_NAME}
+ ${WIDGETS_CC}
+ main.cc
+ component.hh
+ resources.qrc
+ components/view.cc
+ modern-qt/widget/select.cc
components/charts/heatmap.cc
components/charts/heatmap.hh
components/charts/heatmap.impl.hh
)
qt6_add_resources(QRC_FILES resources.qrc)
target_sources(${PROJECT_NAME} PRIVATE ${QRC_FILES})
-#qt_add_resources(QRC_FILES resources.qrc)
-#qt_add_resources(${PROJECT_NAME} ${QRC_FILES} resources.qrc)
-#target_sources(${PROJECT_NAME} PRIVATE ${QRC_FILES})
target_link_libraries(
- ${PROJECT_NAME}
- ${QT_VERSION}::Widgets
- ${QT_VERSION}::Network
- modern-qt
- qcustomplot
+ ${PROJECT_NAME}
+ ${QT_VERSION}::Widgets
+ ${QT_VERSION}::Network
+ modern-qt
+ qcustomplot
+ serial
)
diff --git a/serial/CMakeLists.txt b/serial/CMakeLists.txt
new file mode 100644
index 0000000..a55af33
--- /dev/null
+++ b/serial/CMakeLists.txt
@@ -0,0 +1,51 @@
+project(serial)
+
+if(APPLE)
+ find_library(IOKIT_LIBRARY IOKit)
+ find_library(FOUNDATION_LIBRARY Foundation)
+endif()
+
+if(UNIX AND NOT APPLE)
+ # If Linux, add rt and pthread
+ set(rt_LIBRARIES rt)
+ set(pthread_LIBRARIES pthread)
+endif ()
+
+## Sources
+set(serial_SRCS
+ src/serial.cc
+ include/serial/serial.h
+ include/serial/v8stdint.h
+)
+
+if(APPLE)
+ # If OSX
+ list(APPEND serial_SRCS src/impl/unix.cc)
+ list(APPEND serial_SRCS src/impl/list_ports/list_ports_osx.cc)
+elseif(UNIX)
+ # If unix
+ list(APPEND serial_SRCS src/impl/unix.cc)
+ list(APPEND serial_SRCS src/impl/list_ports/list_ports_linux.cc)
+else()
+ # If windows
+ list(APPEND serial_SRCS src/impl/win.cc)
+ list(APPEND serial_SRCS src/impl/list_ports/list_ports_win.cc)
+endif()
+
+## Add serial library
+add_library(${PROJECT_NAME} ${serial_SRCS})
+if(APPLE)
+ target_link_libraries(${PROJECT_NAME} ${FOUNDATION_LIBRARY} ${IOKIT_LIBRARY})
+elseif(UNIX)
+ target_link_libraries(${PROJECT_NAME} rt pthread)
+else()
+ target_link_libraries(${PROJECT_NAME} setupapi)
+endif()
+
+set_target_properties(serial PROPERTIES
+ AUTOMOC OFF
+ AUTORCC OFF
+ AUTOUIC OFF
+)
+## Include headers
+include_directories(include)
\ No newline at end of file
diff --git a/serial/examples/serial_example.cc b/serial/examples/serial_example.cc
new file mode 100644
index 0000000..a829635
--- /dev/null
+++ b/serial/examples/serial_example.cc
@@ -0,0 +1,182 @@
+/***
+ * This example expects the serial port has a loopback on it.
+ *
+ * Alternatively, you could use an Arduino:
+ *
+ *
+ * void setup() {
+ * Serial.begin();
+ * }
+ *
+ * void loop() {
+ * if (Serial.available()) {
+ * Serial.write(Serial.read());
+ * }
+ * }
+ *
+ */
+
+#include
+#include
+#include
+
+// OS Specific sleep
+#ifdef _WIN32
+#include
+#else
+#include
+#endif
+
+#include "serial/serial.h"
+
+using std::string;
+using std::exception;
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::vector;
+
+void my_sleep(unsigned long milliseconds) {
+#ifdef _WIN32
+ Sleep(milliseconds); // 100 ms
+#else
+ usleep(milliseconds*1000); // 100 ms
+#endif
+}
+
+void enumerate_ports()
+{
+ vector devices_found = serial::list_ports();
+
+ vector::iterator iter = devices_found.begin();
+
+ while( iter != devices_found.end() )
+ {
+ serial::PortInfo device = *iter++;
+
+ printf( "(%s, %s, %s)\n", device.port.c_str(), device.description.c_str(),
+ device.hardware_id.c_str() );
+ }
+}
+
+void print_usage()
+{
+ cerr << "Usage: test_serial {-e|} ";
+ cerr << " [test string]" << endl;
+}
+
+int run(int argc, char **argv)
+{
+ if(argc < 2) {
+ print_usage();
+ return 0;
+ }
+
+ // Argument 1 is the serial port or enumerate flag
+ string port(argv[1]);
+
+ if( port == "-e" ) {
+ enumerate_ports();
+ return 0;
+ }
+ else if( argc < 3 ) {
+ print_usage();
+ return 1;
+ }
+
+ // Argument 2 is the baudrate
+ unsigned long baud = 0;
+#if defined(WIN32) && !defined(__MINGW32__)
+ sscanf_s(argv[2], "%lu", &baud);
+#else
+ sscanf(argv[2], "%lu", &baud);
+#endif
+
+ // port, baudrate, timeout in milliseconds
+ serial::Serial my_serial(port, baud, serial::Timeout::simpleTimeout(1000));
+
+ cout << "Is the serial port open?";
+ if(my_serial.isOpen())
+ cout << " Yes." << endl;
+ else
+ cout << " No." << endl;
+
+ // Get the Test string
+ int count = 0;
+ string test_string;
+ if (argc == 4) {
+ test_string = argv[3];
+ } else {
+ test_string = "Testing.";
+ }
+
+ // Test the timeout, there should be 1 second between prints
+ cout << "Timeout == 1000ms, asking for 1 more byte than written." << endl;
+ while (count < 10) {
+ size_t bytes_wrote = my_serial.write(test_string);
+
+ string result = my_serial.read(test_string.length()+1);
+
+ cout << "Iteration: " << count << ", Bytes written: ";
+ cout << bytes_wrote << ", Bytes read: ";
+ cout << result.length() << ", String read: " << result << endl;
+
+ count += 1;
+ }
+
+ // Test the timeout at 250ms
+ my_serial.setTimeout(serial::Timeout::max(), 250, 0, 250, 0);
+ count = 0;
+ cout << "Timeout == 250ms, asking for 1 more byte than written." << endl;
+ while (count < 10) {
+ size_t bytes_wrote = my_serial.write(test_string);
+
+ string result = my_serial.read(test_string.length()+1);
+
+ cout << "Iteration: " << count << ", Bytes written: ";
+ cout << bytes_wrote << ", Bytes read: ";
+ cout << result.length() << ", String read: " << result << endl;
+
+ count += 1;
+ }
+
+ // Test the timeout at 250ms, but asking exactly for what was written
+ count = 0;
+ cout << "Timeout == 250ms, asking for exactly what was written." << endl;
+ while (count < 10) {
+ size_t bytes_wrote = my_serial.write(test_string);
+
+ string result = my_serial.read(test_string.length());
+
+ cout << "Iteration: " << count << ", Bytes written: ";
+ cout << bytes_wrote << ", Bytes read: ";
+ cout << result.length() << ", String read: " << result << endl;
+
+ count += 1;
+ }
+
+ // Test the timeout at 250ms, but asking for 1 less than what was written
+ count = 0;
+ cout << "Timeout == 250ms, asking for 1 less than was written." << endl;
+ while (count < 10) {
+ size_t bytes_wrote = my_serial.write(test_string);
+
+ string result = my_serial.read(test_string.length()-1);
+
+ cout << "Iteration: " << count << ", Bytes written: ";
+ cout << bytes_wrote << ", Bytes read: ";
+ cout << result.length() << ", String read: " << result << endl;
+
+ count += 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ try {
+ return run(argc, argv);
+ } catch (exception &e) {
+ cerr << "Unhandled Exception: " << e.what() << endl;
+ }
+}
diff --git a/serial/include/serial/impl/unix.h b/serial/include/serial/impl/unix.h
new file mode 100644
index 0000000..0fb38f2
--- /dev/null
+++ b/serial/include/serial/impl/unix.h
@@ -0,0 +1,221 @@
+/*!
+ * \file serial/impl/unix.h
+ * \author William Woodall
+ * \author John Harrison
+ * \version 0.1
+ *
+ * \section LICENSE
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2012 William Woodall, John Harrison
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * \section DESCRIPTION
+ *
+ * This provides a unix based pimpl for the Serial class. This implementation is
+ * based off termios.h and uses select for multiplexing the IO ports.
+ *
+ */
+
+#if !defined(_WIN32)
+
+#ifndef SERIAL_IMPL_UNIX_H
+#define SERIAL_IMPL_UNIX_H
+
+#include "serial/serial.h"
+
+#include
+
+namespace serial {
+
+using std::size_t;
+using std::string;
+using std::invalid_argument;
+
+using serial::SerialException;
+using serial::IOException;
+
+class MillisecondTimer {
+public:
+ MillisecondTimer(const uint32_t millis);
+ int64_t remaining();
+
+private:
+ static timespec timespec_now();
+ timespec expiry;
+};
+
+class serial::Serial::SerialImpl {
+public:
+ SerialImpl (const string &port,
+ unsigned long baudrate,
+ bytesize_t bytesize,
+ parity_t parity,
+ stopbits_t stopbits,
+ flowcontrol_t flowcontrol);
+
+ virtual ~SerialImpl ();
+
+ void
+ open ();
+
+ void
+ close ();
+
+ bool
+ isOpen () const;
+
+ size_t
+ available ();
+
+ bool
+ waitReadable (uint32_t timeout);
+
+ void
+ waitByteTimes (size_t count);
+
+ size_t
+ read (uint8_t *buf, size_t size = 1);
+
+ size_t
+ write (const uint8_t *data, size_t length);
+
+ void
+ flush ();
+
+ void
+ flushInput ();
+
+ void
+ flushOutput ();
+
+ void
+ sendBreak (int duration);
+
+ void
+ setBreak (bool level);
+
+ void
+ setRTS (bool level);
+
+ void
+ setDTR (bool level);
+
+ bool
+ waitForChange ();
+
+ bool
+ getCTS ();
+
+ bool
+ getDSR ();
+
+ bool
+ getRI ();
+
+ bool
+ getCD ();
+
+ void
+ setPort (const string &port);
+
+ string
+ getPort () const;
+
+ void
+ setTimeout (Timeout &timeout);
+
+ Timeout
+ getTimeout () const;
+
+ void
+ setBaudrate (unsigned long baudrate);
+
+ unsigned long
+ getBaudrate () const;
+
+ void
+ setBytesize (bytesize_t bytesize);
+
+ bytesize_t
+ getBytesize () const;
+
+ void
+ setParity (parity_t parity);
+
+ parity_t
+ getParity () const;
+
+ void
+ setStopbits (stopbits_t stopbits);
+
+ stopbits_t
+ getStopbits () const;
+
+ void
+ setFlowcontrol (flowcontrol_t flowcontrol);
+
+ flowcontrol_t
+ getFlowcontrol () const;
+
+ void
+ readLock ();
+
+ void
+ readUnlock ();
+
+ void
+ writeLock ();
+
+ void
+ writeUnlock ();
+
+protected:
+ void reconfigurePort ();
+
+private:
+ string port_; // Path to the file descriptor
+ int fd_; // The current file descriptor
+
+ bool is_open_;
+ bool xonxoff_;
+ bool rtscts_;
+
+ Timeout timeout_; // Timeout for read operations
+ unsigned long baudrate_; // Baudrate
+ uint32_t byte_time_ns_; // Nanoseconds to transmit/receive a single byte
+
+ parity_t parity_; // Parity
+ bytesize_t bytesize_; // Size of the bytes
+ stopbits_t stopbits_; // Stop Bits
+ flowcontrol_t flowcontrol_; // Flow Control
+
+ // Mutex used to lock the read functions
+ pthread_mutex_t read_mutex;
+ // Mutex used to lock the write functions
+ pthread_mutex_t write_mutex;
+};
+
+}
+
+#endif // SERIAL_IMPL_UNIX_H
+
+#endif // !defined(_WIN32)
diff --git a/serial/include/serial/impl/win.h b/serial/include/serial/impl/win.h
new file mode 100644
index 0000000..2c0c6cd
--- /dev/null
+++ b/serial/include/serial/impl/win.h
@@ -0,0 +1,207 @@
+/*!
+ * \file serial/impl/windows.h
+ * \author William Woodall
+ * \author John Harrison
+ * \version 0.1
+ *
+ * \section LICENSE
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2012 William Woodall, John Harrison
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * \section DESCRIPTION
+ *
+ * This provides a windows implementation of the Serial class interface.
+ *
+ */
+
+#if defined(_WIN32)
+
+#ifndef SERIAL_IMPL_WINDOWS_H
+#define SERIAL_IMPL_WINDOWS_H
+
+#include "serial/serial.h"
+
+#include "windows.h"
+
+namespace serial {
+
+using std::string;
+using std::wstring;
+using std::invalid_argument;
+
+using serial::SerialException;
+using serial::IOException;
+
+class serial::Serial::SerialImpl {
+public:
+ SerialImpl (const string &port,
+ unsigned long baudrate,
+ bytesize_t bytesize,
+ parity_t parity,
+ stopbits_t stopbits,
+ flowcontrol_t flowcontrol);
+
+ virtual ~SerialImpl ();
+
+ void
+ open ();
+
+ void
+ close ();
+
+ bool
+ isOpen () const;
+
+ size_t
+ available ();
+
+ bool
+ waitReadable (uint32_t timeout);
+
+ void
+ waitByteTimes (size_t count);
+
+ size_t
+ read (uint8_t *buf, size_t size = 1);
+
+ size_t
+ write (const uint8_t *data, size_t length);
+
+ void
+ flush ();
+
+ void
+ flushInput ();
+
+ void
+ flushOutput ();
+
+ void
+ sendBreak (int duration);
+
+ void
+ setBreak (bool level);
+
+ void
+ setRTS (bool level);
+
+ void
+ setDTR (bool level);
+
+ bool
+ waitForChange ();
+
+ bool
+ getCTS ();
+
+ bool
+ getDSR ();
+
+ bool
+ getRI ();
+
+ bool
+ getCD ();
+
+ void
+ setPort (const string &port);
+
+ string
+ getPort () const;
+
+ void
+ setTimeout (Timeout &timeout);
+
+ Timeout
+ getTimeout () const;
+
+ void
+ setBaudrate (unsigned long baudrate);
+
+ unsigned long
+ getBaudrate () const;
+
+ void
+ setBytesize (bytesize_t bytesize);
+
+ bytesize_t
+ getBytesize () const;
+
+ void
+ setParity (parity_t parity);
+
+ parity_t
+ getParity () const;
+
+ void
+ setStopbits (stopbits_t stopbits);
+
+ stopbits_t
+ getStopbits () const;
+
+ void
+ setFlowcontrol (flowcontrol_t flowcontrol);
+
+ flowcontrol_t
+ getFlowcontrol () const;
+
+ void
+ readLock ();
+
+ void
+ readUnlock ();
+
+ void
+ writeLock ();
+
+ void
+ writeUnlock ();
+
+protected:
+ void reconfigurePort ();
+
+private:
+ wstring port_; // Path to the file descriptor
+ HANDLE fd_;
+
+ bool is_open_;
+
+ Timeout timeout_; // Timeout for read operations
+ unsigned long baudrate_; // Baudrate
+
+ parity_t parity_; // Parity
+ bytesize_t bytesize_; // Size of the bytes
+ stopbits_t stopbits_; // Stop Bits
+ flowcontrol_t flowcontrol_; // Flow Control
+
+ // Mutex used to lock the read functions
+ HANDLE read_mutex;
+ // Mutex used to lock the write functions
+ HANDLE write_mutex;
+};
+
+}
+
+#endif // SERIAL_IMPL_WINDOWS_H
+
+#endif // if defined(_WIN32)
diff --git a/serial/include/serial/serial.h b/serial/include/serial/serial.h
new file mode 100644
index 0000000..a165785
--- /dev/null
+++ b/serial/include/serial/serial.h
@@ -0,0 +1,775 @@
+/*!
+ * \file serial/serial.h
+ * \author William Woodall
+ * \author John Harrison
+ * \version 0.1
+ *
+ * \section LICENSE
+ *
+ * The MIT License
+ *
+ * Copyright (c) 2012 William Woodall
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * \section DESCRIPTION
+ *
+ * This provides a cross platform interface for interacting with Serial Ports.
+ */
+
+#ifndef SERIAL_H
+#define SERIAL_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, \
+__LINE__, (message) )
+
+namespace serial {
+
+/*!
+ * Enumeration defines the possible bytesizes for the serial port.
+ */
+typedef enum {
+ fivebits = 5,
+ sixbits = 6,
+ sevenbits = 7,
+ eightbits = 8
+} bytesize_t;
+
+/*!
+ * Enumeration defines the possible parity types for the serial port.
+ */
+typedef enum {
+ parity_none = 0,
+ parity_odd = 1,
+ parity_even = 2,
+ parity_mark = 3,
+ parity_space = 4
+} parity_t;
+
+/*!
+ * Enumeration defines the possible stopbit types for the serial port.
+ */
+typedef enum {
+ stopbits_one = 1,
+ stopbits_two = 2,
+ stopbits_one_point_five
+} stopbits_t;
+
+/*!
+ * Enumeration defines the possible flowcontrol types for the serial port.
+ */
+typedef enum {
+ flowcontrol_none = 0,
+ flowcontrol_software,
+ flowcontrol_hardware
+} flowcontrol_t;
+
+/*!
+ * Structure for setting the timeout of the serial port, times are
+ * in milliseconds.
+ *
+ * In order to disable the interbyte timeout, set it to Timeout::max().
+ */
+struct Timeout {
+#ifdef max
+# undef max
+#endif
+ static uint32_t max() {return std::numeric_limits::max();}
+ /*!
+ * Convenience function to generate Timeout structs using a
+ * single absolute timeout.
+ *
+ * \param timeout A long that defines the time in milliseconds until a
+ * timeout occurs after a call to read or write is made.
+ *
+ * \return Timeout struct that represents this simple timeout provided.
+ */
+ static Timeout simpleTimeout(uint32_t timeout) {
+ return Timeout(max(), timeout, 0, timeout, 0);
+ }
+
+ /*! Number of milliseconds between bytes received to timeout on. */
+ uint32_t inter_byte_timeout;
+ /*! A constant number of milliseconds to wait after calling read. */
+ uint32_t read_timeout_constant;
+ /*! A multiplier against the number of requested bytes to wait after
+ * calling read.
+ */
+ uint32_t read_timeout_multiplier;
+ /*! A constant number of milliseconds to wait after calling write. */
+ uint32_t write_timeout_constant;
+ /*! A multiplier against the number of requested bytes to wait after
+ * calling write.
+ */
+ uint32_t write_timeout_multiplier;
+
+ explicit Timeout (uint32_t inter_byte_timeout_=0,
+ uint32_t read_timeout_constant_=0,
+ uint32_t read_timeout_multiplier_=0,
+ uint32_t write_timeout_constant_=0,
+ uint32_t write_timeout_multiplier_=0)
+ : inter_byte_timeout(inter_byte_timeout_),
+ read_timeout_constant(read_timeout_constant_),
+ read_timeout_multiplier(read_timeout_multiplier_),
+ write_timeout_constant(write_timeout_constant_),
+ write_timeout_multiplier(write_timeout_multiplier_)
+ {}
+};
+
+/*!
+ * Class that provides a portable serial port interface.
+ */
+class Serial {
+public:
+ /*!
+ * Creates a Serial object and opens the port if a port is specified,
+ * otherwise it remains closed until serial::Serial::open is called.
+ *
+ * \param port A std::string containing the address of the serial port,
+ * which would be something like 'COM1' on Windows and '/dev/ttyS0'
+ * on Linux.
+ *
+ * \param baudrate An unsigned 32-bit integer that represents the baudrate
+ *
+ * \param timeout A serial::Timeout struct that defines the timeout
+ * conditions for the serial port. \see serial::Timeout
+ *
+ * \param bytesize Size of each byte in the serial transmission of data,
+ * default is eightbits, possible values are: fivebits, sixbits, sevenbits,
+ * eightbits
+ *
+ * \param parity Method of parity, default is parity_none, possible values
+ * are: parity_none, parity_odd, parity_even
+ *
+ * \param stopbits Number of stop bits used, default is stopbits_one,
+ * possible values are: stopbits_one, stopbits_one_point_five, stopbits_two
+ *
+ * \param flowcontrol Type of flowcontrol used, default is
+ * flowcontrol_none, possible values are: flowcontrol_none,
+ * flowcontrol_software, flowcontrol_hardware
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::IOException
+ * \throw std::invalid_argument
+ */
+ Serial (const std::string &port = "",
+ uint32_t baudrate = 9600,
+ Timeout timeout = Timeout(),
+ bytesize_t bytesize = eightbits,
+ parity_t parity = parity_none,
+ stopbits_t stopbits = stopbits_one,
+ flowcontrol_t flowcontrol = flowcontrol_none);
+
+ /*! Destructor */
+ virtual ~Serial ();
+
+ /*!
+ * Opens the serial port as long as the port is set and the port isn't
+ * already open.
+ *
+ * If the port is provided to the constructor then an explicit call to open
+ * is not needed.
+ *
+ * \see Serial::Serial
+ *
+ * \throw std::invalid_argument
+ * \throw serial::SerialException
+ * \throw serial::IOException
+ */
+ void
+ open ();
+
+ /*! Gets the open status of the serial port.
+ *
+ * \return Returns true if the port is open, false otherwise.
+ */
+ bool
+ isOpen () const;
+
+ /*! Closes the serial port. */
+ void
+ close ();
+
+ /*! Return the number of characters in the buffer. */
+ size_t
+ available ();
+
+ /*! Block until there is serial data to read or read_timeout_constant
+ * number of milliseconds have elapsed. The return value is true when
+ * the function exits with the port in a readable state, false otherwise
+ * (due to timeout or select interruption). */
+ bool
+ waitReadable ();
+
+ /*! Block for a period of time corresponding to the transmission time of
+ * count characters at present serial settings. This may be used in con-
+ * junction with waitReadable to read larger blocks of data from the
+ * port. */
+ void
+ waitByteTimes (size_t count);
+
+ /*! Read a given amount of bytes from the serial port into a given buffer.
+ *
+ * The read function will return in one of three cases:
+ * * The number of requested bytes was read.
+ * * In this case the number of bytes requested will match the size_t
+ * returned by read.
+ * * A timeout occurred, in this case the number of bytes read will not
+ * match the amount requested, but no exception will be thrown. One of
+ * two possible timeouts occurred:
+ * * The inter byte timeout expired, this means that number of
+ * milliseconds elapsed between receiving bytes from the serial port
+ * exceeded the inter byte timeout.
+ * * The total timeout expired, which is calculated by multiplying the
+ * read timeout multiplier by the number of requested bytes and then
+ * added to the read timeout constant. If that total number of
+ * milliseconds elapses after the initial call to read a timeout will
+ * occur.
+ * * An exception occurred, in this case an actual exception will be thrown.
+ *
+ * \param buffer An uint8_t array of at least the requested size.
+ * \param size A size_t defining how many bytes to be read.
+ *
+ * \return A size_t representing the number of bytes read as a result of the
+ * call to read.
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::SerialException
+ */
+ size_t
+ read (uint8_t *buffer, size_t size);
+
+ /*! Read a given amount of bytes from the serial port into a give buffer.
+ *
+ * \param buffer A reference to a std::vector of uint8_t.
+ * \param size A size_t defining how many bytes to be read.
+ *
+ * \return A size_t representing the number of bytes read as a result of the
+ * call to read.
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::SerialException
+ */
+ size_t
+ read (std::vector &buffer, size_t size = 1);
+
+ /*! Read a given amount of bytes from the serial port into a give buffer.
+ *
+ * \param buffer A reference to a std::string.
+ * \param size A size_t defining how many bytes to be read.
+ *
+ * \return A size_t representing the number of bytes read as a result of the
+ * call to read.
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::SerialException
+ */
+ size_t
+ read (std::string &buffer, size_t size = 1);
+
+ /*! Read a given amount of bytes from the serial port and return a string
+ * containing the data.
+ *
+ * \param size A size_t defining how many bytes to be read.
+ *
+ * \return A std::string containing the data read from the port.
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::SerialException
+ */
+ std::string
+ read (size_t size = 1);
+
+ /*! Reads in a line or until a given delimiter has been processed.
+ *
+ * Reads from the serial port until a single line has been read.
+ *
+ * \param buffer A std::string reference used to store the data.
+ * \param size A maximum length of a line, defaults to 65536 (2^16)
+ * \param eol A string to match against for the EOL.
+ *
+ * \return A size_t representing the number of bytes read.
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::SerialException
+ */
+ size_t
+ readline (std::string &buffer, size_t size = 65536, std::string eol = "\n");
+
+ /*! Reads in a line or until a given delimiter has been processed.
+ *
+ * Reads from the serial port until a single line has been read.
+ *
+ * \param size A maximum length of a line, defaults to 65536 (2^16)
+ * \param eol A string to match against for the EOL.
+ *
+ * \return A std::string containing the line.
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::SerialException
+ */
+ std::string
+ readline (size_t size = 65536, std::string eol = "\n");
+
+ /*! Reads in multiple lines until the serial port times out.
+ *
+ * This requires a timeout > 0 before it can be run. It will read until a
+ * timeout occurs and return a list of strings.
+ *
+ * \param size A maximum length of combined lines, defaults to 65536 (2^16)
+ *
+ * \param eol A string to match against for the EOL.
+ *
+ * \return A vector containing the lines.
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::SerialException
+ */
+ std::vector
+ readlines (size_t size = 65536, std::string eol = "\n");
+
+ /*! Write a string to the serial port.
+ *
+ * \param data A const reference containing the data to be written
+ * to the serial port.
+ *
+ * \param size A size_t that indicates how many bytes should be written from
+ * the given data buffer.
+ *
+ * \return A size_t representing the number of bytes actually written to
+ * the serial port.
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::SerialException
+ * \throw serial::IOException
+ */
+ size_t
+ write (const uint8_t *data, size_t size);
+
+ /*! Write a string to the serial port.
+ *
+ * \param data A const reference containing the data to be written
+ * to the serial port.
+ *
+ * \return A size_t representing the number of bytes actually written to
+ * the serial port.
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::SerialException
+ * \throw serial::IOException
+ */
+ size_t
+ write (const std::vector &data);
+
+ /*! Write a string to the serial port.
+ *
+ * \param data A const reference containing the data to be written
+ * to the serial port.
+ *
+ * \return A size_t representing the number of bytes actually written to
+ * the serial port.
+ *
+ * \throw serial::PortNotOpenedException
+ * \throw serial::SerialException
+ * \throw serial::IOException
+ */
+ size_t
+ write (const std::string &data);
+
+ /*! Sets the serial port identifier.
+ *
+ * \param port A const std::string reference containing the address of the
+ * serial port, which would be something like 'COM1' on Windows and
+ * '/dev/ttyS0' on Linux.
+ *
+ * \throw std::invalid_argument
+ */
+ void
+ setPort (const std::string &port);
+
+ /*! Gets the serial port identifier.
+ *
+ * \see Serial::setPort
+ *
+ * \throw std::invalid_argument
+ */
+ std::string
+ getPort () const;
+
+ /*! Sets the timeout for reads and writes using the Timeout struct.
+ *
+ * There are two timeout conditions described here:
+ * * The inter byte timeout:
+ * * The inter_byte_timeout component of serial::Timeout defines the
+ * maximum amount of time, in milliseconds, between receiving bytes on
+ * the serial port that can pass before a timeout occurs. Setting this
+ * to zero will prevent inter byte timeouts from occurring.
+ * * Total time timeout:
+ * * The constant and multiplier component of this timeout condition,
+ * for both read and write, are defined in serial::Timeout. This
+ * timeout occurs if the total time since the read or write call was
+ * made exceeds the specified time in milliseconds.
+ * * The limit is defined by multiplying the multiplier component by the
+ * number of requested bytes and adding that product to the constant
+ * component. In this way if you want a read call, for example, to
+ * timeout after exactly one second regardless of the number of bytes
+ * you asked for then set the read_timeout_constant component of
+ * serial::Timeout to 1000 and the read_timeout_multiplier to zero.
+ * This timeout condition can be used in conjunction with the inter
+ * byte timeout condition with out any problems, timeout will simply
+ * occur when one of the two timeout conditions is met. This allows
+ * users to have maximum control over the trade-off between
+ * responsiveness and efficiency.
+ *
+ * Read and write functions will return in one of three cases. When the
+ * reading or writing is complete, when a timeout occurs, or when an
+ * exception occurs.
+ *
+ * A timeout of 0 enables non-blocking mode.
+ *
+ * \param timeout A serial::Timeout struct containing the inter byte
+ * timeout, and the read and write timeout constants and multipliers.
+ *
+ * \see serial::Timeout
+ */
+ void
+ setTimeout (Timeout &timeout);
+
+ /*! Sets the timeout for reads and writes. */
+ void
+ setTimeout (uint32_t inter_byte_timeout, uint32_t read_timeout_constant,
+ uint32_t read_timeout_multiplier, uint32_t write_timeout_constant,
+ uint32_t write_timeout_multiplier)
+ {
+ Timeout timeout(inter_byte_timeout, read_timeout_constant,
+ read_timeout_multiplier, write_timeout_constant,
+ write_timeout_multiplier);
+ return setTimeout(timeout);
+ }
+
+ /*! Gets the timeout for reads in seconds.
+ *
+ * \return A Timeout struct containing the inter_byte_timeout, and read
+ * and write timeout constants and multipliers.
+ *
+ * \see Serial::setTimeout
+ */
+ Timeout
+ getTimeout () const;
+
+ /*! Sets the baudrate for the serial port.
+ *
+ * Possible baudrates depends on the system but some safe baudrates include:
+ * 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 56000,
+ * 57600, 115200
+ * Some other baudrates that are supported by some comports:
+ * 128000, 153600, 230400, 256000, 460800, 500000, 921600
+ *
+ * \param baudrate An integer that sets the baud rate for the serial port.
+ *
+ * \throw std::invalid_argument
+ */
+ void
+ setBaudrate (uint32_t baudrate);
+
+ /*! Gets the baudrate for the serial port.
+ *
+ * \return An integer that sets the baud rate for the serial port.
+ *
+ * \see Serial::setBaudrate
+ *
+ * \throw std::invalid_argument
+ */
+ uint32_t
+ getBaudrate () const;
+
+ /*! Sets the bytesize for the serial port.
+ *
+ * \param bytesize Size of each byte in the serial transmission of data,
+ * default is eightbits, possible values are: fivebits, sixbits, sevenbits,
+ * eightbits
+ *
+ * \throw std::invalid_argument
+ */
+ void
+ setBytesize (bytesize_t bytesize);
+
+ /*! Gets the bytesize for the serial port.
+ *
+ * \see Serial::setBytesize
+ *
+ * \throw std::invalid_argument
+ */
+ bytesize_t
+ getBytesize () const;
+
+ /*! Sets the parity for the serial port.
+ *
+ * \param parity Method of parity, default is parity_none, possible values
+ * are: parity_none, parity_odd, parity_even
+ *
+ * \throw std::invalid_argument
+ */
+ void
+ setParity (parity_t parity);
+
+ /*! Gets the parity for the serial port.
+ *
+ * \see Serial::setParity
+ *
+ * \throw std::invalid_argument
+ */
+ parity_t
+ getParity () const;
+
+ /*! Sets the stopbits for the serial port.
+ *
+ * \param stopbits Number of stop bits used, default is stopbits_one,
+ * possible values are: stopbits_one, stopbits_one_point_five, stopbits_two
+ *
+ * \throw std::invalid_argument
+ */
+ void
+ setStopbits (stopbits_t stopbits);
+
+ /*! Gets the stopbits for the serial port.
+ *
+ * \see Serial::setStopbits
+ *
+ * \throw std::invalid_argument
+ */
+ stopbits_t
+ getStopbits () const;
+
+ /*! Sets the flow control for the serial port.
+ *
+ * \param flowcontrol Type of flowcontrol used, default is flowcontrol_none,
+ * possible values are: flowcontrol_none, flowcontrol_software,
+ * flowcontrol_hardware
+ *
+ * \throw std::invalid_argument
+ */
+ void
+ setFlowcontrol (flowcontrol_t flowcontrol);
+
+ /*! Gets the flow control for the serial port.
+ *
+ * \see Serial::setFlowcontrol
+ *
+ * \throw std::invalid_argument
+ */
+ flowcontrol_t
+ getFlowcontrol () const;
+
+ /*! Flush the input and output buffers */
+ void
+ flush ();
+
+ /*! Flush only the input buffer */
+ void
+ flushInput ();
+
+ /*! Flush only the output buffer */
+ void
+ flushOutput ();
+
+ /*! Sends the RS-232 break signal. See tcsendbreak(3). */
+ void
+ sendBreak (int duration);
+
+ /*! Set the break condition to a given level. Defaults to true. */
+ void
+ setBreak (bool level = true);
+
+ /*! Set the RTS handshaking line to the given level. Defaults to true. */
+ void
+ setRTS (bool level = true);
+
+ /*! Set the DTR handshaking line to the given level. Defaults to true. */
+ void
+ setDTR (bool level = true);
+
+ /*!
+ * Blocks until CTS, DSR, RI, CD changes or something interrupts it.
+ *
+ * Can throw an exception if an error occurs while waiting.
+ * You can check the status of CTS, DSR, RI, and CD once this returns.
+ * Uses TIOCMIWAIT via ioctl if available (mostly only on Linux) with a
+ * resolution of less than +-1ms and as good as +-0.2ms. Otherwise a
+ * polling method is used which can give +-2ms.
+ *
+ * \return Returns true if one of the lines changed, false if something else
+ * occurred.
+ *
+ * \throw SerialException
+ */
+ bool
+ waitForChange ();
+
+ /*! Returns the current status of the CTS line. */
+ bool
+ getCTS ();
+
+ /*! Returns the current status of the DSR line. */
+ bool
+ getDSR ();
+
+ /*! Returns the current status of the RI line. */
+ bool
+ getRI ();
+
+ /*! Returns the current status of the CD line. */
+ bool
+ getCD ();
+
+private:
+ // Disable copy constructors
+ Serial(const Serial&);
+ Serial& operator=(const Serial&);
+
+ // Pimpl idiom, d_pointer
+ class SerialImpl;
+ SerialImpl *pimpl_;
+
+ // Scoped Lock Classes
+ class ScopedReadLock;
+ class ScopedWriteLock;
+
+ // Read common function
+ size_t
+ read_ (uint8_t *buffer, size_t size);
+ // Write common function
+ size_t
+ write_ (const uint8_t *data, size_t length);
+
+};
+
+class SerialException : public std::exception
+{
+ // Disable copy constructors
+ SerialException& operator=(const SerialException&);
+ std::string e_what_;
+public:
+ SerialException (const char *description) {
+ std::stringstream ss;
+ ss << "SerialException " << description << " failed.";
+ e_what_ = ss.str();
+ }
+ SerialException (const SerialException& other) : e_what_(other.e_what_) {}
+ virtual ~SerialException() throw() {}
+ virtual const char* what () const throw () {
+ return e_what_.c_str();
+ }
+};
+
+class IOException : public std::exception
+{
+ // Disable copy constructors
+ IOException& operator=(const IOException&);
+ std::string file_;
+ int line_;
+ std::string e_what_;
+ int errno_;
+public:
+ explicit IOException (std::string file, int line, int errnum)
+ : file_(file), line_(line), errno_(errnum) {
+ std::stringstream ss;
+#if defined(_WIN32) && !defined(__MINGW32__)
+ char error_str [1024];
+ strerror_s(error_str, 1024, errnum);
+#else
+ char * error_str = strerror(errnum);
+#endif
+ ss << "IO Exception (" << errno_ << "): " << error_str;
+ ss << ", file " << file_ << ", line " << line_ << ".";
+ e_what_ = ss.str();
+ }
+ explicit IOException (std::string file, int line, const char * description)
+ : file_(file), line_(line), errno_(0) {
+ std::stringstream ss;
+ ss << "IO Exception: " << description;
+ ss << ", file " << file_ << ", line " << line_ << ".";
+ e_what_ = ss.str();
+ }
+ virtual ~IOException() throw() {}
+ IOException (const IOException& other) : line_(other.line_), e_what_(other.e_what_), errno_(other.errno_) {}
+
+ int getErrorNumber () const { return errno_; }
+
+ virtual const char* what () const throw () {
+ return e_what_.c_str();
+ }
+};
+
+class PortNotOpenedException : public std::exception
+{
+ // Disable copy constructors
+ const PortNotOpenedException& operator=(PortNotOpenedException);
+ std::string e_what_;
+public:
+ PortNotOpenedException (const char * description) {
+ std::stringstream ss;
+ ss << "PortNotOpenedException " << description << " failed.";
+ e_what_ = ss.str();
+ }
+ PortNotOpenedException (const PortNotOpenedException& other) : e_what_(other.e_what_) {}
+ virtual ~PortNotOpenedException() throw() {}
+ virtual const char* what () const throw () {
+ return e_what_.c_str();
+ }
+};
+
+/*!
+ * Structure that describes a serial device.
+ */
+struct PortInfo {
+
+ /*! Address of the serial port (this can be passed to the constructor of Serial). */
+ std::string port;
+
+ /*! Human readable description of serial device if available. */
+ std::string description;
+
+ /*! Hardware ID (e.g. VID:PID of USB serial devices) or "n/a" if not available. */
+ std::string hardware_id;
+
+};
+
+/* Lists the serial ports available on the system
+ *
+ * Returns a vector of available serial ports, each represented
+ * by a serial::PortInfo data structure:
+ *
+ * \return vector of serial::PortInfo.
+ */
+std::vector
+list_ports();
+
+} // namespace serial
+
+#endif
diff --git a/serial/include/serial/v8stdint.h b/serial/include/serial/v8stdint.h
new file mode 100644
index 0000000..f3be96b
--- /dev/null
+++ b/serial/include/serial/v8stdint.h
@@ -0,0 +1,57 @@
+// This header is from the v8 google project:
+// http://code.google.com/p/v8/source/browse/trunk/include/v8stdint.h
+
+// Copyright 2012 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load definitions of standard types.
+
+#ifndef V8STDINT_H_
+#define V8STDINT_H_
+
+#include
+#include
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t; // NOLINT
+typedef unsigned short uint16_t; // NOLINT
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+// intptr_t and friends are defined in crtdefs.h through stdio.h.
+
+#else
+
+#include
+
+#endif
+
+#endif // V8STDINT_H_
diff --git a/serial/src/impl/list_ports/list_ports_linux.cc b/serial/src/impl/list_ports/list_ports_linux.cc
new file mode 100644
index 0000000..db2afb2
--- /dev/null
+++ b/serial/src/impl/list_ports/list_ports_linux.cc
@@ -0,0 +1,336 @@
+#if defined(__linux__)
+
+/*
+ * Copyright (c) 2014 Craig Lilley
+ * This software is made available under the terms of the MIT licence.
+ * A copy of the licence can be obtained from:
+ * http://opensource.org/licenses/MIT
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include "serial/serial.h"
+
+using serial::PortInfo;
+using std::istringstream;
+using std::ifstream;
+using std::getline;
+using std::vector;
+using std::string;
+using std::cout;
+using std::endl;
+
+static vector glob(const vector& patterns);
+static string basename(const string& path);
+static string dirname(const string& path);
+static bool path_exists(const string& path);
+static string realpath(const string& path);
+static string usb_sysfs_friendly_name(const string& sys_usb_path);
+static vector get_sysfs_info(const string& device_path);
+static string read_line(const string& file);
+static string usb_sysfs_hw_string(const string& sysfs_path);
+static string format(const char* format, ...);
+
+vector
+glob(const vector& patterns)
+{
+ vector paths_found;
+
+ if(patterns.size() == 0)
+ return paths_found;
+
+ glob_t glob_results;
+
+ int glob_retval = glob(patterns[0].c_str(), 0, NULL, &glob_results);
+
+ vector::const_iterator iter = patterns.begin();
+
+ while(++iter != patterns.end())
+ {
+ glob_retval = glob(iter->c_str(), GLOB_APPEND, NULL, &glob_results);
+ }
+
+ for(int path_index = 0; path_index < glob_results.gl_pathc; path_index++)
+ {
+ paths_found.push_back(glob_results.gl_pathv[path_index]);
+ }
+
+ globfree(&glob_results);
+
+ return paths_found;
+}
+
+string
+basename(const string& path)
+{
+ size_t pos = path.rfind("/");
+
+ if(pos == std::string::npos)
+ return path;
+
+ return string(path, pos+1, string::npos);
+}
+
+string
+dirname(const string& path)
+{
+ size_t pos = path.rfind("/");
+
+ if(pos == std::string::npos)
+ return path;
+ else if(pos == 0)
+ return "/";
+
+ return string(path, 0, pos);
+}
+
+bool
+path_exists(const string& path)
+{
+ struct stat sb;
+
+ if( stat(path.c_str(), &sb ) == 0 )
+ return true;
+
+ return false;
+}
+
+string
+realpath(const string& path)
+{
+ char* real_path = realpath(path.c_str(), NULL);
+
+ string result;
+
+ if(real_path != NULL)
+ {
+ result = real_path;
+
+ free(real_path);
+ }
+
+ return result;
+}
+
+string
+usb_sysfs_friendly_name(const string& sys_usb_path)
+{
+ unsigned int device_number = 0;
+
+ istringstream( read_line(sys_usb_path + "/devnum") ) >> device_number;
+
+ string manufacturer = read_line( sys_usb_path + "/manufacturer" );
+
+ string product = read_line( sys_usb_path + "/product" );
+
+ string serial = read_line( sys_usb_path + "/serial" );
+
+ if( manufacturer.empty() && product.empty() && serial.empty() )
+ return "";
+
+ return format("%s %s %s", manufacturer.c_str(), product.c_str(), serial.c_str() );
+}
+
+vector
+get_sysfs_info(const string& device_path)
+{
+ string device_name = basename( device_path );
+
+ string friendly_name;
+
+ string hardware_id;
+
+ string sys_device_path = format( "/sys/class/tty/%s/device", device_name.c_str() );
+
+ if( device_name.compare(0,6,"ttyUSB") == 0 )
+ {
+ sys_device_path = dirname( dirname( realpath( sys_device_path ) ) );
+
+ if( path_exists( sys_device_path ) )
+ {
+ friendly_name = usb_sysfs_friendly_name( sys_device_path );
+
+ hardware_id = usb_sysfs_hw_string( sys_device_path );
+ }
+ }
+ else if( device_name.compare(0,6,"ttyACM") == 0 )
+ {
+ sys_device_path = dirname( realpath( sys_device_path ) );
+
+ if( path_exists( sys_device_path ) )
+ {
+ friendly_name = usb_sysfs_friendly_name( sys_device_path );
+
+ hardware_id = usb_sysfs_hw_string( sys_device_path );
+ }
+ }
+ else
+ {
+ // Try to read ID string of PCI device
+
+ string sys_id_path = sys_device_path + "/id";
+
+ if( path_exists( sys_id_path ) )
+ hardware_id = read_line( sys_id_path );
+ }
+
+ if( friendly_name.empty() )
+ friendly_name = device_name;
+
+ if( hardware_id.empty() )
+ hardware_id = "n/a";
+
+ vector result;
+ result.push_back(friendly_name);
+ result.push_back(hardware_id);
+
+ return result;
+}
+
+string
+read_line(const string& file)
+{
+ ifstream ifs(file.c_str(), ifstream::in);
+
+ string line;
+
+ if(ifs)
+ {
+ getline(ifs, line);
+ }
+
+ return line;
+}
+
+string
+format(const char* format, ...)
+{
+ va_list ap;
+
+ size_t buffer_size_bytes = 256;
+
+ string result;
+
+ char* buffer = (char*)malloc(buffer_size_bytes);
+
+ if( buffer == NULL )
+ return result;
+
+ bool done = false;
+
+ unsigned int loop_count = 0;
+
+ while(!done)
+ {
+ va_start(ap, format);
+
+ int return_value = vsnprintf(buffer, buffer_size_bytes, format, ap);
+
+ if( return_value < 0 )
+ {
+ done = true;
+ }
+ else if( return_value >= buffer_size_bytes )
+ {
+ // Realloc and try again.
+
+ buffer_size_bytes = return_value + 1;
+
+ char* new_buffer_ptr = (char*)realloc(buffer, buffer_size_bytes);
+
+ if( new_buffer_ptr == NULL )
+ {
+ done = true;
+ }
+ else
+ {
+ buffer = new_buffer_ptr;
+ }
+ }
+ else
+ {
+ result = buffer;
+ done = true;
+ }
+
+ va_end(ap);
+
+ if( ++loop_count > 5 )
+ done = true;
+ }
+
+ free(buffer);
+
+ return result;
+}
+
+string
+usb_sysfs_hw_string(const string& sysfs_path)
+{
+ string serial_number = read_line( sysfs_path + "/serial" );
+
+ if( serial_number.length() > 0 )
+ {
+ serial_number = format( "SNR=%s", serial_number.c_str() );
+ }
+
+ string vid = read_line( sysfs_path + "/idVendor" );
+
+ string pid = read_line( sysfs_path + "/idProduct" );
+
+ return format("USB VID:PID=%s:%s %s", vid.c_str(), pid.c_str(), serial_number.c_str() );
+}
+
+vector
+serial::list_ports()
+{
+ vector results;
+
+ vector search_globs;
+ search_globs.push_back("/dev/ttyACM*");
+ search_globs.push_back("/dev/ttyS*");
+ search_globs.push_back("/dev/ttyUSB*");
+ search_globs.push_back("/dev/tty.*");
+ search_globs.push_back("/dev/cu.*");
+ search_globs.push_back("/dev/rfcomm*");
+
+ vector devices_found = glob( search_globs );
+
+ vector::iterator iter = devices_found.begin();
+
+ while( iter != devices_found.end() )
+ {
+ string device = *iter++;
+
+ vector sysfs_info = get_sysfs_info( device );
+
+ string friendly_name = sysfs_info[0];
+
+ string hardware_id = sysfs_info[1];
+
+ PortInfo device_entry;
+ device_entry.port = device;
+ device_entry.description = friendly_name;
+ device_entry.hardware_id = hardware_id;
+
+ results.push_back( device_entry );
+
+ }
+
+ return results;
+}
+
+#endif // defined(__linux__)
diff --git a/serial/src/impl/list_ports/list_ports_osx.cc b/serial/src/impl/list_ports/list_ports_osx.cc
new file mode 100644
index 0000000..333c55c
--- /dev/null
+++ b/serial/src/impl/list_ports/list_ports_osx.cc
@@ -0,0 +1,286 @@
+#if defined(__APPLE__)
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#include "serial/serial.h"
+
+using serial::PortInfo;
+using std::string;
+using std::vector;
+
+#define HARDWARE_ID_STRING_LENGTH 128
+
+string cfstring_to_string( CFStringRef cfstring );
+string get_device_path( io_object_t& serial_port );
+string get_class_name( io_object_t& obj );
+io_registry_entry_t get_parent_iousb_device( io_object_t& serial_port );
+string get_string_property( io_object_t& device, const char* property );
+uint16_t get_int_property( io_object_t& device, const char* property );
+string rtrim(const string& str);
+
+string
+cfstring_to_string( CFStringRef cfstring )
+{
+ char cstring[MAXPATHLEN];
+ string result;
+
+ if( cfstring )
+ {
+ Boolean success = CFStringGetCString( cfstring,
+ cstring,
+ sizeof(cstring),
+ kCFStringEncodingASCII );
+
+ if( success )
+ result = cstring;
+ }
+
+ return result;
+}
+
+string
+get_device_path( io_object_t& serial_port )
+{
+ CFTypeRef callout_path;
+ string device_path;
+
+ callout_path = IORegistryEntryCreateCFProperty( serial_port,
+ CFSTR(kIOCalloutDeviceKey),
+ kCFAllocatorDefault,
+ 0 );
+
+ if (callout_path)
+ {
+ if( CFGetTypeID(callout_path) == CFStringGetTypeID() )
+ device_path = cfstring_to_string( static_cast(callout_path) );
+
+ CFRelease(callout_path);
+ }
+
+ return device_path;
+}
+
+string
+get_class_name( io_object_t& obj )
+{
+ string result;
+ io_name_t class_name;
+ kern_return_t kern_result;
+
+ kern_result = IOObjectGetClass( obj, class_name );
+
+ if( kern_result == KERN_SUCCESS )
+ result = class_name;
+
+ return result;
+}
+
+io_registry_entry_t
+get_parent_iousb_device( io_object_t& serial_port )
+{
+ io_object_t device = serial_port;
+ io_registry_entry_t parent = 0;
+ io_registry_entry_t result = 0;
+ kern_return_t kern_result = KERN_FAILURE;
+ string name = get_class_name(device);
+
+ // Walk the IO Registry tree looking for this devices parent IOUSBDevice.
+ while( name != "IOUSBDevice" )
+ {
+ kern_result = IORegistryEntryGetParentEntry( device,
+ kIOServicePlane,
+ &parent );
+
+ if(kern_result != KERN_SUCCESS)
+ {
+ result = 0;
+ break;
+ }
+
+ device = parent;
+
+ name = get_class_name(device);
+ }
+
+ if(kern_result == KERN_SUCCESS)
+ result = device;
+
+ return result;
+}
+
+string
+get_string_property( io_object_t& device, const char* property )
+{
+ string property_name;
+
+ if( device )
+ {
+ CFStringRef property_as_cfstring = CFStringCreateWithCString (
+ kCFAllocatorDefault,
+ property,
+ kCFStringEncodingASCII );
+
+ CFTypeRef name_as_cfstring = IORegistryEntryCreateCFProperty(
+ device,
+ property_as_cfstring,
+ kCFAllocatorDefault,
+ 0 );
+
+ if( name_as_cfstring )
+ {
+ if( CFGetTypeID(name_as_cfstring) == CFStringGetTypeID() )
+ property_name = cfstring_to_string( static_cast(name_as_cfstring) );
+
+ CFRelease(name_as_cfstring);
+ }
+
+ if(property_as_cfstring)
+ CFRelease(property_as_cfstring);
+ }
+
+ return property_name;
+}
+
+uint16_t
+get_int_property( io_object_t& device, const char* property )
+{
+ uint16_t result = 0;
+
+ if( device )
+ {
+ CFStringRef property_as_cfstring = CFStringCreateWithCString (
+ kCFAllocatorDefault,
+ property,
+ kCFStringEncodingASCII );
+
+ CFTypeRef number = IORegistryEntryCreateCFProperty( device,
+ property_as_cfstring,
+ kCFAllocatorDefault,
+ 0 );
+
+ if(property_as_cfstring)
+ CFRelease(property_as_cfstring);
+
+ if( number )
+ {
+ if( CFGetTypeID(number) == CFNumberGetTypeID() )
+ {
+ bool success = CFNumberGetValue( static_cast(number),
+ kCFNumberSInt16Type,
+ &result );
+
+ if( !success )
+ result = 0;
+ }
+
+ CFRelease(number);
+ }
+
+ }
+
+ return result;
+}
+
+string rtrim(const string& str)
+{
+ string result = str;
+
+ string whitespace = " \t\f\v\n\r";
+
+ std::size_t found = result.find_last_not_of(whitespace);
+
+ if (found != std::string::npos)
+ result.erase(found+1);
+ else
+ result.clear();
+
+ return result;
+}
+
+vector
+serial::list_ports(void)
+{
+ vector devices_found;
+ CFMutableDictionaryRef classes_to_match;
+ io_iterator_t serial_port_iterator;
+ io_object_t serial_port;
+ mach_port_t master_port;
+ kern_return_t kern_result;
+
+ kern_result = IOMasterPort(MACH_PORT_NULL, &master_port);
+
+ if(kern_result != KERN_SUCCESS)
+ return devices_found;
+
+ classes_to_match = IOServiceMatching(kIOSerialBSDServiceValue);
+
+ if (classes_to_match == NULL)
+ return devices_found;
+
+ CFDictionarySetValue( classes_to_match,
+ CFSTR(kIOSerialBSDTypeKey),
+ CFSTR(kIOSerialBSDAllTypes) );
+
+ kern_result = IOServiceGetMatchingServices(master_port, classes_to_match, &serial_port_iterator);
+
+ if (KERN_SUCCESS != kern_result)
+ return devices_found;
+
+ while ( (serial_port = IOIteratorNext(serial_port_iterator)) )
+ {
+ string device_path = get_device_path( serial_port );
+ io_registry_entry_t parent = get_parent_iousb_device( serial_port );
+ IOObjectRelease(serial_port);
+
+ if( device_path.empty() )
+ continue;
+
+ PortInfo port_info;
+ port_info.port = device_path;
+ port_info.description = "n/a";
+ port_info.hardware_id = "n/a";
+
+ string device_name = rtrim( get_string_property( parent, "USB Product Name" ) );
+ string vendor_name = rtrim( get_string_property( parent, "USB Vendor Name") );
+ string description = rtrim( vendor_name + " " + device_name );
+ if( !description.empty() )
+ port_info.description = description;
+
+ string serial_number = rtrim(get_string_property( parent, "USB Serial Number" ) );
+ uint16_t vendor_id = get_int_property( parent, "idVendor" );
+ uint16_t product_id = get_int_property( parent, "idProduct" );
+
+ if( vendor_id && product_id )
+ {
+ char cstring[HARDWARE_ID_STRING_LENGTH];
+
+ if(serial_number.empty())
+ serial_number = "None";
+
+ int ret = snprintf( cstring, HARDWARE_ID_STRING_LENGTH, "USB VID:PID=%04x:%04x SNR=%s",
+ vendor_id,
+ product_id,
+ serial_number.c_str() );
+
+ if( (ret >= 0) && (ret < HARDWARE_ID_STRING_LENGTH) )
+ port_info.hardware_id = cstring;
+ }
+
+ devices_found.push_back(port_info);
+ }
+
+ IOObjectRelease(serial_port_iterator);
+ return devices_found;
+}
+
+#endif // defined(__APPLE__)
diff --git a/serial/src/impl/list_ports/list_ports_win.cc b/serial/src/impl/list_ports/list_ports_win.cc
new file mode 100644
index 0000000..7da40c4
--- /dev/null
+++ b/serial/src/impl/list_ports/list_ports_win.cc
@@ -0,0 +1,152 @@
+#if defined(_WIN32)
+
+/*
+ * Copyright (c) 2014 Craig Lilley
+ * This software is made available under the terms of the MIT licence.
+ * A copy of the licence can be obtained from:
+ * http://opensource.org/licenses/MIT
+ */
+
+#include "serial/serial.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+using serial::PortInfo;
+using std::vector;
+using std::string;
+
+static const DWORD port_name_max_length = 256;
+static const DWORD friendly_name_max_length = 256;
+static const DWORD hardware_id_max_length = 256;
+
+// Convert a wide Unicode string to an UTF8 string
+std::string utf8_encode(const std::wstring &wstr)
+{
+ int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
+ std::string strTo( size_needed, 0 );
+ WideCharToMultiByte (CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
+ return strTo;
+}
+
+vector
+serial::list_ports()
+{
+ vector devices_found;
+
+ HDEVINFO device_info_set = SetupDiGetClassDevs(
+ (const GUID *) &GUID_DEVCLASS_PORTS,
+ NULL,
+ NULL,
+ DIGCF_PRESENT);
+
+ unsigned int device_info_set_index = 0;
+ SP_DEVINFO_DATA device_info_data;
+
+ device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
+
+ while(SetupDiEnumDeviceInfo(device_info_set, device_info_set_index, &device_info_data))
+ {
+ device_info_set_index++;
+
+ // Get port name
+
+ HKEY hkey = SetupDiOpenDevRegKey(
+ device_info_set,
+ &device_info_data,
+ DICS_FLAG_GLOBAL,
+ 0,
+ DIREG_DEV,
+ KEY_READ);
+
+ TCHAR port_name[port_name_max_length];
+ DWORD port_name_length = port_name_max_length;
+
+ LONG return_code = RegQueryValueEx(
+ hkey,
+ _T("PortName"),
+ NULL,
+ NULL,
+ (LPBYTE)port_name,
+ &port_name_length);
+
+ RegCloseKey(hkey);
+
+ if(return_code != EXIT_SUCCESS)
+ continue;
+
+ if(port_name_length > 0 && port_name_length <= port_name_max_length)
+ port_name[port_name_length-1] = '\0';
+ else
+ port_name[0] = '\0';
+
+ // Ignore parallel ports
+
+ if(_tcsstr(port_name, _T("LPT")) != NULL)
+ continue;
+
+ // Get port friendly name
+
+ TCHAR friendly_name[friendly_name_max_length];
+ DWORD friendly_name_actual_length = 0;
+
+ BOOL got_friendly_name = SetupDiGetDeviceRegistryProperty(
+ device_info_set,
+ &device_info_data,
+ SPDRP_FRIENDLYNAME,
+ NULL,
+ (PBYTE)friendly_name,
+ friendly_name_max_length,
+ &friendly_name_actual_length);
+
+ if(got_friendly_name == TRUE && friendly_name_actual_length > 0)
+ friendly_name[friendly_name_actual_length-1] = '\0';
+ else
+ friendly_name[0] = '\0';
+
+ // Get hardware ID
+
+ TCHAR hardware_id[hardware_id_max_length];
+ DWORD hardware_id_actual_length = 0;
+
+ BOOL got_hardware_id = SetupDiGetDeviceRegistryProperty(
+ device_info_set,
+ &device_info_data,
+ SPDRP_HARDWAREID,
+ NULL,
+ (PBYTE)hardware_id,
+ hardware_id_max_length,
+ &hardware_id_actual_length);
+
+ if(got_hardware_id == TRUE && hardware_id_actual_length > 0)
+ hardware_id[hardware_id_actual_length-1] = '\0';
+ else
+ hardware_id[0] = '\0';
+
+ #ifdef UNICODE
+ std::string portName = utf8_encode(port_name);
+ std::string friendlyName = utf8_encode(friendly_name);
+ std::string hardwareId = utf8_encode(hardware_id);
+ #else
+ std::string portName = port_name;
+ std::string friendlyName = friendly_name;
+ std::string hardwareId = hardware_id;
+ #endif
+
+ PortInfo port_entry;
+ port_entry.port = portName;
+ port_entry.description = friendlyName;
+ port_entry.hardware_id = hardwareId;
+
+ devices_found.push_back(port_entry);
+ }
+
+ SetupDiDestroyDeviceInfoList(device_info_set);
+
+ return devices_found;
+}
+
+#endif // #if defined(_WIN32)
diff --git a/serial/src/impl/unix.cc b/serial/src/impl/unix.cc
new file mode 100644
index 0000000..a40b0fa
--- /dev/null
+++ b/serial/src/impl/unix.cc
@@ -0,0 +1,1084 @@
+/* Copyright 2012 William Woodall and John Harrison
+ *
+ * Additional Contributors: Christopher Baker @bakercp
+ */
+
+#if !defined(_WIN32)
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(__linux__)
+# include
+#endif
+
+#include
+#include
+#include
+#ifdef __MACH__
+#include
+#include
+#include
+#endif
+
+#include "serial/impl/unix.h"
+
+#ifndef TIOCINQ
+#ifdef FIONREAD
+#define TIOCINQ FIONREAD
+#else
+#define TIOCINQ 0x541B
+#endif
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3)
+#include
+#endif
+
+using std::string;
+using std::stringstream;
+using std::invalid_argument;
+using serial::MillisecondTimer;
+using serial::Serial;
+using serial::SerialException;
+using serial::PortNotOpenedException;
+using serial::IOException;
+
+
+MillisecondTimer::MillisecondTimer (const uint32_t millis)
+ : expiry(timespec_now())
+{
+ int64_t tv_nsec = expiry.tv_nsec + (millis * 1e6);
+ if (tv_nsec >= 1e9) {
+ int64_t sec_diff = tv_nsec / static_cast (1e9);
+ expiry.tv_nsec = tv_nsec % static_cast(1e9);
+ expiry.tv_sec += sec_diff;
+ } else {
+ expiry.tv_nsec = tv_nsec;
+ }
+}
+
+int64_t
+MillisecondTimer::remaining ()
+{
+ timespec now(timespec_now());
+ int64_t millis = (expiry.tv_sec - now.tv_sec) * 1e3;
+ millis += (expiry.tv_nsec - now.tv_nsec) / 1e6;
+ return millis;
+}
+
+timespec
+MillisecondTimer::timespec_now ()
+{
+ timespec time;
+# ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+ host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+ time.tv_sec = mts.tv_sec;
+ time.tv_nsec = mts.tv_nsec;
+# else
+ clock_gettime(CLOCK_MONOTONIC, &time);
+# endif
+ return time;
+}
+
+timespec
+timespec_from_ms (const uint32_t millis)
+{
+ timespec time;
+ time.tv_sec = millis / 1e3;
+ time.tv_nsec = (millis - (time.tv_sec * 1e3)) * 1e6;
+ return time;
+}
+
+Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate,
+ bytesize_t bytesize,
+ parity_t parity, stopbits_t stopbits,
+ flowcontrol_t flowcontrol)
+ : port_ (port), fd_ (-1), is_open_ (false), xonxoff_ (false), rtscts_ (false),
+ baudrate_ (baudrate), parity_ (parity),
+ bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol)
+{
+ pthread_mutex_init(&this->read_mutex, NULL);
+ pthread_mutex_init(&this->write_mutex, NULL);
+ if (port_.empty () == false)
+ open ();
+}
+
+Serial::SerialImpl::~SerialImpl ()
+{
+ close();
+ pthread_mutex_destroy(&this->read_mutex);
+ pthread_mutex_destroy(&this->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.");
+ }
+
+ fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
+
+ if (fd_ == -1) {
+ switch (errno) {
+ case EINTR:
+ // Recurse because this is a recoverable error.
+ open ();
+ return;
+ case ENFILE:
+ case EMFILE:
+ THROW (IOException, "Too many file handles open.");
+ default:
+ THROW (IOException, errno);
+ }
+ }
+
+ reconfigurePort();
+ is_open_ = true;
+}
+
+void
+Serial::SerialImpl::reconfigurePort ()
+{
+ if (fd_ == -1) {
+ // Can only operate on a valid file descriptor
+ THROW (IOException, "Invalid file descriptor, is the serial port open?");
+ }
+
+ struct termios options; // The options for the file descriptor
+
+ if (tcgetattr(fd_, &options) == -1) {
+ THROW (IOException, "::tcgetattr");
+ }
+
+ // set up raw mode / no echo / binary
+ options.c_cflag |= (tcflag_t) (CLOCAL | CREAD);
+ options.c_lflag &= (tcflag_t) ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL |
+ ISIG | IEXTEN); //|ECHOPRT
+
+ options.c_oflag &= (tcflag_t) ~(OPOST);
+ options.c_iflag &= (tcflag_t) ~(INLCR | IGNCR | ICRNL | IGNBRK);
+#ifdef IUCLC
+ options.c_iflag &= (tcflag_t) ~IUCLC;
+#endif
+#ifdef PARMRK
+ options.c_iflag &= (tcflag_t) ~PARMRK;
+#endif
+
+ // setup baud rate
+ bool custom_baud = false;
+ speed_t baud;
+ switch (baudrate_) {
+#ifdef B0
+ case 0: baud = B0; break;
+#endif
+#ifdef B50
+ case 50: baud = B50; break;
+#endif
+#ifdef B75
+ case 75: baud = B75; break;
+#endif
+#ifdef B110
+ case 110: baud = B110; break;
+#endif
+#ifdef B134
+ case 134: baud = B134; break;
+#endif
+#ifdef B150
+ case 150: baud = B150; break;
+#endif
+#ifdef B200
+ case 200: baud = B200; break;
+#endif
+#ifdef B300
+ case 300: baud = B300; break;
+#endif
+#ifdef B600
+ case 600: baud = B600; break;
+#endif
+#ifdef B1200
+ case 1200: baud = B1200; break;
+#endif
+#ifdef B1800
+ case 1800: baud = B1800; break;
+#endif
+#ifdef B2400
+ case 2400: baud = B2400; break;
+#endif
+#ifdef B4800
+ case 4800: baud = B4800; break;
+#endif
+#ifdef B7200
+ case 7200: baud = B7200; break;
+#endif
+#ifdef B9600
+ case 9600: baud = B9600; break;
+#endif
+#ifdef B14400
+ case 14400: baud = B14400; break;
+#endif
+#ifdef B19200
+ case 19200: baud = B19200; break;
+#endif
+#ifdef B28800
+ case 28800: baud = B28800; break;
+#endif
+#ifdef B57600
+ case 57600: baud = B57600; break;
+#endif
+#ifdef B76800
+ case 76800: baud = B76800; break;
+#endif
+#ifdef B38400
+ case 38400: baud = B38400; break;
+#endif
+#ifdef B115200
+ case 115200: baud = B115200; break;
+#endif
+#ifdef B128000
+ case 128000: baud = B128000; break;
+#endif
+#ifdef B153600
+ case 153600: baud = B153600; break;
+#endif
+#ifdef B230400
+ case 230400: baud = B230400; break;
+#endif
+#ifdef B256000
+ case 256000: baud = B256000; break;
+#endif
+#ifdef B460800
+ case 460800: baud = B460800; break;
+#endif
+#ifdef B500000
+ case 500000: baud = B500000; break;
+#endif
+#ifdef B576000
+ case 576000: baud = B576000; break;
+#endif
+#ifdef B921600
+ case 921600: baud = B921600; break;
+#endif
+#ifdef B1000000
+ case 1000000: baud = B1000000; break;
+#endif
+#ifdef B1152000
+ case 1152000: baud = B1152000; break;
+#endif
+#ifdef B1500000
+ case 1500000: baud = B1500000; break;
+#endif
+#ifdef B2000000
+ case 2000000: baud = B2000000; break;
+#endif
+#ifdef B2500000
+ case 2500000: baud = B2500000; break;
+#endif
+#ifdef B3000000
+ case 3000000: baud = B3000000; break;
+#endif
+#ifdef B3500000
+ case 3500000: baud = B3500000; break;
+#endif
+#ifdef B4000000
+ case 4000000: baud = B4000000; break;
+#endif
+ default:
+ custom_baud = true;
+ }
+ if (custom_baud == false) {
+#ifdef _BSD_SOURCE
+ ::cfsetspeed(&options, baud);
+#else
+ ::cfsetispeed(&options, baud);
+ ::cfsetospeed(&options, baud);
+#endif
+ }
+
+ // setup char len
+ options.c_cflag &= (tcflag_t) ~CSIZE;
+ if (bytesize_ == eightbits)
+ options.c_cflag |= CS8;
+ else if (bytesize_ == sevenbits)
+ options.c_cflag |= CS7;
+ else if (bytesize_ == sixbits)
+ options.c_cflag |= CS6;
+ else if (bytesize_ == fivebits)
+ options.c_cflag |= CS5;
+ else
+ throw invalid_argument ("invalid char len");
+ // setup stopbits
+ if (stopbits_ == stopbits_one)
+ options.c_cflag &= (tcflag_t) ~(CSTOPB);
+ else if (stopbits_ == stopbits_one_point_five)
+ // ONE POINT FIVE same as TWO.. there is no POSIX support for 1.5
+ options.c_cflag |= (CSTOPB);
+ else if (stopbits_ == stopbits_two)
+ options.c_cflag |= (CSTOPB);
+ else
+ throw invalid_argument ("invalid stop bit");
+ // setup parity
+ options.c_iflag &= (tcflag_t) ~(INPCK | ISTRIP);
+ if (parity_ == parity_none) {
+ options.c_cflag &= (tcflag_t) ~(PARENB | PARODD);
+ } else if (parity_ == parity_even) {
+ options.c_cflag &= (tcflag_t) ~(PARODD);
+ options.c_cflag |= (PARENB);
+ } else if (parity_ == parity_odd) {
+ options.c_cflag |= (PARENB | PARODD);
+ }
+#ifdef CMSPAR
+ else if (parity_ == parity_mark) {
+ options.c_cflag |= (PARENB | CMSPAR | PARODD);
+ }
+ else if (parity_ == parity_space) {
+ options.c_cflag |= (PARENB | CMSPAR);
+ options.c_cflag &= (tcflag_t) ~(PARODD);
+ }
+#else
+ // CMSPAR is not defined on OSX. So do not support mark or space parity.
+ else if (parity_ == parity_mark || parity_ == parity_space) {
+ throw invalid_argument ("OS does not support mark or space parity");
+ }
+#endif // ifdef CMSPAR
+ else {
+ throw invalid_argument ("invalid parity");
+ }
+ // setup flow control
+ if (flowcontrol_ == flowcontrol_none) {
+ xonxoff_ = false;
+ rtscts_ = false;
+ }
+ if (flowcontrol_ == flowcontrol_software) {
+ xonxoff_ = true;
+ rtscts_ = false;
+ }
+ if (flowcontrol_ == flowcontrol_hardware) {
+ xonxoff_ = false;
+ rtscts_ = true;
+ }
+ // xonxoff
+#ifdef IXANY
+ if (xonxoff_)
+ options.c_iflag |= (IXON | IXOFF); //|IXANY)
+ else
+ options.c_iflag &= (tcflag_t) ~(IXON | IXOFF | IXANY);
+#else
+ if (xonxoff_)
+ options.c_iflag |= (IXON | IXOFF);
+ else
+ options.c_iflag &= (tcflag_t) ~(IXON | IXOFF);
+#endif
+ // rtscts
+#ifdef CRTSCTS
+ if (rtscts_)
+ options.c_cflag |= (CRTSCTS);
+ else
+ options.c_cflag &= (unsigned long) ~(CRTSCTS);
+#elif defined CNEW_RTSCTS
+ if (rtscts_)
+ options.c_cflag |= (CNEW_RTSCTS);
+ else
+ options.c_cflag &= (unsigned long) ~(CNEW_RTSCTS);
+#else
+#error "OS Support seems wrong."
+#endif
+
+ // http://www.unixwiz.net/techtips/termios-vmin-vtime.html
+ // this basically sets the read call up to be a polling read,
+ // but we are using select to ensure there is data available
+ // to read before each call, so we should never needlessly poll
+ options.c_cc[VMIN] = 0;
+ options.c_cc[VTIME] = 0;
+
+ // activate settings
+ ::tcsetattr (fd_, TCSANOW, &options);
+
+ // apply custom baud rate, if any
+ if (custom_baud == true) {
+ // OS X support
+#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4)
+ // Starting with Tiger, the IOSSIOSPEED ioctl can be used to set arbitrary baud rates
+ // other than those specified by POSIX. The driver for the underlying serial hardware
+ // ultimately determines which baud rates can be used. This ioctl sets both the input
+ // and output speed.
+ speed_t new_baud = static_cast (baudrate_);
+ // PySerial uses IOSSIOSPEED=0x80045402
+ if (-1 == ioctl (fd_, IOSSIOSPEED, &new_baud, 1)) {
+ THROW (IOException, errno);
+ }
+ // Linux Support
+#elif defined(__linux__) && defined (TIOCSSERIAL)
+ struct serial_struct ser;
+
+ if (-1 == ioctl (fd_, TIOCGSERIAL, &ser)) {
+ THROW (IOException, errno);
+ }
+
+ // set custom divisor
+ ser.custom_divisor = ser.baud_base / static_cast (baudrate_);
+ // update flags
+ ser.flags &= ~ASYNC_SPD_MASK;
+ ser.flags |= ASYNC_SPD_CUST;
+
+ if (-1 == ioctl (fd_, TIOCSSERIAL, &ser)) {
+ THROW (IOException, errno);
+ }
+#else
+ throw invalid_argument ("OS does not currently support custom bauds");
+#endif
+ }
+
+ // Update byte_time_ based on the new settings.
+ uint32_t bit_time_ns = 1e9 / baudrate_;
+ byte_time_ns_ = bit_time_ns * (1 + bytesize_ + parity_ + stopbits_);
+
+ // Compensate for the stopbits_one_point_five enum being equal to int 3,
+ // and not 1.5.
+ if (stopbits_ == stopbits_one_point_five) {
+ byte_time_ns_ += ((1.5 - stopbits_one_point_five) * bit_time_ns);
+ }
+}
+
+void
+Serial::SerialImpl::close ()
+{
+ if (is_open_ == true) {
+ if (fd_ != -1) {
+ int ret;
+ ret = ::close (fd_);
+ if (ret == 0) {
+ fd_ = -1;
+ } else {
+ THROW (IOException, errno);
+ }
+ }
+ is_open_ = false;
+ }
+}
+
+bool
+Serial::SerialImpl::isOpen () const
+{
+ return is_open_;
+}
+
+size_t
+Serial::SerialImpl::available ()
+{
+ if (!is_open_) {
+ return 0;
+ }
+ int count = 0;
+ if (-1 == ioctl (fd_, TIOCINQ, &count)) {
+ THROW (IOException, errno);
+ } else {
+ return static_cast (count);
+ }
+}
+
+bool
+Serial::SerialImpl::waitReadable (uint32_t timeout)
+{
+ // Setup a select call to block for serial data or a timeout
+ fd_set readfds;
+ FD_ZERO (&readfds);
+ FD_SET (fd_, &readfds);
+ timespec timeout_ts (timespec_from_ms (timeout));
+ int r = pselect (fd_ + 1, &readfds, NULL, NULL, &timeout_ts, NULL);
+
+ if (r < 0) {
+ // Select was interrupted
+ if (errno == EINTR) {
+ return false;
+ }
+ // Otherwise there was some error
+ THROW (IOException, errno);
+ }
+ // Timeout occurred
+ if (r == 0) {
+ return false;
+ }
+ // This shouldn't happen, if r > 0 our fd has to be in the list!
+ if (!FD_ISSET (fd_, &readfds)) {
+ THROW (IOException, "select reports ready to read, but our fd isn't"
+ " in the list, this shouldn't happen!");
+ }
+ // Data available to read.
+ return true;
+}
+
+void
+Serial::SerialImpl::waitByteTimes (size_t count)
+{
+ timespec wait_time = { 0, static_cast(byte_time_ns_ * count)};
+ pselect (0, NULL, NULL, NULL, &wait_time, NULL);
+}
+
+size_t
+Serial::SerialImpl::read (uint8_t *buf, size_t size)
+{
+ // If the port is not open, throw
+ if (!is_open_) {
+ throw PortNotOpenedException ("Serial::read");
+ }
+ size_t bytes_read = 0;
+
+ // Calculate total timeout in milliseconds t_c + (t_m * N)
+ long total_timeout_ms = timeout_.read_timeout_constant;
+ total_timeout_ms += timeout_.read_timeout_multiplier * static_cast (size);
+ MillisecondTimer total_timeout(total_timeout_ms);
+
+ // Pre-fill buffer with available bytes
+ {
+ ssize_t bytes_read_now = ::read (fd_, buf, size);
+ if (bytes_read_now > 0) {
+ bytes_read = bytes_read_now;
+ }
+ }
+
+ while (bytes_read < size) {
+ int64_t timeout_remaining_ms = total_timeout.remaining();
+ if (timeout_remaining_ms <= 0) {
+ // Timed out
+ break;
+ }
+ // Timeout for the next select is whichever is less of the remaining
+ // total read timeout and the inter-byte timeout.
+ uint32_t timeout = std::min(static_cast (timeout_remaining_ms),
+ timeout_.inter_byte_timeout);
+ // Wait for the device to be readable, and then attempt to read.
+ if (waitReadable(timeout)) {
+ // If it's a fixed-length multi-byte read, insert a wait here so that
+ // we can attempt to grab the whole thing in a single IO call. Skip
+ // this wait if a non-max inter_byte_timeout is specified.
+ if (size > 1 && timeout_.inter_byte_timeout == Timeout::max()) {
+ size_t bytes_available = available();
+ if (bytes_available + bytes_read < size) {
+ waitByteTimes(size - (bytes_available + bytes_read));
+ }
+ }
+ // This should be non-blocking returning only what is available now
+ // Then returning so that select can block again.
+ ssize_t bytes_read_now =
+ ::read (fd_, buf + bytes_read, size - bytes_read);
+ // read should always return some data as select reported it was
+ // ready to read when we get to this point.
+ if (bytes_read_now < 1) {
+ // Disconnected devices, at least on Linux, show the
+ // behavior that they are always ready to read immediately
+ // but reading returns nothing.
+ throw SerialException ("device reports readiness to read but "
+ "returned no data (device disconnected?)");
+ }
+ // Update bytes_read
+ bytes_read += static_cast (bytes_read_now);
+ // If bytes_read == size then we have read everything we need
+ if (bytes_read == size) {
+ break;
+ }
+ // If bytes_read < size then we have more to read
+ if (bytes_read < size) {
+ continue;
+ }
+ // If bytes_read > size then we have over read, which shouldn't happen
+ if (bytes_read > size) {
+ throw SerialException ("read over read, too many bytes where "
+ "read, this shouldn't happen, might be "
+ "a logical error!");
+ }
+ }
+ }
+ return bytes_read;
+}
+
+size_t
+Serial::SerialImpl::write (const uint8_t *data, size_t length)
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::write");
+ }
+ fd_set writefds;
+ size_t bytes_written = 0;
+
+ // Calculate total timeout in milliseconds t_c + (t_m * N)
+ long total_timeout_ms = timeout_.write_timeout_constant;
+ total_timeout_ms += timeout_.write_timeout_multiplier * static_cast (length);
+ MillisecondTimer total_timeout(total_timeout_ms);
+
+ bool first_iteration = true;
+ while (bytes_written < length) {
+ int64_t timeout_remaining_ms = total_timeout.remaining();
+ // Only consider the timeout if it's not the first iteration of the loop
+ // otherwise a timeout of 0 won't be allowed through
+ if (!first_iteration && (timeout_remaining_ms <= 0)) {
+ // Timed out
+ break;
+ }
+ first_iteration = false;
+
+ timespec timeout(timespec_from_ms(timeout_remaining_ms));
+
+ FD_ZERO (&writefds);
+ FD_SET (fd_, &writefds);
+
+ // Do the select
+ int r = pselect (fd_ + 1, NULL, &writefds, NULL, &timeout, NULL);
+
+ // Figure out what happened by looking at select's response 'r'
+ /** Error **/
+ if (r < 0) {
+ // Select was interrupted, try again
+ if (errno == EINTR) {
+ continue;
+ }
+ // Otherwise there was some error
+ THROW (IOException, errno);
+ }
+ /** Timeout **/
+ if (r == 0) {
+ break;
+ }
+ /** Port ready to write **/
+ if (r > 0) {
+ // Make sure our file descriptor is in the ready to write list
+ if (FD_ISSET (fd_, &writefds)) {
+ // This will write some
+ ssize_t bytes_written_now =
+ ::write (fd_, data + bytes_written, length - bytes_written);
+
+ // even though pselect returned readiness the call might still be
+ // interrupted. In that case simply retry.
+ if (bytes_written_now == -1 && errno == EINTR) {
+ continue;
+ }
+
+ // write should always return some data as select reported it was
+ // ready to write when we get to this point.
+ if (bytes_written_now < 1) {
+ // Disconnected devices, at least on Linux, show the
+ // behavior that they are always ready to write immediately
+ // but writing returns nothing.
+ std::stringstream strs;
+ strs << "device reports readiness to write but "
+ "returned no data (device disconnected?)";
+ strs << " errno=" << errno;
+ strs << " bytes_written_now= " << bytes_written_now;
+ strs << " bytes_written=" << bytes_written;
+ strs << " length=" << length;
+ throw SerialException(strs.str().c_str());
+ }
+ // Update bytes_written
+ bytes_written += static_cast (bytes_written_now);
+ // If bytes_written == size then we have written everything we need to
+ if (bytes_written == length) {
+ break;
+ }
+ // If bytes_written < size then we have more to write
+ if (bytes_written < length) {
+ continue;
+ }
+ // If bytes_written > size then we have over written, which shouldn't happen
+ if (bytes_written > length) {
+ throw SerialException ("write over wrote, too many bytes where "
+ "written, this shouldn't happen, might be "
+ "a logical error!");
+ }
+ }
+ // This shouldn't happen, if r > 0 our fd has to be in the list!
+ THROW (IOException, "select reports ready to write, but our fd isn't"
+ " in the list, this shouldn't happen!");
+ }
+ }
+ return bytes_written;
+}
+
+void
+Serial::SerialImpl::setPort (const string &port)
+{
+ port_ = port;
+}
+
+string
+Serial::SerialImpl::getPort () const
+{
+ return port_;
+}
+
+void
+Serial::SerialImpl::setTimeout (serial::Timeout &timeout)
+{
+ timeout_ = timeout;
+}
+
+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");
+ }
+ tcdrain (fd_);
+}
+
+void
+Serial::SerialImpl::flushInput ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::flushInput");
+ }
+ tcflush (fd_, TCIFLUSH);
+}
+
+void
+Serial::SerialImpl::flushOutput ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::flushOutput");
+ }
+ tcflush (fd_, TCOFLUSH);
+}
+
+void
+Serial::SerialImpl::sendBreak (int duration)
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::sendBreak");
+ }
+ tcsendbreak (fd_, static_cast (duration / 4));
+}
+
+void
+Serial::SerialImpl::setBreak (bool level)
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::setBreak");
+ }
+
+ if (level) {
+ if (-1 == ioctl (fd_, TIOCSBRK))
+ {
+ stringstream ss;
+ ss << "setBreak failed on a call to ioctl(TIOCSBRK): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ } else {
+ if (-1 == ioctl (fd_, TIOCCBRK))
+ {
+ stringstream ss;
+ ss << "setBreak failed on a call to ioctl(TIOCCBRK): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ }
+}
+
+void
+Serial::SerialImpl::setRTS (bool level)
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::setRTS");
+ }
+
+ int command = TIOCM_RTS;
+
+ if (level) {
+ if (-1 == ioctl (fd_, TIOCMBIS, &command))
+ {
+ stringstream ss;
+ ss << "setRTS failed on a call to ioctl(TIOCMBIS): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ } else {
+ if (-1 == ioctl (fd_, TIOCMBIC, &command))
+ {
+ stringstream ss;
+ ss << "setRTS failed on a call to ioctl(TIOCMBIC): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ }
+}
+
+void
+Serial::SerialImpl::setDTR (bool level)
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::setDTR");
+ }
+
+ int command = TIOCM_DTR;
+
+ if (level) {
+ if (-1 == ioctl (fd_, TIOCMBIS, &command))
+ {
+ stringstream ss;
+ ss << "setDTR failed on a call to ioctl(TIOCMBIS): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ } else {
+ if (-1 == ioctl (fd_, TIOCMBIC, &command))
+ {
+ stringstream ss;
+ ss << "setDTR failed on a call to ioctl(TIOCMBIC): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ }
+}
+
+bool
+Serial::SerialImpl::waitForChange ()
+{
+#ifndef TIOCMIWAIT
+
+while (is_open_ == true) {
+
+ int status;
+
+ if (-1 == ioctl (fd_, TIOCMGET, &status))
+ {
+ stringstream ss;
+ ss << "waitForChange failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ else
+ {
+ if (0 != (status & TIOCM_CTS)
+ || 0 != (status & TIOCM_DSR)
+ || 0 != (status & TIOCM_RI)
+ || 0 != (status & TIOCM_CD))
+ {
+ return true;
+ }
+ }
+
+ usleep(1000);
+ }
+
+ return false;
+#else
+ int command = (TIOCM_CD|TIOCM_DSR|TIOCM_RI|TIOCM_CTS);
+
+ if (-1 == ioctl (fd_, TIOCMIWAIT, &command)) {
+ stringstream ss;
+ ss << "waitForDSR failed on a call to ioctl(TIOCMIWAIT): "
+ << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ return true;
+#endif
+}
+
+bool
+Serial::SerialImpl::getCTS ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::getCTS");
+ }
+
+ int status;
+
+ if (-1 == ioctl (fd_, TIOCMGET, &status))
+ {
+ stringstream ss;
+ ss << "getCTS failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ else
+ {
+ return 0 != (status & TIOCM_CTS);
+ }
+}
+
+bool
+Serial::SerialImpl::getDSR ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::getDSR");
+ }
+
+ int status;
+
+ if (-1 == ioctl (fd_, TIOCMGET, &status))
+ {
+ stringstream ss;
+ ss << "getDSR failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ else
+ {
+ return 0 != (status & TIOCM_DSR);
+ }
+}
+
+bool
+Serial::SerialImpl::getRI ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::getRI");
+ }
+
+ int status;
+
+ if (-1 == ioctl (fd_, TIOCMGET, &status))
+ {
+ stringstream ss;
+ ss << "getRI failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ else
+ {
+ return 0 != (status & TIOCM_RI);
+ }
+}
+
+bool
+Serial::SerialImpl::getCD ()
+{
+ if (is_open_ == false) {
+ throw PortNotOpenedException ("Serial::getCD");
+ }
+
+ int status;
+
+ if (-1 == ioctl (fd_, TIOCMGET, &status))
+ {
+ stringstream ss;
+ ss << "getCD failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno);
+ throw(SerialException(ss.str().c_str()));
+ }
+ else
+ {
+ return 0 != (status & TIOCM_CD);
+ }
+}
+
+void
+Serial::SerialImpl::readLock ()
+{
+ int result = pthread_mutex_lock(&this->read_mutex);
+ if (result) {
+ THROW (IOException, result);
+ }
+}
+
+void
+Serial::SerialImpl::readUnlock ()
+{
+ int result = pthread_mutex_unlock(&this->read_mutex);
+ if (result) {
+ THROW (IOException, result);
+ }
+}
+
+void
+Serial::SerialImpl::writeLock ()
+{
+ int result = pthread_mutex_lock(&this->write_mutex);
+ if (result) {
+ THROW (IOException, result);
+ }
+}
+
+void
+Serial::SerialImpl::writeUnlock ()
+{
+ int result = pthread_mutex_unlock(&this->write_mutex);
+ if (result) {
+ THROW (IOException, result);
+ }
+}
+
+#endif // !defined(_WIN32)
diff --git a/serial/src/impl/win.cc b/serial/src/impl/win.cc
new file mode 100644
index 0000000..889e06f
--- /dev/null
+++ b/serial/src/impl/win.cc
@@ -0,0 +1,646 @@
+#if defined(_WIN32)
+
+/* Copyright 2012 William Woodall and John Harrison */
+
+#include
+
+#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(0, windows_com_port_prefix.size(), 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(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(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(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)
+
diff --git a/serial/src/serial.cc b/serial/src/serial.cc
new file mode 100644
index 0000000..a9e6f84
--- /dev/null
+++ b/serial/src/serial.cc
@@ -0,0 +1,432 @@
+/* Copyright 2012 William Woodall and John Harrison */
+#include
+
+#if !defined(_WIN32) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
+# include
+#endif
+
+#if defined (__MINGW32__)
+# define alloca __builtin_alloca
+#endif
+
+#include "serial/serial.h"
+
+#ifdef _WIN32
+#include "serial/impl/win.h"
+#else
+#include "serial/impl/unix.h"
+#endif
+
+using std::invalid_argument;
+using std::min;
+using std::numeric_limits;
+using std::vector;
+using std::size_t;
+using std::string;
+
+using serial::Serial;
+using serial::SerialException;
+using serial::IOException;
+using serial::bytesize_t;
+using serial::parity_t;
+using serial::stopbits_t;
+using serial::flowcontrol_t;
+
+class Serial::ScopedReadLock {
+public:
+ ScopedReadLock(SerialImpl *pimpl) : pimpl_(pimpl) {
+ this->pimpl_->readLock();
+ }
+ ~ScopedReadLock() {
+ this->pimpl_->readUnlock();
+ }
+private:
+ // Disable copy constructors
+ ScopedReadLock(const ScopedReadLock&);
+ const ScopedReadLock& operator=(ScopedReadLock);
+
+ SerialImpl *pimpl_;
+};
+
+class Serial::ScopedWriteLock {
+public:
+ ScopedWriteLock(SerialImpl *pimpl) : pimpl_(pimpl) {
+ this->pimpl_->writeLock();
+ }
+ ~ScopedWriteLock() {
+ this->pimpl_->writeUnlock();
+ }
+private:
+ // Disable copy constructors
+ ScopedWriteLock(const ScopedWriteLock&);
+ const ScopedWriteLock& operator=(ScopedWriteLock);
+ SerialImpl *pimpl_;
+};
+
+Serial::Serial (const string &port, uint32_t baudrate, serial::Timeout timeout,
+ bytesize_t bytesize, parity_t parity, stopbits_t stopbits,
+ flowcontrol_t flowcontrol)
+ : pimpl_(new SerialImpl (port, baudrate, bytesize, parity,
+ stopbits, flowcontrol))
+{
+ pimpl_->setTimeout(timeout);
+}
+
+Serial::~Serial ()
+{
+ delete pimpl_;
+}
+
+void
+Serial::open ()
+{
+ pimpl_->open ();
+}
+
+void
+Serial::close ()
+{
+ pimpl_->close ();
+}
+
+bool
+Serial::isOpen () const
+{
+ return pimpl_->isOpen ();
+}
+
+size_t
+Serial::available ()
+{
+ return pimpl_->available ();
+}
+
+bool
+Serial::waitReadable ()
+{
+ serial::Timeout timeout(pimpl_->getTimeout ());
+ return pimpl_->waitReadable(timeout.read_timeout_constant);
+}
+
+void
+Serial::waitByteTimes (size_t count)
+{
+ pimpl_->waitByteTimes(count);
+}
+
+size_t
+Serial::read_ (uint8_t *buffer, size_t size)
+{
+ return this->pimpl_->read (buffer, size);
+}
+
+size_t
+Serial::read (uint8_t *buffer, size_t size)
+{
+ ScopedReadLock lock(this->pimpl_);
+ return this->pimpl_->read (buffer, size);
+}
+
+size_t
+Serial::read (std::vector &buffer, size_t size)
+{
+ ScopedReadLock lock(this->pimpl_);
+ uint8_t *buffer_ = new uint8_t[size];
+ size_t bytes_read = 0;
+
+ try {
+ bytes_read = this->pimpl_->read (buffer_, size);
+ }
+ catch (const std::exception &e) {
+ delete[] buffer_;
+ throw;
+ }
+
+ buffer.insert (buffer.end (), buffer_, buffer_+bytes_read);
+ delete[] buffer_;
+ return bytes_read;
+}
+
+size_t
+Serial::read (std::string &buffer, size_t size)
+{
+ ScopedReadLock lock(this->pimpl_);
+ uint8_t *buffer_ = new uint8_t[size];
+ size_t bytes_read = 0;
+ try {
+ bytes_read = this->pimpl_->read (buffer_, size);
+ }
+ catch (const std::exception &e) {
+ delete[] buffer_;
+ throw;
+ }
+ buffer.append (reinterpret_cast(buffer_), bytes_read);
+ delete[] buffer_;
+ return bytes_read;
+}
+
+string
+Serial::read (size_t size)
+{
+ std::string buffer;
+ this->read (buffer, size);
+ return buffer;
+}
+
+size_t
+Serial::readline (string &buffer, size_t size, string eol)
+{
+ ScopedReadLock lock(this->pimpl_);
+ size_t eol_len = eol.length ();
+ uint8_t *buffer_ = static_cast
+ (alloca (size * sizeof (uint8_t)));
+ size_t read_so_far = 0;
+ while (true)
+ {
+ size_t bytes_read = this->read_ (buffer_ + read_so_far, 1);
+ read_so_far += bytes_read;
+ if (bytes_read == 0) {
+ break; // Timeout occured on reading 1 byte
+ }
+ if(read_so_far < eol_len) continue;
+ if (string (reinterpret_cast
+ (buffer_ + read_so_far - eol_len), eol_len) == eol) {
+ break; // EOL found
+ }
+ if (read_so_far == size) {
+ break; // Reached the maximum read length
+ }
+ }
+ buffer.append(reinterpret_cast (buffer_), read_so_far);
+ return read_so_far;
+}
+
+string
+Serial::readline (size_t size, string eol)
+{
+ std::string buffer;
+ this->readline (buffer, size, eol);
+ return buffer;
+}
+
+vector
+Serial::readlines (size_t size, string eol)
+{
+ ScopedReadLock lock(this->pimpl_);
+ std::vector lines;
+ size_t eol_len = eol.length ();
+ uint8_t *buffer_ = static_cast
+ (alloca (size * sizeof (uint8_t)));
+ size_t read_so_far = 0;
+ size_t start_of_line = 0;
+ while (read_so_far < size) {
+ size_t bytes_read = this->read_ (buffer_+read_so_far, 1);
+ read_so_far += bytes_read;
+ if (bytes_read == 0) {
+ if (start_of_line != read_so_far) {
+ lines.push_back (
+ string (reinterpret_cast (buffer_ + start_of_line),
+ read_so_far - start_of_line));
+ }
+ break; // Timeout occured on reading 1 byte
+ }
+ if(read_so_far < eol_len) continue;
+ if (string (reinterpret_cast
+ (buffer_ + read_so_far - eol_len), eol_len) == eol) {
+ // EOL found
+ lines.push_back(
+ string(reinterpret_cast (buffer_ + start_of_line),
+ read_so_far - start_of_line));
+ start_of_line = read_so_far;
+ }
+ if (read_so_far == size) {
+ if (start_of_line != read_so_far) {
+ lines.push_back(
+ string(reinterpret_cast (buffer_ + start_of_line),
+ read_so_far - start_of_line));
+ }
+ break; // Reached the maximum read length
+ }
+ }
+ return lines;
+}
+
+size_t
+Serial::write (const string &data)
+{
+ ScopedWriteLock lock(this->pimpl_);
+ return this->write_ (reinterpret_cast(data.c_str()),
+ data.length());
+}
+
+size_t
+Serial::write (const std::vector &data)
+{
+ ScopedWriteLock lock(this->pimpl_);
+ return this->write_ (&data[0], data.size());
+}
+
+size_t
+Serial::write (const uint8_t *data, size_t size)
+{
+ ScopedWriteLock lock(this->pimpl_);
+ return this->write_(data, size);
+}
+
+size_t
+Serial::write_ (const uint8_t *data, size_t length)
+{
+ return pimpl_->write (data, length);
+}
+
+void
+Serial::setPort (const string &port)
+{
+ ScopedReadLock rlock(this->pimpl_);
+ ScopedWriteLock wlock(this->pimpl_);
+ bool was_open = pimpl_->isOpen ();
+ if (was_open) close();
+ pimpl_->setPort (port);
+ if (was_open) open ();
+}
+
+string
+Serial::getPort () const
+{
+ return pimpl_->getPort ();
+}
+
+void
+Serial::setTimeout (serial::Timeout &timeout)
+{
+ pimpl_->setTimeout (timeout);
+}
+
+serial::Timeout
+Serial::getTimeout () const {
+ return pimpl_->getTimeout ();
+}
+
+void
+Serial::setBaudrate (uint32_t baudrate)
+{
+ pimpl_->setBaudrate (baudrate);
+}
+
+uint32_t
+Serial::getBaudrate () const
+{
+ return uint32_t(pimpl_->getBaudrate ());
+}
+
+void
+Serial::setBytesize (bytesize_t bytesize)
+{
+ pimpl_->setBytesize (bytesize);
+}
+
+bytesize_t
+Serial::getBytesize () const
+{
+ return pimpl_->getBytesize ();
+}
+
+void
+Serial::setParity (parity_t parity)
+{
+ pimpl_->setParity (parity);
+}
+
+parity_t
+Serial::getParity () const
+{
+ return pimpl_->getParity ();
+}
+
+void
+Serial::setStopbits (stopbits_t stopbits)
+{
+ pimpl_->setStopbits (stopbits);
+}
+
+stopbits_t
+Serial::getStopbits () const
+{
+ return pimpl_->getStopbits ();
+}
+
+void
+Serial::setFlowcontrol (flowcontrol_t flowcontrol)
+{
+ pimpl_->setFlowcontrol (flowcontrol);
+}
+
+flowcontrol_t
+Serial::getFlowcontrol () const
+{
+ return pimpl_->getFlowcontrol ();
+}
+
+void Serial::flush ()
+{
+ ScopedReadLock rlock(this->pimpl_);
+ ScopedWriteLock wlock(this->pimpl_);
+ pimpl_->flush ();
+}
+
+void Serial::flushInput ()
+{
+ ScopedReadLock lock(this->pimpl_);
+ pimpl_->flushInput ();
+}
+
+void Serial::flushOutput ()
+{
+ ScopedWriteLock lock(this->pimpl_);
+ pimpl_->flushOutput ();
+}
+
+void Serial::sendBreak (int duration)
+{
+ pimpl_->sendBreak (duration);
+}
+
+void Serial::setBreak (bool level)
+{
+ pimpl_->setBreak (level);
+}
+
+void Serial::setRTS (bool level)
+{
+ pimpl_->setRTS (level);
+}
+
+void Serial::setDTR (bool level)
+{
+ pimpl_->setDTR (level);
+}
+
+bool Serial::waitForChange()
+{
+ return pimpl_->waitForChange();
+}
+
+bool Serial::getCTS ()
+{
+ return pimpl_->getCTS ();
+}
+
+bool Serial::getDSR ()
+{
+ return pimpl_->getDSR ();
+}
+
+bool Serial::getRI ()
+{
+ return pimpl_->getRI ();
+}
+
+bool Serial::getCD ()
+{
+ return pimpl_->getCD ();
+}