Jelajahi Sumber

add: 为bsp/win32添加rs232库,马上就能操作COM口进行刷机了

Wendal Chen 5 tahun lalu
induk
melakukan
2efc6d695e

+ 9 - 1
bsp/win32/CMakeLists.txt

@@ -38,11 +38,19 @@ add_library(luat ${TOPROOT}/luat/modules/luat_main.c
             )
 
 #-----------------------
+# LuaFileSystem --> lfs
 aux_source_directory(./rock/LuaFileSystem ROCK_LFS_SRCS)
 add_library(rock_lfs ${ROCK_LFS_SRCS})
 #-----------------------
 
+#------------------------
+# librs232 -> rs232
+include_directories(./rock/librs232/include)
+aux_source_directory(./rock/librs232/src ROCK_LIB_RS232_SRCS)
+add_library(rock_librs232 ${ROCK_LIB_RS232_SRCS} ./rock/librs232/bindings/lua/luars232.c)
+#------------------------
+
 # 指定生成目标
 add_executable(luatos src/main_win32.c)
 target_link_libraries(luat freertos10 winmm)
-target_link_libraries(luatos freertos10 lua luatos_msys luat winmm cjson rock_lfs)
+target_link_libraries(luatos freertos10 lua luatos_msys luat winmm cjson rock_lfs rock_librs232)

+ 424 - 0
bsp/win32/lib/rs232.lua

@@ -0,0 +1,424 @@
+------------------------------------------------------------------
+--
+--  Author: Alexey Melnichuk <alexeymelnichuck@gmail.com>
+--
+--  Copyright (C) 2015-2016 Alexey Melnichuk <alexeymelnichuck@gmail.com>
+--
+--  This file is part of lua-rs232 library.
+--
+-- 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.
+------------------------------------------------------------------
+
+local rs232 = require "rs232.core"
+
+local function class(base)
+  local t = base and setmetatable({}, base) or {}
+  t.__index = t
+  t.__class = t
+  t.__base  = base
+
+  function t.new(...)
+    local o = setmetatable({}, t)
+    if o.__init then
+      if t == ... then -- we call as Class:new()
+        return o:__init(select(2, ...))
+      else             -- we call as Class.new()
+        return o:__init(...)
+      end
+    end
+    return o
+  end
+
+  return t
+end
+
+local function select_by_prefix(pfx, p)
+  local names  = {}
+  local values = {}
+  p = p or ''
+
+  for k, v in pairs(rs232) do
+    if type(k) == 'string' and type(v) == 'number' then
+      if k:sub(1, #pfx) == pfx then
+        local name = p .. k:sub(#pfx+1)
+        assert(not names[v])
+        names[v]     = name
+        values[name] = v
+      end
+    end
+  end
+
+  return values, names
+end
+
+local ERROR, ERROR_NAMES = select_by_prefix'RS232_ERR_'
+
+local BAUD_NAMES, BAUD do
+local tmp = select_by_prefix'RS232_BAUD_'
+BAUD_NAMES, BAUD = {}, {}
+
+for k, v in pairs(tmp) do
+  BAUD_NAMES[ '_'    .. k ] = v
+  BAUD_NAMES['BAUD_' .. k ] = v
+  BAUD[v] =  'BAUD_' .. k
+end
+
+end
+
+local DATA_NAMES, DATA do
+
+local tmp = select_by_prefix'RS232_DATA_'
+DATA_NAMES, DATA = {}, {}
+
+for k, v in pairs(tmp) do
+  DATA_NAMES[ '_' .. k    ] = v
+  DATA_NAMES['DATA_' .. k ] = v
+  DATA[v] =  'DATA_' .. k
+end
+
+end
+
+local DTR_NAMES, DTR = select_by_prefix'RS232_DTR_'
+
+local RTS_NAMES, RTS = select_by_prefix'RS232_RTS_'
+
+local FLOW_NAMES, FLOW = select_by_prefix'RS232_FLOW_'
+
+local PARITY_NAMES, PARITY = select_by_prefix'RS232_PARITY_'
+
+local STOP_NAMES, STOP do
+
+local tmp = select_by_prefix'RS232_STOP_'
+STOP_NAMES, STOP = {}, {}
+
+for k, v in pairs(tmp) do
+  STOP_NAMES[ '_' .. k  ] = v
+  STOP_NAMES['STOP_' .. k ] = v
+  STOP[v] =  'STOP_' .. k
+end
+
+end
+
+local RS232Error = class() do
+
+function RS232Error:__init(no, ext)
+  self._no   = no
+  self._name = assert(ERROR_NAMES[no])
+  self._ext  = ext
+  return self
+end
+
+function RS232Error:cat()  return 'RS232'     end
+
+function RS232Error:no()   return self._no    end
+
+function RS232Error:name() return self._name end
+
+function RS232Error:msg()  return rs232.error_tostring(self._no) end
+
+function RS232Error:ext()  return self._ext   end
+
+function RS232Error:__eq(rhs)
+  return self._no == rhs._no
+end
+
+function RS232Error:__tostring()
+  local err = string.format("[%s][%s] %s (%d)",
+    self:cat(), self:name(), self:msg(), self:no()
+  )
+  if self:ext() then
+    err = string.format("%s - %s", err, self:ext())
+  end
+  return err
+end
+
+end
+
+local ERRORS = {
+  [rs232.RS232_ERR_TIMEOUT] = RS232Error.new(rs232.RS232_ERR_TIMEOUT);
+}
+
+local function Error(e)
+  return ERRORS[e] or RS232Error.new(e)
+end
+
+local function F(e, ...)
+  if e ~= rs232.RS232_ERR_NOERROR then
+    return nil, Error(e)
+  end
+
+  return true, ...
+end
+
+local Port = class() do
+
+function Port:__init(name, opt)
+  self._name = assert(name)
+  self._opt = {}
+  for k,v in pairs(opt or {})do
+    self._opt[k] = v
+  end
+
+  return self
+end
+
+function Port:open()
+  local ok, ret = F(rs232.open(self._name))
+  if not ok then return nil, ret end
+  self._p = ret
+
+  ok, ret = self:set(self._opt)
+  if not ok then
+    self._p:close()
+    return nil, err
+  end
+
+  return self, tostring(self._p)
+end
+
+function Port:close(...)
+  if not self._p then return end
+
+  self._p:close()
+  self._p = nil
+  return 
+end
+
+function Port:set(port_opt)
+  local p = self._p
+
+  local ok, err
+
+  if port_opt.baud ~= nil then
+    ok, err = self:set_baud_rate(port_opt.baud)
+    if not ok then return nil, err end
+  end
+
+  if port_opt.baud_rate ~= nil then
+    ok, err = self:set_baud_rate(port_opt.baud_rate)
+    if not ok then return nil, err end
+  end
+
+  if port_opt.data_bits ~= nil then
+    ok, err = self:set_data_bits(port_opt.data_bits)
+    if not ok then return nil, err end
+  end
+
+  if port_opt.parity ~= nil then
+    ok, err = self:set_parity(port_opt.parity)
+    if not ok then return nil, err end
+  end
+
+  if port_opt.stop_bits ~= nil then
+    ok, err = self:set_stop_bits(port_opt.stop_bits)
+    if not ok then return nil, err end
+  end
+
+  if port_opt.flow_control ~= nil then
+    ok, err = self:set_flow_control(port_opt.flow_control)
+    if not ok then return nil, err end
+  end
+
+  if port_opt.rts ~= nil then
+    ok, err = self:set_rts(port_opt.rts)
+    if not ok then return nil, err end
+  end
+
+  if port_opt.dtr ~= nil then
+    ok, err = self:set_dtr(port_opt.dtr)
+    if not ok then return nil, err end
+  end
+
+  return self
+end
+
+function Port:read(...)
+  local e, data, len = self._p:read(...)
+
+  if e ~= rs232.RS232_ERR_NOERROR then
+    if e == rs232.RS232_ERR_TIMEOUT then
+      return data or ''
+    end
+    return data, Error(e)
+  end
+
+  return data
+end
+
+function Port:write(...)
+  local ok, len = F(self._p:write(...))
+  if not ok then return nil, len end
+  return len
+end
+
+function Port:flush(...)
+  local ok, err = F(self._p:flush(...))
+  if not ok then return nil, err end
+  return self
+end
+
+function Port:in_queue_clear(...)
+  local ok, err = F(self._p:in_queue_clear(...))
+  if not ok then return nil, err end
+  return self
+end
+
+function Port:in_queue(...)
+  local ok, len = F(self._p:in_queue(...))
+  if not ok then return nil, len end
+  return len
+end
+
+function Port:device(...)
+  local ok, name = F(self._p:in_queue(...))
+  if not ok then return nil, name end
+  return name
+end
+
+function Port:fd(...)
+  local ok, fd = F(self._p:fd(...))
+  if not ok then return nil, fd end
+  return fd
+end
+
+local function check_val(value, NAMES, VALUES)
+  local v
+  if type(value) == 'string' then
+    v = assert( NAMES[value:upper()], 'Unsupported value:' .. tostring(value) )
+  else
+    v = value
+    assert( VALUES[value], 'Unsupported value:' .. tostring(value) )
+  end
+  return v
+end
+
+function Port:set_baud_rate(value)
+  local v = check_val(value, BAUD_NAMES, BAUD)
+
+  local ok, err = F(self._p:set_baud_rate(v))
+  if not ok then return nil, err end
+
+  return self
+end
+
+function Port:baud_rate()
+  local val = self._p:baud_rate()
+  return val, BAUD[val]
+end
+
+function Port:set_data_bits(value)
+  local v = check_val(value, DATA_NAMES, DATA)
+
+  local ok, err = F(self._p:set_data_bits(v))
+  if not ok then return nil, err end
+
+  return self
+end
+
+function Port:data_bits()
+  local val = self._p:data_bits()
+  return val, DATA[val]
+end
+
+function Port:set_stop_bits(value)
+  local v = check_val(value, STOP_NAMES, STOP)
+
+  local ok, err = F(self._p:set_stop_bits(v))
+  if not ok then return nil, err end
+
+  return self
+end
+
+function Port:stop_bits()
+  local val = self._p:stop_bits()
+  return val, STOP[val]
+end
+
+function Port:set_parity(value)
+  local v = check_val(value, PARITY_NAMES, PARITY)
+
+  local ok, err = F(self._p:set_parity(v))
+  if not ok then return nil, err end
+
+  return self
+end
+
+function Port:parity()
+  local val = self._p:parity()
+  return val, PARITY[val]
+end
+
+function Port:set_flow_control(value)
+  local v = check_val(value, FLOW_NAMES, FLOW)
+
+  local ok, err = F(self._p:set_flow_control(v))
+  if not ok then return nil, err end
+
+  return self
+end
+
+function Port:flow_control()
+  local val = self._p:flow_control()
+  return val, FLOW[val]
+end
+
+function Port:set_dtr(value)
+  local v = check_val(value, DTR_NAMES, DTR)
+
+  local ok, err = F(self._p:set_dtr(v))
+  if not ok then return nil, err end
+
+  return self
+end
+
+function Port:dtr()
+  local val = self._p:dtr()
+  return val, DTR[val]
+end
+
+function Port:set_rts(value)
+  local v = check_val(value, RTS_NAMES, RTS)
+
+  local ok, err = F(self._p:set_rts(v))
+  if not ok then return nil, err end
+
+  return self
+end
+
+function Port:rts()
+  local val = self._p:rts()
+  return val, RTS[val]
+end
+
+function Port:__tostring()
+  return "RS232 Port " .. tostring(self._p)
+end
+
+end
+
+return setmetatable({
+  _NAME      = "rs232";
+  _VERSION   = "0.1.1-dev";
+  _COPYRIGHT = "Copyright (C) 2015-2016 Alexey Melnichuk";
+  _LICENSE   = "MIT";
+
+  port  = Port.new;
+  error = RS232Error.new;
+  ERROR = ERROR;
+},{__index = rs232})

+ 2 - 0
bsp/win32/port/luat_base_win32.c

@@ -5,6 +5,7 @@
 
 LUAMOD_API int luaopen_win32( lua_State *L );
 int luaopen_lfs(lua_State * L);
+int luaopen_rs232_core(lua_State * L);
 
 static const luaL_Reg loadedlibs[] = {
   {"_G", luaopen_base}, // _G
@@ -30,6 +31,7 @@ static const luaL_Reg loadedlibs[] = {
   {"mqttcore", luaopen_mqttcore},      // 
   {"libcoap", luaopen_libcoap},        // 
   {"lfs", luaopen_lfs},                //
+  {"rs232.core", luaopen_rs232_core},
   {NULL, NULL}
 };
 

+ 558 - 0
bsp/win32/rock/librs232/bindings/lua/luars232.c

@@ -0,0 +1,558 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <ynezz@true.cz>, 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 <lauxlib.h>
+#include <lua.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "librs232/rs232.h"
+
+#define MODULE_TIMESTAMP __DATE__ " " __TIME__
+#define MODULE_NAMESPACE "luars232"
+#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 <ynezz@true.cz>, Gaben Ltd."
+
+#ifndef luaL_reg
+#define luaL_reg luaL_Reg
+#endif
+
+static struct {
+	const char *name;
+	unsigned long value;
+} luars232_ulong_consts[] = {
+	/* baudrates */
+	{ "RS232_BAUD_300", RS232_BAUD_300 },
+	{ "RS232_BAUD_2400", RS232_BAUD_2400 },
+	{ "RS232_BAUD_4800", RS232_BAUD_4800 },
+	{ "RS232_BAUD_9600", RS232_BAUD_9600 },
+	{ "RS232_BAUD_19200", RS232_BAUD_19200 },
+	{ "RS232_BAUD_38400", RS232_BAUD_38400 },
+	{ "RS232_BAUD_57600", RS232_BAUD_57600 },
+	{ "RS232_BAUD_115200", RS232_BAUD_115200 },
+	{ "RS232_BAUD_460800", RS232_BAUD_460800 },
+	/* databits */
+	{ "RS232_DATA_5", RS232_DATA_5 },
+	{ "RS232_DATA_6", RS232_DATA_6 },
+	{ "RS232_DATA_7", RS232_DATA_7 },
+	{ "RS232_DATA_8", RS232_DATA_8 },
+	/* stop bits */
+	{ "RS232_STOP_1", RS232_STOP_1 },
+	{ "RS232_STOP_2", RS232_STOP_2 },
+	/* parity */
+	{ "RS232_PARITY_NONE", RS232_PARITY_NONE },
+	{ "RS232_PARITY_ODD", RS232_PARITY_ODD },
+	{ "RS232_PARITY_EVEN", RS232_PARITY_EVEN },
+	/* flow */
+	{ "RS232_FLOW_OFF", RS232_FLOW_OFF },
+	{ "RS232_FLOW_HW", RS232_FLOW_HW },
+	{ "RS232_FLOW_XON_XOFF", RS232_FLOW_XON_XOFF },
+	/* DTR and RTS */
+	{ "RS232_DTR_ON", RS232_DTR_ON },
+	{ "RS232_DTR_OFF", RS232_DTR_OFF },
+	{ "RS232_RTS_ON", RS232_RTS_ON },
+	{ "RS232_RTS_OFF", RS232_RTS_OFF },
+	/* errors */
+	{ "RS232_ERR_NOERROR", RS232_ERR_NOERROR },
+	{ "RS232_ERR_UNKNOWN", RS232_ERR_UNKNOWN },
+	{ "RS232_ERR_OPEN", RS232_ERR_OPEN },
+	{ "RS232_ERR_CLOSE", RS232_ERR_CLOSE },
+	{ "RS232_ERR_FLUSH", RS232_ERR_FLUSH },
+	{ "RS232_ERR_CONFIG", RS232_ERR_CONFIG },
+	{ "RS232_ERR_READ", RS232_ERR_READ },
+	{ "RS232_ERR_WRITE", RS232_ERR_WRITE },
+	{ "RS232_ERR_SELECT", RS232_ERR_SELECT },
+	{ "RS232_ERR_TIMEOUT", RS232_ERR_TIMEOUT },
+	{ "RS232_ERR_IOCTL", RS232_ERR_IOCTL },
+	{ "RS232_ERR_PORT_CLOSED", RS232_ERR_PORT_CLOSED },
+	{ "RS232_ERR_BREAK", RS232_ERR_BREAK },
+	{ "RS232_ERR_FRAME", RS232_ERR_FRAME },
+	{ "RS232_ERR_PARITY", RS232_ERR_PARITY },
+	{ "RS232_ERR_RXOVERFLOW", RS232_ERR_RXOVERFLOW },
+	{ "RS232_ERR_OVERRUN", RS232_ERR_OVERRUN },
+	{ NULL, 0 }
+};
+
+#ifdef WIN32
+BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 
+{
+	return TRUE;
+}
+#endif
+
+/*
+ * rs232 = require("luars232")
+ * error, port = rs232.open(device)
+ * error, port = rs232.open("/dev/ttyUSB0")
+ */
+static int lua_port_open(lua_State *L)
+{
+	int ret = 0;
+	struct rs232_port_t *p = NULL;
+	struct rs232_port_t **ud = NULL;
+	const char *dev = luaL_checkstring(L, 1);
+
+	p = rs232_init();
+	if (p == NULL) {
+		lua_pushinteger(L, RS232_ERR_CONFIG);
+		lua_pushnil(L);
+		return 2;
+	}
+
+	DBG("p=%p \n", (void *)p);
+
+	rs232_set_device(p, dev);
+	ret = rs232_open(p);
+	if (ret > RS232_ERR_NOERROR) {
+		free(p->pt);
+		free(p);
+		lua_pushinteger(L, ret);
+		lua_pushnil(L);
+		return 2;
+	}
+
+	lua_pushinteger(L, RS232_ERR_NOERROR);
+	ud = lua_newuserdata(L, sizeof(struct rs232_port_t *));
+	*ud = p;
+
+	luaL_getmetatable(L, MODULE_NAMESPACE);
+	lua_setmetatable(L, -2);
+
+	return 2;
+}
+
+/* 
+ * error, data, read_len = port:read(max_read_len [[, timeout_ms], forced])
+ *
+ * if forced > 0 then read() blocks until 'timeout_ms' or there's 'max_read_len'
+ * bytes available
+ */
+static int lua_port_read(lua_State *L)
+{
+	int ret = 0;
+	int argc = 0;
+	int forced = 0;
+	unsigned int timeout = 0;
+	unsigned int len = 0;
+	unsigned int bytes_read = 0;
+	unsigned char tmp[128];
+	unsigned char *data = tmp;
+	struct rs232_port_t *p = NULL;
+
+	p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE);
+	lua_remove(L, 1);
+
+	if (p == NULL || !rs232_port_open(p)) {
+		lua_pushinteger(L, RS232_ERR_PORT_CLOSED);
+		lua_pushnil(L);
+		lua_pushinteger(L, 0);
+		return 3;
+	}
+
+	argc = lua_gettop(L);
+	len = (unsigned int) luaL_checkinteger(L, 1);
+	if(len > sizeof(tmp)){
+		data = (unsigned char*) malloc(len);
+		memset(data, 0, len);
+	}
+
+	switch (argc) {
+	case 1:
+		ret = rs232_read(p, data, len, &bytes_read);
+		break;
+	case 2:
+	case 3:
+		timeout = (unsigned int) luaL_checknumber(L, 2);
+		if(lua_isnumber(L, 3))
+			forced = (lua_tointeger(L, 3) > 0) ? 1 : 0;
+			else if (!lua_isnoneornil(L, 3)) {
+				luaL_checktype(L, 3, LUA_TBOOLEAN);
+				forced = lua_toboolean(L, 3);
+			}
+
+		if (forced > 0)
+			ret = rs232_read_timeout_forced(p, data, len, &bytes_read, timeout);
+		else
+			ret = rs232_read_timeout(p, data, len, &bytes_read, timeout);
+		break;
+	default:
+		lua_pushinteger(L, RS232_ERR_UNKNOWN);
+		lua_pushnil(L);
+		lua_pushinteger(L, 0);
+		return 3;
+	}
+
+	DBG("ret=%d hex='%s' bytes_read=%d\n",
+	    ret, rs232_hex_dump(data, bytes_read), bytes_read);
+
+	lua_pushinteger(L, ret);
+	if (bytes_read > 0)
+		lua_pushlstring(L, (char *) data, bytes_read);
+	else
+		lua_pushnil(L);
+
+	if (data != tmp)
+		free(data);
+
+	lua_pushinteger(L, bytes_read);
+	return 3;
+}
+
+/* 
+ * error, bytes = port:in_queue()
+ */
+static int lua_port_in_queue(lua_State *L)
+{
+	int ret = 0;
+	struct rs232_port_t *p = NULL;
+	unsigned int in_bytes = 0;
+
+	p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE);
+	lua_remove(L, 1);
+
+	if (p == NULL || !rs232_port_open(p)) {
+		lua_pushinteger(L, RS232_ERR_PORT_CLOSED);
+		lua_pushinteger(L, 0);
+		return 2;
+	}
+
+	ret = rs232_in_queue(p, &in_bytes);
+	lua_pushinteger(L, ret);
+	lua_pushinteger(L, in_bytes);
+	return 2;
+}
+
+/* 
+ * error = port:in_queue_clear()
+ */
+static int lua_port_in_queue_clear(lua_State *L)
+{
+	struct rs232_port_t *p = NULL;
+
+	p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE);
+	lua_remove(L, 1);
+
+	if (p == NULL || !rs232_port_open(p)) {
+		lua_pushinteger(L, RS232_ERR_PORT_CLOSED);
+		return 1;
+	}
+
+	rs232_in_queue_clear(p);
+	lua_pushinteger(L, RS232_ERR_NOERROR);
+	return 1;
+}
+
+/*
+ * error, written_len = port:write(data [, timeout_ms])
+ */
+static int lua_port_write(lua_State *L)
+{
+	int ret = 0;
+	int argc = 0;
+	unsigned int timeout = 0;
+	unsigned int wlen = 0;
+	size_t len = 0;
+	const char *data;
+	struct rs232_port_t *p = NULL;
+
+	p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE);
+	lua_remove(L, 1);
+
+	if (p == NULL || !rs232_port_open(p)) {
+		lua_pushinteger(L, RS232_ERR_PORT_CLOSED);
+		lua_pushinteger(L, 0);
+		return 2;
+	}
+
+	argc = lua_gettop(L);
+	switch (argc) {
+	case 1: {
+		data = luaL_checklstring(L, 1, &len);
+		ret = rs232_write(p, (unsigned char*) data, (unsigned int) len, &wlen);
+		break;
+	}
+	case 2:
+		data = luaL_checklstring(L, 1, &len);
+		timeout = (unsigned int) luaL_checknumber(L, 2);
+		ret = rs232_write_timeout(p, (unsigned char*) data, (unsigned int) len, &wlen, timeout);
+		break;
+	default:
+		lua_pushinteger(L, RS232_ERR_CONFIG);
+		lua_pushinteger(L, 0);
+		return 2;
+	}
+
+	lua_pushinteger(L, ret);
+	lua_pushinteger(L, wlen);
+	return 2;
+}
+
+/* error = port:close() */
+static int lua_port_close(lua_State *L)
+{
+	struct rs232_port_t *p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE);
+
+	if (p == NULL || !rs232_port_open(p)) {
+		lua_pushinteger(L, RS232_ERR_PORT_CLOSED);
+		return 1;
+	}
+
+	lua_pushinteger(L, rs232_close(p));
+	return 1;
+}
+
+/* __gc */
+static int lua_port_gc(lua_State *L)
+{
+	struct rs232_port_t **p = (struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE);
+
+	if (*p != NULL) {
+		rs232_end(*p);
+		*p = NULL;
+	}
+
+	return 1;
+}
+
+/* error = port:flush() */
+static int lua_port_flush(lua_State *L)
+{
+	struct rs232_port_t *p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE);
+
+	if (p == NULL || !rs232_port_open(p)) {
+		lua_pushinteger(L, RS232_ERR_PORT_CLOSED);
+		return 1;
+	}
+
+	lua_pushinteger(L, rs232_flush(p));
+	return 1;
+}
+
+/* __tostring metamethod */
+static int lua_port_tostring(lua_State *L)
+{
+	const char *ret;
+	struct rs232_port_t *p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE);
+
+	if (p == NULL) {
+		lua_pushnil(L);
+		return 1;
+	}
+
+	ret = rs232_to_string(p);
+	if (ret == NULL)
+		lua_pushnil(L);
+	else
+		lua_pushstring(L, ret);
+
+	return 1;
+}
+
+static int lua_port_device(lua_State *L)
+{
+	struct rs232_port_t *p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE);
+	const char *ret = rs232_get_device(p);
+	if (ret == NULL)
+		lua_pushnil(L);
+	else
+		lua_pushstring(L, ret);
+
+	return 1;
+}
+
+static int lua_port_fd(lua_State *L)
+{
+	struct rs232_port_t *p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE);
+	lua_pushinteger(L, rs232_fd(p));
+	return 1;
+}
+
+/*
+ * print(port:error_tostring(error))
+ */
+static int lua_port_strerror(lua_State *L)
+{
+	const char *ret = rs232_strerror((unsigned int) luaL_checkinteger(L, 1));
+	if (ret == NULL)
+		lua_pushnil(L);
+	else
+		lua_pushstring(L, ret);
+
+	return 1;
+}
+
+#define FN_SET_PORT(type) \
+	static int lua_port_set_##type(lua_State *L) \
+	{ \
+		struct rs232_port_t *p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE); \
+		lua_pushinteger(L, rs232_set_##type(p, (unsigned int) luaL_checknumber(L, 2))); \
+		return 1; \
+	} \
+
+#define FN_GET_PORT(type) \
+	static int lua_port_get_##type(lua_State *L) \
+	{ \
+		struct rs232_port_t *p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE); \
+		lua_pushinteger(L, rs232_get_##type(p)); \
+		return 1; \
+	}
+
+#define FN_GET_PORT_STRING(type) \
+	static int lua_port_get_str##type(lua_State *L) \
+	{ \
+		struct rs232_port_t *p = *(struct rs232_port_t**) luaL_checkudata(L, 1, MODULE_NAMESPACE); \
+		int param = (int) luaL_optinteger(L, 2, -1); \
+		const char *ret = rs232_str##type(param == -1 ? rs232_get_##type(p) : (unsigned int) param); \
+		if (ret == NULL) { \
+			lua_pushnil(L); \
+		} else { \
+			lua_pushstring(L, ret); \
+		} \
+		return 1; \
+	}
+
+FN_SET_PORT(baud)
+FN_SET_PORT(data)
+FN_SET_PORT(stop)
+FN_SET_PORT(parity)
+FN_SET_PORT(flow)
+FN_SET_PORT(dtr)
+FN_SET_PORT(rts)
+
+FN_GET_PORT(baud)
+FN_GET_PORT(data)
+FN_GET_PORT(stop)
+FN_GET_PORT(parity)
+FN_GET_PORT(flow)
+FN_GET_PORT(dtr)
+FN_GET_PORT(rts)
+
+FN_GET_PORT_STRING(baud)
+FN_GET_PORT_STRING(data)
+FN_GET_PORT_STRING(stop)
+FN_GET_PORT_STRING(parity)
+FN_GET_PORT_STRING(flow)
+FN_GET_PORT_STRING(dtr)
+FN_GET_PORT_STRING(rts)
+
+static luaL_reg port_methods[] = {
+	{ "__tostring", lua_port_tostring },
+	{ "__gc", lua_port_gc },
+	{ "read", lua_port_read },
+	{ "write", lua_port_write },
+	{ "close", lua_port_close },
+	{ "flush", lua_port_flush },
+	{ "device", lua_port_device },
+	{ "fd", lua_port_fd },
+	{ "in_queue", lua_port_in_queue },
+	{ "in_queue_clear", lua_port_in_queue_clear },
+	/* baud */
+	{ "baud_rate", lua_port_get_baud },
+	{ "baud_rate_tostring", lua_port_get_strbaud },
+	{ "set_baud_rate", lua_port_set_baud },
+	/* data */
+	{ "data_bits", lua_port_get_data },
+	{ "data_bits_tostring", lua_port_get_strdata },
+	{ "set_data_bits", lua_port_set_data },
+	/* stop */
+	{ "stop_bits", lua_port_get_stop },
+	{ "stop_bits_tostring", lua_port_get_strstop },
+	{ "set_stop_bits", lua_port_set_stop },
+	/* parity */
+	{ "parity", lua_port_get_parity },
+	{ "parity_tostring", lua_port_get_strparity },
+	{ "set_parity", lua_port_set_parity },
+	/* flow */
+	{ "flow_control", lua_port_get_flow },
+	{ "flow_control_tostring", lua_port_get_strflow },
+	{ "set_flow_control", lua_port_set_flow },
+	/* dtr */
+	{ "dtr", lua_port_get_dtr },
+	{ "dtr_tostring", lua_port_get_strdtr },
+	{ "set_dtr", lua_port_set_dtr },
+	/* rts */
+	{ "rts", lua_port_get_rts },
+	{ "rts_tostring", lua_port_get_strrts },
+	{ "set_rts", lua_port_set_rts },
+	{ NULL, NULL }
+};
+
+static luaL_reg port_functions[] = {
+	{ "open", lua_port_open },
+	{ "error_tostring", lua_port_strerror },
+	{ NULL, NULL }
+};
+
+static void create_metatables(lua_State *L, const char *name, const luaL_reg *methods)
+{
+	luaL_newmetatable(L, name);
+	lua_pushvalue(L, -1);
+	lua_setfield(L, -2, "__index");
+#if LUA_VERSION_NUM < 502
+	luaL_register(L, NULL, methods);
+#else
+	luaL_setfuncs(L, methods, 0);
+#endif
+}
+
+RS232_LIB int luaopen_luars232(lua_State *L)
+{
+	int i;
+	create_metatables(L, MODULE_NAMESPACE, port_methods);
+#if LUA_VERSION_NUM < 502
+	luaL_register(L, MODULE_NAMESPACE, port_functions);
+#else
+	luaL_newlib(L, port_functions);
+#endif
+
+	for (i = 0; luars232_ulong_consts[i].name != NULL; i++) {
+		lua_pushstring(L, luars232_ulong_consts[i].name);
+		lua_pushnumber(L, luars232_ulong_consts[i].value);
+		lua_settable(L, -3);
+	}
+
+	lua_pushstring(L, MODULE_VERSION);
+	lua_setfield(L, -2, "_VERSION");
+
+	lua_pushstring(L, MODULE_BUILD);
+	lua_setfield(L, -2, "_BUILD");
+
+	lua_pushstring(L, MODULE_TIMESTAMP);
+	lua_setfield(L, -2, "_TIMESTAMP");
+
+	lua_pushstring(L, MODULE_COPYRIGHT);
+	lua_setfield(L, -2, "_COPYRIGHT");
+
+	DBG("[*] luaopen_luars232(Version: '%s' Build: '%s' TimeStamp: '%s')\n",
+	    MODULE_VERSION, MODULE_BUILD, MODULE_TIMESTAMP);
+
+	return 1;
+}
+
+RS232_LIB int luaopen_rs232_core(lua_State *L){
+	return luaopen_luars232(L);
+}

+ 226 - 0
bsp/win32/rock/librs232/include/librs232/rs232.h

@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <ynezz@true.cz>, 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 256
+
+#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) || defined(_MSC_VER)
+ #include "librs232/rs232_windows.h"
+//  #ifdef _MSC_VER
+//   #pragma warning(disable:4996)
+//   #define snprintf _snprintf
+//  #endif
+// #else
+//  #include "librs232/rs232_posix.h"
+// #endif
+
+/*Ensure destination string is zero terminated*/
+#define strncpyz(dest, src, size) strncpy(dest, src, size); dest[size-1]='\0'
+
+#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_BREAK,
+	RS232_ERR_FRAME,
+	RS232_ERR_PARITY,
+	RS232_ERR_RXOVERFLOW,
+	RS232_ERR_OVERRUN,
+	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_queue(struct rs232_port_t *p, unsigned int *in_bytes);
+RS232_LIB void rs232_in_queue_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__ */

+ 75 - 0
bsp/win32/rock/librs232/include/librs232/rs232_windows.h

@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <ynezz@true.cz>, 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;
+	OVERLAPPED oWait;
+	unsigned char wait_progress;
+	DWORD wait_mask;
+};
+
+#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__ */

+ 314 - 0
bsp/win32/rock/librs232/src/rs232.c

@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <ynezz@true.cz>, 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;
+}

+ 1190 - 0
bsp/win32/rock/librs232/src/rs232_windows.c

@@ -0,0 +1,1190 @@
+/*
+ * Copyright (c) 2011 Petr Stetiar <ynezz@true.cz>, 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 <assert.h>
+
+#ifndef UNDER_CE
+#include <errno.h>
+#endif
+
+#include "librs232/rs232.h"
+
+typedef DWORD monotonic_time_t;
+typedef DWORD monotonic_diff_t;
+
+static monotonic_time_t GetMonotonicTime(){
+	return GetTickCount();
+}
+
+static monotonic_diff_t GetMonotonicDelta(monotonic_time_t StartTime, monotonic_time_t EndTime){
+	if(StartTime > EndTime)
+		return (MAXDWORD - StartTime) + EndTime;
+	return EndTime - StartTime;
+}
+
+#define READ_EVENTS     (EV_RXCHAR | EV_ERR | EV_BREAK)
+#define READ_LATENTENCY 500
+#define R_BUFFER_SIZE   1024
+#define W_BUFFER_SIZE   1024
+
+#define USE_OVERLAPPED
+
+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;
+}
+
+#ifdef RS232_DEBUG
+static char * last_error(void)
+{
+	DWORD err = 0;
+	DWORD ret = 0;
+	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 (%ld)", errbuf, ret);
+	}
+	else
+		snprintf(retbuf, MAX_PATH, "LastError: %ld (FormatMessageA failed)", ret);
+
+	return retbuf;
+}
+#endif
+
+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) {
+		free(p);
+		return NULL;
+	}
+
+	DBG("p=%p p->pt=%p\n", (void *)p, p->pt);
+
+	memset(p->dev, 0, RS232_STRLEN_DEVICE+1);
+	strncpyz(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 = READ_LATENTENCY;
+	wx->w_timeout = READ_LATENTENCY;
+	wx->r_buffer = R_BUFFER_SIZE;
+	wx->w_buffer = W_BUFFER_SIZE;
+
+	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 (!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;
+
+	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_queue(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_queue_clear(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;
+
+	if (!PurgeComm(wx->fd, PURGE_RXABORT | PURGE_RXCLEAR)) {
+		DBG("PurgeComm() %s\n", last_error());
+		return;
+	}
+
+	return;
+}
+
+//{ Non overlapped IO
+
+static unsigned int
+rs232_read_impl(struct rs232_port_t *p, unsigned char *buf, unsigned int buf_len,
+	unsigned int *read_len)
+{
+	DWORD r = 0;
+	struct rs232_windows_t *wx = p->pt;
+
+	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;
+
+	return RS232_ERR_NOERROR;
+}
+
+static unsigned int
+rs232_read_timeout_impl(struct rs232_port_t *p, unsigned char *buf,
+		   unsigned int buf_len, unsigned int *read_len,
+		   unsigned int timeout)
+{
+	DWORD r = 0;
+	struct rs232_windows_t *wx = p->pt;
+	unsigned int rt = wx->r_timeout;
+
+	*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;
+
+	/* 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;
+}
+
+/* this function waits either for timeout or buf_len bytes,
+   whatever happens first and doesn't return earlier */
+static unsigned int
+rs232_read_timeout_forced_impl(struct rs232_port_t *p, unsigned char *buf,
+		   unsigned int buf_len, unsigned int *read_len,
+		   unsigned int timeout)
+{
+	monotonic_time_t started = GetMonotonicTime();
+
+	*read_len = 0;
+	while(*read_len < buf_len){
+		monotonic_diff_t elapsed = GetMonotonicDelta(started, GetMonotonicTime());
+		unsigned int ret, readed = 0;
+		if(elapsed >= timeout){
+			return RS232_ERR_TIMEOUT;
+		}
+
+		ret = rs232_read_timeout_impl(p, &buf[*read_len], buf_len - *read_len, &readed, timeout - elapsed);
+		if(ret == RS232_ERR_NOERROR){
+			*read_len += readed;
+		}
+		else if(ret != RS232_ERR_TIMEOUT){
+			return ret;
+		}
+	}
+
+	return RS232_ERR_NOERROR;
+}
+
+static unsigned int
+rs232_write_impl(struct rs232_port_t *p, const unsigned char *buf, unsigned int buf_len,
+		unsigned int *write_len)
+{
+	DWORD w = 0;
+	struct rs232_windows_t *wx = p->pt;
+
+	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;
+
+	return RS232_ERR_NOERROR;
+}
+
+static unsigned int
+rs232_write_timeout_impl(struct rs232_port_t *p, const unsigned char *buf,
+			unsigned int buf_len, unsigned int *write_len,
+			unsigned int timeout)
+{
+	DWORD w = 0;
+	struct rs232_windows_t *wx = p->pt;
+	unsigned int wt = wx->w_timeout;
+
+	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;
+
+	return RS232_ERR_NOERROR;
+}
+
+//}
+
+//{ Overlapped IO
+
+static unsigned int
+poll_ovl(struct rs232_port_t *p, unsigned int timeout, DWORD events, DWORD *mask){
+	struct rs232_windows_t *wx = p->pt;
+	DWORD ret;
+	monotonic_time_t started = GetMonotonicTime();
+
+	if(!SetCommMask(wx->fd, events))
+		return RS232_ERR_IOCTL;
+
+	*mask = 0;
+
+	while(1){
+		monotonic_diff_t elapsed = GetMonotonicDelta(started, GetMonotonicTime());
+		if(elapsed >= timeout)
+			return RS232_ERR_TIMEOUT;
+
+		if(!wx->wait_progress){
+			wx->wait_mask = 0;
+			if (WaitCommEvent(wx->fd, &wx->wait_mask, &wx->oWait))
+				goto readed;
+
+			if (GetLastError() != ERROR_IO_PENDING)
+				return RS232_ERR_IOCTL;
+		}
+
+		wx->wait_progress = 1;
+
+		ret = WaitForSingleObject(wx->oWait.hEvent, timeout - elapsed);
+		if(ret != WAIT_OBJECT_0)
+			return RS232_ERR_TIMEOUT;
+
+		if(!GetOverlappedResult(wx->fd, &wx->oWait, &ret, FALSE))
+			return RS232_ERR_IOCTL;
+
+readed:
+
+		wx->wait_progress = 0;
+
+		if(wx->wait_mask)
+			break;
+	}
+
+	*mask = wx->wait_mask;
+	return RS232_ERR_NOERROR;
+}
+
+static unsigned int
+read_n_ovl(struct rs232_port_t *p, unsigned char *buf, unsigned int buf_len,
+		DWORD *read_len, DWORD *ermask
+)
+{
+	/* read avaliable data without waiting */
+	struct rs232_windows_t *wx = p->pt;
+	OVERLAPPED ovl = {0};
+	COMSTAT cs = {0};
+	DWORD readed, avaliable;
+
+	*read_len = 0;
+
+	if (!ClearCommError(wx->fd, ermask, &cs))
+		return RS232_ERR_IOCTL;
+
+	avaliable = cs.cbInQue > buf_len ? buf_len : cs.cbInQue;
+	if (avaliable) {
+		if (!ReadFile(wx->fd, buf, avaliable, &readed, &ovl)) {
+			if (GetLastError() != ERROR_IO_PENDING)
+				return RS232_ERR_READ;
+
+			if(!GetOverlappedResult(wx->fd, &ovl, &readed, TRUE))
+				return RS232_ERR_READ;
+		}
+		*read_len = readed;
+	}
+
+	return RS232_ERR_NOERROR;
+}
+
+static unsigned int
+rs232_read_ovl(struct rs232_port_t *p, unsigned char *buf,
+		unsigned int buf_len, unsigned int *read_len,
+		unsigned int timeout, unsigned int lat,
+		DWORD events, unsigned int *evmask
+)
+{
+	struct rs232_windows_t *wx = p->pt;
+	monotonic_time_t started = GetMonotonicTime();
+	unsigned char *ptr = buf;
+
+	*read_len = 0;
+
+	while (1) {
+		DWORD readed, error_mask, event_mask;
+		unsigned int ret;
+		monotonic_diff_t elapsed = GetMonotonicDelta(started, GetMonotonicTime());
+
+		if (elapsed > timeout)
+			return *read_len ? RS232_ERR_NOERROR : RS232_ERR_TIMEOUT;
+
+		ret = read_n_ovl(p, ptr, buf_len - *read_len, &readed, &error_mask);
+
+		if (error_mask) {
+			if (error_mask & CE_BREAK)
+				ret = RS232_ERR_BREAK;
+
+			if (error_mask & CE_RXOVER)
+				ret = RS232_ERR_RXOVERFLOW;
+
+			if (error_mask & CE_OVERRUN)
+				ret = RS232_ERR_OVERRUN;
+
+			if (error_mask & CE_FRAME)
+				ret = RS232_ERR_FRAME;
+
+			if (error_mask & CE_RXPARITY)
+				ret = RS232_ERR_PARITY;
+		}
+
+		if (ret != RS232_ERR_NOERROR)
+			return ret;
+
+		if(readed){/* reduce timeout to max latentency*/
+			if((timeout - elapsed) > lat)
+				timeout = elapsed + lat;
+		}
+
+		if(!readed){
+			ret = poll_ovl(p, timeout - elapsed, events, &event_mask);
+
+			if(evmask) *evmask = event_mask;
+
+			if(ret != RS232_ERR_NOERROR)
+				return *read_len ? RS232_ERR_NOERROR : RS232_ERR_TIMEOUT;
+
+			//! @todo check event_mask (e.g. (event_mask & EV_BREAK) || (event_mask & EV_ERR))
+
+			continue;
+		}
+
+		ptr += readed;
+		*read_len += readed;
+
+		assert(*read_len <= buf_len);
+
+		if(*read_len == buf_len)
+			break;
+	}
+
+	return RS232_ERR_NOERROR;
+}
+
+static unsigned int
+rs232_read_ovl_impl(struct rs232_port_t *p, unsigned char *buf, unsigned int buf_len,
+	unsigned int *read_len)
+{
+	struct rs232_windows_t *wx = p->pt;
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	return rs232_read_ovl(p, buf, buf_len, read_len, INFINITE, READ_LATENTENCY, READ_EVENTS, NULL);
+}
+
+/* this function waits either for timeout or buf_len bytes,
+   whatever happens first and doesn't return earlier */
+static unsigned int
+rs232_read_timeout_forced_ovl_impl(struct rs232_port_t *p, unsigned char *buf,
+		   unsigned int buf_len, unsigned int *read_len,
+		   unsigned int timeout)
+{
+	struct rs232_windows_t *wx = p->pt;
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	return rs232_read_ovl(p, buf, buf_len, read_len, timeout, timeout, READ_EVENTS, NULL);
+}
+
+static unsigned int
+rs232_read_timeout_ovl_impl(struct rs232_port_t *p, unsigned char *buf,
+		   unsigned int buf_len, unsigned int *read_len,
+		   unsigned int timeout)
+{
+	struct rs232_windows_t *wx = p->pt;
+
+	if (!rs232_port_open(p))
+		return RS232_ERR_PORT_CLOSED;
+
+	return rs232_read_ovl(p, buf, buf_len, read_len, timeout, READ_LATENTENCY, READ_EVENTS, NULL);
+}
+
+static unsigned int
+rs232_write_ovl(struct rs232_port_t *p, const unsigned char *buf, unsigned int buf_len,
+		unsigned int *write_len)
+{
+	OVERLAPPED oWrite = {0};
+	DWORD w = 0;
+	struct rs232_windows_t *wx = p->pt;
+
+	*write_len = 0;
+
+	if(!WriteFile(wx->fd, buf, buf_len, &w, &oWrite)){
+		DWORD ret;
+		if(GetLastError() != ERROR_IO_PENDING){
+			DBG("WriteFile() %s\n", last_error());
+			return RS232_ERR_WRITE;
+		}
+		if(!GetOverlappedResult(wx->fd, &oWrite, &ret, TRUE)){
+			DBG("OverlappedResult() %s\n", last_error());
+			// wtf? If we get error how cancel current write operation?
+			return RS232_ERR_WRITE;
+		}
+		w = ret;
+	}
+
+	*write_len = w;
+	return RS232_ERR_NOERROR;
+}
+
+static unsigned int
+rs232_write_ovl_impl(struct rs232_port_t *p, const unsigned char *buf, unsigned int buf_len,
+		unsigned int *write_len)
+{
+	struct rs232_windows_t *wx = p->pt;
+	unsigned int ret;
+
+	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;
+
+	ret = rs232_write_ovl(p, buf, buf_len, write_len);
+	if (ret != RS232_ERR_NOERROR) {
+		return ret;
+	}
+
+	if (buf_len != *write_len)
+		DBG("WriteFile() %s\n", last_error());
+
+	DBG("write_len=%d hex='%s' ascii='%s'\n", *write_len, rs232_hex_dump(buf, w),
+	    rs232_ascii_dump(buf, w));
+
+	return RS232_ERR_NOERROR;
+}
+
+static unsigned int
+rs232_write_timeout_ovl_impl(struct rs232_port_t *p, const unsigned char *buf,
+			unsigned int buf_len, unsigned int *write_len,
+			unsigned int timeout)
+{
+	struct rs232_windows_t *wx = p->pt;
+	unsigned int wt = wx->w_timeout;
+	unsigned int ret;
+
+	if (port_timeout(p, wx->r_timeout, timeout))
+		return RS232_ERR_UNKNOWN;
+
+	ret = rs232_write_ovl(p, buf, buf_len, write_len);
+	if (ret != RS232_ERR_NOERROR)
+		return ret;
+
+	if (port_timeout(p, wx->r_timeout, wt))
+		return RS232_ERR_UNKNOWN;
+
+	return RS232_ERR_NOERROR;
+}
+
+//}
+
+//{ Public API IP
+
+RS232_LIB unsigned int
+rs232_read(struct rs232_port_t *p, unsigned char *buf, unsigned int buf_len,
+	unsigned int *read_len)
+{
+	unsigned int ret;
+
+	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;
+
+#ifdef USE_OVERLAPPED
+	ret = rs232_read_ovl_impl(p, buf, buf_len, read_len);
+#else
+	ret = rs232_read_impl(p, buf, buf_len, read_len);
+#endif
+
+	DBG("read_len=%d hex='%s' ascii='%s'\n", r, rs232_hex_dump(buf, *read_len),
+		rs232_ascii_dump(buf, *read_len));
+
+	return ret;
+}
+
+/* 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)
+{
+	unsigned int ret;
+
+	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;
+
+#ifdef USE_OVERLAPPED
+	ret = rs232_read_timeout_forced_ovl_impl(p, buf, buf_len, read_len, timeout);
+#else
+	ret = rs232_read_timeout_forced_impl(p, buf, buf_len, read_len, timeout);
+#endif
+
+	DBG("read_len=%d hex='%s' ascii='%s'\n", r, rs232_hex_dump(buf, *read_len),
+		rs232_ascii_dump(buf, *read_len));
+
+	return ret;
+}
+
+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 ret;
+
+	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;
+
+#ifdef USE_OVERLAPPED
+	ret = rs232_read_timeout_ovl_impl(p, buf, buf_len, read_len, timeout);
+#else
+	ret = rs232_read_timeout_impl(p, buf, buf_len, read_len, timeout);
+#endif
+
+	DBG("read_len=%d hex='%s' ascii='%s'\n", r, rs232_hex_dump(buf, *read_len),
+		rs232_ascii_dump(buf, *read_len));
+
+	return ret;
+}
+
+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 ret;
+
+	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;
+
+#ifdef USE_OVERLAPPED
+	ret = rs232_write_ovl_impl(p, buf, buf_len, write_len);
+#else
+	ret = rs232_write_impl(p, buf, buf_len, write_len);
+#endif
+
+	DBG("write_len=%d hex='%s' ascii='%s'\n", w, rs232_hex_dump(buf, *write_len),
+	    rs232_ascii_dump(buf, *write_len));
+
+	return ret;
+}
+
+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 ret;
+
+	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;
+
+#ifdef USE_OVERLAPPED
+	ret = rs232_write_timeout_ovl_impl(p, buf, buf_len, write_len, timeout);
+#else
+	ret = rs232_write_timeout_impl(p, buf, buf_len, write_len, timeout);
+#endif
+
+	DBG("write_len=%d hex='%s' ascii='%s'\n", w, rs232_hex_dump(buf, *write_len),
+	    rs232_ascii_dump(buf, *write_len));
+
+	return ret;
+}
+
+//}
+
+static char *
+fix_device_name(char *device, char *ret)
+{
+	char *s = device;
+	/* meh, Windows CE is special and can't handle URN path, just COM1: format */
+
+	if((s[0] == '\\')&&(s[1] == '\\')&&(s[2] == '.')&&(s[3] == '\\')){
+#ifdef UNDER_CE
+	/* remove URN prefix */
+	snprintf(ret, RS232_STRLEN_DEVICE, "%s", s + 4);
+	return ret;
+#else
+	return s;
+#endif
+	}
+
+#ifdef UNDER_CE
+	return s;
+#else
+	snprintf(ret, RS232_STRLEN_DEVICE, "\\\\.\\%s", s);
+	return ret;
+#endif
+}
+
+RS232_LIB unsigned int
+rs232_open(struct rs232_port_t *p)
+{
+	char tmp[RS232_STRLEN_DEVICE+1] = {0};
+	wchar_t *wname = a2w(fix_device_name(p->dev, tmp));
+	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, tmp));
+
+	if (wname == NULL)
+		return RS232_ERR_UNKNOWN;
+
+	wx->fd = CreateFileW(wname, GENERIC_READ | GENERIC_WRITE,
+			0, NULL, OPEN_EXISTING,
+#ifdef USE_OVERLAPPED
+			FILE_FLAG_OVERLAPPED,
+#else
+			0,
+#endif
+			NULL);
+
+	if (wname)
+		free(wname);
+
+	if (wx->fd == INVALID_HANDLE_VALUE) {
+		DBG("CreateFile() %s\n", last_error());
+		return RS232_ERR_OPEN;
+	}
+
+#ifdef USE_OVERLAPPED
+	memset(&wx->oWait, 0, sizeof(wx->oWait));
+	wx->oWait.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if(wx->oWait.hEvent == NULL){
+		CloseHandle(wx->fd);
+		wx->fd = INVALID_HANDLE_VALUE;
+		return RS232_ERR_OPEN;
+	}
+	wx->wait_progress = 0;
+#endif
+
+	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);
+	strncpyz(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_RTS_OFF:
+		pdcb.fRtsControl = RTS_CONTROL_DISABLE;
+		break;
+	case RS232_RTS_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("CloseHandle() %s\n", last_error());
+		return RS232_ERR_CLOSE;
+	}
+
+#ifdef USE_OVERLAPPED
+	ret = CloseHandle(wx->oWait.hEvent);
+	if (ret == 0) {
+		DBG("CloseHandle() %s\n", last_error());
+		 return RS232_ERR_CLOSE;
+	}
+
+	wx->wait_progress = 0;
+#endif
+
+	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;
+}

+ 7 - 0
luat/include/luat_conf_default.h

@@ -35,6 +35,13 @@
     #define LUAL_BUFFERSIZE LUAT_CONF_LAUX_BUFFSIZE
 #endif
 
+// 如果是windows, 默认打开LUA_COMPAT_BITLIB
+#ifdef LUA_USE_WINDOWS
+#ifndef LUA_COMPAT_BITLIB
+#define LUA_COMPAT_BITLIB 1
+#endif
+#endif
+
 //------------------------------
 // LuatOS 特性
 //-----------------------------