summaryrefslogtreecommitdiff
path: root/Source/serial.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/serial.cpp')
-rwxr-xr-xSource/serial.cpp905
1 files changed, 905 insertions, 0 deletions
diff --git a/Source/serial.cpp b/Source/serial.cpp
new file mode 100755
index 0000000..67e9be3
--- /dev/null
+++ b/Source/serial.cpp
@@ -0,0 +1,905 @@
+/* Serial port object for use with wxWidgets
+ * Copyright 2010, Paul Stoffregen (paul@pjrc.com)
+ * Modified by: Leonardo Laguna Ruiz
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "serial.h"
+
+
+#if defined(LINUX)
+ #include <sys/types.h>
+ #include <fcntl.h>
+ #include <errno.h>
+ #include <sys/select.h>
+ #include <termios.h>
+ #include <unistd.h>
+ #include <dirent.h>
+ #include <sys/stat.h>
+ #include <sys/ioctl.h>
+ #include <linux/serial.h>
+ #include <errno.h>
+ #include <stdio.h>
+ #include <string.h>
+#elif defined(MACOSX)
+ #include <stdio.h>
+ #include <string.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/ioctl.h>
+ #include <errno.h>
+ #include <paths.h>
+ #include <termios.h>
+ #include <sysexits.h>
+ #include <sys/param.h>
+ #include <sys/select.h>
+ #include <sys/time.h>
+ #include <time.h>
+ #include <CoreFoundation/CoreFoundation.h>
+ #include <IOKit/IOKitLib.h>
+ #include <IOKit/serial/IOSerialKeys.h>
+ #include <IOKit/IOBSD.h>
+#elif defined(WINDOWS)
+ #include <windows.h>
+ #define win32_err(s) FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, \
+ GetLastError(), 0, (s), sizeof(s), NULL)
+ #define QUERYDOSDEVICE_BUFFER_SIZE 262144
+ #if _MSC_VER
+ #define snprintf _snprintf_s
+ #endif
+#else
+ #error "This platform is unsupported, sorry"
+#endif
+
+
+
+
+Serial::Serial()
+{
+ port_is_open = 0;
+ baud_rate = 38400; // default baud rate
+}
+
+Serial::~Serial()
+{
+ Close();
+}
+
+
+
+// Open a port, by name. Return 0 on success, non-zero for error
+int Serial::Open(const string& name)
+{
+ Close();
+#if defined(LINUX)
+ struct serial_struct kernel_serial_settings;
+ int bits;
+ port_fd = open(name.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (port_fd < 0) {
+ if (errno == EACCES) {
+ error_msg = "Unable to access " + name + ", insufficient permission";
+ // TODO: we could look at the permission bits and owner
+ // to make a better message here
+ } else if (errno == EISDIR) {
+ error_msg = "Unable to open " + name +
+ ", Object is a directory, not a serial port";
+ } else if (errno == ENODEV || errno == ENXIO) {
+ error_msg = "Unable to open " + name +
+ ", Serial port hardware not installed";
+ } else if (errno == ENOENT) {
+ error_msg = "Unable to open " + name +
+ ", Device name does not exist";
+ } else {
+ error_msg = "Unable to open " + name +
+ ", " + strerror(errno);
+ }
+ return -1;
+ }
+ if (ioctl(port_fd, TIOCMGET, &bits) < 0) {
+ close(port_fd);
+ error_msg = "Unable to query serial port signals";
+ return -1;
+ }
+ bits &= ~(TIOCM_DTR | TIOCM_RTS);
+ if (ioctl(port_fd, TIOCMSET, &bits) < 0) {
+ close(port_fd);
+ error_msg = "Unable to control serial port signals";
+ return -1;
+ }
+ if (tcgetattr(port_fd, &settings_orig) != 0) {
+ close(port_fd);
+ error_msg = "Unable to query serial port settings (perhaps not a serial port)";
+ return -1;
+ }
+ memset(&settings, 0, sizeof(settings));
+ settings.c_iflag = IGNBRK | IGNPAR;
+ settings.c_cflag = CS8 | CREAD | HUPCL | CLOCAL;
+ Set_baud(baud_rate);
+ if (ioctl(port_fd, TIOCGSERIAL, &kernel_serial_settings) == 0) {
+ kernel_serial_settings.flags |= ASYNC_LOW_LATENCY;
+ ioctl(port_fd, TIOCSSERIAL, &kernel_serial_settings);
+ }
+ tcflush(port_fd, TCIFLUSH);
+#elif defined(MACOSX)
+ int bits;
+ port_fd = open(name.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (port_fd < 0) {
+ error_msg = "Unable to open " + name + ", " + strerror(errno);
+ return -1;
+ }
+ if (ioctl(port_fd, TIOCEXCL) == -1) {
+ close(port_fd);
+ error_msg = "Unable to get exclussive access to port " + name;
+ return -1;
+ }
+ if (ioctl(port_fd, TIOCMGET, &bits) < 0) {
+ close(port_fd);
+ error_msg = "Unable to query serial port signals on " + name;
+ return -1;
+ }
+ bits &= ~(TIOCM_DTR | TIOCM_RTS);
+ if (ioctl(port_fd, TIOCMSET, &bits) < 0) {
+ close(port_fd);
+ error_msg = "Unable to control serial port signals on " + name;
+ return -1;
+ }
+ if (tcgetattr(port_fd, &settings_orig) < 0) {
+ close(port_fd);
+ error_msg = "Unable to access baud rate on port " + name;
+ return -1;
+ }
+ memset(&settings, 0, sizeof(settings));
+ settings.c_cflag = CS8 | CLOCAL | CREAD | HUPCL;
+ settings.c_iflag = IGNBRK | IGNPAR;
+ Set_baud(baud_rate);
+ tcflush(port_fd, TCIFLUSH);
+#elif defined(WINDOWS)
+ COMMCONFIG cfg;
+ COMMTIMEOUTS timeouts;
+ int got_default_cfg=0, port_num;
+ char buf[1024], name_createfile[64], name_commconfig[64], *p;
+ DWORD len;
+
+ snprintf(buf, sizeof(buf), "%s", name.c_str());
+ p = strstr(buf, "COM");
+ if (p && sscanf(p + 3, "%d", &port_num) == 1) {
+ //printf("port_num = %d\n", port_num);
+ snprintf(name_createfile, sizeof(name_createfile), "\\\\.\\COM%d", port_num);
+ snprintf(name_commconfig, sizeof(name_commconfig), "COM%d", port_num);
+ } else {
+ snprintf(name_createfile, sizeof(name_createfile), "%s", name.c_str());
+ snprintf(name_commconfig, sizeof(name_commconfig), "%s", name.c_str());
+ }
+ len = sizeof(COMMCONFIG);
+ if (GetDefaultCommConfig(name_commconfig, &cfg, &len)) {
+ // this prevents unintentionally raising DTR when opening
+ // might only work on COM1 to COM9
+ got_default_cfg = 1;
+ memcpy(&port_cfg_orig, &cfg, sizeof(COMMCONFIG));
+ cfg.dcb.fDtrControl = DTR_CONTROL_DISABLE;
+ cfg.dcb.fRtsControl = RTS_CONTROL_DISABLE;
+ SetDefaultCommConfig(name_commconfig, &cfg, sizeof(COMMCONFIG));
+ } else {
+ printf("error with GetDefaultCommConfig\n");
+ }
+ port_handle = CreateFile(name_createfile, GENERIC_READ | GENERIC_WRITE,
+ 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+ if (port_handle == INVALID_HANDLE_VALUE) {
+ win32_err(buf);
+ error_msg = "Unable to open " + name + ", " + buf;
+ return -1;
+ }
+ len = sizeof(COMMCONFIG);
+ if (!GetCommConfig(port_handle, &port_cfg, &len)) {
+ CloseHandle(port_handle);
+ win32_err(buf);
+ error_msg = "Unable to read communication config on " + name + ", " + buf;
+ return -1;
+ }
+ if (!got_default_cfg) {
+ memcpy(&port_cfg_orig, &port_cfg, sizeof(COMMCONFIG));
+ }
+ // http://msdn2.microsoft.com/en-us/library/aa363188(VS.85).aspx
+ port_cfg.dcb.BaudRate = baud_rate;
+ port_cfg.dcb.fBinary = TRUE;
+ port_cfg.dcb.fParity = FALSE;
+ port_cfg.dcb.fOutxCtsFlow = FALSE;
+ port_cfg.dcb.fOutxDsrFlow = FALSE;
+ port_cfg.dcb.fDtrControl = DTR_CONTROL_ENABLE;
+ port_cfg.dcb.fDsrSensitivity = FALSE;
+ port_cfg.dcb.fTXContinueOnXoff = TRUE; // ???
+ port_cfg.dcb.fOutX = FALSE;
+ port_cfg.dcb.fInX = FALSE;
+ port_cfg.dcb.fErrorChar = FALSE;
+ port_cfg.dcb.fNull = FALSE;
+ port_cfg.dcb.fRtsControl = RTS_CONTROL_DISABLE;
+ port_cfg.dcb.fAbortOnError = FALSE;
+ port_cfg.dcb.ByteSize = 8;
+ port_cfg.dcb.Parity = NOPARITY;
+ port_cfg.dcb.StopBits = ONESTOPBIT;
+ if (!SetCommConfig(port_handle, &port_cfg, sizeof(COMMCONFIG))) {
+ CloseHandle(port_handle);
+ win32_err(buf);
+ error_msg = "Unable to write communication config to " + name + ", " + buf;
+ return -1;
+ }
+ if (!EscapeCommFunction(port_handle, CLRDTR | CLRRTS)) {
+ CloseHandle(port_handle);
+ win32_err(buf);
+ error_msg = "Unable to control serial port signals on " + name + ", " + buf;
+ return -1;
+ }
+ // http://msdn2.microsoft.com/en-us/library/aa363190(VS.85).aspx
+ // setting to all zeros means timeouts are not used
+ //timeouts.ReadIntervalTimeout = 0;
+ timeouts.ReadIntervalTimeout = MAXDWORD;
+ timeouts.ReadTotalTimeoutMultiplier = 0;
+ timeouts.ReadTotalTimeoutConstant = 0;
+ timeouts.WriteTotalTimeoutMultiplier = 0;
+ timeouts.WriteTotalTimeoutConstant = 0;
+ if (!SetCommTimeouts(port_handle, &timeouts)) {
+ CloseHandle(port_handle);
+ win32_err(buf);
+ error_msg = "Unable to write timeout settings to " + name + ", " + buf;
+ return -1;
+ }
+#endif
+ port_name = name;
+ port_is_open = 1;
+ return 0;
+}
+
+string Serial::get_name(void)
+{
+ if (!port_is_open) return "";
+ return port_name;
+}
+
+
+
+// Return the last error message in a (hopefully) user friendly format
+string Serial::error_message(void)
+{
+ return error_msg;
+}
+
+int Serial::Is_open(void)
+{
+ return port_is_open;
+}
+
+// Close the port
+void Serial::Close(void)
+{
+ if (!port_is_open) return;
+ Output_flush();
+ Input_discard();
+ port_is_open = 0;
+ port_name = "";
+#if defined(LINUX) || defined(MACOSX)
+ tcsetattr(port_fd, TCSANOW, &settings_orig);
+ close(port_fd);
+#elif defined(WINDOWS)
+ //SetCommConfig(port_handle, &port_cfg_orig, sizeof(COMMCONFIG));
+ CloseHandle(port_handle);
+#endif
+}
+
+
+
+
+
+
+// set the baud rate
+int Serial::Set_baud(int baud)
+{
+ if (baud <= 0) return -1;
+ //printf("set_baud: %d\n", baud);
+ baud_rate = baud;
+ if (!port_is_open) return -1;
+#if defined(LINUX)
+ speed_t spd;
+ switch (baud) {
+ case 230400: spd = B230400; break;
+ case 115200: spd = B115200; break;
+ case 57600: spd = B57600; break;
+ case 38400: spd = B38400; break;
+ case 19200: spd = B19200; break;
+ case 9600: spd = B9600; break;
+ case 4800: spd = B4800; break;
+ case 2400: spd = B2400; break;
+ case 1800: spd = B1800; break;
+ case 1200: spd = B1200; break;
+ case 600: spd = B600; break;
+ case 300: spd = B300; break;
+ case 200: spd = B200; break;
+ case 150: spd = B150; break;
+ case 134: spd = B134; break;
+ case 110: spd = B110; break;
+ case 75: spd = B75; break;
+ case 50: spd = B50; break;
+ #ifdef B460800
+ case 460800: spd = B460800; break;
+ #endif
+ #ifdef B500000
+ case 500000: spd = B500000; break;
+ #endif
+ #ifdef B576000
+ case 576000: spd = B576000; break;
+ #endif
+ #ifdef B921600
+ case 921600: spd = B921600; break;
+ #endif
+ #ifdef B1000000
+ case 1000000: spd = B1000000; break;
+ #endif
+ #ifdef B1152000
+ case 1152000: spd = B1152000; break;
+ #endif
+ #ifdef B1500000
+ case 1500000: spd = B1500000; break;
+ #endif
+ #ifdef B2000000
+ case 2000000: spd = B2000000; break;
+ #endif
+ #ifdef B2500000
+ case 2500000: spd = B2500000; break;
+ #endif
+ #ifdef B3000000
+ case 3000000: spd = B3000000; break;
+ #endif
+ #ifdef B3500000
+ case 3500000: spd = B3500000; break;
+ #endif
+ #ifdef B4000000
+ case 4000000: spd = B4000000; break;
+ #endif
+ #ifdef B7200
+ case 7200: spd = B7200; break;
+ #endif
+ #ifdef B14400
+ case 14400: spd = B14400; break;
+ #endif
+ #ifdef B28800
+ case 28800: spd = B28800; break;
+ #endif
+ #ifdef B76800
+ case 76800: spd = B76800; break;
+ #endif
+ default: {
+ return -1;
+ }
+ }
+ cfsetospeed(&settings, spd);
+ cfsetispeed(&settings, spd);
+ if (tcsetattr(port_fd, TCSANOW, &settings) < 0) return -1;
+#elif defined(MACOSX)
+ cfsetospeed(&settings, (speed_t)baud);
+ cfsetispeed(&settings, (speed_t)baud);
+ if (tcsetattr(port_fd, TCSANOW, &settings) < 0) return -1;
+#elif defined(WINDOWS)
+ DWORD len = sizeof(COMMCONFIG);
+ port_cfg.dcb.BaudRate = baud;
+ SetCommConfig(port_handle, &port_cfg, len);
+#endif
+ return 0;
+}
+
+int Serial::Set_baud(const string& baud_str)
+{
+ unsigned long b;
+ b = atoi(baud_str.c_str());
+ if (!b) return -1;
+ return Set_baud((int)b);
+}
+
+
+// Read from the serial port. Returns only the bytes that are
+// already received, up to count. This always returns without delay,
+// returning 0 if nothing has been received
+int Serial::Read(void *ptr, int count)
+{
+ if (!port_is_open) return -1;
+ if (count <= 0) return 0;
+#if defined(LINUX)
+ int n, bits;
+ n = read(port_fd, ptr, count);
+ if (n < 0 && (errno == EAGAIN || errno == EINTR)) return 0;
+ if (n == 0 && ioctl(port_fd, TIOCMGET, &bits) < 0) return -99;
+ return n;
+#elif defined(MACOSX)
+ int n;
+ n = read(port_fd, ptr, count);
+ if (n < 0 && (errno == EAGAIN || errno == EINTR)) return 0;
+ return n;
+#elif defined(WINDOWS)
+ // first, we'll find out how many bytes have been received
+ // and are currently waiting for us in the receive buffer.
+ // http://msdn.microsoft.com/en-us/library/ms885167.aspx
+ // http://msdn.microsoft.com/en-us/library/ms885173.aspx
+ // http://source.winehq.org/WineAPI/ClearCommError.html
+ COMSTAT st;
+ DWORD errmask=0, num_read, num_request;
+ OVERLAPPED ov;
+ int r;
+ if (!ClearCommError(port_handle, &errmask, &st)) return -1;
+ //printf("Read, %d requested, %lu buffered\n", count, st.cbInQue);
+ if (st.cbInQue <= 0) return 0;
+ // now do a ReadFile, now that we know how much we can read
+ // a blocking (non-overlapped) read would be simple, but win32
+ // is all-or-nothing on async I/O and we must have it enabled
+ // because it's the only way to get a timeout for WaitCommEvent
+ num_request = ((DWORD)count < st.cbInQue) ? (DWORD)count : st.cbInQue;
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (ov.hEvent == NULL) return -1;
+ ov.Internal = ov.InternalHigh = 0;
+ ov.Offset = ov.OffsetHigh = 0;
+ if (ReadFile(port_handle, ptr, num_request, &num_read, &ov)) {
+ // this should usually be the result, since we asked for
+ // data we knew was already buffered
+ //printf("Read, immediate complete, num_read=%lu\n", num_read);
+ r = num_read;
+ } else {
+ if (GetLastError() == ERROR_IO_PENDING) {
+ if (GetOverlappedResult(port_handle, &ov, &num_read, TRUE)) {
+ //printf("Read, delayed, num_read=%lu\n", num_read);
+ r = num_read;
+ } else {
+ //printf("Read, delayed error\n");
+ r = -1;
+ }
+ } else {
+ //printf("Read, error\n");
+ r = -1;
+ }
+ }
+ CloseHandle(ov.hEvent);
+ // TODO: how much overhead does creating new event objects on
+ // every I/O request really cost? Would it be better to create
+ // just 3 when we open the port, and reset/reuse them every time?
+ // Would mutexes or critical sections be needed to protect them?
+ return r;
+#endif
+}
+
+// Write to the serial port. This blocks until the data is
+// sent (or in a buffer to be sent). All bytes are written,
+// unless there is some sort of error.
+int Serial::Write(const void *ptr, int len)
+{
+ //printf("Write %d\n", len);
+ if (!port_is_open) return -1;
+#if defined(LINUX) || defined(MACOSX)
+ int n, written=0;
+ fd_set wfds;
+ struct timeval tv;
+ while (written < len) {
+ n = write(port_fd, (const char *)ptr + written, len - written);
+ if (n < 0 && (errno == EAGAIN || errno == EINTR)) n = 0;
+ //printf("Write, n = %d\n", n);
+ if (n < 0) return -1;
+ if (n > 0) {
+ written += n;
+ } else {
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ FD_ZERO(&wfds);
+ FD_SET(port_fd, &wfds);
+ n = select(port_fd+1, NULL, &wfds, NULL, &tv);
+ if (n < 0 && errno == EINTR) n = 1;
+ if (n <= 0) return -1;
+ }
+ }
+ return written;
+#elif defined(WINDOWS)
+ DWORD num_written;
+ OVERLAPPED ov;
+ int r;
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (ov.hEvent == NULL) return -1;
+ ov.Internal = ov.InternalHigh = 0;
+ ov.Offset = ov.OffsetHigh = 0;
+ if (WriteFile(port_handle, ptr, len, &num_written, &ov)) {
+ //printf("Write, immediate complete, num_written=%lu\n", num_written);
+ r = num_written;
+ } else {
+ if (GetLastError() == ERROR_IO_PENDING) {
+ if (GetOverlappedResult(port_handle, &ov, &num_written, TRUE)) {
+ //printf("Write, delayed, num_written=%lu\n", num_written);
+ r = num_written;
+ } else {
+ //printf("Write, delayed error\n");
+ r = -1;
+ }
+ } else {
+ //printf("Write, error\n");
+ r = -1;
+ }
+ };
+ CloseHandle(ov.hEvent);
+ return r;
+#endif
+}
+
+// Wait up to msec for data to become available for reading.
+// return 0 if timeout, or non-zero if one or more bytes are
+// received and can be read. -1 if an error occurs
+int Serial::Input_wait(int msec)
+{
+ if (!port_is_open) return -1;
+#if defined(LINUX) || defined(MACOSX)
+ fd_set rfds;
+ struct timeval tv;
+ tv.tv_sec = msec / 1000;
+ tv.tv_usec = (msec % 1000) * 1000;
+ FD_ZERO(&rfds);
+ FD_SET(port_fd, &rfds);
+ return select(port_fd+1, &rfds, NULL, NULL, &tv);
+#elif defined(WINDOWS)
+ // http://msdn2.microsoft.com/en-us/library/aa363479(VS.85).aspx
+ // http://msdn2.microsoft.com/en-us/library/aa363424(VS.85).aspx
+ // http://source.winehq.org/WineAPI/WaitCommEvent.html
+ COMSTAT st;
+ DWORD errmask=0, eventmask=EV_RXCHAR, ret;
+ OVERLAPPED ov;
+ int r;
+ // first, request comm event when characters arrive
+ if (!SetCommMask(port_handle, EV_RXCHAR)) return -1;
+ // look if there are characters in the buffer already
+ if (!ClearCommError(port_handle, &errmask, &st)) return -1;
+ //printf("Input_wait, %lu buffered, timeout = %d ms\n", st.cbInQue, msec);
+ if (st.cbInQue > 0) return 1;
+
+ ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (ov.hEvent == NULL) return -1;
+ ov.Internal = ov.InternalHigh = 0;
+ ov.Offset = ov.OffsetHigh = 0;
+ if (WaitCommEvent(port_handle, &eventmask, &ov)) {
+ //printf("Input_wait, WaitCommEvent, immediate success\n");
+ r = 1;
+ } else {
+ if (GetLastError() == ERROR_IO_PENDING) {
+ ret = WaitForSingleObject(ov.hEvent, msec);
+ if (ret == WAIT_OBJECT_0) {
+ //printf("Input_wait, WaitCommEvent, delayed success\n");
+ r = 1;
+ } else if (ret == WAIT_TIMEOUT) {
+ //printf("Input_wait, WaitCommEvent, timeout\n");
+ GetCommMask(port_handle, &eventmask);
+ r = 0;
+ } else { // WAIT_FAILED or WAIT_ABANDONED
+ //printf("Input_wait, WaitCommEvent, delayed error\n");
+ r = -1;
+ }
+ } else {
+ //printf("Input_wait, WaitCommEvent, immediate error\n");
+ r = -1;
+ }
+ }
+ SetCommMask(port_handle, 0);
+ CloseHandle(ov.hEvent);
+ return r;
+#endif
+}
+
+// Discard all received data that hasn't been read
+void Serial::Input_discard(void)
+{
+ if (!port_is_open) return;
+#if defined(LINUX) || defined(MACOSX)
+ // does this really work properly (and is it thread safe) on Linux??
+ tcflush(port_fd, TCIFLUSH);
+#elif defined(WINDOWS)
+ PurgeComm(port_handle, PURGE_RXCLEAR);
+#endif
+}
+
+// Wait for all transmitted data with Write to actually leave the serial port
+void Serial::Output_flush(void)
+{
+ if (!port_is_open) return;
+#if defined(LINUX) || defined(MACOSX)
+ tcdrain(port_fd);
+#elif defined(WINDOWS)
+ FlushFileBuffers(port_handle);
+#endif
+}
+
+
+// set DTR and RTS, 0 = low, 1=high, -1=unchanged
+int Serial::Set_control(int dtr, int rts)
+{
+ if (!port_is_open) return -1;
+#if defined(LINUX) || defined(MACOSX)
+ // on Mac OS X, "man 4 tty"
+ // on Linux, where is this actually documented?
+ int bits;
+ if (ioctl(port_fd, TIOCMGET, &bits) < 0) return -1;
+ if (dtr == 1) {
+ bits |= TIOCM_DTR;
+ } else if (dtr == 0) {
+ bits &= ~TIOCM_DTR;
+ }
+ if (rts == 1) {
+ bits |= TIOCM_RTS;
+ } else if (rts == 0) {
+ bits &= ~TIOCM_RTS;
+ }
+ if (ioctl(port_fd, TIOCMSET, &bits) < 0) return -1;;
+#elif defined(WINDOWS)
+ // http://msdn.microsoft.com/en-us/library/aa363254(VS.85).aspx
+ if (dtr == 1) {
+ if (!EscapeCommFunction(port_handle, SETDTR)) return -1;
+ } else if (dtr == 0) {
+ if (!EscapeCommFunction(port_handle, CLRDTR)) return -1;
+ }
+ if (rts == 1) {
+ if (!EscapeCommFunction(port_handle, SETRTS)) return -1;
+ } else if (rts == 0) {
+ if (!EscapeCommFunction(port_handle, CLRRTS)) return -1;
+ }
+#endif
+ return 0;
+}
+
+
+
+#if defined(LINUX)
+// All linux serial port device names. Hopefully all of them anyway. This
+// is a long list, but each entry takes only a few bytes and a quick strcmp()
+static const char *devnames[] = {
+"S", // "normal" Serial Ports - MANY drivers using this
+"USB", // USB to serial converters
+"ACM", // USB serial modem, CDC class, Abstract Control Model
+"MI", // MOXA Smartio/Industio family multiport serial... nice card, I have one :-)
+"MX", // MOXA Intellio family multiport serial
+"C", // Cyclades async multiport, no longer available, but I have an old ISA one! :-)
+"D", // Digiboard (still in 2.6 but no longer supported), new Moschip MCS9901
+"P", // Hayes ESP serial cards (obsolete)
+"M", // PAM Software's multimodem & Multitech ISI-Cards
+"E", // Stallion intelligent multiport (no longer made)
+"L", // RISCom/8 multiport serial
+"W", // specialix IO8+ multiport serial
+"X", // Specialix SX series cards, also SI & XIO series
+"SR", // Specialix RIO serial card 257+
+"n", // Digi International Neo (yes lowercase 'n', drivers/serial/jsm/jsm_driver.c)
+"FB", // serial port on the 21285 StrongArm-110 core logic chip
+"AM", // ARM AMBA-type serial ports (no DTR/RTS)
+"AMA", // ARM AMBA-type serial ports (no DTR/RTS)
+"AT", // Atmel AT91 / AT32 Serial ports
+"BF", // Blackfin 5xx serial ports (Analog Devices embedded DSP chips)
+"CL", // CLPS711x serial ports (ARM processor)
+"A", // ICOM Serial
+"SMX", // Motorola IMX serial ports
+"SOIC", // ioc3 serial
+"IOC", // ioc4 serial
+"PSC", // Freescale MPC52xx PSCs configured as UARTs
+"MM", // MPSC (UART mode) on Marvell GT64240, GT64260, MV64340...
+"B", // Mux console found in some PA-RISC servers
+"NX", // NetX serial port
+"PZ", // PowerMac Z85c30 based ESCC cell found in the "macio" ASIC
+"SAC", // Samsung S3C24XX onboard UARTs
+"SA", // SA11x0 serial ports
+"AM", // KS8695 serial ports & Sharp LH7A40X embedded serial ports
+"TX", // TX3927/TX4927/TX4925/TX4938 internal SIO controller
+"SC", // Hitachi SuperH on-chip serial module
+"SG", // C-Brick Serial Port (and console) SGI Altix machines
+"HV", // SUN4V hypervisor console
+"UL", // Xilinx uartlite serial controller
+"VR", // NEC VR4100 series Serial Interface Unit
+"CPM", // CPM (SCC/SMC) serial ports; core driver
+"Y", // Amiga A2232 board
+"SL", // Microgate SyncLink ISA and PCI high speed multiprotocol serial
+"SLG", // Microgate SyncLink GT (might be sync HDLC only?)
+"SLM", // Microgate SyncLink Multiport high speed multiprotocol serial
+"CH", // Chase Research AT/PCI-Fast serial card
+"F", // Computone IntelliPort serial card
+"H", // Chase serial card
+"I", // virtual modems
+"R", // Comtrol RocketPort
+"SI", // SmartIO serial card
+"T", // Technology Concepts serial card
+"V" // Comtrol VS-1000 serial controller
+};
+#define NUM_DEVNAMES (sizeof(devnames) / sizeof(const char *))
+#endif
+
+
+
+#if defined(MACOSX)
+static void macos_ports(io_iterator_t * PortIterator, vector<string>& list)
+{
+ io_object_t modemService;
+ CFTypeRef nameCFstring;
+ char s[MAXPATHLEN];
+
+ while ((modemService = IOIteratorNext(*PortIterator))) {
+ nameCFstring = IORegistryEntryCreateCFProperty(modemService,
+ CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
+ if (nameCFstring) {
+ if (CFStringGetCString((const __CFString *)nameCFstring,
+ s, sizeof(s), kCFStringEncodingASCII)) {
+ list.push_back(s);
+ }
+ CFRelease(nameCFstring);
+ }
+ IOObjectRelease(modemService);
+ }
+}
+#endif
+
+
+// Return a list of all serial ports
+vector<string> Serial::port_list()
+{
+ vector<string> list;
+#if defined(LINUX)
+ // This is ugly guessing, but Linux doesn't seem to provide anything else.
+ // If there really is an API to discover serial devices on Linux, please
+ // email paul@pjrc.com with the info. Please?
+ // The really BAD aspect is all ports get DTR raised briefly, because linux
+ // has no way to open the port without raising DTR, and there isn't any way
+ // to tell if the device file really represents hardware without opening it.
+ // maybe sysfs or udev provides a useful API??
+ DIR *dir;
+ struct dirent *f;
+ struct stat st;
+ unsigned int i, len[NUM_DEVNAMES];
+ char s[512];
+ int fd, bits;
+ termios mytios;
+
+ dir = opendir("/dev/");
+ if (dir == NULL) return list;
+ for (i=0; i<NUM_DEVNAMES; i++) len[i] = strlen(devnames[i]);
+ // Read all the filenames from the /dev directory...
+ while ((f = readdir(dir)) != NULL) {
+ // ignore everything that doesn't begin with "tty"
+ if (strncmp(f->d_name, "tty", 3)) continue;
+ // ignore anything that's not a known serial device name
+ for (i=0; i<NUM_DEVNAMES; i++) {
+ if (!strncmp(f->d_name + 3, devnames[i], len[i])) break;
+ }
+ if (i >= NUM_DEVNAMES) continue;
+ snprintf(s, sizeof(s), "/dev/%s", f->d_name);
+ // check if it's a character type device (almost certainly is)
+ if (stat(s, &st) != 0 || !(st.st_mode & S_IFCHR)) continue;
+ // now see if we can open the file - if the device file is
+ // populating /dev but doesn't actually represent a loaded
+ // driver, this is where we will detect it.
+ fd = open(s, O_RDONLY | O_NOCTTY | O_NONBLOCK);
+ if (fd < 0) {
+ // if permission denied, give benefit of the doubt
+ // (otherwise the port will be invisible to the user
+ // and we won't have a to alert them to the permssion
+ // problem)
+ if (errno == EACCES) list.push_back(s);
+ // any other error, assume it's not a real device
+ continue;
+ }
+ // does it respond to termios requests? (probably will since
+ // the name began with tty). Some devices where a single
+ // driver exports multiple names will open but this is where
+ // we can really tell if they work with real hardare.
+ if (tcgetattr(fd, &mytios) != 0) {
+ close(fd);
+ continue;
+ }
+ // does it respond to reading the control signals? If it's
+ // some sort of non-serial terminal (eg, pseudo terminals)
+ // this is where we will detect it's not really a serial port
+ if (ioctl(fd, TIOCMGET, &bits) < 0) {
+ close(fd);
+ continue;
+ }
+ // it passed all the tests, it's a serial port, or some sort
+ // of "terminal" that looks exactly like a real serial port!
+ close(fd);
+ // unfortunately, Linux always raises DTR when open is called.
+ // not nice! Every serial port is going to get DTR raised
+ // and then lowered. I wish there were a way to prevent this,
+ // but it seems impossible.
+ list.push_back(s);
+ }
+ closedir(dir);
+#elif defined(MACOSX)
+ // adapted from SerialPortSample.c, by Apple
+ // http://developer.apple.com/samplecode/SerialPortSample/listing2.html
+ // and also testserial.c, by Keyspan
+ // http://www.keyspan.com/downloads-files/developer/macosx/KesypanTestSerial.c
+ // www.rxtx.org, src/SerialImp.c seems to be based on Keyspan's testserial.c
+ // neither keyspan nor rxtx properly release memory allocated.
+ // more documentation at:
+ // http://developer.apple.com/documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/chapter_2_section_6.html
+ mach_port_t masterPort;
+ CFMutableDictionaryRef classesToMatch;
+ io_iterator_t serialPortIterator;
+ if (IOMasterPort(NULL, &masterPort) != KERN_SUCCESS) return list;
+ // a usb-serial adaptor is usually considered a "modem",
+ // especially when it implements the CDC class spec
+ classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
+ if (!classesToMatch) return list;
+ CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey),
+ CFSTR(kIOSerialBSDModemType));
+ if (IOServiceGetMatchingServices(masterPort, classesToMatch,
+ &serialPortIterator) != KERN_SUCCESS) return list;
+ macos_ports(&serialPortIterator, list);
+ IOObjectRelease(serialPortIterator);
+ // but it might be considered a "rs232 port", so repeat this
+ // search for rs232 ports
+ classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
+ if (!classesToMatch) return list;
+ CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey),
+ CFSTR(kIOSerialBSDRS232Type));
+ if (IOServiceGetMatchingServices(masterPort, classesToMatch,
+ &serialPortIterator) != KERN_SUCCESS) return list;
+ macos_ports(&serialPortIterator, list);
+ IOObjectRelease(serialPortIterator);
+#elif defined(WINDOWS)
+ // http://msdn.microsoft.com/en-us/library/aa365461(VS.85).aspx
+ // page with 7 ways - not all of them work!
+ // http://www.naughter.com/enumser.html
+ // may be possible to just query the windows registary
+ // http://it.gps678.com/2/ca9c8631868fdd65.html
+ // search in HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM
+ // Vista has some special new way, vista-only
+ // http://msdn2.microsoft.com/en-us/library/aa814070(VS.85).aspx
+ char *buffer, *p;
+ //DWORD size = QUERYDOSDEVICE_BUFFER_SIZE;
+ DWORD ret;
+
+ buffer = (char *)malloc(QUERYDOSDEVICE_BUFFER_SIZE);
+ if (buffer == NULL) return list;
+ memset(buffer, 0, QUERYDOSDEVICE_BUFFER_SIZE);
+ ret = QueryDosDeviceA(NULL, buffer, QUERYDOSDEVICE_BUFFER_SIZE);
+ if (ret) {
+ //printf("Detect Serial using QueryDosDeviceA: ");
+ for (p = buffer; *p; p += strlen(p) + 1) {
+ if (strncmp(p, "COM", 3)) continue;
+ list.push_back(string(p) );
+ //printf(": %s\n", p);
+ }
+ } else {
+ char buf[1024];
+ win32_err(buf);
+ printf("QueryDosDeviceA failed, error \"%s\"\n", buf);
+ printf("Detect Serial using brute force GetDefaultCommConfig probing: ");
+ for (int i=1; i<=32; i++) {
+ printf("try %s", buf);
+ COMMCONFIG cfg;
+ DWORD len;
+ snprintf(buf, sizeof(buf), "COM%d", i);
+ if (GetDefaultCommConfig(buf, &cfg, &len)) {
+ std::ostringstream name;
+ name << "COM" << i << ":";
+ list.push_back(name.str());
+ printf(": %s", buf);
+ }
+ }
+ }
+ free(buffer);
+#endif
+ std::sort (list.begin(), list.end());
+ return list;
+}
+
+
+
+
+
+
+
+