Distributed under the LGPL
*/
+#ifdef WIN32
+#include <windows.h>
+#else
#include <termios.h>
#include <fcntl.h>
#include <errno.h>
+#endif
#include <msp/strings/formatter.h>
#include "except.h"
#include "serial.h"
using namespace Msp;
using namespace Msp::IO;
-#ifndef WIN32
-void set_baud_rate(termios &t, unsigned baud)
+#ifdef WIN32
+typedef DCB DeviceState;
+#else
+typedef termios DeviceState;
+#endif
+
+void get_state(Handle handle, DeviceState &state)
{
+#ifdef WIN32
+ GetCommState(handle, &state);
+#else
+ tcgetattr(handle, &state);
+#endif
+}
+
+void set_state(Handle handle, DeviceState &state)
+{
+#ifdef WIN32
+ if(SetCommState(handle, &state)==0)
+ throw SystemError("Cannot set serial port parameters", GetLastError());
+#else
+ if(tcsetattr(handle, TCSADRAIN, &state)==-1)
+ throw SystemError("Cannot set serial port parameters", errno);
+#endif
+}
+
+void set_baud_rate(DeviceState &state, unsigned baud)
+{
+#ifdef WIN32
+ state.BaudRate = baud;
+#else
speed_t speed;
switch(baud)
{
default: throw InvalidParameterValue("Invalid baud rate");
}
- cfsetospeed(&t, speed);
- cfsetispeed(&t, speed);
+ cfsetospeed(&state, speed);
+ cfsetispeed(&state, speed);
+#endif
}
-void set_data_bits(termios &t, unsigned bits)
+void set_data_bits(DeviceState &state, unsigned bits)
{
+#ifdef WIN32
+ state.ByteSize = bits;
+#else
tcflag_t flag;
switch(bits)
{
default: throw InvalidParameterValue("Invalid data bit count");
}
- t.c_cflag = (t.c_cflag&~CSIZE)|flag;
+ state.c_cflag = (state.c_cflag&~CSIZE)|flag;
+#endif
}
-void set_parity(termios &t, Serial::Parity par)
+void set_parity(DeviceState &state, Serial::Parity par)
{
+#ifdef WIN32
+ switch(par)
+ {
+ case Serial::NONE: state.Parity = NOPARITY; break;
+ case Serial::EVEN: state.Parity = EVENPARITY; break;
+ case Serial::ODD: state.Parity = ODDPARITY; break;
+ default: throw InvalidParameterValue("Invalid parity");
+ }
+#else
tcflag_t flag;
switch(par)
{
default: throw InvalidParameterValue("Invalid parity");
}
- t.c_cflag = (t.c_cflag&~(PARENB|PARODD))|flag;
+ state.c_cflag = (state.c_cflag&~(PARENB|PARODD))|flag;
+#endif
}
-void set_stop_bits(termios &t, unsigned bits)
+void set_stop_bits(DeviceState &state, unsigned bits)
{
+#ifdef WIN32
+ switch(bits)
+ {
+ case 1: state.StopBits = ONESTOPBIT; break;
+ case 2: state.StopBits = TWOSTOPBITS; break;
+ default: throw InvalidParameterValue("Invalid stop bit count");
+ }
+#else
tcflag_t flag;
switch(bits)
{
default: throw InvalidParameterValue("Invalid stop bit count");
}
- t.c_cflag = (t.c_cflag&~CSTOPB)|flag;
-}
+ state.c_cflag = (state.c_cflag&~CSTOPB)|flag;
#endif
+}
}
string::size_type comma = descr.find(',');
string port = descr.substr(0, comma);
-#ifndef WIN32
+#ifdef WIN32
+ port = "\\\\.\\"+port;
+
+ handle = CreateFile(port.c_str(), GENERIC_READ|GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if(handle==INVALID_HANDLE_VALUE)
+ throw SystemError(format("Can't open serial port '%s'", port), GetLastError());
+ mode = M_READ|M_WRITE;
+
+ COMMTIMEOUTS timeouts;
+ timeouts.ReadIntervalTimeout = MAXDWORD;
+ timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
+ timeouts.ReadTotalTimeoutConstant = MAXDWORD-1;
+ timeouts.WriteTotalTimeoutMultiplier = 0;
+ timeouts.WriteTotalTimeoutConstant = 0;
+ SetCommTimeouts(handle, &timeouts);
+#else
if(port.compare(0, 5, "/dev/"))
port = "/dev/"+port;
termios t;
tcgetattr(handle, &t);
t.c_lflag &= ~(ECHO|ICANON);
+ t.c_oflag &= ~OPOST;
tcsetattr(handle, TCSADRAIN, &t);
-#else
- throw Exception("Serial ports not supported in win32 yet");
#endif
if(comma!=string::npos)
void Serial::set_baud_rate(unsigned rate)
{
-#ifndef WIN32
- termios t;
- tcgetattr(handle, &t);
- ::set_baud_rate(t, rate);
- tcsetattr(handle, TCSADRAIN, &t);
-#endif
+ DeviceState state;
+ get_state(handle, state);
+ ::set_baud_rate(state, rate);
+ set_state(handle, state);
}
void Serial::set_data_bits(unsigned bits)
{
-#ifndef WIN32
- termios t;
- tcgetattr(handle, &t);
- ::set_data_bits(t, bits);
- tcsetattr(handle, TCSADRAIN, &t);
-#endif
+ DeviceState state;
+ get_state(handle, state);
+ ::set_data_bits(state, bits);
+ set_state(handle, state);
}
void Serial::set_parity(Parity par)
{
-#ifndef WIN32
- termios t;
- tcgetattr(handle, &t);
- ::set_parity(t, par);
- tcsetattr(handle, TCSADRAIN, &t);
-#endif
+ DeviceState state;
+ get_state(handle, state);
+ ::set_parity(state, par);
+ set_state(handle, state);
}
void Serial::set_stop_bits(unsigned bits)
{
-#ifndef WIN32
- termios t;
- tcgetattr(handle, &t);
- ::set_stop_bits(t, bits);
- tcsetattr(handle, TCSADRAIN, &t);
-#endif
+ DeviceState state;
+ get_state(handle, state);
+ ::set_stop_bits(state, bits);
+ set_state(handle, state);
}
void Serial::set_parameters(const string ¶ms)
if(params[i+3]!='1' && params[i+3]!='2')
throw InvalidParameterValue("Invalid stop bit count");
-#ifndef WIN32
- termios t;
- tcgetattr(handle, &t);
- ::set_baud_rate(t, lexical_cast<unsigned>(params.substr(0, i)));
- ::set_data_bits(t, params[i+1]-'0');
- ::set_parity(t, (params[i+2]=='E' ? EVEN : params[i+2]=='O' ? ODD : NONE));
- ::set_stop_bits(t, params[i+3]-'0');
- tcsetattr(handle, TCSADRAIN, &t);
+ DeviceState state;
+ get_state(handle, state);
+ ::set_baud_rate(state, lexical_cast<unsigned>(params.substr(0, i)));
+ ::set_data_bits(state, params[i+1]-'0');
+ ::set_parity(state, (params[i+2]=='E' ? EVEN : params[i+2]=='O' ? ODD : NONE));
+ ::set_stop_bits(state, params[i+3]-'0');
+ set_state(handle, state);
+}
+
+Handle Serial::get_event_handle()
+{
+#ifdef WIN32
+ throw Exception("Serial port events not supported on win32 yet");
+#else
+ return handle;
#endif
}
void Serial::close()
{
-#ifndef WIN32
+#ifdef WIN32
+ CloseHandle(handle);
+#else
::close(handle);
#endif
}
if(size==0)
return 0;
-#ifndef WIN32
+#ifdef WIN32
+ DWORD ret;
+ if(WriteFile(handle, buf, size, &ret, 0)==0)
+ throw SystemError("Writing to serial port failed", GetLastError());
+#else
int ret = ::write(handle, buf, size);
if(ret==-1)
{
if(size==0)
return 0;
-#ifndef WIN32
+#ifdef WIN32
+ DWORD ret;
+ if(ReadFile(handle, buf, size, &ret, 0)==0)
+ throw SystemError("Reading from serial port failed", GetLastError());
+#else
int ret = ::read(handle, buf, size);
if(ret==-1)
{