blob: ac72405be0f8fc27ecf6430948569a2b2821c2c5 [file] [log] [blame]
// Copyright (C) 1999-2005 Open Source Telecom Corporation.
// Copyright (C) 2006-2010 David Sugar, Tycho Softworks.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction. Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License. This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// Common C++. If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way. To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//
#include <cc++/config.h>
#ifdef CCXX_WITHOUT_EXTRAS
#include <cc++/export.h>
#endif
#include <cc++/exception.h>
#include <cc++/thread.h>
#include <cc++/file.h>
#ifndef CCXX_WITHOUT_EXTRAS
#include <cc++/export.h>
#endif
#include <cc++/serial.h>
#include "private.h"
#include <cstdlib>
#include <climits>
#ifdef WIN32
#define B256000 CBR_256000
#define B128000 CBR_128000
#define B115200 CBR_115200
#define B57600 CBR_57600
#define B56000 CBR_56000
#define B38400 CBR_38400
#define B19200 CBR_19200
#define B14400 CBR_14400
#define B9600 CBR_9600
#define B4800 CBR_4800
#define B2400 CBR_2400
#define B1200 CBR_1200
#define B600 CBR_600
#define B300 CBR_300
#define B110 CBR_110
#include <io.h>
#include <fcntl.h>
#else
#include <sys/ioctl.h>
#include <termios.h>
#endif
#include <cerrno>
#include <iostream>
#ifdef CCXX_NAMESPACES
namespace ost {
using std::streambuf;
using std::iostream;
using std::ios;
#endif
#ifndef MAX_INPUT
#define MAX_INPUT 255
#endif
#ifndef MAX_CANON
#define MAX_CANON MAX_INPUT
#endif
#ifdef __FreeBSD__
#undef _PC_MAX_INPUT
#undef _PC_MAX_CANON
#endif
#if defined(__QNX__)
#define CRTSCTS (IHFLOW | OHFLOW)
#endif
#if defined(_THR_UNIXWARE) || defined(__hpux) || defined(_AIX)
#include <sys/termiox.h>
#define CRTSCTS (CTSXON | RTSXOFF)
#endif
// IRIX
#ifndef CRTSCTS
#ifdef CNEW_RTSCTS
#define CRTSCTS (CNEW_RTSCTS)
#endif
#endif
#if defined(CTSXON) && defined(RTSXOFF) && !defined(CRTSCTS)
#define CRTSCTS (CTSXON | RTSXOFF)
#endif
#ifndef CRTSCTS
#define CRTSCTS 0
#endif
Serial::Serial(const char *fname)
{
initSerial();
open(fname);
#ifdef WIN32
if(dev == INVALID_HANDLE_VALUE)
#else
if(dev < 0)
#endif
{
error(errOpenFailed);
return;
}
#ifdef WIN32
COMMTIMEOUTS CommTimeOuts ;
GetCommTimeouts(dev, &CommTimeOuts);
// CommTimeOuts.ReadIntervalTimeout = MAXDWORD;
CommTimeOuts.ReadIntervalTimeout = 0;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ;
CommTimeOuts.WriteTotalTimeoutConstant = 1000;
SetCommTimeouts(dev, &CommTimeOuts) ;
#else
if(!isatty(dev)) {
Serial::close();
error(errOpenNoTty);
return;
}
#endif
}
Serial::~Serial()
{
endSerial();
}
void Serial::initConfig(void)
{
#ifdef WIN32
#define ASCII_XON 0x11
#define ASCII_XOFF 0x13
DCB * attr = (DCB *)current;
DCB * orig = (DCB *)original;
attr->DCBlength = sizeof(DCB);
orig->DCBlength = sizeof(DCB);
GetCommState(dev, orig);
GetCommState(dev, attr);
attr->DCBlength = sizeof(DCB);
attr->BaudRate = 1200;
attr->Parity = NOPARITY;
attr->ByteSize = 8;
attr->XonChar = ASCII_XON;
attr->XoffChar = ASCII_XOFF;
attr->XonLim = 100;
attr->XoffLim = 100;
attr->fOutxDsrFlow = 0;
attr->fDtrControl = DTR_CONTROL_ENABLE;
attr->fOutxCtsFlow = 1;
attr->fRtsControl = RTS_CONTROL_ENABLE;
attr->fInX = attr->fOutX = 0;
attr->fBinary = true;
attr->fParity = true;
SetCommState(dev, attr);
#else
struct termios *attr = (struct termios *)current;
struct termios *orig = (struct termios *)original;
long ioflags = fcntl(dev, F_GETFL);
tcgetattr(dev, (struct termios *)original);
tcgetattr(dev, (struct termios *)current);
attr->c_oflag = attr->c_lflag = 0;
attr->c_cflag = CLOCAL | CREAD | HUPCL;
attr->c_iflag = IGNBRK;
memset(attr->c_cc, 0, sizeof(attr->c_cc));
attr->c_cc[VMIN] = 1;
// inherit original settings, maybe we should keep more??
cfsetispeed(attr, cfgetispeed(orig));
cfsetospeed(attr, cfgetospeed(orig));
attr->c_cflag |= orig->c_cflag & (CRTSCTS | CSIZE | PARENB | PARODD | CSTOPB);
attr->c_iflag |= orig->c_iflag & (IXON | IXANY | IXOFF);
tcsetattr(dev, TCSANOW, attr);
fcntl(dev, F_SETFL, ioflags & ~O_NDELAY);
#if defined(TIOCM_RTS) && defined(TIOCMODG)
int mcs = 0;
ioctl(dev, TIOCMODG, &mcs);
mcs |= TIOCM_RTS;
ioctl(dev, TIOCMODS, &mcs);
#endif
#ifdef _COHERENT
ioctl(dev, TIOCSRTS, 0);
#endif
#endif // WIN32
}
void Serial::restore(void)
{
#ifdef WIN32
memcpy(current, original, sizeof(DCB));
SetCommState(dev, (DCB *)current);
#else
memcpy(current, original, sizeof(struct termios));
tcsetattr(dev, TCSANOW, (struct termios *)current);
#endif
}
void Serial::initSerial(void)
{
flags.thrown = false;
flags.linebuf = false;
errid = errSuccess;
errstr = NULL;
dev = INVALID_HANDLE_VALUE;
#ifdef WIN32
current = new DCB;
original = new DCB;
#else
current = new struct termios;
original = new struct termios;
#endif
}
void Serial::endSerial(void)
{
#ifdef WIN32
if(dev == INVALID_HANDLE_VALUE && original)
SetCommState(dev, (DCB *)original);
if(current)
delete (DCB *)current;
if(original)
delete (DCB *)original;
#else
if(dev < 0 && original)
tcsetattr(dev, TCSANOW, (struct termios *)original);
if(current)
delete (struct termios *)current;
if(original)
delete (struct termios *)original;
#endif
Serial::close();
current = NULL;
original = NULL;
}
Serial::Error Serial::error(Error err, char *errs)
{
errid = err;
errstr = errs;
if(!err)
return err;
if(flags.thrown)
return err;
// prevents recursive throws
flags.thrown = true;
#ifdef CCXX_EXCEPTIONS
if(Thread::getException() == Thread::throwObject)
throw((Serial *)this);
#ifdef COMMON_STD_EXCEPTION
else if(Thread::getException() == Thread::throwException) {
if(!errs)
errs = (char *)"";
throw SerException(String(errs));
}
#endif
#endif
return err;
}
int Serial::setPacketInput(int size, unsigned char btimer)
{
#ifdef WIN32
// Still to be done......
return 0;
#else
#ifdef _PC_MAX_INPUT
int max = fpathconf(dev, _PC_MAX_INPUT);
#else
int max = MAX_INPUT;
#endif
struct termios *attr = (struct termios *)current;
if(size > max)
size = max;
attr->c_cc[VEOL] = attr->c_cc[VEOL2] = 0;
attr->c_cc[VMIN] = (unsigned char)size;
attr->c_cc[VTIME] = btimer;
attr->c_lflag &= ~ICANON;
tcsetattr(dev, TCSANOW, attr);
bufsize = size;
return size;
#endif
}
int Serial::setLineInput(char newline, char nl1)
{
#ifdef WIN32
// Still to be done......
return 0;
#else
struct termios *attr = (struct termios *)current;
attr->c_cc[VMIN] = attr->c_cc[VTIME] = 0;
attr->c_cc[VEOL] = newline;
attr->c_cc[VEOL2] = nl1;
attr->c_lflag |= ICANON;
tcsetattr(dev, TCSANOW, attr);
#ifdef _PC_MAX_CANON
bufsize = fpathconf(dev, _PC_MAX_CANON);
#else
bufsize = MAX_CANON;
#endif
return bufsize;
#endif
}
void Serial::flushInput(void)
{
#ifdef WIN32
PurgeComm(dev, PURGE_RXABORT | PURGE_RXCLEAR);
#else
tcflush(dev, TCIFLUSH);
#endif
}
void Serial::flushOutput(void)
{
#ifdef WIN32
PurgeComm(dev, PURGE_TXABORT | PURGE_TXCLEAR);
#else
tcflush(dev, TCOFLUSH);
#endif
}
void Serial::waitOutput(void)
{
#ifdef WIN32
#else
tcdrain(dev);
#endif
}
Serial &Serial::operator=(const Serial &ser)
{
Serial::close();
if(ser.dev < 0)
return *this;
#ifdef WIN32
HANDLE process = GetCurrentProcess();
int result = DuplicateHandle(process, ser.dev, process, &dev, DUPLICATE_SAME_ACCESS, 0, 0);
if (0 == result) {
memcpy(current, ser.current, sizeof(DCB));
memcpy(original, ser.original, sizeof(DCB));
}
#else
dev = dup(ser.dev);
memcpy(current, ser.current, sizeof(struct termios));
memcpy(original, ser.original, sizeof(struct termios));
#endif
return *this;
}
void Serial::open(const char * fname)
{
#ifndef WIN32
int cflags = O_RDWR | O_NDELAY;
dev = ::open(fname, cflags);
if(dev > -1)
initConfig();
#else
// open COMM device
dev = CreateFile(fname,
GENERIC_READ | GENERIC_WRITE,
0, // exclusive access
NULL, // no security attrs
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING,
NULL);
if(dev != INVALID_HANDLE_VALUE)
initConfig();
#endif
}
#ifdef WIN32
int Serial::aRead(char * Data, const int Length)
{
unsigned long dwLength = 0, dwError, dwReadLength;
COMSTAT cs;
OVERLAPPED ol;
// Return zero if handle is invalid
if(dev == INVALID_HANDLE_VALUE)
return 0;
// Read max length or only what is available
ClearCommError(dev, &dwError, &cs);
// If not requiring an exact byte count, get whatever is available
if(Length > (int)cs.cbInQue)
dwReadLength = cs.cbInQue;
else
dwReadLength = Length;
memset(&ol, 0, sizeof(OVERLAPPED));
ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(dwReadLength > 0) {
if(ReadFile(dev, Data, dwReadLength, &dwLength, &ol) == FALSE) {
if(GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(ol.hEvent, INFINITE);
GetOverlappedResult(dev, &ol, &dwLength, TRUE);
}
else
ClearCommError(dev, &dwError, &cs);
}
}
if(ol.hEvent != INVALID_HANDLE_VALUE)
CloseHandle(ol.hEvent);
return dwLength;
}
int Serial::aWrite(const char * Data, const int Length)
{
COMSTAT cs;
unsigned long dwError = 0;
OVERLAPPED ol;
// Clear the com port of any error condition prior to read
ClearCommError(dev, &dwError, &cs);
unsigned long retSize = 0;
memset(&ol, 0, sizeof(OVERLAPPED));
ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(WriteFile(dev, Data, Length, &retSize, &ol) == FALSE) {
if(GetLastError() == ERROR_IO_PENDING) {
WaitForSingleObject(ol.hEvent, INFINITE);
GetOverlappedResult(dev, &ol, &retSize, TRUE);
}
else
ClearCommError(dev, &dwError, &cs);
}
if(ol.hEvent != INVALID_HANDLE_VALUE)
CloseHandle(ol.hEvent);
return retSize;
}
#else
int Serial::aRead(char *Data, const int Length)
{
return ::read(dev, Data, Length);
}
int Serial::aWrite(const char *Data, const int Length)
{
return ::write(dev, Data, Length);
}
#endif
void Serial::close()
{
#ifdef WIN32
CloseHandle(dev);
#else
::close(dev);
#endif
dev = INVALID_HANDLE_VALUE;
}
/*
const int iAsync::getTimeOuts(unsigned long & readTimeout, unsigned long & writeTimeout)
{
return GetCommTimeouts(_TheHandle, &CommTimeOuts);
}
const int iAsync::setTimeOuts(unsigned long readTimeout, unsigned long writeTimeout)
{
COMMTIMEOUTS CommTimeOuts ;
getTimeOuts(CommTimeOuts);
CommTimeOuts.ReadIntervalTimeout = readTimeout;
CommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;
CommTimeOuts.ReadTotalTimeoutConstant = 0;
CommTimeOuts.WriteTotalTimeoutMultiplier = 0 ;
CommTimeOuts.WriteTotalTimeoutConstant = 1000;
return GetCommTimeouts(_TheHandle, &CommTimeOuts);
}
return SetCommTimeouts(_TheHandle, &CommTimeOuts) ;
{
DCB dcb ;
dcb.DCBlength = sizeof(DCB) ;
GetCommState(_TheHandle, &dcb) ;
// hardcode this stuff for now.....
dcb.BaudRate = _TheBaudrate;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_ENABLE ;
dcb.fOutxCtsFlow = 1;
dcb.fRtsControl = RTS_CONTROL_HANDSHAKE ;
dcb.fInX = dcb.fOutX = 0;
dcb.XonChar = ASCII_XON;
dcb.XoffChar = ASCII_XOFF;
dcb.XonLim = 100;
dcb.XoffLim = 100;
// other various settings
dcb.fBinary = TRUE;
dcb.fParity = TRUE;
GetCommState(_TheHandle, &dcb) ;
dcb.DCBlength = sizeof(DCB) ;
dcb.BaudRate = _TheBaudrate;
SetCommState(_TheHandle, &dcb) ;
}
*/
Serial::Error Serial::setSpeed(unsigned long speed)
{
unsigned long rate;
switch(speed) {
#ifdef B115200
case 115200:
rate = B115200;
break;
#endif
#ifdef B57600
case 57600:
rate = B57600;
break;
#endif
#ifdef B38400
case 38400:
rate = B38400;
break;
#endif
case 19200:
rate = B19200;
break;
case 9600:
rate = B9600;
break;
case 4800:
rate = B4800;
break;
case 2400:
rate = B2400;
break;
case 1200:
rate = B1200;
break;
case 600:
rate = B600;
break;
case 300:
rate = B300;
break;
case 110:
rate = B110;
break;
#ifdef B0
case 0:
rate = B0;
break;
#endif
default:
return error(errSpeedInvalid);
}
#ifdef WIN32
DCB * dcb = (DCB *)current;
dcb->DCBlength = sizeof(DCB);
GetCommState(dev, dcb);
dcb->BaudRate = rate;
SetCommState(dev, dcb) ;
#else
struct termios *attr = (struct termios *)current;
cfsetispeed(attr, rate);
cfsetospeed(attr, rate);
tcsetattr(dev, TCSANOW, attr);
#endif
return errSuccess;
}
Serial::Error Serial::setFlowControl(Flow flow)
{
#ifdef WIN32
DCB * attr = (DCB *)current;
attr->XonChar = ASCII_XON;
attr->XoffChar = ASCII_XOFF;
attr->XonLim = 100;
attr->XoffLim = 100;
switch(flow) {
case flowSoft:
attr->fInX = attr->fOutX = 1;
break;
case flowBoth:
attr->fInX = attr->fOutX = 1;
case flowHard:
attr->fOutxCtsFlow = 1;
attr->fRtsControl = RTS_CONTROL_HANDSHAKE;
break;
case flowNone:
break;
default:
return error(errFlowInvalid);
}
SetCommState(dev, attr);
#else
struct termios *attr = (struct termios *)current;
attr->c_cflag &= ~CRTSCTS;
attr->c_iflag &= ~(IXON | IXANY | IXOFF);
switch(flow) {
case flowSoft:
attr->c_iflag |= (IXON | IXANY | IXOFF);
break;
case flowBoth:
attr->c_iflag |= (IXON | IXANY | IXOFF);
case flowHard:
attr->c_cflag |= CRTSCTS;
break;
case flowNone:
break;
default:
return error(errFlowInvalid);
}
tcsetattr(dev, TCSANOW, attr);
#endif
return errSuccess;
}
Serial::Error Serial::setStopBits(int bits)
{
#ifdef WIN32
DCB * attr = (DCB *)current;
switch(bits) {
case 1:
attr->StopBits = ONESTOPBIT;
break;
case 2:
attr->StopBits = TWOSTOPBITS;
break;
default:
return error(errStopbitsInvalid);
}
SetCommState(dev, attr);
#else
struct termios *attr = (struct termios *)current;
attr->c_cflag &= ~CSTOPB;
switch(bits) {
case 1:
break;
case 2:
attr->c_cflag |= CSTOPB;
break;
default:
return error(errStopbitsInvalid);
}
tcsetattr(dev, TCSANOW, attr);
#endif
return errSuccess;
}
Serial::Error Serial::setCharBits(int bits)
{
#ifdef WIN32
DCB * attr = (DCB *)current;
switch(bits) {
case 5:
case 6:
case 7:
case 8:
attr->ByteSize = bits;
break;
default:
return error(errCharsizeInvalid);
}
SetCommState(dev, attr);
#else
struct termios *attr = (struct termios *)current;
attr->c_cflag &= ~CSIZE;
switch(bits) {
case 5:
attr->c_cflag |= CS5;
break;
case 6:
attr->c_cflag |= CS6;
break;
case 7:
attr->c_cflag |= CS7;
break;
case 8:
attr->c_cflag |= CS8;
break;
default:
return error(errCharsizeInvalid);
}
tcsetattr(dev, TCSANOW, attr);
#endif
return errSuccess;
}
Serial::Error Serial::setParity(Parity parity)
{
#ifdef WIN32
DCB * attr = (DCB *)current;
switch(parity) {
case parityEven:
attr->Parity = EVENPARITY;
break;
case parityOdd:
attr->Parity = ODDPARITY;
break;
case parityNone:
attr->Parity = NOPARITY;
break;
default:
return error(errParityInvalid);
}
SetCommState(dev, attr);
#else
struct termios *attr = (struct termios *)current;
attr->c_cflag &= ~(PARENB | PARODD);
switch(parity) {
case parityEven:
attr->c_cflag |= PARENB;
break;
case parityOdd:
attr->c_cflag |= (PARENB | PARODD);
break;
case parityNone:
break;
default:
return error(errParityInvalid);
}
tcsetattr(dev, TCSANOW, attr);
#endif
return errSuccess;
}
void Serial::sendBreak(void)
{
#ifdef WIN32
SetCommBreak(dev);
Thread::sleep(100L);
ClearCommBreak(dev);
#else
tcsendbreak(dev, 0);
#endif
}
void Serial::toggleDTR(timeout_t millisec)
{
#ifdef WIN32
EscapeCommFunction(dev, CLRDTR);
if(millisec) {
Thread::sleep(millisec);
EscapeCommFunction(dev, SETDTR);
}
#else
struct termios tty, old;
tcgetattr(dev, &tty);
tcgetattr(dev, &old);
cfsetospeed(&tty, B0);
cfsetispeed(&tty, B0);
tcsetattr(dev, TCSANOW, &tty);
if(millisec) {
Thread::sleep(millisec);
tcsetattr(dev, TCSANOW, &old);
}
#endif
}
bool Serial::isPending(Pending pending, timeout_t timeout)
{
#ifdef WIN32
unsigned long dwError;
COMSTAT cs;
ClearCommError(dev, &dwError, &cs);
if(timeout == 0 || ((pending == pendingInput) && (0 != cs.cbInQue)) ||
((pending == pendingOutput) && (0 != cs.cbOutQue)) || (pending == pendingError))
{
switch(pending) {
case pendingInput:
return (0 != cs.cbInQue);
case pendingOutput:
return (0 != cs.cbOutQue);
case pendingError:
return false;
}
}
else {
Thread::Cancel save = Thread::enterCancel();
OVERLAPPED ol;
DWORD dwMask;
DWORD dwEvents = 0;
BOOL suc;
memset(&ol, 0, sizeof(OVERLAPPED));
ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(pending == pendingInput)
dwMask = EV_RXCHAR;
else if(pending == pendingOutput)
dwMask = EV_TXEMPTY;
else // on error
dwMask = EV_ERR;
SetCommMask(dev, dwMask);
// let's wait for event or timeout
if((suc = WaitCommEvent(dev, &dwEvents, &ol)) == FALSE) {
if(GetLastError() == ERROR_IO_PENDING) {
DWORD transferred;
dwError = WaitForSingleObject(ol.hEvent, timeout);
if (dwError != WAIT_OBJECT_0)
SetCommMask(dev, 0);
suc = GetOverlappedResult(dev, &ol, &transferred, TRUE);
if (suc)
suc = (dwEvents & dwMask) ? TRUE : FALSE;
}
else
ClearCommError(dev, &dwError, &cs);
}
if(ol.hEvent != INVALID_HANDLE_VALUE)
CloseHandle(ol.hEvent);
Thread::exitCancel(save);
if(suc == FALSE)
return false;
return true;
}
#else
int status;
#ifdef HAVE_POLL
struct pollfd pfd;
pfd.fd = dev;
pfd.revents = 0;
switch(pending) {
case pendingInput:
pfd.events = POLLIN;
break;
case pendingOutput:
pfd.events = POLLOUT;
break;
case pendingError:
pfd.events = POLLERR | POLLHUP;
break;
}
status = 0;
while(status < 1) {
if(timeout == TIMEOUT_INF)
status = poll(&pfd, 1, -1);
else
status = poll(&pfd, 1, timeout);
if(status < 1) {
if(status == -1 && errno == EINTR)
continue;
return false;
}
}
if(pfd.revents & pfd.events)
return true;
#else
struct timeval tv;
fd_set grp;
struct timeval *tvp = &tv;
if(timeout == TIMEOUT_INF)
tvp = NULL;
else {
tv.tv_usec = (timeout % 1000) * 1000;
tv.tv_sec = timeout / 1000;
}
FD_ZERO(&grp);
FD_SET(dev, &grp);
switch(pending) {
case pendingInput:
status = select(dev + 1, &grp, NULL, NULL, tvp);
break;
case pendingOutput:
status = select(dev + 1, NULL, &grp, NULL, tvp);
break;
case pendingError:
status = select(dev + 1, NULL, NULL, &grp, tvp);
break;
}
if(status < 1)
return false;
if(FD_ISSET(dev, &grp))
return true;
#endif
#endif // WIN32
return false;
}
TTYStream::TTYStream(const char *filename, timeout_t to)
: streambuf(),
Serial(filename),
#ifdef HAVE_OLD_IOSTREAM
iostream()
#else
iostream((streambuf *)this)
#endif
{
#ifdef HAVE_OLD_IOSTREAM
init((streambuf *)this);
#endif
gbuf = pbuf = NULL;
timeout = to;
if(INVALID_HANDLE_VALUE != dev)
allocate();
}
TTYStream::TTYStream()
: streambuf(),
Serial(),
#ifdef HAVE_OLD_IOSTREAM
iostream()
#else
iostream((streambuf *)this)
#endif
{
#ifdef HAVE_OLD_IOSTREAM
init((streambuf *)this);
#endif
timeout = 0;
gbuf = pbuf = NULL;
}
TTYStream::~TTYStream()
{
endStream();
endSerial();
}
void TTYStream::endStream(void)
{
if(bufsize)
sync();
if(gbuf) {
delete[] gbuf;
gbuf = NULL;
}
if(pbuf) {
delete[] pbuf;
pbuf = NULL;
}
bufsize = 0;
clear();
}
void TTYStream::allocate(void)
{
if(INVALID_HANDLE_VALUE == dev)
return;
#ifdef _PC_MAX_INPUT
bufsize = fpathconf(dev, _PC_MAX_INPUT);
#else
bufsize = MAX_INPUT;
#endif
gbuf = new char[bufsize];
pbuf = new char[bufsize];
if(!pbuf || !gbuf) {
error(errResourceFailure);
return;
}
clear();
#if !(defined(STLPORT) || defined(__KCC))
setg(gbuf, gbuf + bufsize, 0);
#endif
setg(gbuf, gbuf + bufsize, gbuf + bufsize);
setp(pbuf, pbuf + bufsize);
}
int TTYStream::doallocate()
{
if(bufsize)
return 0;
allocate();
return 1;
}
void TTYStream::interactive(bool iflag)
{
#ifdef WIN32
if(dev == INVALID_HANDLE_VALUE)
#else
if(dev < 0)
#endif
return;
if(bufsize >= 1)
endStream();
if(iflag) {
// setting to unbuffered mode
bufsize = 1;
gbuf = new char[bufsize];
#if !(defined(STLPORT) || defined(__KCC))
#if defined(__GNUC__) && (__GNUC__ < 3)
setb(0,0);
#endif
#endif
setg(gbuf, gbuf+bufsize, gbuf+bufsize);
setp(pbuf, pbuf);
return;
}
if(bufsize < 2)
allocate();
}
int TTYStream::uflow(void)
{
int rlen;
unsigned char ch;
if(bufsize < 2) {
if(timeout) {
if(Serial::isPending(pendingInput, timeout))
rlen = aRead((char *)&ch, 1);
else
rlen = -1;
}
else
rlen = aRead((char *)&ch, 1);
if(rlen < 1) {
if(rlen < 0)
clear(ios::failbit | rdstate());
return EOF;
}
return ch;
}
else {
ch = underflow();
gbump(1);
return ch;
}
}
int TTYStream::underflow(void)
{
ssize_t rlen = 1;
if(!gptr())
return EOF;
if(gptr() < egptr())
return (unsigned char)*gptr();
rlen = (ssize_t)((gbuf + bufsize) - eback());
if(timeout && !Serial::isPending(pendingInput, timeout))
rlen = -1;
else
rlen = aRead((char *)eback(), rlen);
if(rlen < 1) {
if(rlen < 0) {
clear(ios::failbit | rdstate());
error(errInput);
}
return EOF;
}
setg(eback(), eback(), eback() + rlen);
return (unsigned char) *gptr();
}
int TTYStream::sync(void)
{
if(bufsize > 1 && pbase() && ((pptr() - pbase()) > 0)) {
overflow(0);
waitOutput();
setp(pbuf, pbuf + bufsize);
}
setg(gbuf, gbuf + bufsize, gbuf + bufsize);
return 0;
}
int TTYStream::overflow(int c)
{
unsigned char ch;
ssize_t rlen, req;
if(bufsize < 2) {
if(c == EOF)
return 0;
ch = (unsigned char)(c);
rlen = aWrite((char *)&ch, 1);
if(rlen < 1) {
if(rlen < 0)
clear(ios::failbit | rdstate());
return EOF;
}
else
return c;
}
if(!pbase())
return EOF;
req = (ssize_t)(pptr() - pbase());
if(req) {
rlen = aWrite((char *)pbase(), req);
if(rlen < 1) {
if(rlen < 0)
clear(ios::failbit | rdstate());
return EOF;
}
req -= rlen;
}
if(req)
// memmove(pptr(), pptr() + rlen, req);
memmove(pbuf, pbuf + rlen, req);
setp(pbuf + req, pbuf + bufsize);
if(c != EOF) {
*pptr() = (unsigned char)c;
pbump(1);
}
return c;
}
bool TTYStream::isPending(Pending pending, timeout_t timer)
{
// if(pending == pendingInput && in_avail())
// return true;
// else if(pending == pendingOutput)
// flush();
return Serial::isPending(pending, timer);
}
ttystream::ttystream() :
TTYStream()
{
setError(false);
}
ttystream::ttystream(const char *name) :
TTYStream()
{
setError(false);
open(name);
}
void ttystream::close(void)
{
#ifdef WIN32
if (INVALID_HANDLE_VALUE == dev)
#else
if(dev < 0)
#endif
return;
endStream();
restore();
TTYStream::close();
}
void ttystream::open(const char *name)
{
const char *cpp;
char *cp;
char pathname[256];
size_t namelen;
long opt;
if (INVALID_HANDLE_VALUE != dev) {
restore();
close();
}
cpp = strrchr(name, ':');
if(cpp)
namelen = cpp - name;
else
namelen = strlen(name);
cp = pathname;
#ifndef WIN32
if(*name != '/') {
strcpy(pathname, "/dev/");
cp += 5;
}
if((cp - pathname) + namelen > 255) {
error(errResourceFailure);
return;
}
#endif
setString(cp, pathname - cp + sizeof(pathname), name);
cp += namelen;
#ifdef WIN32
*cp++ = ':';
#endif
*cp = 0;
Serial::open(pathname);
if(INVALID_HANDLE_VALUE == dev) {
error(errOpenFailed);
return;
}
allocate();
setString(pathname, sizeof(pathname), name + namelen);
cp = pathname + 1;
if(*pathname == ':')
cp = strtok(cp, ",");
else
cp = NULL;
while(cp) {
switch(*cp) {
case 'h':
case 'H':
setFlowControl(flowHard);
break;
case 's':
case 'S':
setFlowControl(flowSoft);
break;
case 'b':
case 'B':
setFlowControl(flowBoth);
break;
case 'n':
case 'N':
setParity(parityNone);
break;
case 'O':
case 'o':
setParity(parityOdd);
break;
case 'e':
case 'E':
setParity(parityEven);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
opt = atol(cp);
if(opt == 1 || opt == 2) {
setStopBits((int)opt);
break;
}
if(opt > 4 && opt < 9) {
setCharBits((int)opt);
break;
}
setSpeed(opt);
break;
default:
error(errOptionInvalid);
}
cp = strtok(NULL, ",");
}
}
TTYSession::TTYSession(const char *filename, int pri, int stack) :
Thread(pri, stack), TTYStream(filename)
{
setError(false);
}
TTYSession::~TTYSession()
{
terminate();
endSerial();
}
#ifndef WIN32
// Not supporting this right now........
//
SerialPort::SerialPort(SerialService *svc, const char *name) :
Serial(name),
detect_pending(true),
detect_output(false),
detect_disconnect(true)
{
next = prev = NULL;
service = NULL;
#ifdef WIN32
if(INVALID_HANDLE_VALUE != dev)
#else
if(dev > -1)
#endif
{
setError(false);
service = svc;
svc->attach(this);
}
}
SerialPort::~SerialPort()
{
if(service)
service->detach(this);
endSerial();
}
void SerialPort::expired(void)
{
}
void SerialPort::pending(void)
{
}
void SerialPort::disconnect(void)
{
}
void SerialPort::output(void)
{
}
void SerialPort::setTimer(timeout_t ptimer)
{
TimerPort::setTimer(ptimer);
service->update();
}
void SerialPort::incTimer(timeout_t ptimer)
{
TimerPort::incTimer(ptimer);
service->update();
}
void SerialPort::setDetectPending( bool val )
{
if ( detect_pending != val ) {
detect_pending = val;
#ifdef USE_POLL
if ( ufd ) {
if ( val ) {
ufd->events |= POLLIN;
} else {
ufd->events &= ~POLLIN;
}
}
#endif
service->update();
}
}
void SerialPort::setDetectOutput( bool val )
{
if ( detect_output != val ) {
detect_output = val;
#ifdef USE_POLL
if ( ufd ) {
if ( val ) {
ufd->events |= POLLOUT;
} else {
ufd->events &= ~POLLOUT;
}
}
#endif
service->update();
}
}
SerialService::SerialService(int pri, size_t stack, const char *id) :
Thread(pri, stack), Mutex(id)
{
long opt;
first = last = NULL;
count = 0;
FD_ZERO(&connect);
if(::pipe(iosync)) {
#ifdef CCXX_EXCEPTIONS
switch(Thread::getException()) {
case throwObject:
throw(this);
return;
#ifdef COMMON_STD_EXCEPTION
case throwException:
throw(ThrException("no service pipe"));
return;
#endif
default:
return;
}
#else
return;
#endif
}
hiwater = iosync[0] + 1;
FD_SET(iosync[0], &connect);
opt = fcntl(iosync[0], F_GETFL);
fcntl(iosync[0], F_SETFL, opt | O_NDELAY);
}
SerialService::~SerialService()
{
update(0);
terminate();
while(first)
delete first;
}
void SerialService::onUpdate(unsigned char flag)
{
}
void SerialService::onEvent(void)
{
}
void SerialService::onCallback(SerialPort *port)
{
}
void SerialService::attach(SerialPort *port)
{
enterMutex();
#ifdef USE_POLL
port->ufd = 0;
#endif
if(last)
last->next = port;
port->prev = last;
last = port;
FD_SET(port->dev, &connect);
if(port->dev >= hiwater)
hiwater = port->dev + 1;
if(!first) {
first = port;
leaveMutex();
++count;
start();
}
else {
leaveMutex();
update();
++count;
}
}
void SerialService::detach(SerialPort *port)
{
enterMutex();
#ifndef USE_POLL
FD_CLR(port->dev, &connect);
#endif
if(port->prev)
port->prev->next = port->next;
else
first = port->next;
if(port->next)
port->next->prev = port->prev;
else
last = port->prev;
--count;
leaveMutex();
update();
}
void SerialService::update(unsigned char flag)
{
if(::write(iosync[1], (char *)&flag, 1) < 1) {
#ifdef CCXX_EXCEPTIONS
switch(Thread::getException()) {
case throwObject:
throw(this);
return;
#ifdef COMMON_STD_EXCEPTION
case throwException:
throw(ThrException("update failed"));
return;
#endif
default:
return;
}
#else
return;
#endif
}
}
void SerialService::run(void)
{
timeout_t timer, expires;
SerialPort *port;
unsigned char buf;
#ifdef USE_POLL
Poller mfd;
pollfd *p_ufd;
int lastcount = 0;
// initialize ufd in all attached ports :
// probably don't need this but it can't hurt.
enterMutex();
port = first;
while(port) {
port->ufd = 0;
port = port->next;
}
leaveMutex();
#else
struct timeval timeout, *tvp;
fd_set inp, out, err;
int dev;
FD_ZERO(&inp);
FD_ZERO(&out);
FD_ZERO(&err);
#endif
setCancel(cancelDeferred);
for(;;) {
timer = TIMEOUT_INF;
while(1 == ::read(iosync[0], (char *)&buf, 1)) {
if(buf) {
onUpdate(buf);
continue;
}
setCancel(cancelImmediate);
sleep(TIMEOUT_INF);
exit();
}
#ifdef USE_POLL
bool reallocate = false;
enterMutex();
onEvent();
port = first;
while(port) {
onCallback(port);
if ( ( p_ufd = port->ufd ) ) {
if ( ( POLLHUP | POLLNVAL ) & p_ufd->revents ) {
// Avoid infinite loop from disconnected sockets
port->detect_disconnect = false;
p_ufd->events &= ~POLLHUP;
port->disconnect();
}
if ( ( POLLIN | POLLPRI ) & p_ufd->revents )
port->pending();
if ( POLLOUT & p_ufd->revents )
port->output();
} else {
reallocate = true;
}
retry:
expires = port->getTimer();
if(expires > 0)
if(expires < timer)
timer = expires;
if(!expires) {
port->endTimer();
port->expired();
goto retry;
}
port = port->next;
}
//
// reallocate things if we saw a ServerPort without
// ufd set !
if ( reallocate || ( ( count + 1 ) != lastcount ) ) {
lastcount = count + 1;
p_ufd = mfd.getList( count + 1 );
// Set up iosync polling
p_ufd->fd = iosync[0];
p_ufd->events = POLLIN | POLLHUP;
p_ufd ++;
port = first;
while(port) {
p_ufd->fd = port->dev;
p_ufd->events =
( port->detect_disconnect ? POLLHUP : 0 )
| ( port->detect_output ? POLLOUT : 0 )
| ( port->detect_pending ? POLLIN : 0 )
;
port->ufd = p_ufd;
p_ufd ++;
port = port->next;
}
}
leaveMutex();
poll( mfd.getList(), count + 1, timer );
#else
enterMutex();
onEvent();
port = first;
while(port) {
onCallback(port);
dev = port->dev;
if(FD_ISSET(dev, &err)) {
port->detect_disconnect = false;
port->disconnect();
}
if(FD_ISSET(dev, &inp))
port->pending();
if(FD_ISSET(dev, &out))
port->output();
retry:
expires = port->getTimer();
if(expires > 0)
if(expires < timer)
timer = expires;
if(!expires) {
port->endTimer();
port->expired();
goto retry;
}
port = port->next;
}
FD_ZERO(&inp);
FD_ZERO(&out);
FD_ZERO(&err);
int so;
port = first;
while(port) {
so = port->dev;
if(port->detect_pending)
FD_SET(so, &inp);
if(port->detect_output)
FD_SET(so, &out);
if(port->detect_disconnect)
FD_SET(so, &err);
port = port->next;
}
leaveMutex();
if(timer == TIMEOUT_INF)
tvp = NULL;
else {
tvp = &timeout;
timeout.tv_sec = timer / 1000;
timeout.tv_usec = (timer % 1000) * 1000;
}
select(hiwater, &inp, &out, &err, tvp);
#endif
}
}
#endif
#ifdef CCXX_NAMESPACES
}
#endif
/** EMACS **
* Local variables:
* mode: c++
* c-basic-offset: 8
* End:
*/