# HG changeset patch
# User John Donoghue
# Date 1416412545 18000
#      Wed Nov 19 10:55:45 2014 -0500
# Node ID bc232eee20e08695b7533cd17322ee34cc489d0b
# Parent  26ed548f404ff65d497dfa8fe95cb4494c6a5305
add win32 serial functionality

* src/configure.ac: check for windows.h, __WIN32__ to determine windows.

* src/serial/serial.cc(serial): use default comport COM1:, use oflags of 0.

* src/serial/serial_class.cc:
  Add windows.h if in windows, call windows functions for serial in Windows.

* src/serial/serial_class.h: include windows.h, define INVALID_FD, use windows types for fd, config.

diff -r 26ed548f404f -r bc232eee20e0 src/configure.ac
--- a/src/configure.ac	Wed Nov 19 07:51:30 2014 -0500
+++ b/src/configure.ac	Wed Nov 19 10:55:45 2014 -0500
@@ -93,6 +93,24 @@
   AC_SUBST(VISALIBS, [$ac_cv_search_viOpenDefaultRM])
 fi
 
+#
+AC_MSG_CHECKING([for windows])
+have_windows=no
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+   #include <windows.h>
+   #ifndef __WIN32__
+     #error "Not windows!"
+   #endif
+  ]], [])],
+ [AC_MSG_RESULT([yes])
+  have_windows=yes],
+ [AC_MSG_RESULT([no])])
+
+# if windows, we can build some of the packages just by having
+# windows.h
+if test $have_windows = yes; then
+  build_serial=yes
+fi
 
 #
 
diff -r 26ed548f404f -r bc232eee20e0 src/serial/serial.cc
--- a/src/serial/serial.cc	Wed Nov 19 07:51:30 2014 -0500
+++ b/src/serial/serial.cc	Wed Nov 19 10:55:45 2014 -0500
@@ -62,14 +62,22 @@
     }
 
     // Default values
+#ifndef __WIN32__
     string path("/dev/ttyUSB0");
+#else
+    string path("COM1:");
+#endif
     unsigned int baud_rate = 115200;
     short timeout = -1;
 
     unsigned short bytesize = 8;
     string parity("N");
     unsigned short stopbits = 1;
+#ifndef __WIN32__
     int oflags = O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY; 
+#else
+    int oflags = 0;
+#endif
     // O_SYNC - All writes immediately effective, no buffering
     // O_NOCTTY - Do not make serial terminal the controlling terminal for the process
     // O_NDELAY - Do not care what state the DCD signal line is in. Used for open only, later disabled.
diff -r 26ed548f404f -r bc232eee20e0 src/serial/serial_class.cc
--- a/src/serial/serial_class.cc	Wed Nov 19 07:51:30 2014 -0500
+++ b/src/serial/serial_class.cc	Wed Nov 19 10:55:45 2014 -0500
@@ -31,6 +31,8 @@
 #include <errno.h>
 #include <termios.h>
 #include <unistd.h>
+#else
+ #include <windows.h>
 #endif
 
 using std::string;
@@ -39,31 +41,56 @@
 
 volatile bool read_interrupt = false;
 
+#ifdef __WIN32__
+char * winerror(int err)
+{
+  static char errstring[100];
+
+  if(err != 0) return strerror(err);
+  else
+  {
+    DWORD e;
+
+    e = GetLastError();
+
+    if(FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, 0, e, 
+      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errstring,
+      sizeof(errstring)-1, 0) < 0)
+      errstring[0] = '\0';
+  }
+  return errstring;
+}
+#define STRERROR winerror
+#else
+#define STRERROR strerror
+#endif
+
 DEFINE_OCTAVE_ALLOCATOR (octave_serial);
 DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_serial, "octave_serial", "octave_serial");
 
 octave_serial::octave_serial()
 {
-    this->fd = -1;
+    this->fd = INVALID_FD;
 }
 
 int octave_serial::open(string path, int flags)
 {
+ #ifndef __WIN32__
     this->fd = ::open(path.c_str(), flags);
 
-    if (this->get_fd() > 0)
+    if (this->fd > INVALID_FD)
     {   
         // Check whether fd is an open file descriptor referring to a terminal 
         if(!isatty(fd)) 
         { 
-            error("serial: Interface does not refer to a terminal: %s\n", strerror(errno));
+            error("serial: Interface does not refer to a terminal: %s\n", STRERROR(errno));
             this->close();
             return -1;
         }
 
         if (tcgetattr(this->fd, &this->config) < 0)
         {
-            error("serial: Failed to get terminal attributes: %s\n", strerror(errno));
+            error("serial: Failed to get terminal attributes: %s\n", STRERROR(errno));
             this->close();
             return -1;
         }
@@ -75,17 +102,17 @@
         this->config.c_lflag = 0; // Local modes
         this->config.c_cc[VMIN] = 1;
 
-        if (tcsetattr(this->get_fd(), TCSANOW, &this->config) < 0)
+        if (tcsetattr(this->fd, TCSANOW, &this->config) < 0)
         {
-            error("serial: Failed to set default terminal attributes: %s\n", strerror(errno));
+            error("serial: Failed to set default terminal attributes: %s\n", STRERROR(errno));
             this->close();
             return -1; 
         }
 
         // Disable NDELAY
-        if (fcntl(this->get_fd(), F_SETFL, 0) < 0)
+        if (fcntl(this->fd, F_SETFL, 0) < 0)
         {
-            error("serial: Failed to disable NDELAY flag: %s\n", strerror(errno));
+            error("serial: Failed to disable NDELAY flag: %s\n", STRERROR(errno));
             this->close();
             return -1; 
         }
@@ -94,9 +121,47 @@
     } 
     else
     {
-        error("serial: Error opening the interface: %s\n", strerror(errno));
+        error("serial: Error opening the interface: %s\n", STRERROR(errno));
         return -1;
     }
+  #else
+    // open device
+    this->fd = CreateFile(path.c_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+
+    if (this->fd == INVALID_FD)
+    {
+        error("serial: Error opening the interface: %s\n", STRERROR(errno));
+        return -1;
+    }
+
+    // clear any errors that may be on the port
+    COMSTAT stats;
+    DWORD err;
+    ClearCommError(this->fd, &err, &stats);
+
+    // set up device settings
+    this->config.DCBlength = sizeof(this->config);
+    if(GetCommState(this->fd,&this->config) == 0)
+    {
+        error("serial: Failed to get terminal attributes: %s\n", STRERROR(errno));
+        this->close();
+        return -1;
+    }
+   
+    this->timeouts.ReadIntervalTimeout = MAXDWORD;
+    this->timeouts.ReadTotalTimeoutMultiplier = 0;
+    this->timeouts.ReadTotalTimeoutConstant = 0;
+    this->timeouts.WriteTotalTimeoutMultiplier = 0;
+    this->timeouts.WriteTotalTimeoutConstant = 0;
+
+    if(SetCommTimeouts(this->fd, &this->timeouts) == FALSE)
+    {
+        error("serial: Failed to disable timeouts: %s\n", STRERROR(errno));
+        this->close();
+        return -1; 
+    }
+
+  #endif
 
     return this->get_fd();
 }
@@ -119,7 +184,7 @@
 
 int octave_serial::read(uint8_t *buf, unsigned int len)
 {
-    if (this->get_fd() < 0)
+    if (this->fd == INVALID_FD)
     {
         error("srl_read: Interface must be opened first...");
         return 0;
@@ -131,12 +196,21 @@
     // While not interrupted in blocking mode
     while (!read_interrupt) 
     {
-        read_retval = ::read(this->get_fd(), (void *)(buf + bytes_read), len - bytes_read);
+#ifndef __WIN32__
+        read_retval = ::read(this->fd, (void *)(buf + bytes_read), len - bytes_read);
+#else
+        DWORD readsz;
+        read_retval = -1;
+        if(ReadFile(this->fd, buf, len, &readsz, NULL) == TRUE)
+        {
+           read_retval = readsz;
+        }
+#endif
         //printf("read_retval: %d\n\r", read_retval);
 
         if (read_retval < 0)
         {
-            error("srl_read: Error while reading: %s\n", strerror(errno));
+            error("srl_read: Error while reading: %s\n", STRERROR(errno));
             break;
         }
 
@@ -156,29 +230,49 @@
 
 int octave_serial::write(string str)
 {
-    if (this->get_fd() < 0)
+    int wrote_ret = -1;
+    if (this->fd == INVALID_FD)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+#ifndef __WIN32__
+    wrote_ret = ::write(this->fd, str.c_str(), str.length());
+#else
+    DWORD wrote;
+    if(WriteFile(this->fd, str.c_str(), str.length(), &wrote, NULL) == TRUE)
+    {
+      wrote_ret = wrote;
+    }
+#endif
+
+   return wrote_ret;
+}
+
+int octave_serial::write(uint8_t *buf, unsigned int len)
+{
+    int wrote_ret = -1;
+    if (this->fd == INVALID_FD)
     {
         error("serial: Interface must be opened first...");
         return -1;
     }
 
-    return ::write(get_fd(), str.c_str(), str.length());
-}
-
-int octave_serial::write(uint8_t *buf, unsigned int len)
-{
-    if (this->get_fd() < 0)
+#ifndef __WIN32__
+    wrote_ret = ::write(this->fd, buf, len);
+#else
+    DWORD wrote;
+    if(WriteFile(this->fd, buf, len, &wrote, NULL) == TRUE)
     {
-        error("serial: Interface must be opened first...");
-        return -1;
+      wrote_ret = wrote;
     }
-
-    return ::write(get_fd(), buf, len);
+#endif
+    return wrote_ret;
 }
 
 int octave_serial::set_timeout(short timeout)
 {
-    if (this->get_fd() < 0)
+    if (this->fd == INVALID_FD)
     {
         error("serial: Interface must be opened first...");
         return -1;
@@ -201,30 +295,51 @@
     {
         this->blocking_read = false;
     }
-
+#ifndef __WIN32__
     BITMASK_CLEAR(this->config.c_lflag, ICANON); // Set non-canonical mode
     this->config.c_cc[VMIN] = 0;
     this->config.c_cc[VTIME] = (unsigned) timeout; // Set timeout of 'timeout * 10' seconds
 
-    if (tcsetattr(this->get_fd(), TCSANOW, &this->config) < 0) {
+    if (tcsetattr(this->fd, TCSANOW, &this->config) < 0) {
         error("srl_timeout: error setting timeout...");
         return -1;
     }
+#else
+
+    this->timeouts.ReadIntervalTimeout = MAXDWORD;
+    this->timeouts.ReadTotalTimeoutMultiplier = 0;
+    this->timeouts.ReadTotalTimeoutConstant = timeout*100;
+    this->timeouts.WriteTotalTimeoutMultiplier = 0;
+    this->timeouts.WriteTotalTimeoutConstant = 0;
+
+    if(SetCommTimeouts(this->fd, &this->timeouts) == FALSE)
+    {
+        error("srl_timeout: error setting timeout...");
+        return -1; 
+    }
+#endif
 
     return 1;
 }
 
 int octave_serial::get_timeout()
 {
+    int timeout = -1;
+
     if (blocking_read)
         return -1;
-    else
-        return this->config.c_cc[VTIME];
+#ifndef __WIN32__    
+    timeout = this->config.c_cc[VTIME];
+#else
+    timeout = this->timeouts.ReadTotalTimeoutConstant/100;
+#endif
+
+    return timeout;
 }
 
 int octave_serial::set_stopbits(unsigned short stopbits)
 {
-    if (this->get_fd() < 0)
+    if (this->fd == INVALID_FD)
     {
         error("serial: Interface must be opened first...");
         return -1;
@@ -237,49 +352,71 @@
     if (stopbits == 1)
     {
         // Set to one stop bit
+#ifndef __WIN32__
         BITMASK_CLEAR(this->config.c_cflag, CSTOPB);
+#else
+        this->config.StopBits = ONESTOPBIT;
+#endif
     }
     else if (stopbits == 2)
     {
         // Set to two stop bits
+#ifndef __WIN32__
         BITMASK_SET(this->config.c_cflag, CSTOPB);
+#else
+        this->config.StopBits = TWOSTOPBITS;
+#endif
     }
     else
     {
         error("srl_stopbits: Only 1 or 2 stop bits are supported...");
         return false;
     }
-
-    if (tcsetattr(this->get_fd(), TCSANOW, &this->config) < 0) {
-        error("srl_stopbits: error setting stop bits: %s\n", strerror(errno));
+#ifndef __WIN32__
+    if (tcsetattr(this->fd, TCSANOW, &this->config) < 0) {
+        error("srl_stopbits: error setting stop bits: %s\n", STRERROR(errno));
         return false;
     }
+#else
+    if(SetCommState(this->fd,&this->config) == FALSE)
+    {
+        error("srl_stopbits: error setting stop bits: %s\n", STRERROR(errno));
+        return false;
+    }
+#endif
 
     return true;
 }
 
 int octave_serial::get_stopbits()
 {
-    if (this->get_fd() < 0)
+    if (this->fd == INVALID_FD)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+#ifndef __WIN32__
+    if (BITMASK_CHECK(this->config.c_cflag, CSTOPB))
+        return 2;
+    else
+        return 1;
+#else
+    if(this->config.StopBits == TWOSTOPBITS)
+        return 2;
+    else
+        return 1;
+#endif
+}
+
+int octave_serial::set_bytesize(unsigned short bytesize)
+{
+    if (this->fd == INVALID_FD)
     {
         error("serial: Interface must be opened first...");
         return -1;
     }
 
-    if (BITMASK_CHECK(this->config.c_cflag, CSTOPB))
-        return 2;
-    else
-        return 1;
-}
-
-int octave_serial::set_bytesize(unsigned short bytesize)
-{
-    if (this->get_fd() < 0)
-    {
-        error("serial: Interface must be opened first...");
-        return -1;
-    }
-
+#ifndef __WIN32__
     tcflag_t c_bytesize = 0;
 
     switch (bytesize) 
@@ -300,17 +437,32 @@
     // Apply new
     BITMASK_SET(this->config.c_cflag, c_bytesize);
 
-    if (tcsetattr(this->get_fd(), TCSANOW, &this->config) < 0) {
-        error("srl_bytesize: error setting byte size: %s\n", strerror(errno));
+    if (tcsetattr(this->fd, TCSANOW, &this->config) < 0) {
+        error("srl_bytesize: error setting byte size: %s\n", STRERROR(errno));
         return false;
     }
+#else
+    if(bytesize < 5 || bytesize > 8)
+    {
+        error("srl_bytesize: expecting value between [5..8]...");
+        return false;
+    }
+    this->config.ByteSize = bytesize;
+
+    if(SetCommState(this->fd,&this->config) == FALSE)
+    {
+        error("srl_stopbits: error setting byte size: %s\n", STRERROR(errno));
+        return false;
+    }
+
+#endif
 
     return true;
 }
 
 int octave_serial::get_bytesize()
 {
-    if (this->get_fd() < 0)
+    if (this->fd == INVALID_FD)
     {
         error("serial: Interface must be opened first...");
         return -1;
@@ -318,6 +470,7 @@
 
     int retval = -1;
 
+#ifndef __WIN32__
     if (BITMASK_CHECK(this->config.c_cflag, CS5))
         retval = 5;
     else if (BITMASK_CHECK(this->config.c_cflag, CS6))
@@ -326,18 +479,22 @@
         retval = 7;
     else if (BITMASK_CHECK(this->config.c_cflag, CS8))
         retval = 8;
+#else
+    retval = this->config.ByteSize;
+#endif
 
     return retval;
 }
 
 int octave_serial::set_baudrate(unsigned int baud) 
 {
-    if (this->get_fd() < 0)
+    if (this->fd == INVALID_FD)
     {
         error("serial: Interface must be opened first...");
         return -1;
     }
 
+#ifndef __WIN32__
     speed_t baud_rate = 0;
 
     switch (baud) 
@@ -390,17 +547,73 @@
     cfsetispeed(&this->config, baud_rate);
     cfsetospeed(&this->config, baud_rate);
 
-    if (tcsetattr(this->get_fd(), TCSANOW, &this->config) < 0) {
-        error("srl_baudrate: error setting baud rate: %s\n", strerror(errno));
+    if (tcsetattr(this->fd, TCSANOW, &this->config) < 0) {
+        error("srl_baudrate: error setting baud rate: %s\n", STRERROR(errno));
         return false;
     }
+#else
+    DWORD baud_rate;
+    switch (baud) 
+    {
+    case 0: 
+        baud_rate = baud; break;
+    case 50:
+        baud_rate = baud; break;
+    case 75:
+        baud_rate = baud; break;
+    case 110:
+        baud_rate = baud; break;
+    case 134:
+        baud_rate = baud; break;
+    case 150:
+        baud_rate = baud; break;
+    case 200:
+        baud_rate = baud; break;
+    case 300:
+        baud_rate = baud; break;
+    case 600:
+        baud_rate = baud; break;
+    case 1200:
+        baud_rate = baud; break;
+    case 1800:
+        baud_rate = baud; break;
+    case 2400:
+        baud_rate = baud; break;
+    case 4800:
+        baud_rate = baud; break;
+    case 9600:
+        baud_rate = baud; break;
+    case 19200:
+        baud_rate = baud; break;
+    case 38400:
+        baud_rate = baud; break;
+    case 57600:
+        baud_rate = baud; break;
+    case 115200:
+        baud_rate = baud; break;
+    case 230400:
+        baud_rate = baud; break;
+    default:
+        error("srl_baudrate: currently only 0, 50, 75, 110, \
+                134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, \
+                9600 19200, 38400, 57600, 115200 and 230400 baud rates are supported...");
+        return false;
+    }
+
+    if(SetCommState(this->fd,&this->config) == FALSE)
+    {
+        error("srl_stopbits: error setting baud rate: %s\n", STRERROR(errno));
+        return false;
+    }
+
+#endif
 
     return true;
 }
 
 int octave_serial::get_baudrate()
 {
-    if (this->get_fd() < 0)
+    if (this->fd == INVALID_FD)
     {
         error("serial: Interface must be opened first...");
         return -1;
@@ -408,6 +621,7 @@
 
     int retval = -1;
 
+#ifndef __WIN32__
     speed_t baudrate = cfgetispeed(&this->config);
 
     if (baudrate == B0)
@@ -448,6 +662,9 @@
         retval = 115200;
     else if (baudrate == B230400)
         retval = 230400;
+#else
+    retval = this->config.BaudRate;
+#endif
 
     return retval;
 
@@ -455,7 +672,7 @@
 
 int octave_serial::flush(unsigned short queue_selector)
 {
-    if (this->get_fd() < 0)
+    if (this->fd == INVALID_FD)
     {
         error("serial: Interface must be opened first...");
         return -1;
@@ -471,21 +688,33 @@
 
     switch (queue_selector)
     {
+#ifndef __WIN32__
     case 0: flag = TCOFLUSH; break;
     case 1: flag = TCIFLUSH; break;
     case 2: flag = TCIOFLUSH; break;
+#else
+    case 0: flag = PURGE_TXCLEAR; break;
+    case 1: flag = PURGE_RXCLEAR; break;
+    case 2: flag = PURGE_RXCLEAR|PURGE_TXCLEAR; break;
+#endif
     default:
         error("srl_flush: only [0..2] values are accepted...");
         return false;
     }
-
-    return ::tcflush(this->get_fd(), flag);
+#ifndef __WIN32__
+    return ::tcflush(this->fd, flag);
+#else
+   if(PurgeComm(this->fd,flag) == FALSE)
+     return -1;
+   else
+     return 0; 
+#endif
 }
 
 
 int octave_serial::set_parity(string parity)
 {
-    if (this->get_fd() < 0)
+    if (this->fd == INVALID_FD)
     {
         error("serial: Interface must be opened first...");
         return -1;
@@ -501,26 +730,36 @@
 
     if (parity == "n" || parity == "none")
     {
+#ifndef __WIN32__
         // Disable parity generation/checking
         BITMASK_CLEAR(this->config.c_cflag, PARENB);
+#else
+        this->config.Parity = NOPARITY;
+#endif
     }
     else if (parity == "e" || parity == "even")
     {
+#ifndef __WIN32__
         // Enable parity generation/checking
         BITMASK_SET(this->config.c_cflag, PARENB);
 
         // Set to Even
         BITMASK_CLEAR(this->config.c_cflag, PARODD);
-
+#else
+        this->config.Parity = EVENPARITY;
+#endif
     }
     else if (parity == "o" || parity == "odd")
     {
+#ifndef __WIN32__
         // Enable parity generation/checking
         BITMASK_SET(this->config.c_cflag, PARENB);
 
         // Set to Odd
         BITMASK_SET(this->config.c_cflag, PARODD);
-
+#else
+        this->config.Parity = ODDPARITY;
+#endif
     }
     else
     {
@@ -528,37 +767,59 @@
         return false;
     }
 
-    if (tcsetattr(this->get_fd(), TCSANOW, &this->config) < 0) {
-        error("srl_parity: error setting parity: %s\n", strerror(errno));
+#ifndef __WIN32__
+    if (tcsetattr(this->fd, TCSANOW, &this->config) < 0) {
+        error("srl_parity: error setting parity: %s\n", STRERROR(errno));
         return false;
     }
+#else
+    if(SetCommState(this->fd,&this->config) == FALSE)
+    {
+        error("srl_parity: error setting parity: %s\n", STRERROR(errno));
+        return false;
+    }
+#endif
 
     return true;
 }
 
 string octave_serial::get_parity()
 {
+#ifndef __WIN32__
     if (!BITMASK_CHECK(this->config.c_cflag, PARENB))
         return "None";
     else if (BITMASK_CHECK(this->config.c_cflag, PARODD))
         return "Odd";
     else
         return "Even";
+#else
+    if(this->config.Parity == NOPARITY) 
+        return "None";
+    else if(this->config.Parity == ODDPARITY) 
+        return "Odd";
+    else
+        return "Even";
+#endif
 }
 
 int octave_serial::get_fd()
 {
-    return this->fd;
+    return (int)this->fd;
 }
 
 int octave_serial::close()
 {
     int retval = -1;
 
-    if (this->get_fd() > 0)
+    if (this->fd != INVALID_FD)
     {
-        retval = ::close(this->get_fd());
-        this->fd = -1;
+#ifndef __WIN32__
+        retval = ::close(this->fd);
+#else
+        if(CloseHandle(this->fd))
+          retval = 0;
+#endif
+        this->fd = INVALID_FD;
     }
 
     return retval;
diff -r 26ed548f404f -r bc232eee20e0 src/serial/serial_class.h
--- a/src/serial/serial_class.h	Wed Nov 19 07:51:30 2014 -0500
+++ b/src/serial/serial_class.h	Wed Nov 19 10:55:45 2014 -0500
@@ -20,7 +20,11 @@
 #include <octave/ov-int32.h>
 
 #include <string>
+#ifndef __WIN32__
 #include <termios.h>
+#else
+#include <windows.h>
+#endif
 
 using std::string;
 
@@ -29,6 +33,12 @@
 #define BITMASK_TOGGLE(x,y) ((x) ^= (y))
 #define BITMASK_CHECK(x,y) ((x) & (y))
 
+#ifndef __WIN32__
+  #define INVALID_FD -1
+#else
+  #define INVALID_FD INVALID_HANDLE_VALUE
+#endif
+
 class octave_serial : public octave_base_value 
 {
 public:
@@ -63,11 +73,11 @@
 
 
     // Overloaded base functions
-    double serial_value() const { return (double)this->fd; }
+    double serial_value() const { return (double)((unsigned long)this->fd); }
 
     virtual double scalar_value (bool frc_str_conv = false) const 
     {
-        return (double)this->fd;
+        return (double)((unsigned long)this->fd);
     }
 
     void print (std::ostream& os, bool pr_as_read_syntax = false) const;
@@ -80,8 +90,14 @@
 
 
 private:
+  #ifndef __WIN32__
     int fd;
     struct termios config;
+  #else
+    HANDLE fd;
+    DCB config;
+    COMMTIMEOUTS timeouts;
+  #endif
 
     volatile bool blocking_read;
 
