cwSerialPort.h/cpp,cwSerialPortSrv.h/cpp : serialPort now manages multiple ports.
This commit is contained in:
parent
24c68c158f
commit
465d7f6fd6
501
cwSerialPort.cpp
501
cwSerialPort.cpp
@ -17,32 +17,53 @@ namespace cw
|
||||
{
|
||||
typedef struct port_str
|
||||
{
|
||||
const char* _deviceStr;
|
||||
int _deviceH;
|
||||
unsigned _baudRate;
|
||||
unsigned _cfgFlags;
|
||||
callbackFunc_t _cbFunc;
|
||||
void* _cbArg;
|
||||
struct termios _ttyAttrs;
|
||||
struct pollfd _pollfd;
|
||||
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;
|
||||
} port_t;
|
||||
|
||||
inline port_t* _portHandleToPtr(handle_t h){ return handleToPtr<handle_t,port_t>(h); }
|
||||
|
||||
void _setClosedState( port_t* p )
|
||||
typedef struct device_str
|
||||
{
|
||||
if( p->_deviceStr != nullptr )
|
||||
cw::mem::free(const_cast<char*>(p->_deviceStr));
|
||||
unsigned _recvBufByteN;
|
||||
void* _recvBuf;
|
||||
port_t* _portL;
|
||||
unsigned _pollfdN;
|
||||
struct pollfd* _pollfd;
|
||||
} device_t;
|
||||
|
||||
p->_deviceH = -1;
|
||||
p->_deviceStr = nullptr;
|
||||
p->_baudRate = 0;
|
||||
p->_cfgFlags = 0;
|
||||
p->_cbFunc = nullptr;
|
||||
p->_cbArg = nullptr;
|
||||
inline device_t* _handleToPtr(handle_t h)
|
||||
{ return handleToPtr<handle_t,device_t>(h); }
|
||||
|
||||
bool _isPortOpen( port_t* p )
|
||||
{ return p->_deviceH != -1; }
|
||||
|
||||
// Given a 'userId' return the assoc'd port record.
|
||||
port_t* _idToPort( device_t* d, unsigned userId, bool errorFl = true )
|
||||
{
|
||||
port_t* p = d->_portL;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
rc_t _getAttributes( port_t* p, struct termios& attr )
|
||||
{
|
||||
if( tcgetattr(p->_deviceH, &attr) == -1 )
|
||||
@ -51,62 +72,179 @@ namespace cw
|
||||
return kOkRC;
|
||||
}
|
||||
|
||||
rc_t _poll( port_t* p, unsigned timeOutMs )
|
||||
// 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 )
|
||||
{
|
||||
readN_Ref += n;
|
||||
|
||||
if( buf == nullptr || bufByteN == 0 )
|
||||
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 )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
int sysRC;
|
||||
|
||||
if((sysRC = ::poll(&p->_pollfd,1,timeOutMs)) == 0)
|
||||
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)
|
||||
rc = kTimeOutRC;
|
||||
else
|
||||
{
|
||||
// ::poll() encountered a system exception
|
||||
if( sysRC < 0 )
|
||||
rc = cwLogSysError(kReadFailRC,errno,"Poll failed on serial port.");
|
||||
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 )
|
||||
// ... then read the data
|
||||
if((rc = _receive( d, p, readN_Ref, buf, bufByteN )) != kOkRC )
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
rc_t _destroy( port_t* p )
|
||||
rc_t _closePort( device_t* d, port_t* p )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
if (tcsetattr(p->_deviceH, TCSANOW, &p->_ttyAttrs) == -1)
|
||||
{
|
||||
rc = cwLogSysError(kSetAttrFailRC,errno,"Error resetting tty attributes on serial device '%s'.",p->_deviceStr);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
// if the port is already closed
|
||||
if( p->_deviceH != -1 )
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
if (tcsetattr(p->_deviceH, TCSANOW, &p->_ttyAttrs) == -1)
|
||||
{
|
||||
rc = cwLogSysError(kSetAttrFailRC,errno,"Error resetting tty attributes on serial device '%s'.",p->_deviceStr);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
if( ::close(p->_deviceH ) != 0 )
|
||||
{
|
||||
rc = cwLogSysError(kCloseFailRC,errno,"Port close failed on serial dvice '%s'.", p->_deviceStr);
|
||||
goto errLabel;
|
||||
}
|
||||
|
||||
_setClosedState(p);
|
||||
}
|
||||
|
||||
mem::release(p);
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
errLabel:
|
||||
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);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
@ -114,18 +252,89 @@ namespace cw
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::serialPort::create( handle_t& h, const char* deviceStr, unsigned baudRate, unsigned cfgFlags, callbackFunc_t cbFunc, void* cbArg )
|
||||
cw::rc_t cw::serialPort::create( handle_t& h, unsigned recvBufByteN )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
struct termios options;
|
||||
|
||||
// if the port is already open then close it
|
||||
rc_t rc;
|
||||
if((rc = destroy(h)) != kOkRC )
|
||||
return rc;
|
||||
|
||||
port_t* p = mem::allocZ<port_t>();
|
||||
device_t* d = mem::allocZ<device_t>();
|
||||
|
||||
p->_deviceH = -1;
|
||||
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;
|
||||
|
||||
// if a new port record must be allocated
|
||||
if( p == nullptr )
|
||||
{
|
||||
unsigned portN = 0;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// open the port
|
||||
if( (p->_deviceH = ::open(deviceStr, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1 )
|
||||
@ -213,7 +422,6 @@ cw::rc_t cw::serialPort::create( handle_t& h, const char* deviceStr, unsigned ba
|
||||
// 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));
|
||||
@ -230,41 +438,46 @@ cw::rc_t cw::serialPort::create( handle_t& h, const char* deviceStr, unsigned ba
|
||||
// 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;
|
||||
}
|
||||
|
||||
memset(&p->_pollfd,0,sizeof(p->_pollfd));
|
||||
p->_pollfd.fd = p->_deviceH;
|
||||
p->_pollfd.events = POLLIN;
|
||||
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;
|
||||
|
||||
p->_deviceStr = cw::mem::allocStr( deviceStr );
|
||||
p->_baudRate = baudRate;
|
||||
p->_cfgFlags = cfgFlags;
|
||||
p->_cbFunc = cbFunc;
|
||||
p->_cbArg = cbArg;
|
||||
|
||||
h.set(p);
|
||||
h.set(d);
|
||||
|
||||
errLabel:
|
||||
if( rc != kOkRC )
|
||||
_destroy(p);
|
||||
_closePort(d,p);
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
cw::rc_t cw::serialPort::destroy(handle_t& h )
|
||||
cw::rc_t cw::serialPort::destroyPort(handle_t h, unsigned userId )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
if( !isopen(h) )
|
||||
if( !isopen(h,userId) )
|
||||
return rc;
|
||||
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
|
||||
if((rc = _destroy(p)) != kOkRC )
|
||||
// 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 )
|
||||
return rc;
|
||||
|
||||
h.clear();
|
||||
@ -272,27 +485,48 @@ cw::rc_t cw::serialPort::destroy(handle_t& h )
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool cw::serialPort::isopen( handle_t h)
|
||||
unsigned cw::serialPort::portCount( handle_t h )
|
||||
{
|
||||
if( !h.isValid() )
|
||||
return false;
|
||||
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
return p->_deviceH != -1;
|
||||
device_t* d = _handleToPtr(h);
|
||||
return d->_pollfdN;
|
||||
}
|
||||
|
||||
cw::rc_t cw::serialPort::send( handle_t h, const void* byteA, unsigned byteN )
|
||||
unsigned cw::serialPort::portIndexToId( handle_t h, unsigned index )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
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;
|
||||
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
return kInvalidId;
|
||||
}
|
||||
|
||||
if( !isopen(h) )
|
||||
return cwLogWarningRC( kResourceNotAvailableRC, "An attempt was made to transmit from a closed serial port.");
|
||||
|
||||
bool cw::serialPort::isopen( handle_t h, unsigned userId )
|
||||
{
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
|
||||
if((p = _idToPort(d,userId)) == nullptr )
|
||||
return false;
|
||||
|
||||
return _isPortOpen(p);
|
||||
}
|
||||
|
||||
cw::rc_t cw::serialPort::send( handle_t h, unsigned userId, const void* byteA, unsigned byteN )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
|
||||
if( byteN == 0 )
|
||||
return rc;
|
||||
|
||||
if((p = _idToPort(d,userId)) == nullptr )
|
||||
return kInvalidArgRC;
|
||||
|
||||
|
||||
// implement a non blocking write - if less than all the bytes were written then iterate
|
||||
unsigned i = 0;
|
||||
do
|
||||
@ -314,91 +548,77 @@ cw::rc_t cw::serialPort::send( handle_t h, const void* byteA, unsigned byteN )
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::serialPort::receive( handle_t h, unsigned& readN_Ref)
|
||||
// Non-blocking read
|
||||
cw::rc_t cw::serialPort::receive_nb( handle_t h, unsigned userId, unsigned& readN_Ref, void* buf, unsigned bufN)
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
const unsigned bufN = 512;
|
||||
char buf[ bufN ];
|
||||
rc_t rc = kOkRC;
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
|
||||
readN_Ref = 0;
|
||||
if((p = _idToPort(d,userId)) == nullptr )
|
||||
return kInvalidArgRC;
|
||||
|
||||
if((rc = receive(h,buf,bufN,readN_Ref)) == kOkRC )
|
||||
if( readN_Ref > 0 && p->_cbFunc != nullptr )
|
||||
p->_cbFunc( p->_cbArg, buf, readN_Ref );
|
||||
return _receive( d, p, readN_Ref, buf,bufN );
|
||||
|
||||
return rc;
|
||||
|
||||
}
|
||||
|
||||
cw::rc_t cw::serialPort::receive( handle_t h, unsigned timeOutMs, unsigned& readN_Ref)
|
||||
{
|
||||
rc_t rc;
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
device_t* d = _handleToPtr(h);
|
||||
|
||||
if((rc = _poll(p,timeOutMs)) == kOkRC )
|
||||
rc = receive(h,readN_Ref);
|
||||
return rc;
|
||||
return _poll(d,timeOutMs,readN_Ref);
|
||||
|
||||
}
|
||||
|
||||
cw::rc_t cw::serialPort::receive( handle_t h, void* buf, unsigned bufN, unsigned& readN_Ref)
|
||||
cw::rc_t cw::serialPort::receive( handle_t h, unsigned timeOutMs, unsigned& readN_Ref, unsigned userId, void* buf, unsigned bufByteN )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
|
||||
readN_Ref = 0;
|
||||
if((p = _idToPort(d,userId)) == nullptr )
|
||||
return kInvalidArgRC;
|
||||
|
||||
if( !isopen(h) )
|
||||
return cwLogWarningRC( kResourceNotAvailableRC, "An attempt was made to read from a closed serial port.");
|
||||
|
||||
int n = 0;
|
||||
|
||||
// if attempt to read the port succeeded ...
|
||||
if((n =read( p->_deviceH, buf, bufN )) != -1 )
|
||||
readN_Ref = 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;
|
||||
return _poll(d,timeOutMs,readN_Ref,userId,buf,bufByteN);
|
||||
}
|
||||
|
||||
cw::rc_t cw::serialPort::receive( handle_t h, void* buf, unsigned bufByteN, unsigned timeOutMs, unsigned& readN_Ref )
|
||||
const char* cw::serialPort::device( handle_t h, unsigned userId)
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
if((rc = _poll(p,timeOutMs)) == kOkRC )
|
||||
rc = receive(h,buf,bufByteN,readN_Ref);
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
if((p = _idToPort(d,userId)) == NULL)
|
||||
return NULL;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
const char* cw::serialPort::device( handle_t h)
|
||||
{
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
return p->_deviceStr;
|
||||
}
|
||||
|
||||
unsigned cw::serialPort::baudRate( handle_t h)
|
||||
unsigned cw::serialPort::baudRate( handle_t h, unsigned userId )
|
||||
{
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
if((p = _idToPort(d,userId)) == NULL)
|
||||
return 0;
|
||||
|
||||
return p->_baudRate;
|
||||
}
|
||||
|
||||
unsigned cw::serialPort::cfgFlags( handle_t h)
|
||||
unsigned cw::serialPort::cfgFlags( handle_t h, unsigned userId )
|
||||
{
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
if((p = _idToPort(d,userId)) == NULL)
|
||||
return 0;
|
||||
|
||||
return p->_cfgFlags;
|
||||
}
|
||||
|
||||
unsigned cw::serialPort::readInBaudRate( handle_t h )
|
||||
unsigned cw::serialPort::readInBaudRate( handle_t h, unsigned userId )
|
||||
{
|
||||
struct termios attr;
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
if((p = _idToPort(d,userId)) == NULL)
|
||||
return 0;
|
||||
|
||||
if((_getAttributes(p,attr)) != kOkRC )
|
||||
return 0;
|
||||
@ -407,10 +627,13 @@ unsigned cw::serialPort::readInBaudRate( handle_t h )
|
||||
|
||||
}
|
||||
|
||||
unsigned cw::serialPort::readOutBaudRate( handle_t h)
|
||||
unsigned cw::serialPort::readOutBaudRate( handle_t h, unsigned userId )
|
||||
{
|
||||
struct termios attr;
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
if((p = _idToPort(d,userId)) == NULL)
|
||||
return 0;
|
||||
|
||||
if((_getAttributes(p,attr)) != kOkRC )
|
||||
return 0;
|
||||
@ -419,11 +642,15 @@ unsigned cw::serialPort::readOutBaudRate( handle_t h)
|
||||
|
||||
}
|
||||
|
||||
unsigned cw::serialPort::readCfgFlags( handle_t h)
|
||||
unsigned cw::serialPort::readCfgFlags( handle_t h, unsigned userId )
|
||||
{
|
||||
struct termios attr;
|
||||
unsigned result = 0;
|
||||
port_t* p = _portHandleToPtr(h);
|
||||
unsigned result = 0;
|
||||
device_t* d = _handleToPtr(h);
|
||||
port_t* p;
|
||||
|
||||
if((p = _idToPort(d,userId)) == NULL)
|
||||
return 0;
|
||||
|
||||
if((_getAttributes(p,attr)) == false )
|
||||
return 0;
|
||||
|
@ -31,42 +31,49 @@ namespace cw
|
||||
};
|
||||
|
||||
|
||||
typedef handle<struct port_str> handle_t;
|
||||
typedef void (*callbackFunc_t)( void* cbArg, const void* byteA, unsigned byteN );
|
||||
typedef handle<struct device_str> handle_t;
|
||||
typedef void (*callbackFunc_t)( void* cbArg, unsigned userId, const void* byteA, unsigned byteN );
|
||||
|
||||
rc_t create( handle_t& h, unsigned recvBufByteN=512 );
|
||||
rc_t destroy( handle_t& h );
|
||||
|
||||
rc_t createPort( handle_t h, unsigned userId, const char* device, unsigned baudRate, unsigned cfgFlags, callbackFunc_t cbFunc, void* cbArg );
|
||||
rc_t destroyPort( handle_t h, unsigned userId );
|
||||
|
||||
unsigned portCount( handle_t h );
|
||||
unsigned portIndexToId( handle_t h, unsigned index );
|
||||
|
||||
bool isopen( handle_t h, unsigned userId );
|
||||
|
||||
rc_t send( handle_t h, unsigned userId, const void* byteA, unsigned byteN );
|
||||
|
||||
|
||||
rc_t create( handle_t& h, const char* device, unsigned baudRate, unsigned cfgFlags, callbackFunc_t cbFunc, void* cbArg );
|
||||
rc_t destroy(handle_t& h );
|
||||
|
||||
bool isopen( handle_t h);
|
||||
|
||||
rc_t send( handle_t h, const void* byteA, unsigned byteN );
|
||||
// Non-blocking - Receive data from a specific port if data is available.
|
||||
// If buf==nullptr then use the ports callback function to deliver the received data,
|
||||
// otherwise return the data in buf[bufByteN].
|
||||
rc_t receive_nb( handle_t h, unsigned userId, unsigned& readN_Ref, void* buf=nullptr, unsigned bufByteN=0);
|
||||
|
||||
|
||||
// Make callback to listener with result of read - Non-blocking
|
||||
rc_t receive( handle_t h, unsigned& readN_Ref);
|
||||
|
||||
// Make callback to listener with result of read - Block for up to timeOutMs.
|
||||
// Blocking - Wait up to timeOutMs milliseconds for data to be available on any of the ports.
|
||||
// Deliver received data via the port callback function.
|
||||
// readN_Ref returns the total count of bytes read across all ports.
|
||||
rc_t receive( handle_t h, unsigned timeOutMs, unsigned& readN_Ref);
|
||||
|
||||
// Return result of read in buf[bufByteN] - Non-blocking.
|
||||
rc_t receive( handle_t h, void* buf, unsigned bufByteN, unsigned& readN_Ref);
|
||||
// Blocking - Wait up to timeOutMs milliseconds for data to be available on a specific port. Return the received data in buf[bufByteN].
|
||||
rc_t receive( handle_t h, unsigned timeOutMs, unsigned& readN_Ref, unsigned userId, void* buf, unsigned bufByteN );
|
||||
|
||||
// Return result of read in buf[bufByteN] - Block for up to timeOutMs.
|
||||
rc_t receive( handle_t h, void* buf, unsigned bufByteN, unsigned timeOutMs, unsigned& readN_Ref );
|
||||
|
||||
const char* device( handle_t h);
|
||||
const char* device( handle_t h, unsigned userId );
|
||||
|
||||
// Get the baud rate and cfgFlags used to initialize the port
|
||||
unsigned baudRate( handle_t h);
|
||||
unsigned cfgFlags( handle_t h);
|
||||
unsigned baudRate( handle_t h, unsigned userId );
|
||||
unsigned cfgFlags( handle_t h, unsigned userId );
|
||||
|
||||
// Get the baud rate and cfg flags by reading the device.
|
||||
// Note the the returned buad rate is a system id rather than the actual baud rate,
|
||||
// however the cfgFlags are converted to the same kXXXFl defined in this class.
|
||||
unsigned readInBaudRate( handle_t h );
|
||||
unsigned readOutBaudRate( handle_t h);
|
||||
unsigned readCfgFlags( handle_t h);
|
||||
unsigned readInBaudRate( handle_t h, unsigned userId );
|
||||
unsigned readOutBaudRate( handle_t h, unsigned userId );
|
||||
unsigned readCfgFlags( handle_t h, unsigned userId );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,9 +13,9 @@ namespace cw
|
||||
{
|
||||
typedef struct this_str
|
||||
{
|
||||
serialPort::handle_t portH;
|
||||
serialPort::handle_t mgrH;
|
||||
thread::handle_t threadH;
|
||||
unsigned _pollPeriodMs;
|
||||
unsigned pollPeriodMs;
|
||||
} this_t;
|
||||
|
||||
inline this_t* _handleToPtr( handle_t h ) { return handleToPtr<handle_t,this_t>(h); }
|
||||
@ -24,24 +24,56 @@ namespace cw
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
if((rc = serialPort::destroy(p->portH)) != kOkRC )
|
||||
return rc;
|
||||
|
||||
if((rc = thread::destroy(p->threadH)) != kOkRC )
|
||||
return rc;
|
||||
|
||||
if((rc = serialPort::destroy(p->mgrH)) != kOkRC )
|
||||
return rc;
|
||||
|
||||
|
||||
mem::release(p);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Do periodic non-blocking reads of each serial port and sleep between reads.
|
||||
bool threadCallbackAlt( void* arg )
|
||||
{
|
||||
this_t* p = static_cast<this_t*>(arg);
|
||||
unsigned portN = serialPort::portCount( p->mgrH );
|
||||
|
||||
unsigned readN;
|
||||
|
||||
for(unsigned i=0; i<portN; ++i)
|
||||
{
|
||||
|
||||
rc_t rc = serialPort::receive_nb( p->mgrH, serialPort::portIndexToId(p->mgrH,i), readN );
|
||||
|
||||
if( rc != kOkRC && rc != kTimeOutRC )
|
||||
{
|
||||
cwLogError(rc,"Serial server receive failed.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( readN == 0)
|
||||
sleepMs(20);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wait for data to arrive on any port.
|
||||
bool threadCallback( void* arg )
|
||||
{
|
||||
this_t* p = static_cast<this_t*>(arg);
|
||||
|
||||
unsigned readN;
|
||||
if( serialPort::isopen(p->portH) )
|
||||
serialPort::receive(p->portH,p->_pollPeriodMs,readN);
|
||||
rc_t rc = serialPort::receive(p->mgrH,p->pollPeriodMs,readN);
|
||||
if( rc != kOkRC && rc != kTimeOutRC )
|
||||
{
|
||||
cwLogError(rc,"Serial server receive failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
@ -51,19 +83,19 @@ namespace cw
|
||||
|
||||
|
||||
|
||||
cw::rc_t cw::serialPortSrv::create( handle_t& h, const char* deviceStr, unsigned baudRate, unsigned cfgFlags, serialPort::callbackFunc_t cbFunc, void* cbArg, unsigned pollPeriodMs )
|
||||
cw::rc_t cw::serialPortSrv::create( handle_t& h, unsigned pollPeriodMs, unsigned recvBufByteN )
|
||||
{
|
||||
rc_t rc = kOkRC;
|
||||
|
||||
this_t* p = mem::allocZ<this_t>();
|
||||
|
||||
if((rc = serialPort::create( p->portH, deviceStr, baudRate, cfgFlags, cbFunc, cbArg )) != kOkRC )
|
||||
goto errLabel;
|
||||
if((rc = serialPort::create( p->mgrH, recvBufByteN)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
if((rc = thread::create( p->threadH, threadCallback, p)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
p->_pollPeriodMs = pollPeriodMs;
|
||||
p->pollPeriodMs = pollPeriodMs;
|
||||
|
||||
errLabel:
|
||||
if( rc != kOkRC )
|
||||
@ -91,10 +123,10 @@ cw::rc_t cw::serialPortSrv::destroy(handle_t& h )
|
||||
}
|
||||
|
||||
|
||||
cw::serialPort::handle_t cw::serialPortSrv::portHandle( handle_t h )
|
||||
cw::serialPort::handle_t cw::serialPortSrv::serialHandle( handle_t h )
|
||||
{
|
||||
this_t* p = _handleToPtr(h);
|
||||
return p->portH;
|
||||
return p->mgrH;
|
||||
}
|
||||
|
||||
cw::thread::handle_t cw::serialPortSrv::threadHandle( handle_t h )
|
||||
@ -116,22 +148,22 @@ cw::rc_t cw::serialPortSrv::pause( handle_t h )
|
||||
}
|
||||
|
||||
|
||||
cw::rc_t cw::serialPortSrv::send( handle_t h, const void* byteA, unsigned byteN )
|
||||
cw::rc_t cw::serialPortSrv::send( handle_t h, unsigned portId, const void* byteA, unsigned byteN )
|
||||
{
|
||||
this_t* p = _handleToPtr(h);
|
||||
return cw::serialPort::send(p->portH,byteA,byteN);
|
||||
return cw::serialPort::send(p->mgrH,portId,byteA,byteN);
|
||||
}
|
||||
|
||||
|
||||
namespace cw
|
||||
{
|
||||
|
||||
void serialPortSrvTestCb( void* arg, const void* byteA, unsigned byteN )
|
||||
void serialPortSrvTestCb( void* arg, unsigned userId, const void* byteA, unsigned byteN )
|
||||
{
|
||||
const char* text = static_cast<const char*>(byteA);
|
||||
|
||||
for(unsigned i=0; i<byteN; ++i)
|
||||
printf("%c:%i ",text[i],(int)text[i]);
|
||||
printf("id:%i %c:%i\n",userId,text[i],(int)text[i]);
|
||||
|
||||
if( byteN )
|
||||
fflush(stdout);
|
||||
@ -141,18 +173,30 @@ namespace cw
|
||||
cw::rc_t cw::serialPortSrvTest()
|
||||
{
|
||||
// Use this test an Arduino running study/serial/arduino_xmt_rcv/main.c
|
||||
rc_t rc = kOkRC;
|
||||
const char* device = "/dev/ttyACM0";
|
||||
unsigned baud = 38400;
|
||||
unsigned serialCfgFlags = serialPort::kDefaultCfgFlags;
|
||||
unsigned pollPeriodMs = 50;
|
||||
serialPortSrv::handle_t h;
|
||||
|
||||
rc = serialPortSrv::create(h,device,baud,serialCfgFlags,&serialPortSrvTestCb,nullptr,pollPeriodMs);
|
||||
rc_t rc = kOkRC;
|
||||
bool quitFl = false;
|
||||
unsigned pollPeriodMs = 50;
|
||||
unsigned portId[] = {0,1};
|
||||
const char* device[] = {"/dev/ttyACM1","/dev/ttyACM0"};
|
||||
unsigned baud[] = {38400,38400};
|
||||
unsigned serialCfgFlags[] = {serialPort::kDefaultCfgFlags,serialPort::kDefaultCfgFlags};
|
||||
unsigned portN = 2; //sizeof(portId)/sizeof(portId[0]);
|
||||
unsigned portIdx = 0;
|
||||
serialPortSrv::handle_t h;
|
||||
|
||||
// open the serial port mgr
|
||||
if((rc = serialPortSrv::create(h,pollPeriodMs)) != kOkRC )
|
||||
return rc;
|
||||
|
||||
// open the serial ports
|
||||
for(unsigned i=0; i<portN; ++i)
|
||||
if((rc = serialPort::createPort( serialPortSrv::serialHandle(h), portId[i], device[i], baud[i], serialCfgFlags[i], &serialPortSrvTestCb, nullptr)) != kOkRC )
|
||||
goto errLabel;
|
||||
|
||||
// start the server
|
||||
serialPortSrv::start(h);
|
||||
|
||||
bool quitFl = false;
|
||||
printf("q=quit\n");
|
||||
while(!quitFl)
|
||||
{
|
||||
@ -162,11 +206,17 @@ cw::rc_t cw::serialPortSrvTest()
|
||||
quitFl = true;
|
||||
else
|
||||
if( '0' <= c and c <= 'z' )
|
||||
serialPortSrv::send(h,&c,1);
|
||||
{
|
||||
// send the output to consecutive ports
|
||||
serialPortSrv::send(h,portId[portIdx],&c,1);
|
||||
portIdx = (portIdx+1) % portN;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
errLabel:
|
||||
|
||||
serialPortSrv::destroy(h);
|
||||
return rc;
|
||||
}
|
||||
|
@ -7,16 +7,16 @@ namespace cw
|
||||
{
|
||||
typedef handle<struct this_str> handle_t;
|
||||
|
||||
rc_t create( handle_t& h, const char* device, unsigned baudRate, unsigned cfgFlags, serialPort::callbackFunc_t cbFunc, void* cbArg, unsigned pollPeriodMs );
|
||||
rc_t create( handle_t& h, unsigned pollPeriodMs=50, unsigned recvBufByteN=512 );
|
||||
rc_t destroy(handle_t& h );
|
||||
|
||||
serialPort::handle_t portHandle( handle_t h );
|
||||
serialPort::handle_t serialHandle( handle_t h );
|
||||
thread::handle_t threadHandle( handle_t h );
|
||||
|
||||
rc_t start( handle_t h );
|
||||
rc_t pause( handle_t h );
|
||||
|
||||
rc_t send( handle_t h, const void* byteA, unsigned byteN );
|
||||
rc_t send( handle_t h, unsigned portId, const void* byteA, unsigned byteN );
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user