From ca310c91993737b853ac977dd81b4b9d6c948583 Mon Sep 17 00:00:00 2001
From: John D <john.donoghue@ieee.org>
Date: Fri, 21 Nov 2014 07:47:41 -0500
Subject: [PATCH] Add win32 serial implementation

* src/configure.ac: add check for windows, and auto enable serial build

* src/serial/Makefile.in: use BUILD_FOR_WINDOWS flag to set OBJ sources of
  serial_class_win32.o or serial_class_lin.o

* src/serial/serial.cc: remove OS idependant file open flags

* src/serial/serial_class.h: include either serial_class_XXX.h depending
  on build OS.

* src/serial_class.cc: rename to serial_class_lin.cc

* src/serial_class.h: rename to serial_class_lin.h

* src/serial/serial_class_win32.cc: new file

* src/serial/serial_class_win32.h: new file
---
 src/configure.ac                 |  23 +-
 src/serial/Makefile.in           |   7 +-
 src/serial/serial.cc             |   7 +-
 src/serial/serial_class.cc       | 626 --------------------------------------
 src/serial/serial_class.h        |  83 +----
 src/serial/serial_class_lin.cc   | 631 +++++++++++++++++++++++++++++++++++++++
 src/serial/serial_class_lin.h    |  98 ++++++
 src/serial/serial_class_win32.cc | 560 ++++++++++++++++++++++++++++++++++
 src/serial/serial_class_win32.h  |  95 ++++++
 9 files changed, 1418 insertions(+), 712 deletions(-)
 delete mode 100644 src/serial/serial_class.cc
 create mode 100644 src/serial/serial_class_lin.cc
 create mode 100644 src/serial/serial_class_lin.h
 create mode 100644 src/serial/serial_class_win32.cc
 create mode 100644 src/serial/serial_class_win32.h

diff --git a/src/configure.ac b/src/configure.ac
index 661a221..9bd99ea 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -27,6 +27,26 @@ build_vxi11=no
 build_visa=no
 build_modbus=no
 
+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
+
+  AC_SUBST([BUILD_FOR_WINDOWS], [1], [build using native windows functions])
+fi
+
 # check for i2c
 # if linux/i2c-dev.h exists we are probably under linux, so build usbtmc as well
 AC_CHECK_HEADERS([linux/i2c-dev.h],[build_i2c=yes build_usbtmc=yes])
@@ -154,6 +174,8 @@ AC_MSG_NOTICE([
 
 $PACKAGE_NAME is now configured with
 
+   BUILD FOR WINDOWS  $have_windows
+
    BUILD SERIAL:      $build_serial
    BUILD PARALLEL:    $build_parallel
    BUILD I2C:         $build_i2c
@@ -168,5 +190,4 @@ $PACKAGE_NAME is now configured with
    VXI11 INCLUDE:     $RPCINCLUDE
    VISA LIBS:         $VISALIBS
    MODBUS LIBS:       $MODBUSLIBS
-
 ])
diff --git a/src/serial/Makefile.in b/src/serial/Makefile.in
index 75f18a0..091e4d9 100644
--- a/src/serial/Makefile.in
+++ b/src/serial/Makefile.in
@@ -1,5 +1,10 @@
 OCT := serial.oct srl_write.oct srl_read.oct __srl_properties__.oct
-OBJ := serial_class.o
+
+ifeq ("@BUILD_FOR_WINDOWS@","1")
+  OBJ := serial_class_win32.o
+else
+  OBJ := serial_class_lin.o
+endif
 
 MKOCTFILE ?= mkoctfile
 
diff --git a/src/serial/serial.cc b/src/serial/serial.cc
index 38d5f97..912178a 100644
--- a/src/serial/serial.cc
+++ b/src/serial/serial.cc
@@ -69,11 +69,6 @@ The serial() shall return instance of @var{octave_serial} class as the result @v
     unsigned short bytesize = 8;
     string parity("N");
     unsigned short stopbits = 1;
-    int oflags = O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY; 
-    // 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.
-
     // Parse the function arguments
     if (args.length() > 0)
     {
@@ -121,7 +116,7 @@ The serial() shall return instance of @var{octave_serial} class as the result @v
     octave_serial* retval = new octave_serial();
 
     // Open the interface
-    if (retval->open(path, oflags) < 0)
+    if (retval->open(path) < 0)
         return octave_value();
 
     retval->set_baudrate(baud_rate);
diff --git a/src/serial/serial_class.cc b/src/serial/serial_class.cc
deleted file mode 100644
index 279cc37..0000000
--- a/src/serial/serial_class.cc
+++ /dev/null
@@ -1,626 +0,0 @@
-// Copyright (C) 2012   Andrius Sutas   <andrius.sutas@gmail.com>
-//
-// 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 <octave/oct.h>
-
-#ifdef HAVE_CONFIG_H
-#include "../config.h"
-#endif
-
-#ifdef BUILD_SERIAL
-#include <iostream>
-#include <string>
-#include <algorithm>
-
-#ifndef __WIN32__
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <errno.h>
-#ifdef __linux__
-#include <termio.h>
-#else
-#include <termios.h>
-#endif
-#include <unistd.h>
-#endif
-
-using std::string;
-
-#include "serial_class.h"
-
-volatile bool read_interrupt = false;
-
-DEFINE_OCTAVE_ALLOCATOR (octave_serial);
-DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_serial, "octave_serial", "octave_serial");
-
-octave_serial::octave_serial()
-{
-    this->fd = -1;
-}
-
-int octave_serial::open(string path, int flags)
-{
-    this->fd = ::open(path.c_str(), flags);
-
-    if (this->get_fd() > 0)
-    {   
-        // 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));
-            this->close();
-            return -1;
-        }
-
-        if (tcgetattr(this->fd, &this->config) < 0)
-        {
-            error("serial: Failed to get terminal attributes: %s\n", strerror(errno));
-            this->close();
-            return -1;
-        }
-
-        // Clear all settings
-        this->config.c_iflag = 0; // Input modes
-        this->config.c_oflag = 0; // Output modes
-        this->config.c_cflag = CS8 | CREAD | CLOCAL; // Control modes, 8n1
-        this->config.c_lflag = 0; // Local modes
-        this->config.c_cc[VMIN] = 1;
-
-        if (tcsetattr(this->get_fd(), TCSANOW, &this->config) < 0)
-        {
-            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)
-        {
-            error("serial: Failed to disable NDELAY flag: %s\n", strerror(errno));
-            this->close();
-            return -1; 
-        }
-
-        this->blocking_read = true;
-    } 
-    else
-    {
-        error("serial: Error opening the interface: %s\n", strerror(errno));
-        return -1;
-    }
-
-    return this->get_fd();
-}
-
-octave_serial::~octave_serial()
-{
-    this->close();
-}
-
-void octave_serial::print (std::ostream& os, bool pr_as_read_syntax )
-{
-    print_raw(os, pr_as_read_syntax);
-    newline(os);
-}
-
-void octave_serial::print (std::ostream& os, bool pr_as_read_syntax ) const
-{
-    print_raw(os, pr_as_read_syntax);
-    newline(os);
-}
-
-void octave_serial::print_raw (std::ostream& os, bool pr_as_read_syntax) const
-{
-    os << this->fd;
-}
-
-int octave_serial::read(uint8_t *buf, unsigned int len)
-{
-    if (this->get_fd() < 0)
-    {
-        error("srl_read: Interface must be opened first...");
-        return 0;
-    }
-
-    size_t bytes_read = 0;
-    ssize_t read_retval = -1;
-
-    // While not interrupted in blocking mode
-    while (!read_interrupt) 
-    {
-        read_retval = ::read(this->get_fd(), (void *)(buf + bytes_read), len - bytes_read);
-        //printf("read_retval: %d\n\r", read_retval);
-
-        if (read_retval < 0)
-        {
-            error("srl_read: Error while reading: %s\n", strerror(errno));
-            break;
-        }
-
-        bytes_read += read_retval;
-
-        // Required number of bytes read
-        if (bytes_read >= len)
-            break;
-
-        // Timeout while in non-blocking mode
-        if (read_retval == 0 && !this->blocking_read)
-            break;
-    }
-
-    return bytes_read;
-}
-
-int octave_serial::write(string str)
-{
-    if (this->get_fd() < 0)
-    {
-        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)
-    {
-        error("serial: Interface must be opened first...");
-        return -1;
-    }
-
-    return ::write(get_fd(), buf, len);
-}
-
-int octave_serial::set_timeout(short timeout)
-{
-    if (this->get_fd() < 0)
-    {
-        error("serial: Interface must be opened first...");
-        return -1;
-    }
-
-    if (timeout < -1 || timeout > 255)
-    {
-        error("srl_timeout: timeout value must be between [-1..255]...");
-        return -1;
-    }
-
-    // Disable custom timeout, enable blocking read
-    if (timeout < 0)
-    {
-        this->blocking_read = true;
-        timeout = 5;
-    } 
-    // Enable custom timeout, disable blocking read
-    else 
-    {
-        this->blocking_read = false;
-    }
-
-    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) {
-        error("srl_timeout: error setting timeout...");
-        return -1;
-    }
-
-    return 1;
-}
-
-int octave_serial::get_timeout()
-{
-    if (blocking_read)
-        return -1;
-    else
-        return this->config.c_cc[VTIME];
-}
-
-int octave_serial::set_stopbits(unsigned short stopbits)
-{
-    if (this->get_fd() < 0)
-    {
-        error("serial: Interface must be opened first...");
-        return -1;
-    }
-
-    /*
-     * CSTOPB Send two stop bits, else one.
-     */
-
-    if (stopbits == 1)
-    {
-        // Set to one stop bit
-        BITMASK_CLEAR(this->config.c_cflag, CSTOPB);
-    }
-    else if (stopbits == 2)
-    {
-        // Set to two stop bits
-        BITMASK_SET(this->config.c_cflag, CSTOPB);
-    }
-    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));
-        return false;
-    }
-
-    return true;
-}
-
-int octave_serial::get_stopbits()
-{
-    if (this->get_fd() < 0)
-    {
-        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;
-    }
-
-    tcflag_t c_bytesize = 0;
-
-    switch (bytesize) 
-    {
-    case 5: c_bytesize = CS5; break;
-    case 6: c_bytesize = CS6; break;
-    case 7: c_bytesize = CS7; break;
-    case 8: c_bytesize = CS8; break;
-
-    default:
-        error("srl_bytesize: expecting value between [5..8]...");
-        return false;
-    }
-
-    // Clear bitmask CSIZE
-    BITMASK_CLEAR(this->config.c_cflag, CSIZE);
-
-    // 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));
-        return false;
-    }
-
-    return true;
-}
-
-int octave_serial::get_bytesize()
-{
-    if (this->get_fd() < 0)
-    {
-        error("serial: Interface must be opened first...");
-        return -1;
-    }
-
-    int retval = -1;
-
-    if (BITMASK_CHECK(this->config.c_cflag, CS5))
-        retval = 5;
-    else if (BITMASK_CHECK(this->config.c_cflag, CS6))
-        retval = 6;
-    else if (BITMASK_CHECK(this->config.c_cflag, CS7))
-        retval = 7;
-    else if (BITMASK_CHECK(this->config.c_cflag, CS8))
-        retval = 8;
-
-    return retval;
-}
-
-int octave_serial::set_baudrate(unsigned int baud) 
-{
-    if (this->get_fd() < 0)
-    {
-        error("serial: Interface must be opened first...");
-        return -1;
-    }
-
-    speed_t baud_rate = 0;
-
-    switch (baud) 
-    {
-    case 0: 
-        baud_rate = B0; break;
-    case 50:
-        baud_rate = B50; break;
-    case 75:
-        baud_rate = B75; break;
-    case 110:
-        baud_rate = B110; break;
-    case 134:
-        baud_rate = B134; break;
-    case 150:
-        baud_rate = B150; break;
-    case 200:
-        baud_rate = B200; break;
-    case 300:
-        baud_rate = B300; break;
-    case 600:
-        baud_rate = B600; break;
-    case 1200:
-        baud_rate = B1200; break;
-    case 1800:
-        baud_rate = B1800; break;
-    case 2400:
-        baud_rate = B2400; break;
-    case 4800:
-        baud_rate = B4800; break;
-    case 9600:
-        baud_rate = B9600; break;
-    case 19200:
-        baud_rate = B19200; break;
-    case 38400:
-        baud_rate = B38400; break;
-    case 57600:
-        baud_rate = B57600; break;
-    case 115200:
-        baud_rate = B115200; break;
-    case 230400:
-        baud_rate = B230400; 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;
-    }
-
-    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));
-        return false;
-    }
-
-    return true;
-}
-
-int octave_serial::get_baudrate()
-{
-    if (this->get_fd() < 0)
-    {
-        error("serial: Interface must be opened first...");
-        return -1;
-    }
-
-    int retval = -1;
-
-    speed_t baudrate = cfgetispeed(&this->config);
-
-    if (baudrate == B0)
-        retval = 0;
-    else if (baudrate == B50)
-        retval = 50;
-    else if (baudrate == B75)
-        retval = 75;
-    else if (baudrate == B110)
-        retval = 110;
-    else if (baudrate == B134)
-        retval = 134;
-    else if (baudrate == B150)
-        retval = 150;
-    else if (baudrate == B200)
-        retval = 200;
-    else if (baudrate == B300)
-        retval = 300;
-    else if (baudrate == B600)
-        retval = 600;
-    else if (baudrate == B1200)
-        retval = 1200;
-    else if (baudrate == B1800)
-        retval = 1800;
-    else if (baudrate == B2400)
-        retval = 2400;
-    else if (baudrate == B4800)
-        retval = 4800;
-    else if (baudrate == B9600)
-        retval = 9600;
-    else if (baudrate == B19200)
-        retval = 19200;
-    else if (baudrate == B38400)
-        retval = 38400;
-    else if (baudrate == B57600)
-        retval = 57600;
-    else if (baudrate == B115200)
-        retval = 115200;
-    else if (baudrate == B230400)
-        retval = 230400;
-
-    return retval;
-
-}
-
-int octave_serial::flush(unsigned short queue_selector)
-{
-    if (this->get_fd() < 0)
-    {
-        error("serial: Interface must be opened first...");
-        return -1;
-    }
-
-    /*
-     * TCIOFLUSH Flush both pending input and untransmitted output.
-     * TCOFLUSH Flush untransmitted output.
-     * TCIFLUSH Flush pending input.
-     */
-
-    int flag;
-
-    switch (queue_selector)
-    {
-    case 0: flag = TCOFLUSH; break;
-    case 1: flag = TCIFLUSH; break;
-    case 2: flag = TCIOFLUSH; break;
-    default:
-        error("srl_flush: only [0..2] values are accepted...");
-        return false;
-    }
-
-    return ::tcflush(this->get_fd(), flag);
-}
-
-
-int octave_serial::set_parity(string parity)
-{
-    if (this->get_fd() < 0)
-    {
-        error("serial: Interface must be opened first...");
-        return -1;
-    }
-
-    // Convert string to lowercase
-    std::transform(parity.begin(), parity.end(), parity.begin(), ::tolower);
-
-    /*
-     * PARENB Enable parity generation on output and parity checking for input.
-     * PARODD If set, then parity for input and output is odd; otherwise even parity is used.
-     */
-
-    if (parity == "n" || parity == "none")
-    {
-        // Disable parity generation/checking
-        BITMASK_CLEAR(this->config.c_cflag, PARENB);
-    }
-    else if (parity == "e" || parity == "even")
-    {
-        // Enable parity generation/checking
-        BITMASK_SET(this->config.c_cflag, PARENB);
-
-        // Set to Even
-        BITMASK_CLEAR(this->config.c_cflag, PARODD);
-
-    }
-    else if (parity == "o" || parity == "odd")
-    {
-        // Enable parity generation/checking
-        BITMASK_SET(this->config.c_cflag, PARENB);
-
-        // Set to Odd
-        BITMASK_SET(this->config.c_cflag, PARODD);
-
-    }
-    else
-    {
-        error("srl_parity: Only [N]one, [E]ven or [O]dd parities are supported...");
-        return false;
-    }
-
-    if (tcsetattr(this->get_fd(), TCSANOW, &this->config) < 0) {
-        error("srl_parity: error setting parity: %s\n", strerror(errno));
-        return false;
-    }
-
-    return true;
-}
-
-string octave_serial::get_parity()
-{
-    if (!BITMASK_CHECK(this->config.c_cflag, PARENB))
-        return "None";
-    else if (BITMASK_CHECK(this->config.c_cflag, PARODD))
-        return "Odd";
-    else
-        return "Even";
-}
-
-void octave_serial::get_control_line_status(void)
-{
-  if (this->get_fd() < 0)
-    {
-      error("serial: Interface must be opened first...");
-      return;
-    }
-  ioctl(this->get_fd(), TIOCMGET, &this->status);
-}
-
-bool octave_serial::get_control_line(string control_signal)
-{
-  get_control_line_status ();
-    
-  if (control_signal == "DTR")
-    return (this->status & TIOCM_DTR);
-  else if (control_signal == "RTS")
-    return (this->status & TIOCM_RTS);
-
-  error("serial: Unknown control signal...");
-  return false;
-
-}
-
-void octave_serial::set_control_line(string control_signal, bool set)
-{
-
-  get_control_line_status ();
-  
-  int signal;
-    
-  if (control_signal == "DTR")
-    signal = TIOCM_DTR;
-  else if (control_signal == "RTS")
-    signal = TIOCM_RTS;
-  else
-    {
-      error("serial: Unknown control signal...");
-      return;
-    }
-    
-  if (set)
-    this->status |= signal;
-  else
-    this->status &= ~signal;
-    
-  ioctl(this->get_fd(), TIOCMSET, &this->status);
-
-}
-
-int octave_serial::get_fd()
-{
-    return this->fd;
-}
-
-int octave_serial::close()
-{
-    int retval = -1;
-
-    if (this->get_fd() > 0)
-    {
-        retval = ::close(this->get_fd());
-        this->fd = -1;
-    }
-
-    return retval;
-}
-#endif
diff --git a/src/serial/serial_class.h b/src/serial/serial_class.h
index 58aee35..1c8ed93 100644
--- a/src/serial/serial_class.h
+++ b/src/serial/serial_class.h
@@ -16,83 +16,10 @@
 #ifndef SERIAL_CLASS_H
 #define SERIAL_CLASS_H
 
-#include <octave/oct.h>
-#include <octave/ov-int32.h>
-
-#include <string>
-#include <termios.h>
-
-using std::string;
-
-#define BITMASK_SET(x,y) ((x) |= (y))
-#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
-#define BITMASK_TOGGLE(x,y) ((x) ^= (y))
-#define BITMASK_CHECK(x,y) ((x) & (y))
-
-class octave_serial : public octave_base_value 
-{
-public:
-    octave_serial();
-    ~octave_serial();
-
-    int write(string /* buffer */);
-    int write(uint8_t* /* buffer */, unsigned int /* buffer size */);
-
-    int read(uint8_t* /* buffer */, unsigned int /* buffer size */);
-
-    int open(string /* path */, int /* open flags */);
-    int close();
-    int get_fd();
-
-    int flush(unsigned short /* stream select */);
-
-    int set_timeout(short /* timeout */);
-    int get_timeout();
-
-    int set_baudrate(unsigned int /* baudrate */);
-    int get_baudrate();
-
-    int set_bytesize(unsigned short /* bytesize */);
-    int get_bytesize();
-
-    int set_parity(string /* parity */);
-    string get_parity();
-
-    int set_stopbits(unsigned short /* stop bits */);
-    int get_stopbits();
-
-    bool get_control_line(string);
-    void set_control_line(string, bool);
-
-    // Overloaded base functions
-    double serial_value() const { return (double)this->fd; }
-
-    virtual double scalar_value (bool frc_str_conv = false) const 
-    {
-        return (double)this->fd;
-    }
-
-    void print (std::ostream& os, bool pr_as_read_syntax = false);
-    void print (std::ostream& os, bool pr_as_read_syntax = false) const;
-    void print_raw (std::ostream& os, bool pr_as_read_syntax) const;
-
-    // Properties
-    bool is_constant (void) const { return true;}
-    bool is_defined (void) const { return true;}
-    bool print_as_scalar (void) const { return true;}
-
-
-private:
-    int fd;
-    int status;
-    struct termios config;
-
-    volatile bool blocking_read;
-
-    void get_control_line_status(void);
-
-    DECLARE_OCTAVE_ALLOCATOR
-    DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
-};
+#ifdef __WIN32__
+ #include "serial_class_win32.h"
+#else
+ #include "serial_class_lin.h"
+#endif
 
 #endif
diff --git a/src/serial/serial_class_lin.cc b/src/serial/serial_class_lin.cc
new file mode 100644
index 0000000..80b6b7f
--- /dev/null
+++ b/src/serial/serial_class_lin.cc
@@ -0,0 +1,631 @@
+// Copyright (C) 2012   Andrius Sutas   <andrius.sutas@gmail.com>
+//
+// 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 <octave/oct.h>
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#ifdef BUILD_SERIAL
+#include <iostream>
+#include <string>
+#include <algorithm>
+
+#ifndef __WIN32__
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#ifdef __linux__
+#include <termio.h>
+#else
+#include <termios.h>
+#endif
+#include <unistd.h>
+#endif
+
+using std::string;
+
+#include "serial_class.h"
+
+volatile bool read_interrupt = false;
+
+DEFINE_OCTAVE_ALLOCATOR (octave_serial);
+DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_serial, "octave_serial", "octave_serial");
+
+octave_serial::octave_serial()
+{
+    this->fd = -1;
+}
+
+int octave_serial::open(string path)
+{
+    int flags = O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY; 
+    // 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.
+
+    this->fd = ::open(path.c_str(), flags);
+
+    if (this->get_fd() > 0)
+    {   
+        // 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));
+            this->close();
+            return -1;
+        }
+
+        if (tcgetattr(this->fd, &this->config) < 0)
+        {
+            error("serial: Failed to get terminal attributes: %s\n", strerror(errno));
+            this->close();
+            return -1;
+        }
+
+        // Clear all settings
+        this->config.c_iflag = 0; // Input modes
+        this->config.c_oflag = 0; // Output modes
+        this->config.c_cflag = CS8 | CREAD | CLOCAL; // Control modes, 8n1
+        this->config.c_lflag = 0; // Local modes
+        this->config.c_cc[VMIN] = 1;
+
+        if (tcsetattr(this->get_fd(), TCSANOW, &this->config) < 0)
+        {
+            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)
+        {
+            error("serial: Failed to disable NDELAY flag: %s\n", strerror(errno));
+            this->close();
+            return -1; 
+        }
+
+        this->blocking_read = true;
+    } 
+    else
+    {
+        error("serial: Error opening the interface: %s\n", strerror(errno));
+        return -1;
+    }
+
+    return this->get_fd();
+}
+
+octave_serial::~octave_serial()
+{
+    this->close();
+}
+
+void octave_serial::print (std::ostream& os, bool pr_as_read_syntax )
+{
+    print_raw(os, pr_as_read_syntax);
+    newline(os);
+}
+
+void octave_serial::print (std::ostream& os, bool pr_as_read_syntax ) const
+{
+    print_raw(os, pr_as_read_syntax);
+    newline(os);
+}
+
+void octave_serial::print_raw (std::ostream& os, bool pr_as_read_syntax) const
+{
+    os << this->fd;
+}
+
+int octave_serial::read(uint8_t *buf, unsigned int len)
+{
+    if (this->get_fd() < 0)
+    {
+        error("srl_read: Interface must be opened first...");
+        return 0;
+    }
+
+    size_t bytes_read = 0;
+    ssize_t read_retval = -1;
+
+    // While not interrupted in blocking mode
+    while (!read_interrupt) 
+    {
+        read_retval = ::read(this->get_fd(), (void *)(buf + bytes_read), len - bytes_read);
+        //printf("read_retval: %d\n\r", read_retval);
+
+        if (read_retval < 0)
+        {
+            error("srl_read: Error while reading: %s\n", strerror(errno));
+            break;
+        }
+
+        bytes_read += read_retval;
+
+        // Required number of bytes read
+        if (bytes_read >= len)
+            break;
+
+        // Timeout while in non-blocking mode
+        if (read_retval == 0 && !this->blocking_read)
+            break;
+    }
+
+    return bytes_read;
+}
+
+int octave_serial::write(string str)
+{
+    if (this->get_fd() < 0)
+    {
+        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)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    return ::write(get_fd(), buf, len);
+}
+
+int octave_serial::set_timeout(short timeout)
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    if (timeout < -1 || timeout > 255)
+    {
+        error("srl_timeout: timeout value must be between [-1..255]...");
+        return -1;
+    }
+
+    // Disable custom timeout, enable blocking read
+    if (timeout < 0)
+    {
+        this->blocking_read = true;
+        timeout = 5;
+    } 
+    // Enable custom timeout, disable blocking read
+    else 
+    {
+        this->blocking_read = false;
+    }
+
+    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) {
+        error("srl_timeout: error setting timeout...");
+        return -1;
+    }
+
+    return 1;
+}
+
+int octave_serial::get_timeout()
+{
+    if (blocking_read)
+        return -1;
+    else
+        return this->config.c_cc[VTIME];
+}
+
+int octave_serial::set_stopbits(unsigned short stopbits)
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    /*
+     * CSTOPB Send two stop bits, else one.
+     */
+
+    if (stopbits == 1)
+    {
+        // Set to one stop bit
+        BITMASK_CLEAR(this->config.c_cflag, CSTOPB);
+    }
+    else if (stopbits == 2)
+    {
+        // Set to two stop bits
+        BITMASK_SET(this->config.c_cflag, CSTOPB);
+    }
+    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));
+        return false;
+    }
+
+    return true;
+}
+
+int octave_serial::get_stopbits()
+{
+    if (this->get_fd() < 0)
+    {
+        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;
+    }
+
+    tcflag_t c_bytesize = 0;
+
+    switch (bytesize) 
+    {
+    case 5: c_bytesize = CS5; break;
+    case 6: c_bytesize = CS6; break;
+    case 7: c_bytesize = CS7; break;
+    case 8: c_bytesize = CS8; break;
+
+    default:
+        error("srl_bytesize: expecting value between [5..8]...");
+        return false;
+    }
+
+    // Clear bitmask CSIZE
+    BITMASK_CLEAR(this->config.c_cflag, CSIZE);
+
+    // 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));
+        return false;
+    }
+
+    return true;
+}
+
+int octave_serial::get_bytesize()
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    int retval = -1;
+
+    if (BITMASK_CHECK(this->config.c_cflag, CS5))
+        retval = 5;
+    else if (BITMASK_CHECK(this->config.c_cflag, CS6))
+        retval = 6;
+    else if (BITMASK_CHECK(this->config.c_cflag, CS7))
+        retval = 7;
+    else if (BITMASK_CHECK(this->config.c_cflag, CS8))
+        retval = 8;
+
+    return retval;
+}
+
+int octave_serial::set_baudrate(unsigned int baud) 
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    speed_t baud_rate = 0;
+
+    switch (baud) 
+    {
+    case 0: 
+        baud_rate = B0; break;
+    case 50:
+        baud_rate = B50; break;
+    case 75:
+        baud_rate = B75; break;
+    case 110:
+        baud_rate = B110; break;
+    case 134:
+        baud_rate = B134; break;
+    case 150:
+        baud_rate = B150; break;
+    case 200:
+        baud_rate = B200; break;
+    case 300:
+        baud_rate = B300; break;
+    case 600:
+        baud_rate = B600; break;
+    case 1200:
+        baud_rate = B1200; break;
+    case 1800:
+        baud_rate = B1800; break;
+    case 2400:
+        baud_rate = B2400; break;
+    case 4800:
+        baud_rate = B4800; break;
+    case 9600:
+        baud_rate = B9600; break;
+    case 19200:
+        baud_rate = B19200; break;
+    case 38400:
+        baud_rate = B38400; break;
+    case 57600:
+        baud_rate = B57600; break;
+    case 115200:
+        baud_rate = B115200; break;
+    case 230400:
+        baud_rate = B230400; 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;
+    }
+
+    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));
+        return false;
+    }
+
+    return true;
+}
+
+int octave_serial::get_baudrate()
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    int retval = -1;
+
+    speed_t baudrate = cfgetispeed(&this->config);
+
+    if (baudrate == B0)
+        retval = 0;
+    else if (baudrate == B50)
+        retval = 50;
+    else if (baudrate == B75)
+        retval = 75;
+    else if (baudrate == B110)
+        retval = 110;
+    else if (baudrate == B134)
+        retval = 134;
+    else if (baudrate == B150)
+        retval = 150;
+    else if (baudrate == B200)
+        retval = 200;
+    else if (baudrate == B300)
+        retval = 300;
+    else if (baudrate == B600)
+        retval = 600;
+    else if (baudrate == B1200)
+        retval = 1200;
+    else if (baudrate == B1800)
+        retval = 1800;
+    else if (baudrate == B2400)
+        retval = 2400;
+    else if (baudrate == B4800)
+        retval = 4800;
+    else if (baudrate == B9600)
+        retval = 9600;
+    else if (baudrate == B19200)
+        retval = 19200;
+    else if (baudrate == B38400)
+        retval = 38400;
+    else if (baudrate == B57600)
+        retval = 57600;
+    else if (baudrate == B115200)
+        retval = 115200;
+    else if (baudrate == B230400)
+        retval = 230400;
+
+    return retval;
+
+}
+
+int octave_serial::flush(unsigned short queue_selector)
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    /*
+     * TCIOFLUSH Flush both pending input and untransmitted output.
+     * TCOFLUSH Flush untransmitted output.
+     * TCIFLUSH Flush pending input.
+     */
+
+    int flag;
+
+    switch (queue_selector)
+    {
+    case 0: flag = TCOFLUSH; break;
+    case 1: flag = TCIFLUSH; break;
+    case 2: flag = TCIOFLUSH; break;
+    default:
+        error("srl_flush: only [0..2] values are accepted...");
+        return false;
+    }
+
+    return ::tcflush(this->get_fd(), flag);
+}
+
+
+int octave_serial::set_parity(string parity)
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    // Convert string to lowercase
+    std::transform(parity.begin(), parity.end(), parity.begin(), ::tolower);
+
+    /*
+     * PARENB Enable parity generation on output and parity checking for input.
+     * PARODD If set, then parity for input and output is odd; otherwise even parity is used.
+     */
+
+    if (parity == "n" || parity == "none")
+    {
+        // Disable parity generation/checking
+        BITMASK_CLEAR(this->config.c_cflag, PARENB);
+    }
+    else if (parity == "e" || parity == "even")
+    {
+        // Enable parity generation/checking
+        BITMASK_SET(this->config.c_cflag, PARENB);
+
+        // Set to Even
+        BITMASK_CLEAR(this->config.c_cflag, PARODD);
+
+    }
+    else if (parity == "o" || parity == "odd")
+    {
+        // Enable parity generation/checking
+        BITMASK_SET(this->config.c_cflag, PARENB);
+
+        // Set to Odd
+        BITMASK_SET(this->config.c_cflag, PARODD);
+
+    }
+    else
+    {
+        error("srl_parity: Only [N]one, [E]ven or [O]dd parities are supported...");
+        return false;
+    }
+
+    if (tcsetattr(this->get_fd(), TCSANOW, &this->config) < 0) {
+        error("srl_parity: error setting parity: %s\n", strerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+string octave_serial::get_parity()
+{
+    if (!BITMASK_CHECK(this->config.c_cflag, PARENB))
+        return "None";
+    else if (BITMASK_CHECK(this->config.c_cflag, PARODD))
+        return "Odd";
+    else
+        return "Even";
+}
+
+void octave_serial::get_control_line_status(void)
+{
+  if (this->get_fd() < 0)
+    {
+      error("serial: Interface must be opened first...");
+      return;
+    }
+  ioctl(this->get_fd(), TIOCMGET, &this->status);
+}
+
+bool octave_serial::get_control_line(string control_signal)
+{
+  get_control_line_status ();
+    
+  if (control_signal == "DTR")
+    return (this->status & TIOCM_DTR);
+  else if (control_signal == "RTS")
+    return (this->status & TIOCM_RTS);
+
+  error("serial: Unknown control signal...");
+  return false;
+
+}
+
+void octave_serial::set_control_line(string control_signal, bool set)
+{
+
+  get_control_line_status ();
+  
+  int signal;
+    
+  if (control_signal == "DTR")
+    signal = TIOCM_DTR;
+  else if (control_signal == "RTS")
+    signal = TIOCM_RTS;
+  else
+    {
+      error("serial: Unknown control signal...");
+      return;
+    }
+    
+  if (set)
+    this->status |= signal;
+  else
+    this->status &= ~signal;
+    
+  ioctl(this->get_fd(), TIOCMSET, &this->status);
+
+}
+
+int octave_serial::get_fd()
+{
+    return this->fd;
+}
+
+int octave_serial::close()
+{
+    int retval = -1;
+
+    if (this->get_fd() > 0)
+    {
+        retval = ::close(this->get_fd());
+        this->fd = -1;
+    }
+
+    return retval;
+}
+#endif
diff --git a/src/serial/serial_class_lin.h b/src/serial/serial_class_lin.h
new file mode 100644
index 0000000..15370b9
--- /dev/null
+++ b/src/serial/serial_class_lin.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2012   Andrius Sutas   <andrius.sutas@gmail.com>
+//
+// 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/>.
+
+#ifndef SERIAL_CLASS_LIN_H
+#define SERIAL_CLASS_LIN_H
+
+#include <octave/oct.h>
+#include <octave/ov-int32.h>
+
+#include <string>
+#include <termios.h>
+
+using std::string;
+
+#define BITMASK_SET(x,y) ((x) |= (y))
+#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
+#define BITMASK_TOGGLE(x,y) ((x) ^= (y))
+#define BITMASK_CHECK(x,y) ((x) & (y))
+
+class octave_serial : public octave_base_value 
+{
+public:
+    octave_serial();
+    ~octave_serial();
+
+    int write(string /* buffer */);
+    int write(uint8_t* /* buffer */, unsigned int /* buffer size */);
+
+    int read(uint8_t* /* buffer */, unsigned int /* buffer size */);
+
+    int open(string /* path */);
+    int close();
+    int get_fd();
+
+    int flush(unsigned short /* stream select */);
+
+    int set_timeout(short /* timeout */);
+    int get_timeout();
+
+    int set_baudrate(unsigned int /* baudrate */);
+    int get_baudrate();
+
+    int set_bytesize(unsigned short /* bytesize */);
+    int get_bytesize();
+
+    int set_parity(string /* parity */);
+    string get_parity();
+
+    int set_stopbits(unsigned short /* stop bits */);
+    int get_stopbits();
+
+    bool get_control_line(string);
+    void set_control_line(string, bool);
+
+    // Overloaded base functions
+    double serial_value() const { return (double)this->fd; }
+
+    virtual double scalar_value (bool frc_str_conv = false) const 
+    {
+        return (double)this->fd;
+    }
+
+    void print (std::ostream& os, bool pr_as_read_syntax = false);
+    void print (std::ostream& os, bool pr_as_read_syntax = false) const;
+    void print_raw (std::ostream& os, bool pr_as_read_syntax) const;
+
+    // Properties
+    bool is_constant (void) const { return true;}
+    bool is_defined (void) const { return true;}
+    bool print_as_scalar (void) const { return true;}
+
+
+private:
+    int fd;
+    int status;
+    struct termios config;
+
+    volatile bool blocking_read;
+
+    void get_control_line_status(void);
+
+    DECLARE_OCTAVE_ALLOCATOR
+    DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+};
+
+#endif
diff --git a/src/serial/serial_class_win32.cc b/src/serial/serial_class_win32.cc
new file mode 100644
index 0000000..407040d
--- /dev/null
+++ b/src/serial/serial_class_win32.cc
@@ -0,0 +1,560 @@
+// Copyright (C) 2014   John Donoghue   <john.donoghue@ieee.org>
+//
+// 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 <octave/oct.h>
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#ifdef BUILD_SERIAL
+#include <iostream>
+#include <string>
+#include <algorithm>
+
+using std::string;
+
+#include "serial_class.h"
+
+volatile bool read_interrupt = false;
+
+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_OCTAVE_ALLOCATOR (octave_serial);
+DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_serial, "octave_serial", "octave_serial");
+
+octave_serial::octave_serial()
+{
+    this->fd = INVALID_HANDLE_VALUE;
+}
+
+int octave_serial::open(string path)
+{
+   this->fd = CreateFile(path.c_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
+
+   if (this->fd == INVALID_HANDLE_VALUE)
+   {
+       error("serial: Error opening the interface: %s\n", winerror(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) == FALSE)
+   {
+       error("serial: Failed to get terminal attributes: %s\n", winerror(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", winerror(errno));
+       this->close();
+       return -1; 
+   }
+
+   return this->get_fd();
+}
+
+octave_serial::~octave_serial()
+{
+    this->close();
+}
+
+void octave_serial::print (std::ostream& os, bool pr_as_read_syntax )
+{
+    print_raw(os, pr_as_read_syntax);
+    newline(os);
+}
+
+void octave_serial::print (std::ostream& os, bool pr_as_read_syntax ) const
+{
+    print_raw(os, pr_as_read_syntax);
+    newline(os);
+}
+
+void octave_serial::print_raw (std::ostream& os, bool pr_as_read_syntax) const
+{
+    os << this->fd;
+}
+
+int octave_serial::read(uint8_t *buf, unsigned int len)
+{
+    if (this->get_fd() < 0)
+    {
+        error("srl_read: Interface must be opened first...");
+        return 0;
+    }
+
+    size_t bytes_read = 0;
+    ssize_t read_retval = -1;
+
+    // While not interrupted in blocking mode
+    while (!read_interrupt) 
+    {
+        DWORD readsz;
+        read_retval = -1;
+        if(ReadFile(this->fd, buf, len, &readsz, NULL) == TRUE)
+        {
+           read_retval = readsz;
+        }
+
+        if(read_retval < 0)
+        {
+            error("srl_read: Error while reading: %s\n", winerror(errno));
+            break;
+        }
+
+        bytes_read += read_retval;
+
+        // Required number of bytes read
+        if (bytes_read >= len)
+            break;
+
+        // Timeout while in non-blocking mode
+        if (read_retval == 0 && !this->blocking_read)
+            break;
+    }
+
+    return bytes_read;
+}
+
+int octave_serial::write(string str)
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+    int wrote_ret = -1;
+    DWORD wrote;
+    if(WriteFile(this->fd, str.c_str(), str.length(), &wrote, NULL) == TRUE)
+    {
+      wrote_ret = wrote;
+    }
+    return wrote_ret;
+}
+
+int octave_serial::write(uint8_t *buf, unsigned int len)
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+    int wrote_ret = -1;
+    DWORD wrote;
+    if(WriteFile(this->fd, buf, len, &wrote, NULL) == TRUE)
+    {
+      wrote_ret = wrote;
+    }
+    return wrote_ret;
+}
+
+int octave_serial::set_timeout(short timeout)
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    if (timeout < -1 || timeout > 255)
+    {
+        error("srl_timeout: timeout value must be between [-1..255]...");
+        return -1;
+    }
+
+    // Disable custom timeout, enable blocking read
+    if (timeout < 0)
+    {
+        this->blocking_read = true;
+        timeout = 5;
+    } 
+    // Enable custom timeout, disable blocking read
+    else 
+    {
+        this->blocking_read = false;
+    }
+
+    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; 
+    }
+
+    return 1;
+}
+
+int octave_serial::get_timeout()
+{
+    if (blocking_read)
+        return -1;
+    else
+        return this->timeouts.ReadTotalTimeoutConstant/100;
+}
+
+int octave_serial::set_stopbits(unsigned short stopbits)
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    if (stopbits == 1)
+    {
+        // Set to one stop bit
+        this->config.StopBits = ONESTOPBIT;
+    }
+    else if (stopbits == 2)
+    {
+        // Set to two stop bits
+        this->config.StopBits = TWOSTOPBITS;
+    }
+    else
+    {
+        error("srl_stopbits: Only 1 or 2 stop bits are supported...");
+        return false;
+    }
+
+    if(SetCommState(this->fd,&this->config) == FALSE)
+    {
+        error("srl_stopbits: error setting stop bits: %s\n", winerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+int octave_serial::get_stopbits()
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+    if(this->config.StopBits == TWOSTOPBITS)
+        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;
+    }
+
+    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", winerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+int octave_serial::get_bytesize()
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    return this->config.ByteSize;
+}
+
+int octave_serial::set_baudrate(unsigned int baud) 
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    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;
+    }
+
+    this->config.BaudRate = baud;
+
+    if(SetCommState(this->fd,&this->config) == FALSE)
+    {
+        error("srl_stopbits: error setting baud rate: %s\n", winerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+int octave_serial::get_baudrate()
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    return this->config.BaudRate;
+}
+
+int octave_serial::flush(unsigned short queue_selector)
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    int flag;
+
+    switch (queue_selector)
+    {
+    case 0: flag = PURGE_TXCLEAR; break;
+    case 1: flag = PURGE_RXCLEAR; break;
+    case 2: flag = PURGE_RXCLEAR|PURGE_TXCLEAR; break;
+    default:
+        error("srl_flush: only [0..2] values are accepted...");
+        return false;
+    }
+
+   if(PurgeComm(this->fd,flag) == FALSE)
+     return -1;
+   else
+     return true;
+}
+
+
+int octave_serial::set_parity(string parity)
+{
+    if (this->get_fd() < 0)
+    {
+        error("serial: Interface must be opened first...");
+        return -1;
+    }
+
+    // Convert string to lowercase
+    std::transform(parity.begin(), parity.end(), parity.begin(), ::tolower);
+
+    /*
+     * PARENB Enable parity generation on output and parity checking for input.
+     * PARODD If set, then parity for input and output is odd; otherwise even parity is used.
+     */
+
+    if (parity == "n" || parity == "none")
+    {
+        // Disable parity generation/checking
+        this->config.Parity = NOPARITY;
+    }
+    else if (parity == "e" || parity == "even")
+    {
+        // Enable parity generation/checking
+        this->config.Parity = EVENPARITY;
+    }
+    else if (parity == "o" || parity == "odd")
+    {
+         this->config.Parity = ODDPARITY;
+    }
+    else
+    {
+        error("srl_parity: Only [N]one, [E]ven or [O]dd parities are supported...");
+        return false;
+    }
+
+    if(SetCommState(this->fd,&this->config) == FALSE)
+    {
+        error("srl_parity: error setting parity: %s\n", winerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+string octave_serial::get_parity()
+{
+    if(this->config.Parity == NOPARITY) 
+        return "None";
+    else if(this->config.Parity == ODDPARITY) 
+        return "Odd";
+    else
+        return "Even";
+}
+
+void octave_serial::get_control_line_status(void)
+{
+    if (this->get_fd() < 0)
+    {
+      error("serial: Interface must be opened first...");
+      return;
+    }
+
+    GetCommState(this->fd, &this->config);
+}
+
+bool octave_serial::get_control_line(string control_signal)
+{
+    get_control_line_status ();
+    
+    if (control_signal == "DTR")
+        return (this->config.fDtrControl == DTR_CONTROL_ENABLE);
+    else if (control_signal == "RTS")
+        return (this->config.fRtsControl == RTS_CONTROL_ENABLE);
+    else
+        error("serial: Unknown control signal...");
+    return false;
+
+}
+
+void octave_serial::set_control_line(string control_signal, bool set)
+{
+
+    get_control_line_status ();
+  
+    int signal;
+    
+    if (control_signal == "DTR")
+    {
+        if(set)
+          this->config.fDtrControl = DTR_CONTROL_ENABLE;
+        else
+          this->config.fDtrControl = DTR_CONTROL_DISABLE;
+    }
+    else if (control_signal == "RTS")
+    {
+        if(set)
+          this->config.fRtsControl = RTS_CONTROL_ENABLE;
+        else
+          this->config.fRtsControl = RTS_CONTROL_DISABLE;
+    }
+    else
+    {
+        error("serial: Unknown control signal...");
+        return;
+    }
+
+    SetCommState(this->fd,&this->config);
+}
+
+int octave_serial::get_fd() const
+{
+    if(this->fd == INVALID_HANDLE_VALUE) return -1;
+    return (int)this->fd;
+}
+
+int octave_serial::close()
+{
+    int retval = -1;
+
+    if (this->fd != INVALID_HANDLE_VALUE)
+    {
+        if(CloseHandle(this->fd))
+           retval = 0;
+
+        this->fd = INVALID_HANDLE_VALUE;
+    }
+
+    return retval;
+}
+#endif
diff --git a/src/serial/serial_class_win32.h b/src/serial/serial_class_win32.h
new file mode 100644
index 0000000..da29c51
--- /dev/null
+++ b/src/serial/serial_class_win32.h
@@ -0,0 +1,95 @@
+// Copyright (C) 2012   Andrius Sutas   <andrius.sutas@gmail.com>
+//
+// 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/>.
+
+#ifndef SERIAL_CLASS_WIN32_H
+#define SERIAL_CLASS_WIN32_H
+
+#include <octave/oct.h>
+#include <octave/ov-int32.h>
+
+#include <windows.h>
+
+#include <string>
+
+using std::string;
+
+class octave_serial : public octave_base_value 
+{
+public:
+    octave_serial();
+    ~octave_serial();
+
+    int write(string /* buffer */);
+    int write(uint8_t* /* buffer */, unsigned int /* buffer size */);
+
+    int read(uint8_t* /* buffer */, unsigned int /* buffer size */);
+
+    int open(string /* path */);
+    int close();
+    int get_fd() const;
+
+    int flush(unsigned short /* stream select */);
+
+    int set_timeout(short /* timeout */);
+    int get_timeout();
+
+    int set_baudrate(unsigned int /* baudrate */);
+    int get_baudrate();
+
+    int set_bytesize(unsigned short /* bytesize */);
+    int get_bytesize();
+
+    int set_parity(string /* parity */);
+    string get_parity();
+
+    int set_stopbits(unsigned short /* stop bits */);
+    int get_stopbits();
+
+    bool get_control_line(string);
+    void set_control_line(string, bool);
+
+    // Overloaded base functions
+    double serial_value() const { return (double)get_fd(); }
+
+    virtual double scalar_value (bool frc_str_conv = false) const 
+    {
+        return (double)get_fd();
+    }
+
+    void print (std::ostream& os, bool pr_as_read_syntax = false);
+    void print (std::ostream& os, bool pr_as_read_syntax = false) const;
+    void print_raw (std::ostream& os, bool pr_as_read_syntax) const;
+
+    // Properties
+    bool is_constant (void) const { return true;}
+    bool is_defined (void) const { return true;}
+    bool print_as_scalar (void) const { return true;}
+
+
+private:
+    HANDLE fd;
+    DWORD status;
+    DCB config;
+    COMMTIMEOUTS timeouts;
+
+    volatile bool blocking_read;
+
+    void get_control_line_status(void);
+
+    DECLARE_OCTAVE_ALLOCATOR
+    DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+};
+
+#endif
-- 
1.8.0.msysgit.0

