Преглед на файлове

Initial port of a module for rs232 from https://github.com/ynezz/librs232

mingodad преди 13 години
родител
ревизия
cf2fd88f0a
променени са 7 файла, в които са добавени 2552 реда и са изтрити 0 реда
  1. 314 0
      ext/librs232/rs232.c
  2. 216 0
      ext/librs232/rs232.h
  3. 817 0
      ext/librs232/rs232_posix.c
  4. 53 0
      ext/librs232/rs232_posix.h
  5. 750 0
      ext/librs232/rs232_windows.c
  6. 72 0
      ext/librs232/rs232_windows.h
  7. 330 0
      ext/sq_rs232.c

+ 314 - 0
ext/librs232/rs232.c

@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <[email protected]>, Gaben Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "librs232/rs232.h"
+
+static const char *
+rs232_baud[] = {
+	"300",
+	"2400",
+	"4800",
+	"9600",
+	"19200",
+	"38400",
+	"57600",
+	"115200",
+	"460800",
+};
+
+static const char *
+rs232_data[] = {
+	"5",
+	"6",
+	"7",
+	"8",
+};
+
+static const char *
+rs232_parity[] = {
+	"none",
+	"odd",
+	"even",
+};
+
+static const char *
+rs232_stop[] = {
+	"1",
+	"2",
+};
+
+static const char *
+rs232_flow[] = {
+	"off",
+	"hardware",
+	"xon/xoff",
+};
+
+static const char *
+rs232_dtr[] = {
+	"off",
+	"on",
+};
+
+static const char *
+rs232_rts[] = {
+	"off",
+	"on",
+};
+
+static const char *
+rs232_error[] = {
+	"no error",
+	"unknown error",
+	"open error",
+	"close error",
+	"flush error",
+	"get/set settings error",
+	"read error",
+	"write error",
+	"select error",
+	"timeout error",
+	"ioctl error",
+	"port closed error",
+};
+
+#ifdef RS232_DEBUG
+const char *
+rs232_hex_dump(const void *data, unsigned int len)
+{
+	static char string[1024] = {0};
+	unsigned char *d = (unsigned char *) data;
+	unsigned int i;
+
+	for (i = 0; len--; i += 3) {
+		if (i >= sizeof(string) - 4)
+			break;
+		snprintf(string+i, 4, "%02x ", *d++);
+	}
+
+	string[i] = '\0';
+	return string;
+}
+
+const char *
+rs232_ascii_dump(const void *data, unsigned int len)
+{
+	static char string[1024] = {0};
+	unsigned char *d = (unsigned char *) data;
+	unsigned int i;
+	unsigned char c;
+
+	for (i = 0; len--; i++) {
+		if (i >= sizeof(string) - 2)
+			break;
+		c = *d++;
+		snprintf(string+i, 2, "%c", isprint(c) ? c : '.');
+	}
+
+	string[i] = '\0';
+	return string;
+}
+#endif
+
+RS232_LIB const char *
+rs232_strerror(unsigned int error)
+{
+	DBG("error=%d\n", error);
+
+	if (error >= RS232_ERR_MAX)
+		return NULL;
+
+	return rs232_error[error];
+}
+
+RS232_LIB const char *
+rs232_strbaud(unsigned int baud)
+{
+	DBG("baud=%d\n", baud);
+
+	if (baud >= RS232_BAUD_MAX)
+		return NULL;
+
+	return rs232_baud[baud];
+}
+
+RS232_LIB const char *
+rs232_strdata(unsigned int data)
+{
+	DBG("data=%d\n", data);
+
+	if (data >= RS232_DATA_MAX)
+		return NULL;
+
+	return rs232_data[data];
+}
+
+RS232_LIB const char *
+rs232_strparity(unsigned int parity)
+{
+	DBG("parity=%d\n", parity);
+
+	if (parity >= RS232_PARITY_MAX)
+		return NULL;
+
+	return rs232_parity[parity];
+}
+
+RS232_LIB const char *
+rs232_strstop(unsigned int stop)
+{
+	DBG("stop=%d\n", stop);
+
+	if (stop >= RS232_STOP_MAX)
+		return NULL;
+
+	return rs232_stop[stop];
+}
+
+RS232_LIB const char *
+rs232_strflow(unsigned int flow)
+{
+	DBG("flow=%d\n", flow);
+
+	if (flow >= RS232_FLOW_MAX)
+		return NULL;
+
+	return rs232_flow[flow];
+}
+
+RS232_LIB const char *
+rs232_strdtr(unsigned int dtr)
+{
+	DBG("dtr=%d\n", dtr);
+
+	if (dtr >= RS232_DTR_MAX)
+		return NULL;
+
+	return rs232_dtr[dtr];
+}
+
+RS232_LIB const char *
+rs232_strrts(unsigned int rts)
+{
+	DBG("rts=%d\n", rts);
+
+	if (rts >= RS232_RTS_MAX)
+		return NULL;
+
+	return rs232_rts[rts];
+}
+
+RS232_LIB const char *
+rs232_to_string(struct rs232_port_t *p)
+{
+	static char str[RS232_STRLEN+1];
+
+	DBG("p=%p\n", (void *)p);
+
+	if (p == NULL)
+		return NULL;
+
+	snprintf(str, RS232_STRLEN, "device: %s, baud: %s, data bits: %s,"
+					" parity: %s, stop bits: %s,"
+					" flow control: %s",
+					p->dev,
+					rs232_strbaud(p->baud),
+					rs232_strdata(p->data),
+					rs232_strparity(p->parity),
+					rs232_strstop(p->stop),
+					rs232_strflow(p->flow));
+
+	return str;
+}
+
+RS232_LIB const char *
+rs232_get_device(struct rs232_port_t *p)
+{
+	DBG("p=%p device: %s\n", (void *)p, p->dev);
+	return p->dev;
+}
+
+RS232_LIB unsigned int
+rs232_get_baud(struct rs232_port_t *p)
+{
+	DBG("p=%p baud: %d\n", (void *)p, p->baud);
+	return p->baud;
+}
+
+RS232_LIB unsigned int
+rs232_get_stop(struct rs232_port_t *p)
+{
+	DBG("p=%p baud: %d\n", (void *)p, p->stop);
+	return p->stop;
+}
+
+RS232_LIB unsigned int
+rs232_get_data(struct rs232_port_t *p)
+{
+	DBG("p=%p data: %d\n", (void *)p, p->data);
+	return p->data;
+
+}
+
+RS232_LIB unsigned int
+rs232_get_parity(struct rs232_port_t *p)
+{
+	DBG("p=%p parity: %d\n", (void *)p, p->parity);
+	return p->parity;
+}
+
+RS232_LIB unsigned int
+rs232_get_flow(struct rs232_port_t *p)
+{
+	DBG("p=%p flow: %d\n", (void *)p, p->flow);
+	return p->flow;
+}
+
+RS232_LIB unsigned int
+rs232_port_open(struct rs232_port_t *p)
+{
+	DBG("p=%p p->status=%d\n", (void *)p, p->status);
+	return p->status;
+}
+
+RS232_LIB unsigned int
+rs232_get_dtr(struct rs232_port_t *p)
+{
+	DBG("p=%p dtr: %d\n", (void *)p, p->dtr);
+	return p->dtr;
+}
+
+RS232_LIB unsigned int
+rs232_get_rts(struct rs232_port_t *p)
+{
+	DBG("p=%p rts: %d\n", (void *)p, p->rts);
+	return p->rts;
+}

+ 216 - 0
ext/librs232/rs232.h

@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <[email protected]>, Gaben Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __LIBRS232_H__
+#define __LIBRS232_H__
+
+#include <time.h>
+
+#define RS232_STRLEN 512
+#define RS232_STRLEN_DEVICE 30
+
+#ifdef __linux__
+#define RS232_PORT_POSIX "/dev/ttyS0"
+#else
+#define RS232_PORT_POSIX "/dev/cua00"
+#endif /* __linux__ */
+
+#define RS232_PORT_WIN32 "COM1"
+
+#if defined(WIN32) || defined(UNDER_CE)
+ #include "librs232/rs232_windows.h"
+ #pragma warning(disable:4996)
+ #define snprintf _snprintf
+#else
+ #include "librs232/rs232_posix.h"
+#endif
+
+#ifdef RS232_DEBUG
+const char* rs232_hex_dump(const void *data, unsigned int len);
+const char* rs232_ascii_dump(const void *data, unsigned int len);
+
+ #if defined(WIN32) || defined(UNDER_CE)
+  #include <windows.h>
+  #define DBG(x, ...) \
+ 		{ \
+			SYSTEMTIME t; \
+			GetLocalTime(&t); \
+			fprintf(stderr, "[%02d:%02d:%02d.%03d] %s(%d):%s: " x, t.wHour, t.wMinute, \
+				t.wSecond, t.wMilliseconds,  __FILE__, __LINE__, __FUNCTION__, ## __VA_ARGS__); \
+		}
+ #else
+  #define DBG(x, ...) \
+		{ \
+			time_t now = time(NULL); \
+			struct tm* t = localtime(&now); \
+			fprintf(stderr, "[%02d:%02d:%02d] %s(%d):%s: " x, t->tm_hour, t->tm_min, \
+				t->tm_sec,  __FILE__, __LINE__, __FUNCTION__, ## __VA_ARGS__); \
+		}
+ #endif /* #if defined(WIN32) || defined(UNDER_CE) */
+#else
+ #define DBG(x, ...)
+ #define rs232_hex_dump(x, y)
+ #define rs232_ascii_dump(x, y)
+#endif /* RS232_DEBUG */
+
+enum rs232_baud_e {
+	RS232_BAUD_300,
+	RS232_BAUD_2400,
+	RS232_BAUD_4800,
+	RS232_BAUD_9600,
+	RS232_BAUD_19200,
+	RS232_BAUD_38400,
+	RS232_BAUD_57600,
+	RS232_BAUD_115200,
+	RS232_BAUD_460800,
+	RS232_BAUD_MAX
+};
+
+enum rs232_data_e {
+	RS232_DATA_5,
+	RS232_DATA_6,
+	RS232_DATA_7,
+	RS232_DATA_8,
+	RS232_DATA_MAX
+};
+
+enum rs232_parity_e {
+	RS232_PARITY_NONE,
+	RS232_PARITY_ODD,
+	RS232_PARITY_EVEN,
+	RS232_PARITY_MAX
+};
+
+enum rs232_stop_e {
+	RS232_STOP_1,
+	RS232_STOP_2,
+	RS232_STOP_MAX
+};
+
+enum rs232_flow_e {
+	RS232_FLOW_OFF,
+	RS232_FLOW_HW,
+	RS232_FLOW_XON_XOFF,
+	RS232_FLOW_MAX
+};
+
+enum rs232_status_e {
+	RS232_PORT_CLOSED,
+	RS232_PORT_OPEN,
+};
+
+enum rs232_dtr_e {
+	RS232_DTR_OFF,
+	RS232_DTR_ON,
+	RS232_DTR_MAX
+};
+
+enum rs232_rts_e {
+	RS232_RTS_OFF,
+	RS232_RTS_ON,
+	RS232_RTS_MAX
+};
+
+struct rs232_port_t {
+	char dev[RS232_STRLEN_DEVICE+1];
+	void *pt; /* platform specific stuff */
+	enum rs232_baud_e baud;
+	enum rs232_data_e data;
+	enum rs232_stop_e stop;
+	enum rs232_flow_e flow;
+	enum rs232_parity_e parity;
+	enum rs232_status_e status;
+	enum rs232_dtr_e dtr;
+	enum rs232_rts_e rts;
+};
+
+enum rs232_error_e {
+	RS232_ERR_NOERROR,
+	RS232_ERR_UNKNOWN,
+	RS232_ERR_OPEN,
+	RS232_ERR_CLOSE,
+	RS232_ERR_FLUSH,
+	RS232_ERR_CONFIG,
+	RS232_ERR_READ,
+	RS232_ERR_WRITE,
+	RS232_ERR_SELECT,
+	RS232_ERR_TIMEOUT,
+	RS232_ERR_IOCTL,
+	RS232_ERR_PORT_CLOSED,
+	RS232_ERR_MAX
+};
+
+#if (defined(WIN32) || defined(UNDER_CE)) && !defined(RS232_STATIC)
+	#ifdef RS232_EXPORT
+		#define RS232_LIB __declspec(dllexport)
+	#else
+		#define RS232_LIB __declspec(dllimport)
+	#endif
+#else
+	#define RS232_LIB
+#endif
+
+RS232_LIB struct rs232_port_t * rs232_init(void);
+RS232_LIB void rs232_end(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_open(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_port_open(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_close(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_flush(struct rs232_port_t *p);
+RS232_LIB void rs232_set_device(struct rs232_port_t *p, const char *device);
+RS232_LIB unsigned int rs232_set_baud(struct rs232_port_t *p, unsigned int baud);
+RS232_LIB unsigned int rs232_set_stop(struct rs232_port_t *p, unsigned int stop);
+RS232_LIB unsigned int rs232_set_data(struct rs232_port_t *p, unsigned int data);
+RS232_LIB unsigned int rs232_set_parity(struct rs232_port_t *p, unsigned int parity);
+RS232_LIB unsigned int rs232_set_flow(struct rs232_port_t *p, unsigned int flow);
+RS232_LIB unsigned int rs232_set_dtr(struct rs232_port_t *p, unsigned int dtr);
+RS232_LIB unsigned int rs232_set_rts(struct rs232_port_t *p, unsigned int rts);
+RS232_LIB const char * rs232_get_device(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_get_baud(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_get_stop(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_get_data(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_get_parity(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_get_flow(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_get_dtr(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_get_rts(struct rs232_port_t *p);
+RS232_LIB unsigned int rs232_read(struct rs232_port_t *p, unsigned char *buf, unsigned int buf_len, unsigned int *read_len);
+RS232_LIB unsigned int rs232_read_timeout(struct rs232_port_t *p, unsigned char *buf, unsigned int buf_len, unsigned int *read_len, unsigned int timeout);
+RS232_LIB unsigned int rs232_read_timeout_forced(struct rs232_port_t *p, unsigned char *buf, unsigned int buf_len, unsigned int *read_len, unsigned int timeout);
+RS232_LIB unsigned int rs232_write(struct rs232_port_t *p, const unsigned char *buf, unsigned int buf_len, unsigned int *write_len);
+RS232_LIB unsigned int rs232_write_timeout(struct rs232_port_t *p, const unsigned char *buf, unsigned int buf_len, unsigned int *write_len, unsigned int timeout);
+RS232_LIB unsigned int rs232_in_qeue(struct rs232_port_t *p, unsigned int *in_bytes);
+RS232_LIB void rs232_in_qeue_clear(struct rs232_port_t *p);
+RS232_LIB const char * rs232_to_string(struct rs232_port_t *p);
+RS232_LIB const char * rs232_strerror(unsigned int error);
+RS232_LIB const char * rs232_strbaud(unsigned int baud);
+RS232_LIB const char * rs232_strdata(unsigned int data);
+RS232_LIB const char * rs232_strparity(unsigned int parity);
+RS232_LIB const char * rs232_strstop(unsigned int stop);
+RS232_LIB const char * rs232_strflow(unsigned int flow);
+RS232_LIB const char * rs232_strdtr(unsigned int dtr);
+RS232_LIB const char * rs232_strrts(unsigned int rts);
+RS232_LIB unsigned int rs232_fd(struct rs232_port_t *p);
+
+#endif /* __LIBRS232_H__ */

+ 817 - 0
ext/librs232/rs232_posix.c

@@ -0,0 +1,817 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <[email protected]>, Gaben Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "librs232/rs232.h"
+
+struct rs232_port_t *
+rs232_init(void)
+{
+	struct rs232_port_t *p = NULL;
+	p = (struct rs232_port_t *) malloc(sizeof(struct rs232_port_t));
+	if (p == NULL)
+		return NULL;
+
+	p->pt = (struct rs232_posix_t *) malloc(sizeof(struct rs232_posix_t));
+	if (p->pt == NULL)
+		return NULL;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	memset(p->pt, 0, sizeof(struct rs232_posix_t));
+	memset(p->dev, 0, RS232_STRLEN_DEVICE+1);
+	strncpy(p->dev, RS232_PORT_POSIX, RS232_STRLEN_DEVICE);
+
+	p->baud = RS232_BAUD_115200;
+	p->data = RS232_DATA_8;
+	p->parity = RS232_PARITY_NONE;
+	p->stop = RS232_STOP_1;
+	p->flow = RS232_FLOW_OFF;
+	p->status = RS232_PORT_CLOSED;
+	p->dtr = RS232_DTR_OFF;
+	p->rts = RS232_RTS_OFF;
+
+	return p;
+}
+
+void
+rs232_end(struct rs232_port_t *p)
+{
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	if (!rs232_port_open(p)) {
+		free(p->pt);
+		free(p);
+		return;
+	}
+
+	rs232_flush(p);
+
+	if (tcsetattr(ux->fd, TCSANOW, &ux->oldterm) < 0) {
+		DBG("tcsetattr() %d %s\n", errno, strerror(errno))
+		return;
+	}
+
+	rs232_close(p);
+	free(p->pt);
+	free(p);
+}
+
+unsigned int
+rs232_in_qeue(struct rs232_port_t *p, unsigned int *in_bytes)
+{
+	fd_set set;
+	int ret;
+	int b;
+	struct timeval tv;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	FD_ZERO(&set);
+	FD_SET(ux->fd, &set);
+	/* don't work reliable with 0 */
+	tv.tv_usec = 1;
+	tv.tv_sec = 0;
+
+	select(ux->fd+1, &set, NULL, NULL, &tv);
+	ret = ioctl(ux->fd, FIONREAD, &b);
+	if (ret == -1) {
+		*in_bytes = 0;
+		DBG("%s\n", "RS232_ERR_IOCTL");
+		return RS232_ERR_IOCTL;
+	}
+
+	*in_bytes = b;
+	DBG("in_bytes=%d\n", b);
+
+	return RS232_ERR_NOERROR;
+}
+
+/* some USB<->RS232 converters buffer a lot, so this function tries to discard
+   this buffer - useful mainly after rs232_open() */
+void
+rs232_in_qeue_clear(struct rs232_port_t *p)
+{
+	fd_set set;
+	unsigned int ret;
+	unsigned int blen;
+	unsigned char *buf = NULL;
+	struct timeval tv;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	if (!rs232_port_open(p))
+		return;
+
+	rs232_in_qeue(p, &blen);
+	if (blen > 0) {
+		buf = (unsigned char*) malloc(blen * sizeof(unsigned char*)+1);
+		if (buf == NULL)
+			return;
+
+		FD_ZERO(&set);
+		FD_SET(ux->fd, &set);
+		tv.tv_usec = 1;
+		tv.tv_sec = 0;
+
+		ret = select(ux->fd+1, &set, NULL, NULL, &tv);
+		DBG("select=%d\n", ret);
+		if (ret <= 0) {
+			free(buf);
+			return;
+		}
+
+		ret = read(ux->fd, buf, blen);
+		DBG("read=%d\n", ret);
+		free(buf);
+	}
+}
+
+unsigned int
+rs232_read(struct rs232_port_t *p, unsigned char *buf, unsigned int buf_len,
+	   unsigned int *read_len)
+{
+	int r;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p buf_len=%d\n", (void *)p, p->pt, buf_len);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	r = read(ux->fd, buf, buf_len);
+	if (r == -1) {
+		*read_len = 0;
+		DBG("errno: %d strerror: %s %s\n",
+		    errno, strerror(errno), "RS232_ERR_READ");
+
+		return RS232_ERR_READ;
+	}
+
+	*read_len = r;
+	DBG("read_len=%d hex='%s' ascii='%s'\n", r, rs232_hex_dump(buf, r),
+		rs232_ascii_dump(buf, r));
+
+	return RS232_ERR_NOERROR;
+}
+
+static int
+duration(struct timeval *t1, struct timeval *t2)
+{
+	return (t2->tv_sec-t1->tv_sec)*1000 + (t2->tv_usec - t1->tv_usec)/1000;
+}
+
+/* this function waits either for timeout or buf_len bytes,
+   whatever happens first and doesn't return earlier */
+unsigned int
+rs232_read_timeout_forced(struct rs232_port_t *p, unsigned char *buf,
+		   unsigned int buf_len, unsigned int *read_len,
+		   unsigned int timeout)
+{
+	int b;
+	int ret;
+	int reti;
+	fd_set set;
+	int r;
+	struct rs232_posix_t *ux = p->pt;
+	struct timeval tv;
+	struct timeval t1;
+	struct timeval t2;
+
+	DBG("p=%p p->pt=%p buf_len=%d timeout=%d\n", (void *)p, p->pt, buf_len,
+	    timeout);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	FD_ZERO(&set);
+	FD_SET(ux->fd, &set);
+	tv.tv_sec = 0;
+	tv.tv_usec = timeout * 1000;
+
+	*read_len = 0;
+	gettimeofday(&t1, NULL);
+
+	while (1) {
+		ret = select(ux->fd+1, &set, NULL, NULL, &tv);
+		gettimeofday(&t2, NULL);
+
+		if (ret == 0) {
+			DBG("%s\n", "select timeout");
+			break;
+		}
+
+		if (ret == -1) {
+			DBG("%s\n", "select error");
+			break;
+		}
+
+		if (duration(&t1, &t2) >= (int) timeout) {
+			DBG("%s\n", "timeout");
+			break;
+		}
+
+		reti = ioctl(ux->fd, FIONREAD, &b);
+		if (reti == -1) {
+			DBG("%s\n", "ioctl error");
+			break;
+		}
+
+		if ((unsigned int) b >= buf_len) {
+			DBG("fionread=%d\n", b);
+			break;
+		}
+	}
+
+	switch (ret) {
+	case 0:
+		DBG("%s\n", "RS232_ERR_TIMEOUT");
+		return RS232_ERR_TIMEOUT;
+	case 1:
+		r = read(ux->fd, buf, buf_len);
+		if (r == -1) {
+			DBG("errno: %d strerror: %s %s\n",
+			    errno, strerror(errno), "RS232_ERR_READ");
+
+			return RS232_ERR_READ;
+		}
+
+		DBG("read_len=%d hex='%s' ascii='%s'\n", r,
+			rs232_hex_dump(buf, r),
+			rs232_ascii_dump(buf, r));
+		*read_len = r;
+		break;
+	default:
+		DBG("%s\n", "RS232_ERR_SELECT");
+		return RS232_ERR_SELECT;
+	}
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_read_timeout(struct rs232_port_t *p, unsigned char *buf,
+		   unsigned int buf_len, unsigned int *read_len,
+		   unsigned int timeout)
+{
+	int ret;
+	fd_set set;
+	int r;
+	struct timeval tv;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p buf_len=%d timeout=%d\n", (void *)p, p->pt,
+		buf_len, timeout);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	FD_ZERO(&set);
+	FD_SET(ux->fd, &set);
+	tv.tv_sec = 0;
+	tv.tv_usec = timeout * 1000;
+	*read_len = 0;
+
+	ret = select(ux->fd+1, &set, NULL, NULL, &tv);
+	switch (ret) {
+	case 0:
+		DBG("%s\n", "RS232_ERR_TIMEOUT");
+		return RS232_ERR_TIMEOUT;
+	case 1:
+		r = read(ux->fd, buf, buf_len);
+		if (r == -1) {
+			DBG("errno: %d strerror: %s %s\n",
+			    errno, strerror(errno), "RS232_ERR_READ");
+			return RS232_ERR_READ;
+		}
+
+		DBG("read_len=%d hex='%s' ascii='%s'\n", r,
+			rs232_hex_dump(buf, r),
+			rs232_ascii_dump(buf, r));
+		*read_len = r;
+		break;
+	default:
+		DBG("%s\n", "RS232_ERR_SELECT");
+		return RS232_ERR_SELECT;
+	}
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_write(struct rs232_port_t *p, const unsigned char *buf, unsigned int buf_len,
+		unsigned int *write_len)
+{
+	int w;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p hex='%s' ascii='%s' buf_len=%d\n", 
+	    (void *)p, p->pt, rs232_hex_dump(buf, buf_len), 
+	    rs232_ascii_dump(buf, buf_len), buf_len);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	w = write(ux->fd, buf, buf_len);
+	if (w == -1) {
+		DBG("errno: %d strerror: %s %s\n",
+		    errno, strerror(errno), "RS232_ERR_WRITE");
+
+		*write_len = 0;
+		return RS232_ERR_WRITE;
+	}
+
+	*write_len = w;
+	DBG("write_len=%d hex='%s' ascii='%s'\n", w, rs232_hex_dump(buf, w),
+		rs232_ascii_dump(buf, w));
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_write_timeout(struct rs232_port_t *p, const unsigned char *buf,
+			unsigned int buf_len, unsigned int *write_len,
+			unsigned int timeout)
+{
+	int ret;
+	fd_set set;
+	int w;
+	struct rs232_posix_t *ux = p->pt;
+	struct timeval tv;
+
+	DBG("p=%p p->pt=%p timeout=%d\n", (void *)p, p->pt, timeout);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	FD_ZERO(&set);
+	FD_SET(ux->fd, &set);
+	tv.tv_sec = 0;
+	tv.tv_usec = timeout * 1000;
+	*write_len = 0;
+
+	ret = select(ux->fd+1, NULL, &set, NULL, &tv);
+	switch (ret) {
+	case 0:
+		DBG("%s\n", "RS232_ERR_TIMEOUT");
+		return RS232_ERR_TIMEOUT;
+	case 1:
+		w = write(ux->fd, buf, buf_len);
+		if (w == -1) {
+			DBG("errno: %d strerror: %s %s\n",
+			    errno, strerror(errno), "RS232_ERR_WRITE");
+
+			return RS232_ERR_WRITE;
+		}
+
+		*write_len = w;
+		DBG("write_len=%d hex='%s' ascii='%s'\n", w,
+			rs232_hex_dump(buf, w),
+			rs232_ascii_dump(buf, w));
+		break;
+	default:
+		DBG("%s\n", "RS232_ERR_SELECT");
+		return RS232_ERR_SELECT;
+	}
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_open(struct rs232_port_t *p)
+{
+	int flags;
+	struct termios term;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	ux->fd = open(p->dev, O_RDWR | O_NOCTTY | O_NDELAY);
+	if (ux->fd < 0) {
+		DBG("open() %d %s\n", errno, strerror(errno))
+		return RS232_ERR_OPEN;
+	}
+
+	/* 
+	 * On OSX (and maybe on more systems), we need to open() the port with
+	 * O_NDELAY, because otherwise it would block forever waiting for DCD
+	 * signal, so here we restore back to blocking operations.
+	 */
+	flags = fcntl(ux->fd, F_GETFL);
+	flags &= ~O_NDELAY;
+	fcntl(ux->fd, F_SETFL, flags);
+
+	if (tcflush(ux->fd, TCIOFLUSH) < 0) {
+		DBG("tcflush() %d %s\n", errno, strerror(errno))
+		return RS232_ERR_CONFIG;
+	}
+
+	GET_PORT_STATE(ux->fd, &term)
+	GET_PORT_STATE(ux->fd, &ux->oldterm)
+
+	term.c_cflag |= (CREAD | CLOCAL);
+	term.c_iflag = IGNPAR;
+	term.c_oflag = 0;
+	term.c_lflag = 0;
+	term.c_cc[VINTR]  = _POSIX_VDISABLE;
+	term.c_cc[VQUIT]  = _POSIX_VDISABLE;
+	term.c_cc[VSTART] = _POSIX_VDISABLE;
+	term.c_cc[VSTOP]  = _POSIX_VDISABLE;
+	term.c_cc[VSUSP]  = _POSIX_VDISABLE;
+	term.c_cc[VEOF]   = _POSIX_VDISABLE;
+	term.c_cc[VEOL]   = _POSIX_VDISABLE;
+	term.c_cc[VERASE] = _POSIX_VDISABLE;
+	term.c_cc[VKILL]  = _POSIX_VDISABLE;
+
+	SET_PORT_STATE(ux->fd, &term)
+
+	rs232_set_baud(p, p->baud);
+	rs232_set_data(p, p->data);
+	rs232_set_parity(p, p->parity);
+	rs232_set_stop(p, p->stop);
+	rs232_set_flow(p, p->flow);
+	p->status = RS232_PORT_OPEN;
+
+	return RS232_ERR_NOERROR;
+}
+
+void
+rs232_set_device(struct rs232_port_t *p, const char *device)
+{
+	DBG("p=%p old=%s new=%s\n", (void *)p, p->dev, device);
+	strncpy(p->dev, device, RS232_STRLEN_DEVICE);
+	return;
+}
+
+unsigned int
+rs232_set_baud(struct rs232_port_t *p, enum rs232_baud_e baud)
+{
+	struct termios term;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p baud=%d (%s bauds)\n",
+	    (void *)p, p->pt, baud, rs232_strbaud(baud));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(ux->fd, &term)
+
+	switch (baud) {
+	case RS232_BAUD_300:
+		cfsetispeed(&term, B300);
+		cfsetospeed(&term, B300);
+		break;
+	case RS232_BAUD_2400:
+		cfsetispeed(&term, B2400);
+		cfsetospeed(&term, B2400);
+		break;
+	case RS232_BAUD_4800:
+		cfsetispeed(&term, B4800);
+		cfsetospeed(&term, B4800);
+		break;
+	case RS232_BAUD_9600:
+		cfsetispeed(&term, B9600);
+		cfsetospeed(&term, B9600);
+		break;
+	case RS232_BAUD_19200:
+		cfsetispeed(&term, B19200);
+		cfsetospeed(&term, B19200);
+		break;
+	case RS232_BAUD_38400:
+		cfsetispeed(&term, B38400);
+		cfsetospeed(&term, B38400);
+		break;
+	case RS232_BAUD_57600:
+		cfsetispeed(&term, B57600);
+		cfsetospeed(&term, B57600);
+		break;
+	case RS232_BAUD_115200:
+		cfsetispeed(&term, B115200);
+		cfsetospeed(&term, B115200);
+		break;
+	case RS232_BAUD_460800:
+		cfsetispeed(&term, B460800);
+		cfsetospeed(&term, B460800);
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(ux->fd, &term)
+	p->baud = baud;
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_set_dtr(struct rs232_port_t *p, enum rs232_dtr_e state)
+{
+	int ret;
+	int set;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p dtr=%d (dtr control %s)\n",
+	    (void *)p, p->pt, state, rs232_strdtr(state));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	ret = ioctl(ux->fd, TIOCMGET, &set);
+	if (ret == -1) {
+		DBG("%s\n", "TIOCMGET RS232_ERR_IOCTL");
+		return RS232_ERR_IOCTL;
+	}
+
+	switch (state) {
+	case RS232_DTR_OFF:
+		set &= ~TIOCM_DTR;
+		break;
+	case RS232_DTR_ON:
+		set |= TIOCM_DTR;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	ret = ioctl(ux->fd, TIOCMSET, &set);
+	if (ret == -1) {
+		DBG("%s\n", "TIOCMSET RS232_ERR_IOCTL");
+		return RS232_ERR_IOCTL;
+	}
+	
+	p->dtr = state;
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_set_rts(struct rs232_port_t *p, enum rs232_rts_e state)
+{
+	int ret;
+	int set;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p rts=%d (rts control %s)\n",
+	    (void *)p, p->pt, state, rs232_strrts(state));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	ret = ioctl(ux->fd, TIOCMGET, &set);
+	if (ret == -1) {
+		DBG("%s\n", "TIOCMGET RS232_ERR_IOCTL");
+		return RS232_ERR_IOCTL;
+	}
+
+	switch (state) {
+	case RS232_RTS_OFF:
+		set &= ~TIOCM_RTS;
+		break;
+	case RS232_RTS_ON:
+		set |= TIOCM_RTS;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	ret = ioctl(ux->fd, TIOCMSET, &set);
+	if (ret == -1) {
+		DBG("%s\n", "TIOCMSET RS232_ERR_IOCTL");
+		return RS232_ERR_IOCTL;
+	}
+	
+	p->rts = state;
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_set_parity(struct rs232_port_t *p, enum rs232_parity_e parity)
+{
+	struct termios term;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p parity=%d (parity %s)\n",
+	    (void *)p, p->pt, parity, rs232_strparity(parity));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(ux->fd, &term)
+
+	switch (parity) {
+	case RS232_PARITY_NONE:
+		term.c_cflag &= ~PARENB;
+		break;
+	case RS232_PARITY_ODD:
+		term.c_cflag |= (PARENB | PARODD);
+		break;
+	case RS232_PARITY_EVEN:
+		term.c_cflag &= ~PARODD;
+		term.c_cflag |= PARENB;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(ux->fd, &term)
+	p->parity = parity;
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_set_stop(struct rs232_port_t *p, enum rs232_stop_e stop)
+{
+	struct termios term;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p stop=%d (%s stop bits)\n",
+	    (void *)p, p->pt, stop, rs232_strstop(stop));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(ux->fd, &term)
+	term.c_cflag &= ~CSTOPB;
+
+	switch (stop) {
+	case RS232_STOP_1:
+		break;
+	case RS232_STOP_2:
+		term.c_cflag |= CSTOPB;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(ux->fd, &term)
+	p->stop = stop;
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_set_data(struct rs232_port_t *p, enum rs232_data_e data)
+{
+	struct termios term;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p data=%d (%s data bits)\n",
+	    (void *)p, p->pt, data, rs232_strdata(data));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(ux->fd, &term)
+	term.c_cflag &= ~CSIZE;
+
+	switch (data) {
+	case RS232_DATA_5:
+		term.c_cflag |= CS5;
+		break;
+	case RS232_DATA_6:
+		term.c_cflag |= CS6;
+		break;
+	case RS232_DATA_7:
+		term.c_cflag |= CS7;
+		break;
+	case RS232_DATA_8:
+		term.c_cflag |= CS8;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(ux->fd, &term)
+	p->data = data;
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_set_flow(struct rs232_port_t *p, enum rs232_flow_e flow)
+{
+	struct termios term;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p flow=%d (flow control %s)\n",
+	    (void *)p, p->pt, flow, rs232_strflow(flow));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(ux->fd, &term)
+
+	switch (flow) {
+	case RS232_FLOW_OFF:
+		term.c_cflag &= ~CRTSCTS;
+		term.c_iflag &= ~(IXON | IXOFF | IXANY);
+		break;
+	case RS232_FLOW_HW:
+		term.c_cflag |= CRTSCTS;
+		term.c_iflag &= ~(IXON | IXOFF | IXANY);
+		break;
+	case RS232_FLOW_XON_XOFF:
+		term.c_cflag &= ~CRTSCTS;
+		term.c_iflag |= (IXON | IXOFF | IXANY);
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(ux->fd, &term)
+	p->flow = flow;
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_flush(struct rs232_port_t *p)
+{
+	int ret;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	ret = tcflush(ux->fd, TCIOFLUSH);
+	if (ret == -1)
+		return RS232_ERR_FLUSH;
+
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_close(struct rs232_port_t *p)
+{
+	int ret;
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	ret = close(ux->fd);
+	if (ret == -1)
+		return RS232_ERR_CLOSE;
+
+	p->status = RS232_PORT_CLOSED;
+	return RS232_ERR_NOERROR;
+}
+
+unsigned int
+rs232_fd(struct rs232_port_t *p)
+{
+	struct rs232_posix_t *ux = p->pt;
+
+	DBG("p=%p p->pt=%p ux->fd=%d\n", (void *)p, p->pt, ux->fd);
+
+	return (unsigned int) ux->fd;
+}

+ 53 - 0
ext/librs232/rs232_posix.h

@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <[email protected]>, Gaben Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __LIBRS232_POSIX_H__
+#define __LIBRS232_POSIX_H__
+
+#include <termios.h>
+
+#ifndef B460800
+#define B460800 460800
+#endif
+
+struct rs232_posix_t {
+	int fd;
+	struct termios oldterm;
+};
+
+#define GET_PORT_STATE(fd, state) \
+	if (tcgetattr(fd, state) < 0) { \
+		DBG("tcgetattr() %d %s\n", errno, strerror(errno)) \
+		return RS232_ERR_CONFIG; \
+	}
+
+#define SET_PORT_STATE(fd, state) \
+	if (tcsetattr(fd, TCSANOW, state) < 0) { \
+		DBG("tcsetattr() %d %s\n", errno, strerror(errno)) \
+		return RS232_ERR_CONFIG; \
+	} \
+
+#endif /* __LIBRS232_POSIX_H__ */

+ 750 - 0
ext/librs232/rs232_windows.c

@@ -0,0 +1,750 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <[email protected]>, Gaben Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef UNDER_CE
+#include <errno.h>
+#endif
+
+#include "librs232/rs232.h"
+
+static wchar_t *
+a2w(const char *astr)
+{
+	size_t len = 0;
+	wchar_t *ret = NULL;
+
+	if (astr == NULL)
+		return NULL;
+
+	len = strlen(astr);
+	if (len > 0) {
+		ret = (wchar_t*)malloc((len*2)+1 * sizeof(wchar_t*));
+		memset(ret, 0, (len*2));
+		MultiByteToWideChar(CP_ACP, 0, astr, -1, ret, (int)len);
+		ret[len] = '\0';
+	} else
+		ret = NULL;
+
+	return ret;
+}
+
+static char * last_error(void)
+{
+	unsigned long err = 0;
+	unsigned long ret = 0;
+	static char errbuf[MAX_PATH+1] = {0};
+	static char retbuf[MAX_PATH+1] = {0};
+
+	err = GetLastError();
+	ret = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, errbuf, MAX_PATH, NULL);
+	if (ret != 0) {
+		/* CRLF fun */
+		errbuf[ret-2] = 0;
+		snprintf(retbuf, MAX_PATH, "LastError: %s (%d)", errbuf, ret);
+	}
+	else
+		snprintf(retbuf, MAX_PATH, "LastError: %d (FormatMessageA failed)", ret);
+
+	return retbuf;
+}
+
+RS232_LIB struct rs232_port_t *
+rs232_init(void)
+{
+	struct rs232_port_t *p = NULL;
+	struct rs232_windows_t *wx = NULL;
+	p = (struct rs232_port_t *) malloc(sizeof(struct rs232_port_t));
+	if (p == NULL)
+		return NULL;
+
+	p->pt = (struct rs232_windows_t *) malloc(sizeof(struct rs232_windows_t));
+	if (p->pt == NULL)
+		return NULL;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	memset(p->dev, 0, RS232_STRLEN_DEVICE+1);
+	strncpy(p->dev, RS232_PORT_WIN32, RS232_STRLEN_DEVICE);
+
+	p->baud = RS232_BAUD_115200;
+	p->data = RS232_DATA_8;
+	p->parity = RS232_PARITY_NONE;
+	p->stop = RS232_STOP_1;
+	p->flow = RS232_FLOW_OFF;
+	p->status = RS232_PORT_CLOSED;
+	p->dtr = RS232_DTR_OFF;
+	p->rts = RS232_RTS_OFF;
+
+	wx = (struct rs232_windows_t *) p->pt;
+	wx->r_timeout = 500;
+	wx->w_timeout = 500;
+	wx->r_buffer = 1024;
+	wx->w_buffer = 1024;
+
+	return p;
+}
+
+static unsigned int
+port_buffers(struct rs232_port_t *p, unsigned int rb, unsigned int wb)
+{
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p rb=%d wb=%d\n", (void *)p, p->pt, rb, wb);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	if (!SetupComm(wx->fd, rb, wb)) {
+		DBG("SetupComm() %s\n", last_error());
+		return RS232_ERR_UNKNOWN;
+	}
+
+	wx->r_buffer = rb;
+	wx->w_buffer = wb;
+
+	return RS232_ERR_NOERROR;
+}
+
+static unsigned int
+port_timeout(struct rs232_port_t *p, unsigned int rt, unsigned int wt)
+{
+	struct rs232_windows_t *wx = p->pt;
+	COMMTIMEOUTS t;
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_TIMEOUTS(wx->fd, &t);
+
+	t.ReadIntervalTimeout = 0;
+	t.ReadTotalTimeoutMultiplier = 0;
+	t.ReadTotalTimeoutConstant = rt;
+	t.WriteTotalTimeoutMultiplier = 0;
+	t.WriteTotalTimeoutConstant = wt;
+
+	SET_PORT_TIMEOUTS(wx->fd, &t);
+
+	wx->w_timeout = wt;
+	wx->r_timeout = rt;
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB void
+rs232_end(struct rs232_port_t *p)
+{
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	if (!rs232_port_open(p)) {
+		free(p->pt);
+		free(p);
+		return;
+	}
+
+	rs232_flush(p);
+
+	if (!SetCommState(wx->fd, &wx->old_dcb)) {
+		DBG("SetCommState() %s\n", last_error());
+		return;
+	}
+
+	if (!SetCommTimeouts(wx->fd, &wx->old_tm)) {
+		DBG("SetCommTimeouts() %s\n", last_error());
+		return;
+	}
+
+	rs232_close(p);
+	free(p->pt);
+	free(p);
+}
+
+RS232_LIB unsigned int
+rs232_in_qeue(struct rs232_port_t *p, unsigned int *in_bytes)
+{
+	COMSTAT cs;
+	unsigned long errmask = 0;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	if (!ClearCommError(wx->fd, &errmask, &cs)) {
+		DBG("ClearCommError() %s\n", last_error());
+		return RS232_ERR_IOCTL;
+	}
+
+	*in_bytes = cs.cbInQue;
+	DBG("in_bytes=%d\n", cs.cbInQue);
+
+	return RS232_ERR_NOERROR;
+}
+
+/* some USB<->RS232 converters buffer a lot, so this function tries to discard
+   this buffer - useful mainly after rs232_open() */
+RS232_LIB void
+rs232_in_qeue_clear(struct rs232_port_t *p)
+{
+	/* TODO */
+	UNREFERENCED_PARAMETER(p);
+	DBG("%s\n", "sorry, not implemented yet");
+}
+
+RS232_LIB unsigned int
+rs232_read(struct rs232_port_t *p, unsigned char *buf, unsigned int buf_len,
+	   unsigned int *read_len)
+{
+	unsigned int r = 0;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p buf_len:%d\n", (void *)p, p->pt, buf_len);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	if (!ReadFile(wx->fd, buf, buf_len, &r, NULL)) {
+		*read_len = 0;
+		DBG("ReadFile() %s\n", last_error());
+		return RS232_ERR_READ;
+	}
+
+	*read_len = r;
+	DBG("read_len=%d hex='%s' ascii='%s'\n", r, rs232_hex_dump(buf, r),
+		rs232_ascii_dump(buf, r));
+
+	return RS232_ERR_NOERROR;
+}
+
+/* this function waits either for timeout or buf_len bytes,
+   whatever happens first and doesn't return earlier */
+RS232_LIB unsigned int
+rs232_read_timeout_forced(struct rs232_port_t *p, unsigned char *buf,
+		   unsigned int buf_len, unsigned int *read_len,
+		   unsigned int timeout)
+{
+	UNREFERENCED_PARAMETER(p);
+	UNREFERENCED_PARAMETER(buf);
+	UNREFERENCED_PARAMETER(timeout);
+	UNREFERENCED_PARAMETER(read_len);
+	UNREFERENCED_PARAMETER(buf_len);
+
+	/* TODO */
+	DBG("%s\n", "sorry, not implemented yet");
+	return RS232_ERR_UNKNOWN;
+}
+
+RS232_LIB unsigned int
+rs232_read_timeout(struct rs232_port_t *p, unsigned char *buf,
+		   unsigned int buf_len, unsigned int *read_len,
+		   unsigned int timeout)
+{
+	unsigned int r = 0;
+	struct rs232_windows_t *wx = p->pt;
+	unsigned int rt = wx->r_timeout;
+
+	DBG("p=%p p->pt=%p buf_len: %d timeout: %d\n", (void *)p, p->pt, buf_len, timeout);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	*read_len = 0;
+
+	if (port_timeout(p, timeout, wx->w_timeout))
+		return RS232_ERR_UNKNOWN;
+
+	if (!ReadFile(wx->fd, buf, buf_len, &r, NULL)) {
+		*read_len = 0;
+		DBG("ReadFile() %s\n", last_error());
+		return RS232_ERR_READ;
+	}
+
+	if (port_timeout(p, rt, wx->w_timeout))
+		return RS232_ERR_UNKNOWN;
+
+	*read_len = r;
+	DBG("read_len=%d hex='%s' ascii='%s'\n", r, rs232_hex_dump(buf, r),
+	    rs232_ascii_dump(buf, r));
+
+	/* TODO - This is lame, since we rely on the fact, that if we read 0 bytes,
+	 * that the read probably timeouted. So we should rather measure the reading
+	 * interval or rework it using overlapped I/O */
+	return *read_len == 0 ? RS232_ERR_TIMEOUT : RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_write(struct rs232_port_t *p, const unsigned char *buf, unsigned int buf_len,
+		unsigned int *write_len)
+{
+	unsigned int w = 0;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p buf_len:%d\n", (void *)p, p->pt, buf_len);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	if (!WriteFile(wx->fd, buf, buf_len, &w, NULL)) {
+		*write_len = 0;
+		DBG("WriteFile() %s\n", last_error());
+		return RS232_ERR_WRITE;
+	}
+
+	if (buf_len != w)
+		DBG("WriteFile() %s\n", last_error());
+
+	*write_len = w;
+	DBG("write_len=%d hex='%s' ascii='%s'\n", w, rs232_hex_dump(buf, w),
+	    rs232_ascii_dump(buf, w));
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_write_timeout(struct rs232_port_t *p, const unsigned char *buf,
+			unsigned int buf_len, unsigned int *write_len,
+			unsigned int timeout)
+{
+	unsigned int w = 0;
+	struct rs232_windows_t *wx = p->pt;
+	unsigned int wt = wx->w_timeout;
+
+	DBG("p=%p p->pt=%p buf_len:%d\n", (void *)p, p->pt, buf_len);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	if (port_timeout(p, wx->r_timeout, timeout))
+		return RS232_ERR_UNKNOWN;
+
+	if (!WriteFile(wx->fd, buf, buf_len, &w, NULL)) {
+		*write_len = 0;
+		DBG("WriteFile() %s\n", last_error());
+		return RS232_ERR_WRITE;
+	}
+
+	if (port_timeout(p, wx->r_timeout, wt))
+		return RS232_ERR_UNKNOWN;
+
+	*write_len = w;
+	DBG("write_len=%d hex='%s' ascii='%s'\n", w, rs232_hex_dump(buf, w),
+	    rs232_ascii_dump(buf, w));
+
+	return RS232_ERR_NOERROR;
+}
+
+static char *
+fix_device_name(char *device)
+{
+	char *s = device;
+	static char ret[RS232_STRLEN_DEVICE+1] = {0};
+
+	while (*s && !isdigit(*s))
+		s++;
+
+	if (s && (atoi(s) > 0)) {
+		/* meh, Windows CE is special and can't handle URN path, just COM1: format */
+#ifndef UNDER_CE
+		snprintf(ret, RS232_STRLEN_DEVICE, "\\\\.\\COM%s", s);
+#else
+		snprintf(ret, RS232_STRLEN_DEVICE, "COM%s:", s);
+#endif
+		return ret;
+	}
+
+	return device;
+}
+
+RS232_LIB unsigned int
+rs232_open(struct rs232_port_t *p)
+{
+	wchar_t *wname = a2w(fix_device_name(p->dev));
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p name='%s' fix='%s'\n",
+	    (void *)p, p->pt, p->dev, fix_device_name(p->dev));
+
+	if (wname == NULL)
+		return RS232_ERR_UNKNOWN;
+
+	wx->fd = CreateFile(wname, GENERIC_READ | GENERIC_WRITE,
+			    FILE_SHARE_READ | FILE_SHARE_WRITE,
+			    NULL, OPEN_EXISTING, 0, NULL);
+
+	if (wname)
+		free(wname);
+
+	if (wx->fd == INVALID_HANDLE_VALUE) {
+		DBG("CreateFile() %s\n", last_error());
+		return RS232_ERR_OPEN;
+	}
+
+	p->status = RS232_PORT_OPEN;
+	rs232_flush(p);
+
+	GET_PORT_STATE(wx->fd, &wx->old_dcb);
+	GET_PORT_TIMEOUTS(wx->fd, &wx->old_tm);
+
+	port_timeout(p, wx->r_timeout, wx->w_timeout);
+	port_buffers(p, wx->r_buffer, wx->w_buffer);
+
+	rs232_set_baud(p, p->baud);
+	rs232_set_data(p, p->data);
+	rs232_set_parity(p, p->parity);
+	rs232_set_stop(p, p->stop);
+	rs232_set_flow(p, p->flow);
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB void
+rs232_set_device(struct rs232_port_t *p, const char *device)
+{
+	DBG("p=%p old=%s new=%s\n", (void *)p, p->dev, device);
+	strncpy(p->dev, device, RS232_STRLEN_DEVICE);
+
+	return;
+}
+
+RS232_LIB unsigned int
+rs232_set_baud(struct rs232_port_t *p, enum rs232_baud_e baud)
+{
+	DCB pdcb;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p baud=%d (%s bauds)\n",
+	    (void *)p, p->pt, baud, rs232_strbaud(baud));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(wx->fd, &pdcb);
+
+	switch (baud) {
+	case RS232_BAUD_300:
+		pdcb.BaudRate = CBR_300;
+		break;
+	case RS232_BAUD_2400:
+		pdcb.BaudRate = CBR_2400;
+		break;
+	case RS232_BAUD_4800:
+		pdcb.BaudRate = CBR_4800;
+		break;
+	case RS232_BAUD_9600:
+		pdcb.BaudRate = CBR_9600;
+		break;
+	case RS232_BAUD_19200:
+		pdcb.BaudRate = CBR_19200;
+		break;
+	case RS232_BAUD_38400:
+		pdcb.BaudRate = CBR_38400;
+		break;
+	case RS232_BAUD_57600:
+		pdcb.BaudRate = CBR_57600;
+		break;
+	case RS232_BAUD_115200:
+		pdcb.BaudRate = CBR_115200;
+		break;
+	case RS232_BAUD_460800:
+		pdcb.BaudRate = CBR_460800;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(wx->fd, &pdcb);
+	p->baud = baud;
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_set_dtr(struct rs232_port_t *p, enum rs232_dtr_e state)
+{
+	DCB pdcb;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p dtr=%d (dtr control %s)\n",
+	    (void *)p, p->pt, state, rs232_strdtr(state));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(wx->fd, &pdcb);
+
+	switch (state) {
+	case RS232_DTR_OFF:
+		pdcb.fDtrControl = DTR_CONTROL_DISABLE;
+		break;
+	case RS232_DTR_ON:
+		pdcb.fDtrControl = DTR_CONTROL_ENABLE;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(wx->fd, &pdcb);
+	p->dtr = state;
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_set_rts(struct rs232_port_t *p, enum rs232_rts_e state)
+{
+	DCB pdcb;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p rts=%d (rts control %s)\n",
+	    (void *)p, p->pt, state, rs232_strrts(state));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(wx->fd, &pdcb);
+
+	switch (state) {
+	case RS232_DTR_OFF:
+		pdcb.fRtsControl = RTS_CONTROL_DISABLE;
+		break;
+	case RS232_DTR_ON:
+		pdcb.fRtsControl = RTS_CONTROL_ENABLE;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(wx->fd, &pdcb);
+	p->rts = state;
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_set_parity(struct rs232_port_t *p, enum rs232_parity_e parity)
+{
+	DCB pdcb;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p parity=%d (parity %s)\n",
+	    (void *)p, p->pt, parity, rs232_strparity(parity));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(wx->fd, &pdcb);
+
+	switch (parity) {
+	case RS232_PARITY_NONE:
+		pdcb.Parity = NOPARITY;
+		break;
+	case RS232_PARITY_ODD:
+		pdcb.Parity = ODDPARITY;
+		break;
+	case RS232_PARITY_EVEN:
+		pdcb.Parity = EVENPARITY;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(wx->fd, &pdcb);
+	p->parity = parity;
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_set_stop(struct rs232_port_t *p, enum rs232_stop_e stop)
+{
+	DCB pdcb;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p stop=%d (%s stop bits)\n",
+	    (void *)p, p->pt, stop, rs232_strstop(stop));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(wx->fd, &pdcb);
+
+	switch (stop) {
+	case RS232_STOP_1:
+		pdcb.StopBits = ONESTOPBIT;
+		break;
+	case RS232_STOP_2:
+		pdcb.StopBits = TWOSTOPBITS;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(wx->fd, &pdcb);
+	p->stop = stop;
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_set_data(struct rs232_port_t *p, enum rs232_data_e data)
+{
+	DCB pdcb;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p data=%d (%s data bits)\n",
+	    (void *)p, p->pt, data, rs232_strdata(data));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(wx->fd, &pdcb);
+
+	switch (data) {
+	case RS232_DATA_5:
+		pdcb.ByteSize = 5;
+		break;
+	case RS232_DATA_6:
+		pdcb.ByteSize = 6;
+		break;
+	case RS232_DATA_7:
+		pdcb.ByteSize = 7;
+		break;
+	case RS232_DATA_8:
+		pdcb.ByteSize = 8;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(wx->fd, &pdcb);
+	p->data = data;
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_set_flow(struct rs232_port_t *p, enum rs232_flow_e flow)
+{
+	DCB pdcb;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p flow=%d (flow control %s)\n",
+	    (void *)p, p->pt, flow, rs232_strflow(flow));
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	GET_PORT_STATE(wx->fd, &pdcb);
+
+	switch (flow) {
+	case RS232_FLOW_OFF:
+		pdcb.fOutxCtsFlow = FALSE;
+		pdcb.fRtsControl = RTS_CONTROL_DISABLE;
+		pdcb.fInX = FALSE;
+		pdcb.fOutX = FALSE;
+		break;
+	case RS232_FLOW_HW:
+		pdcb.fOutxCtsFlow = TRUE;
+		pdcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
+		pdcb.fInX = FALSE;
+		pdcb.fOutX = FALSE;
+		break;
+	case RS232_FLOW_XON_XOFF:
+		pdcb.fOutxCtsFlow = FALSE;
+		pdcb.fRtsControl = RTS_CONTROL_DISABLE;
+		pdcb.fInX = TRUE;
+		pdcb.fOutX = TRUE;
+		break;
+	default:
+		return RS232_ERR_UNKNOWN;
+	}
+
+	SET_PORT_STATE(wx->fd, &pdcb);
+
+	p->flow = flow;
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_flush(struct rs232_port_t *p)
+{
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	if (!FlushFileBuffers(wx->fd)) {
+		DBG("FlushFileBuffers() %s\n", last_error());
+		return RS232_ERR_FLUSH;
+	}
+
+	if (!PurgeComm(wx->fd, PURGE_TXABORT | PURGE_RXABORT |
+		       PURGE_TXCLEAR | PURGE_RXCLEAR)) {
+		DBG("PurgeComm() %s\n", last_error());
+		return RS232_ERR_FLUSH;
+	}
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_close(struct rs232_port_t *p)
+{
+	int ret;
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	ret = CloseHandle(wx->fd);
+	if (ret == 0) {
+		DBG("PurgeComm() %s\n", last_error());
+		return RS232_ERR_CLOSE;
+	}
+
+	return RS232_ERR_NOERROR;
+}
+
+RS232_LIB unsigned int
+rs232_fd(struct rs232_port_t *p)
+{
+	struct rs232_windows_t *wx = p->pt;
+
+	DBG("p=%p p->pt=%p wx->fd=%d\n", (void *)p, p->pt, wx->fd);
+
+	return (unsigned int) wx->fd;
+}

+ 72 - 0
ext/librs232/rs232_windows.h

@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <[email protected]>, Gaben Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ * 
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __LIBRS232_WINDOWS_H__
+#define __LIBRS232_WINDOWS_H__
+
+#include <windows.h>
+
+#ifndef CBR_460800
+#define CBR_460800 460800
+#endif
+
+struct rs232_windows_t {
+	HANDLE fd;
+	COMMTIMEOUTS old_tm;
+	DCB old_dcb;
+	unsigned int r_timeout;
+	unsigned int w_timeout;
+	unsigned int r_buffer;
+	unsigned int w_buffer;
+};
+
+#define GET_PORT_TIMEOUTS(fd, t) \
+	memset(t, 0, sizeof(COMMTIMEOUTS)); \
+	if (!GetCommTimeouts(fd, t)) { \
+		DBG("GetCommTimeouts() %s\n", last_error()); \
+		return RS232_ERR_UNKNOWN; \
+	}
+
+#define SET_PORT_TIMEOUTS(fd, t) \
+	if (!SetCommTimeouts(fd, t)) { \
+		DBG("SetCommTimeouts() %s\n", last_error()); \
+		return RS232_ERR_UNKNOWN; \
+	}
+
+#define GET_PORT_STATE(fd, pdcb) \
+	memset(pdcb, 0, sizeof(DCB)); \
+	if (!GetCommState(fd, pdcb)) { \
+		DBG("GetCommState() %s\n", last_error()); \
+		return RS232_ERR_UNKNOWN; \
+	}
+
+#define SET_PORT_STATE(fd, pdcb) \
+	if (!SetCommState(fd, pdcb)) { \
+		DBG("SetCommState() %s\n", last_error()); \
+		return RS232_ERR_UNKNOWN; \
+	}
+
+#endif /* __LIBRS232_WINDOWS_H__ */

+ 330 - 0
ext/sq_rs232.c

@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <[email protected]>, Gaben Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Ported to Squirrel/SquiLu by Domingo Alvarez Duarte
+ */
+
+#include "squirrel.h"
+#include <string.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+SQ_OPT_STRING_STRLEN();
+
+#include "librs232/rs232.h"
+#include "librs232/rs232.c"
+
+#ifdef WIN32
+#include "librs232/rs232_windows.h"
+#include "librs232/rs232_windows.c"
+#else
+#include "librs232/rs232_posix.h"
+#include "librs232/rs232_posix.c"
+#endif
+
+#define MODULE_TIMESTAMP __DATE__ " " __TIME__
+#define MODULE_VERSION "1.0.3"
+#define MODULE_BUILD "$Id: luars232.c 15 2011-02-23 09:02:20Z sp $"
+#define MODULE_COPYRIGHT "Copyright (c) 2011 Petr Stetiar <[email protected]>, Gaben Ltd."
+
+static const SQChar SQRS232_TAG[] = _SC("SqRs232");
+
+static SQRESULT get_rs232_instance(HSQUIRRELVM v, SQInteger idx, struct rs232_port_t **self){
+    SQRESULT _rc_;
+	if((_rc_ = sq_getinstanceup(v,idx,(SQUserPointer*)self,(void*)SQRS232_TAG)) < 0) return _rc_;
+	if(!*self) return sq_throwerror(v, _SC("rs232 is closed"));
+	return _rc_;
+}
+
+#define GET_sq_rs232_INSTANCE(v, idx) struct rs232_port_t *self=NULL; \
+	if((_rc_ = get_rs232_instance(v,idx,&self)) < 0) return _rc_;
+
+static SQRESULT sq_rs232_release_hook(SQUserPointer p, SQInteger size, HSQUIRRELVM v) {
+    struct rs232_port_t *self = (struct rs232_port_t *)p;
+    if(self) return rs232_close(self);
+    return 0;
+}
+
+/*
+** Creates a new SqRs232.
+*/
+static SQRESULT sq_rs232_constructor (HSQUIRRELVM v) {
+	sq_setinstanceup(v, 1, 0);
+	sq_setreleasehook(v, 1, sq_rs232_release_hook);
+  return 1;
+}
+
+static SQRESULT sq_rs232_open(HSQUIRRELVM v){
+    SQ_FUNC_VARS_NO_TOP(v);
+    struct rs232_port_t *self=NULL;
+    if((_rc_ = sq_getinstanceup(v,1,(SQUserPointer*)&self,(void*)SQRS232_TAG)) < 0) return _rc_;
+	SQ_GET_STRING(v, 2, device);
+	if(self != NULL) sq_throwerror(v, _SC("rs232 already opened"));
+	self = rs232_init();
+	if(self == NULL) sq_throwerror(v, rs232_strerror(RS232_ERR_CONFIG));
+	rs232_set_device(self, (char*)device);
+	int ret = rs232_open(self);
+    if (ret > RS232_ERR_NOERROR) {
+		free(self->pt);
+		free(self);
+		return sq_throwerror(v, rs232_strerror(ret));
+	}
+    sq_setinstanceup(v, 1, self);
+	return 0;
+}
+
+static SQRESULT sq_rs232_close(HSQUIRRELVM v){
+    SQ_FUNC_VARS_NO_TOP(v);
+	GET_sq_rs232_INSTANCE(v, 1);
+	if(self == NULL) return sq_throwerror(v, _SC("rs232 port already closed"));
+	_rc_ = sq_rs232_release_hook(self, 0, v);
+	sq_setinstanceup(v, 1, 0); //next calls will fail with "rs232 port already closed"
+	sq_pushinteger(v, _rc_);
+	return 1;
+}
+
+static SQRESULT sq_rs232_read(HSQUIRRELVM v){
+    SQ_FUNC_VARS(v);
+	GET_sq_rs232_INSTANCE(v, 1);
+	if(self == NULL) return sq_throwerror(v, _SC("rs232 port already closed"));
+	SQ_GET_INTEGER(v, 2, len);
+	unsigned int bytes_read = 0;
+	SQChar *data = NULL;
+
+	switch (_top_) {
+	case 2:{
+            data = sq_getscratchpad(v, len);
+            _rc_ = rs232_read(self, (unsigned char *)data, len, &bytes_read);
+        }
+		break;
+	case 3:
+	case 4:{
+            SQ_GET_INTEGER(v, 3, timeout);
+            SQ_OPT_BOOL(v, 4, forced, SQFalse);
+            data = sq_getscratchpad(v, len);
+            if(forced) _rc_ = rs232_read_timeout_forced(self, (unsigned char *)data, len, &bytes_read, timeout);
+            else _rc_ = rs232_read_timeout(self, (unsigned char *)data, len, &bytes_read, timeout);
+        }
+		break;
+	default:
+		return sq_throwerror(v, _SC("wrong number of parameters %d"), _top_-1);
+	}
+
+	DBG("rc=%d hex='%s' bytes_read=%d\n",
+	    _rc_, rs232_hex_dump(data, bytes_read), bytes_read);
+
+    if(bytes_read > 0) sq_pushstring(v, data, bytes_read);
+    else sq_pushinteger(v, _rc_);
+	return 1;
+}
+
+static SQRESULT sq_rs232_write(HSQUIRRELVM v){
+    SQ_FUNC_VARS(v);
+	GET_sq_rs232_INSTANCE(v, 1);
+	if(self == NULL) return sq_throwerror(v, _SC("rs232 port already closed"));
+    SQ_GET_STRING(v, 2, data);
+	unsigned int wlen = 0;
+
+	switch (_top_) {
+	case 2:{
+            _rc_ = rs232_write(self, (unsigned char*) data, (unsigned int) data_size, &wlen);
+        }
+		break;
+	case 3:{
+            SQ_GET_INTEGER(v, 3, timeout);
+            _rc_ = rs232_write_timeout(self, (unsigned char*) data, (unsigned int) data_size, &wlen, timeout);
+        }
+		break;
+	default:
+		return sq_throwerror(v, _SC("wrong number of parameters %d"), _top_-1);
+	}
+
+    if(_rc_ > RS232_ERR_NOERROR) return sq_throwerror(v, rs232_strerror(_rc_));
+    sq_pushinteger(v, wlen);
+	return 1;
+}
+
+static SQRESULT sq_rs232_flush(HSQUIRRELVM v){
+    SQ_FUNC_VARS_NO_TOP(v);
+	GET_sq_rs232_INSTANCE(v, 1);
+	if(self == NULL) return sq_throwerror(v, _SC("rs232 port already closed"));
+	sq_pushinteger(v, rs232_flush(self));
+	return 1;
+}
+
+static SQRESULT sq_rs232_device(HSQUIRRELVM v){
+    SQ_FUNC_VARS_NO_TOP(v);
+	GET_sq_rs232_INSTANCE(v, 1);
+	if(self == NULL) return sq_throwerror(v, _SC("rs232 port already closed"));
+	const char *ret = rs232_get_device(self);
+	if (ret == NULL) sq_pushnull(v);
+	else sq_pushstring(v, ret, -1);
+	return 1;
+}
+
+static SQRESULT sq_rs232_fd(HSQUIRRELVM v){
+    SQ_FUNC_VARS_NO_TOP(v);
+	GET_sq_rs232_INSTANCE(v, 1);
+	if(self == NULL) return sq_throwerror(v, _SC("rs232 port already closed"));
+	sq_pushinteger(v, rs232_fd(self));
+	return 1;
+}
+
+static SQRESULT sq_rs232_strerror(HSQUIRRELVM v){
+    SQ_FUNC_VARS_NO_TOP(v);
+    SQ_GET_INTEGER(v, 2, error_code);
+    sq_pushstring(v, rs232_strerror(error_code), -1);
+	return 1;
+}
+
+#define FN_GET_SET_PORT(type, sufix) \
+	static SQRESULT sq_rs232_##type##sufix(HSQUIRRELVM v) \
+	{ \
+        SQ_FUNC_VARS(v);\
+        GET_sq_rs232_INSTANCE(v, 1);\
+        if(_top_ > 1) {\
+            SQ_GET_INTEGER(v, 2, iparam);\
+            sq_pushinteger(v, rs232_set_##type(self, (unsigned int)iparam));\
+        }\
+        else sq_pushinteger(v, rs232_get_##type(self));\
+        return 1;\
+	}
+
+#define FN_GET_PORT_STRING(type, sufix) \
+	static SQRESULT sq_rs232_##type##sufix##_tostring(HSQUIRRELVM v) \
+	{ \
+        SQ_FUNC_VARS(v);\
+        GET_sq_rs232_INSTANCE(v, 1);\
+        SQ_OPT_INTEGER(v, 2, iparam, -1);\
+		const char *ret = rs232_str##type(iparam == -1 ? rs232_get_##type(self) : (unsigned int) iparam); \
+		if (ret == NULL) sq_pushnull(v); \
+		else sq_pushstring(v, ret, -1); \
+		return 1; \
+	}
+
+#define FN_GET_SET_STR_PORT(type, sufix) FN_GET_SET_PORT(type, sufix); FN_GET_PORT_STRING(type, sufix)
+
+FN_GET_SET_STR_PORT(baud, _rate);
+FN_GET_SET_STR_PORT(data, _bits);
+FN_GET_SET_STR_PORT(stop, _bits);
+FN_GET_SET_STR_PORT(parity,);
+FN_GET_SET_STR_PORT(flow, _control);
+FN_GET_SET_STR_PORT(dtr,);
+FN_GET_SET_STR_PORT(rts,);
+
+
+#define _DECL_FUNC(name,nparams,tycheck) {_SC(#name), sq_rs232_##name,nparams,tycheck}
+static SQRegFunction rs232_methods[] =
+{
+    _DECL_FUNC(constructor,1,_SC("x")),
+    _DECL_FUNC(open,2,_SC("xs")),
+    _DECL_FUNC(close,1,_SC("x")),
+    _DECL_FUNC(read,-2,_SC("xiib")),
+    _DECL_FUNC(write,-2,_SC("xsi")),
+    _DECL_FUNC(flush,1,_SC("x")),
+    _DECL_FUNC(device,1,_SC("x")),
+    _DECL_FUNC(fd,1,_SC("x")),
+    _DECL_FUNC(strerror,2,_SC(".i")),
+	/* baud */
+    _DECL_FUNC(baud_rate,-1,_SC("xi")),
+    _DECL_FUNC(baud_rate_tostring,2,_SC(".i")),
+	/* data */
+    _DECL_FUNC(data_bits,-1,_SC("xi")),
+    _DECL_FUNC(data_bits_tostring,2,_SC(".i")),
+	/* stop */
+    _DECL_FUNC(stop_bits,-1,_SC("xi")),
+    _DECL_FUNC(stop_bits_tostring,2,_SC(".i")),
+	/* parity */
+    _DECL_FUNC(parity,-1,_SC("xi")),
+    _DECL_FUNC(parity_tostring,2,_SC(".i")),
+	/* flow */
+    _DECL_FUNC(flow_control,-1,_SC("xi")),
+    _DECL_FUNC(flow_control_tostring,2,_SC(".i")),
+	/* dtr */
+    _DECL_FUNC(dtr,-1,_SC("xi")),
+    _DECL_FUNC(dtr_tostring,2,_SC(".i")),
+	/* rts */
+    _DECL_FUNC(rts,-1,_SC("xi")),
+    _DECL_FUNC(rts_tostring,2,_SC(".i")),
+    {0,0}
+};
+
+#define INT_CONST(v,num) 	sq_pushstring(v,_SC(#num),-1);sq_pushinteger(v,num);sq_newslot(v,-3,SQTrue);
+
+SQRESULT sqext_register_rs232(HSQUIRRELVM v)
+{
+    sq_pushstring(v,SQRS232_TAG,-1);
+    sq_newclass(v,SQFalse);
+    sq_settypetag(v,-1,(void*)SQRS232_TAG);
+    sq_insert_reg_funcs(v, rs232_methods);
+
+	INT_CONST(v, RS232_BAUD_300 );
+	INT_CONST(v, RS232_BAUD_2400 );
+	INT_CONST(v, RS232_BAUD_4800 );
+	INT_CONST(v, RS232_BAUD_9600 );
+	INT_CONST(v, RS232_BAUD_19200 );
+	INT_CONST(v, RS232_BAUD_38400 );
+	INT_CONST(v, RS232_BAUD_57600 );
+	INT_CONST(v, RS232_BAUD_115200 );
+	INT_CONST(v, RS232_BAUD_460800 );
+	/* databits */
+	INT_CONST(v, RS232_DATA_5 );
+	INT_CONST(v, RS232_DATA_6 );
+	INT_CONST(v, RS232_DATA_7 );
+	INT_CONST(v, RS232_DATA_8 );
+	/* stop bits */
+	INT_CONST(v, RS232_STOP_1 );
+	INT_CONST(v, RS232_STOP_2 );
+	/* parity */
+	INT_CONST(v, RS232_PARITY_NONE );
+	INT_CONST(v, RS232_PARITY_ODD );
+	INT_CONST(v, RS232_PARITY_EVEN );
+	/* flow */
+	INT_CONST(v, RS232_FLOW_OFF );
+	INT_CONST(v, RS232_FLOW_HW );
+	INT_CONST(v, RS232_FLOW_XON_XOFF );
+	/* DTR and RTS */
+	INT_CONST(v, RS232_DTR_ON );
+	INT_CONST(v, RS232_DTR_OFF );
+	INT_CONST(v, RS232_RTS_ON );
+	INT_CONST(v, RS232_RTS_OFF );
+	/* errors */
+	INT_CONST(v, RS232_ERR_NOERROR );
+	INT_CONST(v, RS232_ERR_UNKNOWN );
+	INT_CONST(v, RS232_ERR_OPEN );
+	INT_CONST(v, RS232_ERR_CLOSE );
+	INT_CONST(v, RS232_ERR_FLUSH );
+	INT_CONST(v, RS232_ERR_CONFIG );
+	INT_CONST(v, RS232_ERR_READ );
+	INT_CONST(v, RS232_ERR_WRITE );
+	INT_CONST(v, RS232_ERR_SELECT );
+	INT_CONST(v, RS232_ERR_TIMEOUT );
+	INT_CONST(v, RS232_ERR_IOCTL );
+	INT_CONST(v, RS232_ERR_PORT_CLOSED );
+
+    sq_newslot(v,-3,SQTrue);
+    return 1;
+}
+