diff --git a/src/impl/unix.cc b/src/impl/unix.cc index 24ad257..28243de 100644 --- a/src/impl/unix.cc +++ b/src/impl/unix.cc @@ -31,6 +31,7 @@ using serial::SerialExecption; using serial::PortNotOpenedException; using serial::IOException; + Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate, long timeout, bytesize_t bytesize, parity_t parity, stopbits_t stopbits, @@ -114,8 +115,131 @@ Serial::SerialImpl::reconfigurePort () #endif // setup baud rate - // TODO(ash_git): validate baud rate - cfsetspeed(&options, baudrate_); + 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 B921600 + case 921600: baud = B921600; break; +#endif + default: + custom_baud = true; +// Mac OS X 10.x Support +#if defined(__APPLE__) && defined(__MACH__) +#define IOSSIOSPEED _IOW('T', 2, speed_t) + int new_baud = static_cast (baudrate_); + if (ioctl (fd_, IOSSIOSPEED, &new_baud, 1) < 0) + { + throw IOException (errno); + } +// Linux Support +#elif defined(__linux__) + struct serial_struct ser; + ioctl(fd_, TIOCGSERIAL, &ser); + // set custom divisor + ser.custom_divisor = ser.baud_base / baudrate; + // update flags + ser.flags &= ~ASYNC_SPD_MASK; + ser.flags |= ASYNC_SPD_CUST; + + if (ioctl(fd_, TIOCSSERIAL, buf) < 0) + { + 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 &= (unsigned long) ~CSIZE; @@ -265,7 +389,6 @@ Serial::SerialImpl::read (char* buf, size_t size) bytes_read = ::read (fd_, buf, size); // read should always return some data as select reported it was // ready to read when we get to this point. - // printf("bytes_read: %lu\n", bytes_read); if (bytes_read < 1) { // Disconnected devices, at least on Linux, show the @@ -291,25 +414,51 @@ Serial::SerialImpl::write (const string &data) { throw PortNotOpenedException ("Serial::write"); } - - ssize_t n = ::write (fd_, data.c_str (), data.length ()); - - if (n != static_cast (data.length ())) + + fd_set writefds; + ssize_t bytes_written = 0; + while (true) { - throw IOException ("Write did not complete"); - } - else if (n == -1) - { - if (errno == EINTR) + if (timeout_ != -1) { - return write (data); + FD_ZERO (&writefds); + FD_SET (fd_, &writefds); + struct timeval timeout; + timeout.tv_sec = timeout_ / 1000; + timeout.tv_usec = static_cast (timeout_ % 1000) * 1000; + int r = select (fd_ + 1, NULL, &writefds, NULL, &timeout); + + if (r == -1 && errno == EINTR) + continue; + + if (r == -1) + { + throw IOException (errno); + } + } + + if (timeout_ == -1 || FD_ISSET (fd_, &writefds)) + { + bytes_written = ::write (fd_, data.c_str (), data.length ()); + // read should always return some data as select reported it was + // ready to read when we get to this point. + if (bytes_written < 1) + { + // Disconnected devices, at least on Linux, show the + // behavior that they are always ready to read immediately + // but reading returns nothing. + throw SerialExecption ("device reports readiness to read but " + "returned no data (device disconnected?)"); + } + break; } else { - throw IOException (errno); + break; } } - return static_cast (n); + + return static_cast (bytes_written); } void @@ -538,3 +687,4 @@ Serial::SerialImpl::getCD() int s = ioctl (fd_, TIOCMGET, 0); return (s & TIOCM_CD) != 0; } +