From 465d7f6fd6cb74f6b5b6232f1770e6b764fb30ce Mon Sep 17 00:00:00 2001 From: kpl Date: Thu, 5 Mar 2020 22:04:53 -0500 Subject: [PATCH] cwSerialPort.h/cpp,cwSerialPortSrv.h/cpp : serialPort now manages multiple ports. --- cwSerialPort.cpp | 527 +++++++++++++++++++++++++++++++------------- cwSerialPort.h | 47 ++-- cwSerialPortSrv.cpp | 108 ++++++--- cwSerialPortSrv.h | 6 +- 4 files changed, 486 insertions(+), 202 deletions(-) diff --git a/cwSerialPort.cpp b/cwSerialPort.cpp index b18b556..331109a 100644 --- a/cwSerialPort.cpp +++ b/cwSerialPort.cpp @@ -17,31 +17,52 @@ 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(h); } - - void _setClosedState( port_t* p ) + typedef struct device_str { - if( p->_deviceStr != nullptr ) - cw::mem::free(const_cast(p->_deviceStr)); - - p->_deviceH = -1; - p->_deviceStr = nullptr; - p->_baudRate = 0; - p->_cfgFlags = 0; - p->_cbFunc = nullptr; - p->_cbArg = nullptr; + unsigned _recvBufByteN; + void* _recvBuf; + port_t* _portL; + unsigned _pollfdN; + struct pollfd* _pollfd; + } device_t; + + inline device_t* _handleToPtr(handle_t h) + { return handleToPtr(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 ) { @@ -50,62 +71,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,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; - 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(); + device_t* d = mem::allocZ(); - p->_deviceH = -1; + if( recvBufByteN > 0 ) + { + d->_recvBuf = mem::allocZ( 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(); + 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(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 ) { @@ -212,7 +421,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)); @@ -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,26 +485,47 @@ 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; + + 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 ) 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; @@ -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; - port_t* p = _portHandleToPtr(h); - const unsigned bufN = 512; - char buf[ bufN ]; + rc_t rc = kOkRC; + device_t* d = _handleToPtr(h); + port_t* p; + + 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; - } - + 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 ) -{ - 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) + const char* cw::serialPort::device( 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 NULL; + 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,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; - 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,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; - 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; diff --git a/cwSerialPort.h b/cwSerialPort.h index 13a98e8..0eed4bf 100644 --- a/cwSerialPort.h +++ b/cwSerialPort.h @@ -31,42 +31,49 @@ namespace cw }; - typedef handle handle_t; - typedef void (*callbackFunc_t)( void* cbArg, const void* byteA, unsigned byteN ); + typedef handle 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 create( handle_t& h, const char* device, unsigned baudRate, unsigned cfgFlags, callbackFunc_t cbFunc, void* cbArg ); - 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 ); - 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 - rc_t receive( handle_t h, unsigned& readN_Ref); + // 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 - 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); - - // 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 ); + // 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 ); - 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 ); } } diff --git a/cwSerialPortSrv.cpp b/cwSerialPortSrv.cpp index a686e2b..bd73436 100644 --- a/cwSerialPortSrv.cpp +++ b/cwSerialPortSrv.cpp @@ -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(h); } @@ -23,25 +23,57 @@ namespace cw rc_t _destroy( this_t* p ) { 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(arg); + unsigned portN = serialPort::portCount( p->mgrH ); + + unsigned readN; + + for(unsigned i=0; imgrH, 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(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(); - - 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(byteA); for(unsigned i=0; i 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 ); }