2019-12-24 15:05:24 +00:00
|
|
|
#include "cwCommon.h"
|
|
|
|
#include "cwLog.h"
|
|
|
|
#include "cwCommonImpl.h"
|
|
|
|
#include "cwMem.h"
|
|
|
|
#include "cwSerialPort.h"
|
|
|
|
|
|
|
|
#include <poll.h>
|
|
|
|
#include <termios.h>
|
|
|
|
#include <unistd.h> // ::close()
|
|
|
|
#include <fcntl.h> // O_RDWR
|
|
|
|
#include <sys/ioctl.h> // TIOCEXCL
|
|
|
|
|
|
|
|
|
|
|
|
namespace cw
|
|
|
|
{
|
|
|
|
namespace serialPort
|
|
|
|
{
|
|
|
|
typedef struct port_str
|
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
unsigned _userId;
|
|
|
|
char* _deviceStr;
|
|
|
|
int _deviceH;
|
|
|
|
unsigned _baudRate;
|
|
|
|
unsigned _cfgFlags;
|
|
|
|
callbackFunc_t _cbFunc;
|
|
|
|
void* _cbArg;
|
|
|
|
struct termios _ttyAttrs;
|
|
|
|
struct pollfd* _pollfd;
|
|
|
|
struct port_str* _link;
|
2019-12-24 15:05:24 +00:00
|
|
|
} port_t;
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
typedef struct device_str
|
|
|
|
{
|
|
|
|
unsigned _recvBufByteN;
|
|
|
|
void* _recvBuf;
|
|
|
|
port_t* _portL;
|
|
|
|
unsigned _pollfdN;
|
|
|
|
struct pollfd* _pollfd;
|
|
|
|
} device_t;
|
|
|
|
|
|
|
|
inline device_t* _handleToPtr(handle_t h)
|
|
|
|
{ return handleToPtr<handle_t,device_t>(h); }
|
|
|
|
|
|
|
|
bool _isPortOpen( port_t* p )
|
|
|
|
{ return p->_deviceH != -1; }
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
// Given a 'userId' return the assoc'd port record.
|
|
|
|
port_t* _idToPort( device_t* d, unsigned userId, bool errorFl = true )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
port_t* p = d->_portL;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
while( p != nullptr )
|
|
|
|
{
|
|
|
|
if( userId == p->_userId )
|
|
|
|
return p;
|
|
|
|
|
|
|
|
p = p->_link;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( errorFl )
|
|
|
|
cwLogError(kInvalidIdRC,"No port was found with id:%i.",userId);
|
|
|
|
|
|
|
|
return nullptr;
|
2019-12-24 15:05:24 +00:00
|
|
|
}
|
2020-03-06 03:04:53 +00:00
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
rc_t _getAttributes( port_t* p, struct termios& attr )
|
|
|
|
{
|
|
|
|
if( tcgetattr(p->_deviceH, &attr) == -1 )
|
|
|
|
return cwLogSysError(kGetAttrFailRC,errno,"Error getting tty attributes from %s.",p->_deviceStr);
|
|
|
|
|
|
|
|
return kOkRC;
|
|
|
|
}
|
2020-03-06 03:04:53 +00:00
|
|
|
|
|
|
|
// Called to read the serial device when data is known to be waiting.
|
|
|
|
rc_t _receive( device_t* d, port_t* p, unsigned& readN_Ref, void* buf=nullptr, unsigned bufByteN=0 )
|
|
|
|
{
|
|
|
|
rc_t rc = kOkRC;
|
|
|
|
void* b = buf;
|
|
|
|
unsigned bN = bufByteN;
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
readN_Ref = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if( !_isPortOpen(p) )
|
|
|
|
return cwLogWarningRC( kResourceNotAvailableRC, "An attempt was made to read from a closed serial port.");
|
|
|
|
|
|
|
|
|
|
|
|
// if a buffer was not given
|
|
|
|
if( b ==nullptr || bufByteN == 0 )
|
|
|
|
{
|
|
|
|
b = d->_recvBuf;
|
|
|
|
bN = d->_recvBufByteN;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if attempt to read the port succeeded ...
|
|
|
|
if((n =read( p->_deviceH, b, bN )) != -1 )
|
|
|
|
{
|
2020-03-17 02:54:14 +00:00
|
|
|
readN_Ref = n;
|
2020-03-06 03:04:53 +00:00
|
|
|
|
2020-03-17 02:54:14 +00:00
|
|
|
if( n>0 && p->_cbFunc != nullptr && (buf == nullptr || bufByteN == 0) )
|
2020-03-06 03:04:53 +00:00
|
|
|
p->_cbFunc( p->_cbArg, p->_userId, b, n );
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// ... or failed and it wasn't because the port was empty
|
|
|
|
if( errno != EAGAIN)
|
|
|
|
rc = cwLogSysError(kReadFailRC,errno,"An attempt to read the serial port '%s' failed.", p->_deviceStr );
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Block devices waiting for data on a port. If userId is valid then wait for data on a specific port otherwise
|
|
|
|
// wait for data on all ports.
|
|
|
|
rc_t _poll( device_t* d, unsigned timeOutMs, unsigned& readN_Ref, unsigned userId=kInvalidId, void* buf=nullptr, unsigned bufByteN=0 )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
|
|
|
rc_t rc = kOkRC;
|
|
|
|
int sysRC;
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
readN_Ref = 0;
|
|
|
|
|
|
|
|
// if there are not ports then there is nothing to do
|
|
|
|
if( d->_pollfdN == 0 )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
struct pollfd* pfd = d->_pollfd; // first struct pollfd
|
|
|
|
unsigned pfdN = d->_pollfdN; // count of pollfd's
|
|
|
|
port_t* p = d->_portL; // port assoc'd with first pollfd
|
|
|
|
|
|
|
|
// if only one port is to be read ...
|
|
|
|
if( userId != kInvalidId )
|
|
|
|
{
|
|
|
|
// ... then locate it
|
|
|
|
if((p = _idToPort(d,userId)) == nullptr )
|
|
|
|
return kInvalidArgRC;
|
|
|
|
|
|
|
|
pfd = p->_pollfd;
|
|
|
|
pfdN = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A port to poll must exist
|
|
|
|
if( p == nullptr )
|
|
|
|
return cwLogError(kInvalidArgRC,"The port with id %i could not be found.",userId);
|
|
|
|
|
|
|
|
|
|
|
|
// block waiting for data on one of the ports
|
|
|
|
if((sysRC = ::poll(pfd,pfdN,timeOutMs)) == 0)
|
2019-12-24 15:05:24 +00:00
|
|
|
rc = kTimeOutRC;
|
|
|
|
else
|
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
// ::poll() encountered a system exception
|
2019-12-24 15:05:24 +00:00
|
|
|
if( sysRC < 0 )
|
2020-03-06 03:04:53 +00:00
|
|
|
return cwLogSysError(kReadFailRC,errno,"Poll failed on serial port.");
|
|
|
|
|
|
|
|
// interate through the ports looking for the ones which have data waiting ...
|
|
|
|
for(unsigned i=0; p!=nullptr; p=p->_link,++i)
|
|
|
|
if( p->_pollfd->revents & POLLIN )
|
2020-03-17 02:54:14 +00:00
|
|
|
{
|
|
|
|
unsigned actualReadN = 0;
|
2020-03-06 03:04:53 +00:00
|
|
|
// ... then read the data
|
2020-03-17 02:54:14 +00:00
|
|
|
if((rc = _receive( d, p, actualReadN, buf, bufByteN )) != kOkRC )
|
2020-03-06 03:04:53 +00:00
|
|
|
return rc;
|
2020-03-17 02:54:14 +00:00
|
|
|
|
|
|
|
readN_Ref += actualReadN;
|
|
|
|
}
|
2020-03-06 03:04:53 +00:00
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
}
|
2020-03-06 03:04:53 +00:00
|
|
|
|
|
|
|
rc_t _closePort( device_t* d, port_t* p )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
|
|
|
rc_t rc = kOkRC;
|
2020-03-06 03:04:53 +00:00
|
|
|
|
|
|
|
// if the port is already closed
|
|
|
|
if( p->_deviceH != -1 )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
// Block until all written output has been sent from the device.
|
|
|
|
// Note that this call is simply passed on to the serial device driver.
|
|
|
|
// See tcsendbreak(3) ("man 3 tcsendbreak") for details.
|
|
|
|
if (tcdrain(p->_deviceH) == -1)
|
|
|
|
{
|
|
|
|
rc = cwLogSysError(kFlushFailRC,errno,"Error waiting for serial device '%s' to drain.", p->_deviceStr );
|
|
|
|
goto errLabel;
|
|
|
|
}
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
// It is good practice to reset a serial port back to the state in
|
|
|
|
// which you found it. This is why we saved the original termios struct
|
|
|
|
// The constant TCSANOW (defined in termios.h) indicates that
|
|
|
|
// the change should take effect immediately.
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
if (tcsetattr(p->_deviceH, TCSANOW, &p->_ttyAttrs) == -1)
|
|
|
|
{
|
|
|
|
rc = cwLogSysError(kSetAttrFailRC,errno,"Error resetting tty attributes on serial device '%s'.",p->_deviceStr);
|
|
|
|
goto errLabel;
|
|
|
|
}
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
if( ::close(p->_deviceH ) != 0 )
|
|
|
|
{
|
|
|
|
rc = cwLogSysError(kCloseFailRC,errno,"Port close failed on serial dvice '%s'.", p->_deviceStr);
|
|
|
|
goto errLabel;
|
|
|
|
}
|
2020-03-06 03:04:53 +00:00
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
|
|
|
|
// reset the state of the port record
|
|
|
|
mem::release(p->_deviceStr);
|
|
|
|
p->_userId = kInvalidId;
|
|
|
|
p->_deviceH = -1;
|
|
|
|
p->_baudRate = 0;
|
|
|
|
p->_cfgFlags = 0;
|
|
|
|
p->_cbFunc = nullptr;
|
|
|
|
p->_cbArg = nullptr;
|
|
|
|
p->_pollfd->events = 0;
|
|
|
|
p->_pollfd->fd = -1;
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
errLabel:
|
2020-03-06 03:04:53 +00:00
|
|
|
return rc;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destroy the manager object.
|
|
|
|
rc_t _destroy( device_t* d )
|
|
|
|
{
|
|
|
|
rc_t rc = kOkRC;
|
|
|
|
port_t* p = d->_portL;
|
|
|
|
|
|
|
|
while( p != nullptr )
|
|
|
|
{
|
|
|
|
port_t* p0 = p->_link;
|
|
|
|
|
|
|
|
if((rc = _closePort(d,p)) != kOkRC )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
mem::release(p);
|
|
|
|
|
|
|
|
p = p0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
mem::release(d->_recvBuf);
|
|
|
|
mem::release(d);
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
cw::rc_t cw::serialPort::create( handle_t& h, unsigned recvBufByteN )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
rc_t rc;
|
2019-12-24 15:05:24 +00:00
|
|
|
if((rc = destroy(h)) != kOkRC )
|
|
|
|
return rc;
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
device_t* d = mem::allocZ<device_t>();
|
|
|
|
|
|
|
|
if( recvBufByteN > 0 )
|
|
|
|
{
|
|
|
|
d->_recvBuf = mem::allocZ<uint8_t>( recvBufByteN );
|
|
|
|
d->_recvBufByteN = recvBufByteN;
|
|
|
|
}
|
|
|
|
|
|
|
|
h.set(d);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
cw::rc_t cw::serialPort::destroy( handle_t& h )
|
|
|
|
{
|
|
|
|
rc_t rc = kOkRC;
|
|
|
|
|
|
|
|
if( !h.isValid() )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
|
|
|
|
if((rc = _destroy(d)) != kOkRC )
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
h.clear();
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
cw::rc_t cw::serialPort::createPort( handle_t h, unsigned userId, const char* deviceStr, unsigned baudRate, unsigned cfgFlags, callbackFunc_t cbFunc, void* cbArg )
|
|
|
|
{
|
|
|
|
rc_t rc = kOkRC;
|
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
struct termios options;
|
|
|
|
port_t* p;
|
|
|
|
|
|
|
|
// locate the port with the given user id
|
|
|
|
if((p = _idToPort(d,userId,false)) != nullptr )
|
|
|
|
if((rc = _closePort(d,p)) != kOkRC )
|
|
|
|
return rc;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
// if a new port record must be allocated
|
|
|
|
if( p == nullptr )
|
|
|
|
{
|
|
|
|
unsigned portN = 0;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
// look for an available port and count the number of existing ports
|
|
|
|
for(p=d->_portL; p!=nullptr; p=p->_link,++portN)
|
|
|
|
if( !_isPortOpen(p) )
|
|
|
|
break;
|
|
|
|
|
|
|
|
if( p == nullptr )
|
|
|
|
{
|
|
|
|
// allocate and link in the new port desc. record
|
|
|
|
p = mem::allocZ<port_t>();
|
|
|
|
p->_deviceH = -1;
|
|
|
|
|
|
|
|
// link in the new port as the first port
|
|
|
|
p->_link = d->_portL;
|
|
|
|
d->_portL = p;
|
|
|
|
|
|
|
|
// A new port has been allocated reallocate the pollfd array
|
|
|
|
mem::release(d->_pollfd);
|
|
|
|
d->_pollfdN = portN + 1;
|
|
|
|
d->_pollfd = mem::allocZ<struct pollfd>(d->_pollfdN);
|
|
|
|
|
|
|
|
// link the port records to the their assoc'd pollfd record in d->_pollfd[]
|
|
|
|
port_t* pp = d->_portL;
|
|
|
|
for(unsigned i=0; pp!=nullptr; ++i,pp=pp->_link)
|
|
|
|
{
|
|
|
|
pp->_pollfd = d->_pollfd + i;
|
|
|
|
pp->_pollfd->fd = pp->_deviceH;
|
|
|
|
pp->_pollfd->events = POLLIN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
// open the port
|
|
|
|
if( (p->_deviceH = ::open(deviceStr, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1 )
|
|
|
|
{
|
|
|
|
rc = cwLogSysError(kOpenFailRC,errno,"Error opening serial '%s'",cwStringNullGuard(deviceStr));
|
|
|
|
goto errLabel;;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note that open() follows POSIX semantics: multiple open() calls to
|
|
|
|
// the same file will succeed unless the TIOCEXCL ioctl is issued.
|
|
|
|
// This will prevent additional opens except by root-owned processes.
|
|
|
|
// See tty(4) ("man 4 tty") and ioctl(2) ("man 2 ioctl") for details.
|
|
|
|
|
|
|
|
if( ioctl(p->_deviceH, TIOCEXCL) == -1 )
|
|
|
|
{
|
|
|
|
rc = cwLogSysError(kResourceNotAvailableRC,errno,"The serial device '%s' is already in use.", cwStringNullGuard(deviceStr));
|
|
|
|
goto errLabel;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Now that the device is open, clear the O_NONBLOCK flag so
|
|
|
|
// subsequent I/O will block.
|
|
|
|
// See fcntl(2) ("man 2 fcntl") for details.
|
|
|
|
/*
|
|
|
|
if (fcntl(_deviceH, F_SETFL, 0) == -1)
|
|
|
|
|
|
|
|
{
|
|
|
|
_error("Error clearing O_NONBLOCK %s - %s(%d).", pr.devFilePath.c_str(), strerror(errno), errno);
|
|
|
|
goto errLabel;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Get the current options and save them so we can restore the
|
|
|
|
// default settings later.
|
|
|
|
if (tcgetattr(p->_deviceH, &p->_ttyAttrs) == -1)
|
|
|
|
{
|
|
|
|
rc = cwLogSysError(kGetAttrFailRC,errno,"Error getting tty attributes from the device '%s'.",deviceStr);
|
|
|
|
goto errLabel;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The serial port attributes such as timeouts and baud rate are set by
|
|
|
|
// modifying the termios structure and then calling tcsetattr to
|
|
|
|
// cause the changes to take effect. Note that the
|
|
|
|
// changes will not take effect without the tcsetattr() call.
|
|
|
|
// See tcsetattr(4) ("man 4 tcsetattr") for details.
|
|
|
|
options = p->_ttyAttrs;
|
|
|
|
|
|
|
|
|
|
|
|
// Set raw input (non-canonical) mode, with reads blocking until either
|
|
|
|
// a single character has been received or a 100ms timeout expires.
|
|
|
|
// See tcsetattr(4) ("man 4 tcsetattr") and termios(4) ("man 4 termios")
|
|
|
|
// for details.
|
|
|
|
cfmakeraw(&options);
|
|
|
|
options.c_cc[VMIN] = 1;
|
|
|
|
options.c_cc[VTIME] = 1;
|
|
|
|
|
|
|
|
|
|
|
|
// The baud rate, word length, and handshake options can be set as follows:
|
|
|
|
|
|
|
|
|
|
|
|
// set baud rate
|
|
|
|
cfsetspeed(&options, baudRate);
|
|
|
|
|
|
|
|
options.c_cflag |= CREAD | CLOCAL; // ignore modem controls
|
|
|
|
|
|
|
|
// set data word size
|
|
|
|
cwClrBits(options.c_cflag, CSIZE); // clear the word size bits
|
|
|
|
cwEnaBits(options.c_cflag, CS5, cwIsFlag(cfgFlags, kDataBits5Fl));
|
|
|
|
cwEnaBits(options.c_cflag, CS6, cwIsFlag(cfgFlags, kDataBits6Fl));
|
|
|
|
cwEnaBits(options.c_cflag, CS7, cwIsFlag(cfgFlags, kDataBits7Fl));
|
|
|
|
cwEnaBits(options.c_cflag, CS8, cwIsFlag(cfgFlags, kDataBits8Fl));
|
|
|
|
|
|
|
|
cwClrBits(options.c_cflag, PARENB); // assume no-parity
|
|
|
|
|
|
|
|
// if the odd or even parity flag is set
|
|
|
|
if( cwIsFlag( cfgFlags, kEvenParityFl) || cwIsFlag( cfgFlags, kOddParityFl ) )
|
|
|
|
{
|
|
|
|
cwSetBits(options.c_cflag, PARENB);
|
|
|
|
|
|
|
|
if( cwIsFlag(cfgFlags, kOddParityFl ) )
|
|
|
|
cwSetBits( options.c_cflag, PARODD);
|
|
|
|
}
|
|
|
|
|
|
|
|
// set two stop bits
|
|
|
|
cwEnaBits( options.c_cflag, CSTOPB, cwIsFlag(cfgFlags, k2StopBitFl));
|
|
|
|
|
|
|
|
// set hardware flow control
|
|
|
|
//cwEnaBits(options.c_cflag, CCTS_OFLOW, cwIsFlag(cfgFlags, kCTS_OutFlowCtlFl));
|
|
|
|
//cwEnaBits(options.c_cflag, CRTS_IFLOW, cwIsFlag(cfgFlags, kRTS_InFlowCtlFl));
|
|
|
|
//cwEnaBits(options.c_cflag, CDTR_IFLOW, cwIsFlag(cfgFlags, kDTR_InFlowCtlFl));
|
|
|
|
//cwEnaBits(options.c_cflag, CDSR_OFLOW, cwIsFlag(cfgFlags, kDSR_OutFlowCtlFl));
|
|
|
|
//cwEnaBits(options.c_cflag, CCAR_OFLOW, cwIsFlag(cfgFlags, kDCD_OutFlowCtlFl));
|
|
|
|
|
|
|
|
cwClrBits(options.c_cflag,CRTSCTS); // turn-off hardware flow control
|
|
|
|
|
|
|
|
// 7 bit words, enable even parity, CTS out ctl flow, RTS in ctl flow
|
|
|
|
// note: set PARODD and PARENB to enable odd parity)
|
|
|
|
//options.c_cflag |= (CS7 | PARENB | CCTS_OFLOW | CRTS_IFLOW );
|
|
|
|
|
|
|
|
// Cause the new options to take effect immediately.
|
|
|
|
if (tcsetattr(p->_deviceH, TCSANOW, &options) == -1)
|
|
|
|
{
|
|
|
|
rc = cwLogSysError(kSetAttrFailRC,errno,"Error setting tty attributes on serial device %.", deviceStr);
|
|
|
|
goto errLabel;
|
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
p->_userId = userId;
|
|
|
|
p->_deviceStr = cw::mem::allocStr( deviceStr );
|
|
|
|
p->_baudRate = baudRate;
|
|
|
|
p->_cfgFlags = cfgFlags;
|
|
|
|
p->_cbFunc = cbFunc;
|
|
|
|
p->_cbArg = cbArg;
|
|
|
|
p->_pollfd->fd = p->_deviceH;
|
|
|
|
p->_pollfd->events = POLLIN;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
h.set(d);
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
errLabel:
|
|
|
|
if( rc != kOkRC )
|
2020-03-06 03:04:53 +00:00
|
|
|
_closePort(d,p);
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
cw::rc_t cw::serialPort::destroyPort(handle_t h, unsigned userId )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
|
|
|
rc_t rc = kOkRC;
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
if( !isopen(h,userId) )
|
2019-12-24 15:05:24 +00:00
|
|
|
return rc;
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
// find the port to close
|
|
|
|
if((p = _idToPort(d,userId)) == nullptr )
|
|
|
|
return kInvalidArgRC;
|
|
|
|
|
|
|
|
// Close the selected port
|
|
|
|
// Note that closed ports are simply marked as closed (deviceH==-1) but not removed from the list.
|
|
|
|
if((rc = _closePort(d,p)) != kOkRC )
|
2019-12-24 15:05:24 +00:00
|
|
|
return rc;
|
|
|
|
|
|
|
|
h.clear();
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
unsigned cw::serialPort::portCount( handle_t h )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
return d->_pollfdN;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned cw::serialPort::portIndexToId( handle_t h, unsigned index )
|
|
|
|
{
|
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
unsigned i = 0;
|
|
|
|
for(port_t* p=d->_portL; p!=nullptr; p=p->_link,++i)
|
|
|
|
if( i == index )
|
|
|
|
return p->_userId;
|
|
|
|
|
|
|
|
return kInvalidId;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool cw::serialPort::isopen( handle_t h, unsigned userId )
|
|
|
|
{
|
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
if((p = _idToPort(d,userId)) == nullptr )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return _isPortOpen(p);
|
2019-12-24 15:05:24 +00:00
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
cw::rc_t cw::serialPort::send( handle_t h, unsigned userId, const void* byteA, unsigned byteN )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
rc_t rc = kOkRC;
|
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
if( byteN == 0 )
|
|
|
|
return rc;
|
2020-03-06 03:04:53 +00:00
|
|
|
|
|
|
|
if((p = _idToPort(d,userId)) == nullptr )
|
|
|
|
return kInvalidArgRC;
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
// implement a non blocking write - if less than all the bytes were written then iterate
|
|
|
|
unsigned i = 0;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
if((n = write( p->_deviceH, ((char*)byteA)+i, byteN-i )) == -1 )
|
|
|
|
{
|
|
|
|
rc = cwLogSysError(kWriteFailRC,errno,"Write failed on serial port '%s'.", p->_deviceStr );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
i += n;
|
|
|
|
|
|
|
|
|
|
|
|
}while( i<byteN );
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
// Non-blocking read
|
|
|
|
cw::rc_t cw::serialPort::receive_nb( handle_t h, unsigned userId, unsigned& readN_Ref, void* buf, unsigned bufN)
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
rc_t rc = kOkRC;
|
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
if((p = _idToPort(d,userId)) == nullptr )
|
|
|
|
return kInvalidArgRC;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
return _receive( d, p, readN_Ref, buf,bufN );
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
cw::rc_t cw::serialPort::receive( handle_t h, unsigned timeOutMs, unsigned& readN_Ref)
|
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
device_t* d = _handleToPtr(h);
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
return _poll(d,timeOutMs,readN_Ref);
|
2019-12-24 15:05:24 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
cw::rc_t cw::serialPort::receive( handle_t h, unsigned timeOutMs, unsigned& readN_Ref, unsigned userId, void* buf, unsigned bufByteN )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
if((p = _idToPort(d,userId)) == nullptr )
|
|
|
|
return kInvalidArgRC;
|
2019-12-24 15:05:24 +00:00
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
return _poll(d,timeOutMs,readN_Ref,userId,buf,bufByteN);
|
2019-12-24 15:05:24 +00:00
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
const char* cw::serialPort::device( handle_t h, unsigned userId)
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
|
|
|
if((p = _idToPort(d,userId)) == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
return p->_deviceStr;
|
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
unsigned cw::serialPort::baudRate( handle_t h, unsigned userId )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
|
|
|
if((p = _idToPort(d,userId)) == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
return p->_baudRate;
|
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
unsigned cw::serialPort::cfgFlags( handle_t h, unsigned userId )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
2020-03-06 03:04:53 +00:00
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
|
|
|
if((p = _idToPort(d,userId)) == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
return p->_cfgFlags;
|
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
unsigned cw::serialPort::readInBaudRate( handle_t h, unsigned userId )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
|
|
|
struct termios attr;
|
2020-03-06 03:04:53 +00:00
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
|
|
|
if((p = _idToPort(d,userId)) == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
if((_getAttributes(p,attr)) != kOkRC )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return cfgetispeed(&attr);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
unsigned cw::serialPort::readOutBaudRate( handle_t h, unsigned userId )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
|
|
|
struct termios attr;
|
2020-03-06 03:04:53 +00:00
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
|
|
|
if((p = _idToPort(d,userId)) == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
if((_getAttributes(p,attr)) != kOkRC )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return cfgetospeed(&attr);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-03-06 03:04:53 +00:00
|
|
|
unsigned cw::serialPort::readCfgFlags( handle_t h, unsigned userId )
|
2019-12-24 15:05:24 +00:00
|
|
|
{
|
|
|
|
struct termios attr;
|
2020-03-06 03:04:53 +00:00
|
|
|
unsigned result = 0;
|
|
|
|
device_t* d = _handleToPtr(h);
|
|
|
|
port_t* p;
|
|
|
|
|
|
|
|
if((p = _idToPort(d,userId)) == NULL)
|
|
|
|
return 0;
|
|
|
|
|
2019-12-24 15:05:24 +00:00
|
|
|
if((_getAttributes(p,attr)) == false )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
switch( attr.c_cflag & CSIZE )
|
|
|
|
{
|
|
|
|
case CS5:
|
|
|
|
cwSetBits( result, kDataBits5Fl);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CS6:
|
|
|
|
cwSetBits( result, kDataBits6Fl );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CS7:
|
|
|
|
cwSetBits( result, kDataBits7Fl);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CS8:
|
|
|
|
cwSetBits( result, kDataBits8Fl);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
cwEnaBits( result, k2StopBitFl, cwIsFlag( attr.c_cflag, CSTOPB ));
|
|
|
|
cwEnaBits( result, k1StopBitFl, !cwIsFlag( attr.c_cflag, CSTOPB ));
|
|
|
|
|
|
|
|
if( cwIsFlag( attr.c_cflag, PARENB ) )
|
|
|
|
{
|
|
|
|
cwEnaBits( result, kOddParityFl, cwIsFlag( attr.c_cflag, PARODD ));
|
|
|
|
cwEnaBits( result, kEvenParityFl, !cwIsFlag( attr.c_cflag, PARODD ));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|