cwSerialPort.h/cpp,cwSerialPortSrv.h/cpp : serialPort now manages multiple ports.

This commit is contained in:
kpl 2020-03-05 22:04:53 -05:00
parent 24c68c158f
commit 465d7f6fd6
4 changed files with 486 additions and 202 deletions

View File

@ -17,31 +17,52 @@ namespace cw
{ {
typedef struct port_str typedef struct port_str
{ {
const char* _deviceStr; unsigned _userId;
int _deviceH; char* _deviceStr;
unsigned _baudRate; int _deviceH;
unsigned _cfgFlags; unsigned _baudRate;
callbackFunc_t _cbFunc; unsigned _cfgFlags;
void* _cbArg; callbackFunc_t _cbFunc;
struct termios _ttyAttrs; void* _cbArg;
struct pollfd _pollfd; struct termios _ttyAttrs;
struct pollfd* _pollfd;
struct port_str* _link;
} port_t; } port_t;
inline port_t* _portHandleToPtr(handle_t h){ return handleToPtr<handle_t,port_t>(h); } typedef struct device_str
void _setClosedState( port_t* p )
{ {
if( p->_deviceStr != nullptr ) unsigned _recvBufByteN;
cw::mem::free(const_cast<char*>(p->_deviceStr)); void* _recvBuf;
port_t* _portL;
p->_deviceH = -1; unsigned _pollfdN;
p->_deviceStr = nullptr; struct pollfd* _pollfd;
p->_baudRate = 0; } device_t;
p->_cfgFlags = 0;
p->_cbFunc = nullptr; inline device_t* _handleToPtr(handle_t h)
p->_cbArg = nullptr; { 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 ) rc_t _getAttributes( port_t* p, struct termios& attr )
{ {
@ -50,62 +71,179 @@ namespace cw
return kOkRC; 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; rc_t rc = kOkRC;
int sysRC; 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; rc = kTimeOutRC;
else else
{ {
// ::poll() encountered a system exception
if( sysRC < 0 ) 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; return rc;
} }
rc_t _destroy( port_t* p ) rc_t _closePort( device_t* d, port_t* p )
{ {
rc_t rc = kOkRC; 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 // if the port is already closed
// 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( p->_deviceH != -1 ) 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 ) if( ::close(p->_deviceH ) != 0 )
{ {
rc = cwLogSysError(kCloseFailRC,errno,"Port close failed on serial dvice '%s'.", p->_deviceStr); rc = cwLogSysError(kCloseFailRC,errno,"Port close failed on serial dvice '%s'.", p->_deviceStr);
goto errLabel; 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: 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; return rc;
} }
@ -114,19 +252,90 @@ 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; rc_t rc;
struct termios options;
// if the port is already open then close it
if((rc = destroy(h)) != kOkRC ) if((rc = destroy(h)) != kOkRC )
return rc; 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 // open the port
if( (p->_deviceH = ::open(deviceStr, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1 ) if( (p->_deviceH = ::open(deviceStr, O_RDWR | O_NOCTTY | O_NONBLOCK)) == -1 )
{ {
@ -212,7 +421,6 @@ cw::rc_t cw::serialPort::create( handle_t& h, const char* deviceStr, unsigned ba
// set two stop bits // set two stop bits
cwEnaBits( options.c_cflag, CSTOPB, cwIsFlag(cfgFlags, k2StopBitFl)); cwEnaBits( options.c_cflag, CSTOPB, cwIsFlag(cfgFlags, k2StopBitFl));
// set hardware flow control // set hardware flow control
//cwEnaBits(options.c_cflag, CCTS_OFLOW, cwIsFlag(cfgFlags, kCTS_OutFlowCtlFl)); //cwEnaBits(options.c_cflag, CCTS_OFLOW, cwIsFlag(cfgFlags, kCTS_OutFlowCtlFl));
@ -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. // Cause the new options to take effect immediately.
if (tcsetattr(p->_deviceH, TCSANOW, &options) == -1) if (tcsetattr(p->_deviceH, TCSANOW, &options) == -1)
{ {
rc = cwLogSysError(kSetAttrFailRC,errno,"Error setting tty attributes on serial device %.", deviceStr); rc = cwLogSysError(kSetAttrFailRC,errno,"Error setting tty attributes on serial device %.", deviceStr);
goto errLabel; goto errLabel;
} }
memset(&p->_pollfd,0,sizeof(p->_pollfd)); p->_userId = userId;
p->_pollfd.fd = p->_deviceH; p->_deviceStr = cw::mem::allocStr( deviceStr );
p->_pollfd.events = POLLIN; 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 ); h.set(d);
p->_baudRate = baudRate;
p->_cfgFlags = cfgFlags;
p->_cbFunc = cbFunc;
p->_cbArg = cbArg;
h.set(p);
errLabel: errLabel:
if( rc != kOkRC ) if( rc != kOkRC )
_destroy(p); _closePort(d,p);
return rc; 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; rc_t rc = kOkRC;
if( !isopen(h) ) if( !isopen(h,userId) )
return rc; 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; return rc;
h.clear(); h.clear();
@ -272,26 +485,47 @@ cw::rc_t cw::serialPort::destroy(handle_t& h )
return rc; return rc;
} }
bool cw::serialPort::isopen( handle_t h) unsigned cw::serialPort::portCount( handle_t h )
{ {
if( !h.isValid() ) device_t* d = _handleToPtr(h);
return false; return d->_pollfdN;
port_t* p = _portHandleToPtr(h);
return p->_deviceH != -1;
} }
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;
return kInvalidId;
}
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;
port_t* p = _portHandleToPtr(h);
if( !isopen(h) )
return cwLogWarningRC( kResourceNotAvailableRC, "An attempt was made to transmit from a closed serial port.");
if( byteN == 0 ) if( byteN == 0 )
return rc; 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 // implement a non blocking write - if less than all the bytes were written then iterate
unsigned i = 0; unsigned i = 0;
@ -314,92 +548,78 @@ 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; rc_t rc = kOkRC;
port_t* p = _portHandleToPtr(h); device_t* d = _handleToPtr(h);
const unsigned bufN = 512; port_t* p;
char buf[ bufN ];
if((p = _idToPort(d,userId)) == nullptr )
return kInvalidArgRC;
return _receive( d, p, readN_Ref, buf,bufN );
readN_Ref = 0;
if((rc = receive(h,buf,bufN,readN_Ref)) == kOkRC )
if( readN_Ref > 0 && p->_cbFunc != nullptr )
p->_cbFunc( p->_cbArg, buf, readN_Ref );
return rc; return rc;
} }
cw::rc_t cw::serialPort::receive( handle_t h, unsigned timeOutMs, unsigned& readN_Ref) cw::rc_t cw::serialPort::receive( handle_t h, unsigned timeOutMs, unsigned& readN_Ref)
{ {
rc_t rc; device_t* d = _handleToPtr(h);
port_t* p = _portHandleToPtr(h);
if((rc = _poll(p,timeOutMs)) == kOkRC ) return _poll(d,timeOutMs,readN_Ref);
rc = receive(h,readN_Ref);
return rc;
} }
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; device_t* d = _handleToPtr(h);
port_t* p = _portHandleToPtr(h); port_t* p;
readN_Ref = 0; if((p = _idToPort(d,userId)) == nullptr )
return kInvalidArgRC;
if( !isopen(h) ) return _poll(d,timeOutMs,readN_Ref,userId,buf,bufByteN);
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;
} }
cw::rc_t cw::serialPort::receive( handle_t h, void* buf, unsigned bufByteN, unsigned timeOutMs, unsigned& readN_Ref )
{
rc_t rc = kOkRC;
port_t* p = _portHandleToPtr(h);
if((rc = _poll(p,timeOutMs)) == kOkRC )
rc = receive(h,buf,bufByteN,readN_Ref);
return rc; const char* cw::serialPort::device( handle_t h, unsigned userId)
}
const char* cw::serialPort::device( handle_t h)
{ {
port_t* p = _portHandleToPtr(h); device_t* d = _handleToPtr(h);
port_t* p;
if((p = _idToPort(d,userId)) == NULL)
return NULL;
return p->_deviceStr; 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; 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; return p->_cfgFlags;
} }
unsigned cw::serialPort::readInBaudRate( handle_t h ) unsigned cw::serialPort::readInBaudRate( handle_t h, unsigned userId )
{ {
struct termios attr; 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 ) if((_getAttributes(p,attr)) != kOkRC )
return 0; return 0;
@ -407,11 +627,14 @@ 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; 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 ) if((_getAttributes(p,attr)) != kOkRC )
return 0; return 0;
@ -419,12 +642,16 @@ 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; struct termios attr;
unsigned result = 0; unsigned result = 0;
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)) == false ) if((_getAttributes(p,attr)) == false )
return 0; return 0;

View File

@ -31,42 +31,49 @@ namespace cw
}; };
typedef handle<struct port_str> handle_t; typedef handle<struct device_str> handle_t;
typedef void (*callbackFunc_t)( void* cbArg, const void* byteA, unsigned byteN ); 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 create( handle_t& h, const char* device, unsigned baudRate, unsigned cfgFlags, callbackFunc_t cbFunc, void* cbArg ); rc_t createPort( handle_t h, unsigned userId, const char* device, unsigned baudRate, unsigned cfgFlags, callbackFunc_t cbFunc, void* cbArg );
rc_t destroy(handle_t& h ); rc_t destroyPort( handle_t h, unsigned userId );
bool isopen( handle_t h); 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, const void* byteA, unsigned byteN ); rc_t send( handle_t h, unsigned userId, const void* byteA, unsigned byteN );
// Make callback to listener with result of read - Non-blocking // Non-blocking - Receive data from a specific port if data is available.
rc_t receive( handle_t h, unsigned& readN_Ref); // 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 - 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); rc_t receive( handle_t h, unsigned timeOutMs, unsigned& readN_Ref);
// Return result of read in buf[bufByteN] - Non-blocking. // 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, void* buf, unsigned bufByteN, unsigned& readN_Ref); 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 // Get the baud rate and cfgFlags used to initialize the port
unsigned baudRate( handle_t h); unsigned baudRate( handle_t h, unsigned userId );
unsigned cfgFlags( handle_t h); unsigned cfgFlags( handle_t h, unsigned userId );
// Get the baud rate and cfg flags by reading the device. // 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, // 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. // however the cfgFlags are converted to the same kXXXFl defined in this class.
unsigned readInBaudRate( handle_t h ); unsigned readInBaudRate( handle_t h, unsigned userId );
unsigned readOutBaudRate( handle_t h); unsigned readOutBaudRate( handle_t h, unsigned userId );
unsigned readCfgFlags( handle_t h); unsigned readCfgFlags( handle_t h, unsigned userId );
} }
} }

View File

@ -13,9 +13,9 @@ namespace cw
{ {
typedef struct this_str typedef struct this_str
{ {
serialPort::handle_t portH; serialPort::handle_t mgrH;
thread::handle_t threadH; thread::handle_t threadH;
unsigned _pollPeriodMs; unsigned pollPeriodMs;
} this_t; } this_t;
inline this_t* _handleToPtr( handle_t h ) { return handleToPtr<handle_t,this_t>(h); } inline this_t* _handleToPtr( handle_t h ) { return handleToPtr<handle_t,this_t>(h); }
@ -23,25 +23,57 @@ namespace cw
rc_t _destroy( this_t* p ) rc_t _destroy( this_t* p )
{ {
rc_t rc = kOkRC; rc_t rc = kOkRC;
if((rc = serialPort::destroy(p->portH)) != kOkRC )
return rc;
if((rc = thread::destroy(p->threadH)) != kOkRC ) if((rc = thread::destroy(p->threadH)) != kOkRC )
return rc; return rc;
if((rc = serialPort::destroy(p->mgrH)) != kOkRC )
return rc;
mem::release(p); mem::release(p);
return rc; 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 ) bool threadCallback( void* arg )
{ {
this_t* p = static_cast<this_t*>(arg); this_t* p = static_cast<this_t*>(arg);
unsigned readN; unsigned readN;
if( serialPort::isopen(p->portH) ) rc_t rc = serialPort::receive(p->mgrH,p->pollPeriodMs,readN);
serialPort::receive(p->portH,p->_pollPeriodMs,readN); if( rc != kOkRC && rc != kTimeOutRC )
{
cwLogError(rc,"Serial server receive failed.");
return false;
}
return true; 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; rc_t rc = kOkRC;
this_t* p = mem::allocZ<this_t>(); this_t* p = mem::allocZ<this_t>();
if((rc = serialPort::create( p->portH, deviceStr, baudRate, cfgFlags, cbFunc, cbArg )) != kOkRC ) if((rc = serialPort::create( p->mgrH, recvBufByteN)) != kOkRC )
goto errLabel; goto errLabel;
if((rc = thread::create( p->threadH, threadCallback, p)) != kOkRC ) if((rc = thread::create( p->threadH, threadCallback, p)) != kOkRC )
goto errLabel; goto errLabel;
p->_pollPeriodMs = pollPeriodMs; p->pollPeriodMs = pollPeriodMs;
errLabel: errLabel:
if( rc != kOkRC ) 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); this_t* p = _handleToPtr(h);
return p->portH; return p->mgrH;
} }
cw::thread::handle_t cw::serialPortSrv::threadHandle( handle_t h ) 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); this_t* p = _handleToPtr(h);
return cw::serialPort::send(p->portH,byteA,byteN); return cw::serialPort::send(p->mgrH,portId,byteA,byteN);
} }
namespace cw 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); const char* text = static_cast<const char*>(byteA);
for(unsigned i=0; i<byteN; ++i) 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 ) if( byteN )
fflush(stdout); fflush(stdout);
@ -140,19 +172,31 @@ namespace cw
cw::rc_t cw::serialPortSrvTest() cw::rc_t cw::serialPortSrvTest()
{ {
// Use this test an Arduino running study/serial/arduino_xmt_rcv/main.c // 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); serialPortSrv::start(h);
bool quitFl = false;
printf("q=quit\n"); printf("q=quit\n");
while(!quitFl) while(!quitFl)
{ {
@ -162,11 +206,17 @@ cw::rc_t cw::serialPortSrvTest()
quitFl = true; quitFl = true;
else else
if( '0' <= c and c <= 'z' ) 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); serialPortSrv::destroy(h);
return rc; return rc;
} }

View File

@ -7,16 +7,16 @@ namespace cw
{ {
typedef handle<struct this_str> handle_t; 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 ); 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 ); thread::handle_t threadHandle( handle_t h );
rc_t start( handle_t h ); rc_t start( handle_t h );
rc_t pause( 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 );
} }