aboutsummaryrefslogtreecommitdiffstats
path: root/source/serial/src/impl/unix.cc
diff options
context:
space:
mode:
Diffstat (limited to 'source/serial/src/impl/unix.cc')
-rwxr-xr-xsource/serial/src/impl/unix.cc1066
1 files changed, 1066 insertions, 0 deletions
diff --git a/source/serial/src/impl/unix.cc b/source/serial/src/impl/unix.cc
new file mode 100755
index 0000000..4309aa6
--- /dev/null
+++ b/source/serial/src/impl/unix.cc
@@ -0,0 +1,1066 @@
+/* Copyright 2012 William Woodall and John Harrison
+ *
+ * Additional Contributors: Christopher Baker @bakercp
+ */
+
+#if !defined(_WIN32)
+
+#include <stdio.h>
+#include <string.h>
+#include <sstream>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <errno.h>
+#include <paths.h>
+#include <sysexits.h>
+#include <termios.h>
+#include <sys/param.h>
+#include <pthread.h>
+
+#if defined(__linux__)
+# include <linux/serial.h>
+#endif
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <time.h>
+#ifdef __MACH__
+#include <AvailabilityMacros.h>
+#include <mach/clock.h>
+#include <mach/mach.h>
+#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 <IOKit/serial/ioss.h>
+#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<int> (1e9);
+ expiry.tv_nsec = tv_nsec % static_cast<int>(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;
+ // 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<speed_t> (baudrate_);
+ 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<int> (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
+ }
+ 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);
+
+ // 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<size_t> (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<long>(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<long> (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<uint32_t> (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<size_t> (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<long> (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);
+ // 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.
+ throw SerialException ("device reports readiness to write but "
+ "returned no data (device disconnected?)");
+ }
+ // Update bytes_written
+ bytes_written += static_cast<size_t> (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<int> (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)