1750 lines
46 KiB
C++
1750 lines
46 KiB
C++
/*===========================================================================
|
|
FILE:
|
|
ProtocolServer.cpp
|
|
|
|
DESCRIPTION:
|
|
Generic protocol packet server
|
|
|
|
PUBLIC CLASSES AND METHODS:
|
|
cProtocolServer
|
|
Abstract base class for protocol servers
|
|
|
|
Copyright (c) 2013, The Linux Foundation. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of The Linux Foundation nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
===========================================================================*/
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Include Files
|
|
//---------------------------------------------------------------------------
|
|
#include "StdAfx.h"
|
|
|
|
#include "ProtocolServer.h"
|
|
#include "ProtocolNotification.h"
|
|
|
|
#include <climits>
|
|
|
|
//---------------------------------------------------------------------------
|
|
// Definitions
|
|
//---------------------------------------------------------------------------
|
|
|
|
// Invalid request ID
|
|
const ULONG INVALID_REQUEST_ID = 0;
|
|
|
|
// Default activity timeout value
|
|
const ULONG DEFAULT_WAIT = 100;
|
|
|
|
// MTU (Maximum Transmission Unit) for auxiliary data (QC USB imposed)
|
|
const ULONG MAX_AUX_MTU_SIZE = 1024 * 256;
|
|
|
|
// USB's MaxPacketSize
|
|
const ULONG MAX_PACKET_SIZE = 512;
|
|
|
|
// Maximum amount of time to wait on external access synchronization object
|
|
#ifdef DEBUG
|
|
// For the sake of debugging do not be so quick to assume failure
|
|
const ULONG DEADLOCK_TIME = 180000;
|
|
#else
|
|
const ULONG DEADLOCK_TIME = 10000;
|
|
#endif
|
|
|
|
// Maximum amount of time to wait for the protocol server to process a command
|
|
const ULONG COMMAND_TIME = DEADLOCK_TIME;
|
|
|
|
/*=========================================================================*/
|
|
// Free Methods
|
|
/*=========================================================================*/
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
ScheduleThread (Free Method)
|
|
|
|
DESCRIPTION:
|
|
Watch schedule for event to process or timeout
|
|
|
|
PARAMETERS:
|
|
pArg [ I ] - The protocol server object
|
|
|
|
RETURN VALUE:
|
|
void * - thread exit value (always NULL)
|
|
===========================================================================*/
|
|
void * ScheduleThread( PVOID pArg )
|
|
{
|
|
// Do we have a server?
|
|
cProtocolServer * pServer = (cProtocolServer *)pArg;
|
|
if (pServer == 0)
|
|
{
|
|
TRACE( "ScheduleThread started with empty pArg."
|
|
" Unable to locate cProtocolServer\n" );
|
|
|
|
ASSERT( 0 );
|
|
return NULL;
|
|
}
|
|
|
|
TRACE( "Schedule thread [%lu] started\n",
|
|
pthread_self() );
|
|
|
|
// Default wait event
|
|
timespec toTime = TimeIn( DEFAULT_WAIT );
|
|
|
|
// Return value checking
|
|
int nRet;
|
|
|
|
while (pServer->mbExiting == false)
|
|
{
|
|
DWORD nTemp;
|
|
nRet = pServer->mThreadScheduleEvent.Wait( TimeFromNow( toTime ), nTemp );
|
|
if (nRet != 0 && nRet != ETIME)
|
|
{
|
|
// Error condition
|
|
TRACE( "ScheduleThread [%lu] ScheduleThread wait error %d, %s\n",
|
|
pthread_self(),
|
|
nRet,
|
|
strerror( nRet ) );
|
|
break;
|
|
}
|
|
|
|
// Time to exit?
|
|
if (pServer->mbExiting == true)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Get Schedule Mutex (non-blocking)
|
|
nRet = pthread_mutex_trylock( &pServer->mScheduleMutex );
|
|
if (nRet == EBUSY)
|
|
{
|
|
// Not an error, we're just too slow
|
|
// Someone else got to the ScheduleMutex before us
|
|
// We'll wait for the signal again
|
|
toTime = TimeIn( DEFAULT_WAIT );
|
|
TRACE( "ScheduleThread [%lu] unable to lock ScheduleMutex\n",
|
|
pthread_self() );
|
|
continue;
|
|
}
|
|
else if (nRet != 0)
|
|
{
|
|
// Error condition
|
|
TRACE( "ScheduleThread [%lu] mScheduleMutex error %d, %s\n",
|
|
pthread_self(),
|
|
nRet,
|
|
strerror( nRet ) );
|
|
break;
|
|
}
|
|
|
|
// Verify time. In the rare event it does move backward
|
|
// it would simply place all our schedule items as due now
|
|
pServer->CheckSystemTime();
|
|
|
|
// Default next wait period
|
|
toTime = TimeIn( DEFAULT_WAIT );
|
|
|
|
timespec curTime = TimeIn( 0 );
|
|
|
|
if (pServer->mpActiveRequest != 0)
|
|
{
|
|
if (pServer->mpActiveRequest->mbWaitingForResponse == true)
|
|
{
|
|
// Waiting on a response, this takes priority over the next
|
|
// scheduled event
|
|
|
|
// Has timeout expired?
|
|
if (pServer->mActiveRequestTimeout <= curTime)
|
|
{
|
|
// Response timeout
|
|
|
|
// Note: This may clear mpActiveRequest
|
|
pServer->RxTimeout();
|
|
}
|
|
else
|
|
{
|
|
// Active response timer is not yet due to expire
|
|
// Default timeout again, or this response's timeout?
|
|
if (pServer->mActiveRequestTimeout <= toTime)
|
|
{
|
|
toTime = pServer->mActiveRequestTimeout;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This should never happen
|
|
|
|
TRACE( "ScheduleThread() Sequencing error: "
|
|
"Active request %lu is not waiting for response ???\n",
|
|
pServer->mpActiveRequest->mID );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pServer->mpActiveRequest == 0
|
|
&& pServer->mRequestSchedule.size() > 0)
|
|
{
|
|
// No response timer active, ready to start the next
|
|
// scheduled item if due
|
|
|
|
timespec scheduledItem = pServer->GetNextRequestTime();
|
|
|
|
// Is item due to be scheduled?
|
|
if (scheduledItem <= curTime)
|
|
{
|
|
// Process scheduled item
|
|
pServer->ProcessRequest();
|
|
}
|
|
else
|
|
{
|
|
// Scheduled item is not yet due to be processed
|
|
// Default timeout again, or this item's start time?
|
|
if (scheduledItem <= toTime)
|
|
{
|
|
toTime = scheduledItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*TRACE( "Updated timer at %llu waiting %lu\n",
|
|
GetTickCount(),
|
|
TimeFromNow( toTime ) ); */
|
|
|
|
// Unlock schedule mutex
|
|
nRet = pthread_mutex_unlock( &pServer->mScheduleMutex );
|
|
if (nRet != 0)
|
|
{
|
|
TRACE( "ScheduleThread Unable to unlock schedule mutex."
|
|
" Error %d: %s\n",
|
|
nRet,
|
|
strerror( nRet ) );
|
|
return false;
|
|
}
|
|
}
|
|
|
|
TRACE( "Schedule thread [%lu] exited\n",
|
|
pthread_self() );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
TimeIn (Free Method)
|
|
|
|
DESCRIPTION:
|
|
Fill timespec with the time it will be in specified milliseconds
|
|
Relative time to Absolute time
|
|
|
|
PARAMETERS:
|
|
millis [ I ] - Milliseconds from current time
|
|
|
|
RETURN VALUE:
|
|
timespec - resulting time (from epoc)
|
|
NOTE: tv_sec of 0 is an error
|
|
===========================================================================*/
|
|
timespec TimeIn( ULONG millis )
|
|
{
|
|
timespec outTime;
|
|
|
|
int nRC = clock_gettime( CLOCK_REALTIME, &outTime );
|
|
if (nRC == 0)
|
|
{
|
|
// Add avoiding an overflow on (long)nsec
|
|
outTime.tv_sec += millis / 1000l;
|
|
outTime.tv_nsec += ( millis % 1000l ) * 1000000l;
|
|
|
|
// Check if we need to carry
|
|
if (outTime.tv_nsec >= 1000000000l)
|
|
{
|
|
outTime.tv_sec += outTime.tv_nsec / 1000000000l;
|
|
outTime.tv_nsec = outTime.tv_nsec % 1000000000l;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
outTime.tv_sec = 0;
|
|
outTime.tv_nsec = 0;
|
|
}
|
|
|
|
return outTime;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
TimeFromNow (Free Method)
|
|
|
|
DESCRIPTION:
|
|
Find the milliseconds from current time this timespec will occur
|
|
Absolute time to Relative time
|
|
|
|
PARAMETERS:
|
|
time [ I ] - Absolute time
|
|
|
|
RETURN VALUE:
|
|
Milliseconds in which absolute time will occur
|
|
0 if time has passed or error has occured
|
|
===========================================================================*/
|
|
ULONG TimeFromNow( timespec time )
|
|
{
|
|
// Assume failure
|
|
ULONG nOutTime = 0;
|
|
|
|
timespec now;
|
|
int nRC = clock_gettime( CLOCK_REALTIME, &now );
|
|
if (nRC == -1)
|
|
{
|
|
TRACE( "Error %d with gettime, %s\n", errno, strerror( errno ) );
|
|
return nOutTime;
|
|
}
|
|
|
|
if (time <= now)
|
|
{
|
|
return nOutTime;
|
|
}
|
|
|
|
nOutTime = (time.tv_sec - now.tv_sec) * 1000l;
|
|
nOutTime += (time.tv_nsec - now.tv_nsec) / 1000000l;
|
|
|
|
return nOutTime;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
GetTickCount (Free Method)
|
|
|
|
DESCRIPTION:
|
|
Provide a number for sequencing reference, similar to the windows
|
|
::GetTickCount().
|
|
|
|
NOTE: This number is based on the time since epoc, not
|
|
uptime.
|
|
|
|
PARAMETERS:
|
|
|
|
RETURN VALUE:
|
|
ULONGLONG - Number of milliseconds system has been up
|
|
===========================================================================*/
|
|
ULONGLONG GetTickCount()
|
|
{
|
|
timespec curtime = TimeIn( 0 );
|
|
|
|
ULONGLONG outtime = curtime.tv_sec * 1000LL;
|
|
outtime += curtime.tv_nsec / 1000000LL;
|
|
|
|
return outtime;
|
|
}
|
|
|
|
/*=========================================================================*/
|
|
// cProtocolServerRxCallback Methods
|
|
/*=========================================================================*/
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
IOComplete (Free Method)
|
|
|
|
DESCRIPTION:
|
|
The I/O has been completed, process the results
|
|
|
|
PARAMETERS:
|
|
status [ I ] - Status of operation
|
|
bytesReceived [ I ] - Bytes received during operation
|
|
|
|
RETURN VALUE:
|
|
None
|
|
===========================================================================*/
|
|
void cProtocolServerRxCallback::IOComplete(
|
|
DWORD status,
|
|
DWORD bytesReceived )
|
|
{
|
|
if (mpServer != 0)
|
|
{
|
|
mpServer->RxComplete( status, bytesReceived );
|
|
}
|
|
}
|
|
|
|
/*=========================================================================*/
|
|
// cProtocolServer::sProtocolReqRsp Methods
|
|
/*=========================================================================*/
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
cProtocolServer::sProtocolReqRsp (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Constructor
|
|
|
|
PARAMETERS:
|
|
requestInfo [ I ] - Underlying request object
|
|
requestID [ I ] - Request ID
|
|
auxDataMTU [ I ] - MTU (Maximum Transmission Unit) for auxiliary data
|
|
|
|
RETURN VALUE:
|
|
None
|
|
===========================================================================*/
|
|
cProtocolServer::sProtocolReqRsp::sProtocolReqRsp(
|
|
const sProtocolRequest & requestInfo,
|
|
ULONG requestID,
|
|
ULONG auxDataMTU )
|
|
: mRequest( requestInfo ),
|
|
mID( requestID ),
|
|
mAttempts( 0 ),
|
|
mEncodedSize( requestInfo.GetSize() ),
|
|
mRequiredAuxTxs( 0 ),
|
|
mCurrentAuxTx( 0 ),
|
|
mbWaitingForResponse( false )
|
|
{
|
|
ULONG auxDataSz = 0;
|
|
const BYTE * pAuxData = requestInfo.GetAuxiliaryData( auxDataSz );
|
|
|
|
// Compute the number of required auxiliary data transmissions?
|
|
if (auxDataMTU > 0 && pAuxData != 0 && auxDataSz > 0)
|
|
{
|
|
mRequiredAuxTxs = 1;
|
|
if (auxDataSz > auxDataMTU)
|
|
{
|
|
mRequiredAuxTxs = auxDataSz / auxDataMTU;
|
|
if ((auxDataSz % auxDataMTU) != 0)
|
|
{
|
|
mRequiredAuxTxs++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
cProtocolServer::sProtocolReqRsp (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Copy constructor
|
|
|
|
PARAMETERS:
|
|
reqRsp [ I ] - Object being copied
|
|
|
|
RETURN VALUE:
|
|
None
|
|
===========================================================================*/
|
|
cProtocolServer::sProtocolReqRsp::sProtocolReqRsp(
|
|
const sProtocolReqRsp & reqRsp )
|
|
: mRequest( reqRsp.mRequest ),
|
|
mID( reqRsp.mID ),
|
|
mAttempts( reqRsp.mAttempts ),
|
|
mEncodedSize( reqRsp.mEncodedSize ),
|
|
mRequiredAuxTxs( reqRsp.mRequiredAuxTxs ),
|
|
mCurrentAuxTx( reqRsp.mCurrentAuxTx ),
|
|
mbWaitingForResponse( reqRsp.mbWaitingForResponse )
|
|
{
|
|
// Nothing to do
|
|
};
|
|
|
|
/*=========================================================================*/
|
|
// cProtocolServer Methods
|
|
/*=========================================================================*/
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
cProtocolServer (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Constructor
|
|
|
|
PARAMETERS:
|
|
rxType [ I ] - Protocol type to assign to incoming data
|
|
txType [ I ] - Protocol type to verify for outgoing data
|
|
bufferSzRx [ I ] - Size of data buffer for incoming data
|
|
logSz [ I ] - Size of log (number of buffers)
|
|
|
|
SEQUENCING:
|
|
None (constructs sequencing objects)
|
|
|
|
RETURN VALUE:
|
|
None
|
|
===========================================================================*/
|
|
cProtocolServer::cProtocolServer(
|
|
eProtocolType rxType,
|
|
eProtocolType txType,
|
|
ULONG bufferSzRx,
|
|
ULONG logSz )
|
|
: mpConnection( 0 ),
|
|
mConnectionType( eConnectionType_Begin ),
|
|
mRxCallback(),
|
|
mScheduleThreadID( 0 ),
|
|
mThreadScheduleEvent(),
|
|
mbExiting( false ),
|
|
mpServerControl( 0 ),
|
|
mLastRequestID( 1 ),
|
|
mpActiveRequest( 0 ),
|
|
mpRxBuffer( 0 ),
|
|
mRxBufferSize( bufferSzRx ),
|
|
mRxType( rxType ),
|
|
mTxType( txType ),
|
|
mLog( logSz )
|
|
{
|
|
mLastTime = TimeIn( 0 );
|
|
|
|
// Allocate receive buffer?
|
|
if (mRxBufferSize > 0)
|
|
{
|
|
mpRxBuffer = new BYTE[mRxBufferSize];
|
|
}
|
|
|
|
// Before continuing verify receive buffer was allocated
|
|
if (mpRxBuffer != 0)
|
|
{
|
|
// Schedule mutex
|
|
int nRet = pthread_mutex_init( &mScheduleMutex, NULL );
|
|
if (nRet != 0)
|
|
{
|
|
TRACE( "Unable to init schedule mutex. Error %d: %s\n",
|
|
nRet,
|
|
strerror( nRet ) );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
~cProtocolServer (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Destructor
|
|
|
|
SEQUENCING:
|
|
None (destroys sequencing objects)
|
|
|
|
RETURN VALUE:
|
|
None
|
|
===========================================================================*/
|
|
cProtocolServer::~cProtocolServer()
|
|
{
|
|
// This should have already been called, but ...
|
|
Exit();
|
|
|
|
// Schedule mutex
|
|
int nRet = pthread_mutex_destroy( &mScheduleMutex );
|
|
if (nRet != 0)
|
|
{
|
|
TRACE( "Unable to destroy schedule mutex. Error %d: %s\n",
|
|
nRet,
|
|
strerror( nRet ) );
|
|
}
|
|
|
|
// Free receive buffer
|
|
if (mpRxBuffer != 0)
|
|
{
|
|
delete [] mpRxBuffer;
|
|
mpRxBuffer = 0;
|
|
}
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
HandleRemoveRequest (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Remove a previously added protocol request
|
|
|
|
Note: if a request is being processed, it cannot be inturrupted
|
|
|
|
PARAMETERS:
|
|
reqID [ I ] - Server assigned request ID
|
|
|
|
SEQUENCING:
|
|
Calling process must have lock on mScheduleMutex
|
|
|
|
RETURN VALUE:
|
|
bool
|
|
===========================================================================*/
|
|
bool cProtocolServer::HandleRemoveRequest( ULONG reqID )
|
|
{
|
|
// Assume failure
|
|
bool bRC = false;
|
|
|
|
// Find and erase request from request map
|
|
std::map <ULONG, sProtocolReqRsp *>::iterator pReqIter;
|
|
pReqIter = mRequestMap.find( reqID );
|
|
|
|
if (pReqIter != mRequestMap.end())
|
|
{
|
|
sProtocolReqRsp * pReqRsp = pReqIter->second;
|
|
if (pReqRsp != 0)
|
|
{
|
|
delete pReqRsp;
|
|
}
|
|
|
|
mRequestMap.erase( pReqIter );
|
|
|
|
// Success!
|
|
bRC = true;
|
|
|
|
// Find and erase request from schedule
|
|
bool bFound = false;
|
|
int entryIndex = -1;
|
|
|
|
std::set <tSchedule>::iterator pScheduleIter;
|
|
pScheduleIter = mRequestSchedule.begin();
|
|
|
|
while (pScheduleIter != mRequestSchedule.end())
|
|
{
|
|
entryIndex++;
|
|
|
|
tSchedule entry = *pScheduleIter;
|
|
if (entry.second == reqID)
|
|
{
|
|
bFound = true;
|
|
mRequestSchedule.erase( pScheduleIter );
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pScheduleIter++;
|
|
}
|
|
}
|
|
|
|
// Note: schedule will be updated when mutex is unlocked/signaled
|
|
}
|
|
else if (mpActiveRequest != 0 && mpActiveRequest->mID == reqID)
|
|
{
|
|
const sProtocolRequest & req = mpActiveRequest->mRequest;
|
|
const cProtocolNotification * pNotifier = req.GetNotifier();
|
|
|
|
// Cancel the response timer (when active)
|
|
if (mpActiveRequest->mbWaitingForResponse == true)
|
|
{
|
|
// Schedule will be updated when mutex is unlocked
|
|
|
|
// Failure to receive response, notify client
|
|
if (pNotifier != 0)
|
|
{
|
|
pNotifier->Notify( ePROTOCOL_EVT_RSP_ERR,
|
|
(DWORD)reqID,
|
|
ECANCELED );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is the active request, cancel the underlying transmit
|
|
// Note: Because ProcessRequest and RemoveRequest are both muxed
|
|
// with ScheduleMutex, it is impossible to for the write
|
|
// to actually be in progress when this code is reached.
|
|
if (mpConnection != 0)
|
|
{
|
|
mpConnection->CancelTx();
|
|
}
|
|
|
|
// Failure to send request, notify client
|
|
if (pNotifier != 0)
|
|
{
|
|
pNotifier->Notify( ePROTOCOL_EVT_REQ_ERR,
|
|
(DWORD)reqID,
|
|
ECANCELED );
|
|
}
|
|
}
|
|
|
|
// Now delete the request
|
|
delete mpActiveRequest;
|
|
mpActiveRequest = 0;
|
|
|
|
// Success!
|
|
bRC = true;
|
|
}
|
|
else
|
|
{
|
|
TRACE( "cProtocolServer::RemoveRequest( %lu ),"
|
|
" invalid request ID\n",
|
|
reqID );
|
|
}
|
|
|
|
return bRC;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
ScheduleRequest (Internal Method)
|
|
|
|
DESCRIPTION:
|
|
Schedule a request for transmission
|
|
|
|
PARAMETERS:
|
|
reqID [ I ] - ID of the request being scheduled this ID must exist
|
|
in the internal request/schedule maps
|
|
|
|
schedule [ I ] - Value in milliseconds that indicates the approximate
|
|
time from now that the request is to be sent out, the
|
|
actual time that the request is sent will be greater
|
|
than or equal to this value dependant on requests
|
|
scheduled before the request in question and
|
|
standard server processing time
|
|
|
|
SEQUENCING:
|
|
Calling process must have lock on mScheduleMutex
|
|
|
|
RETURN VALUE:
|
|
bool
|
|
===========================================================================*/
|
|
bool cProtocolServer::ScheduleRequest(
|
|
ULONG reqID,
|
|
ULONG schedule )
|
|
{
|
|
// Assume failure
|
|
bool bRC = false;
|
|
|
|
// Schedule adjust is in milliseconds
|
|
timespec schTimer = TimeIn( schedule );
|
|
|
|
// Create the schedule entry
|
|
tSchedule newEntry( schTimer, reqID );
|
|
|
|
// Fit this request into the schedule (ordered by scheduled time)
|
|
mRequestSchedule.insert( newEntry );
|
|
|
|
// Note: timer will be updated when mScheduleMutex is unlocked
|
|
|
|
return bRC;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
RescheduleActiveRequest (Internal Method)
|
|
|
|
DESCRIPTION:
|
|
Reschedule (or cleanup) the active request
|
|
|
|
SEQUENCING:
|
|
Calling process must have lock on mScheduleMutex
|
|
|
|
RETURN VALUE:
|
|
None
|
|
===========================================================================*/
|
|
void cProtocolServer::RescheduleActiveRequest()
|
|
{
|
|
// Are there more attempts to be made?
|
|
if (mpActiveRequest->mAttempts < mpActiveRequest->mRequest.GetRequests())
|
|
{
|
|
// Yes, first reset the request
|
|
mpActiveRequest->Reset();
|
|
|
|
// Now add it back to the request map
|
|
mRequestMap[mpActiveRequest->mID] = mpActiveRequest;
|
|
|
|
TRACE( "RescheduleActiveRequest(): req %lu rescheduled\n", mpActiveRequest->mID );
|
|
|
|
// Lastly reschedule the request
|
|
ScheduleRequest( mpActiveRequest->mID,
|
|
mpActiveRequest->mRequest.GetFrequency() );
|
|
|
|
}
|
|
else
|
|
{
|
|
TRACE( "RescheduleActiveRequest(): req %lu removed\n", mpActiveRequest->mID );
|
|
|
|
// No, we are through with this request
|
|
delete mpActiveRequest;
|
|
}
|
|
|
|
// There is no longer an active request
|
|
mpActiveRequest = 0;
|
|
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
ProcessRequest (Internal Method)
|
|
|
|
DESCRIPTION:
|
|
Process a single outgoing protocol request, this consists of removing
|
|
the request ID from the head of the schedule, looking up the internal
|
|
request object in the request map, sending out the request, and setting
|
|
up the response timer (if a response is required)
|
|
|
|
SEQUENCING:
|
|
Calling process must have lock on mScheduleMutex
|
|
|
|
RETURN VALUE:
|
|
===========================================================================*/
|
|
void cProtocolServer::ProcessRequest()
|
|
{
|
|
// Is there already an active request?
|
|
if (mpActiveRequest != 0 || mpConnection == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Grab request ID from the schedule
|
|
std::set <tSchedule>::iterator pScheduleIter;
|
|
pScheduleIter = mRequestSchedule.begin();
|
|
|
|
// Did we find the request?
|
|
if (pScheduleIter == mRequestSchedule.end())
|
|
{
|
|
// No
|
|
return;
|
|
}
|
|
|
|
// Yes, grab the request ID
|
|
ULONG reqID = pScheduleIter->second;
|
|
|
|
// Remove from schedule
|
|
mRequestSchedule.erase( pScheduleIter );
|
|
|
|
// Look up the internal request object
|
|
std::map <ULONG, sProtocolReqRsp *>::iterator pReqIter;
|
|
pReqIter = mRequestMap.find( reqID );
|
|
|
|
// Request not found around?
|
|
if (pReqIter == mRequestMap.end() || pReqIter->second == 0)
|
|
{
|
|
// No
|
|
return;
|
|
}
|
|
|
|
// Set this request as the active request
|
|
mpActiveRequest = pReqIter->second;
|
|
|
|
TRACE( "ProcessRequest(): req %lu started\n", mpActiveRequest->mID );
|
|
|
|
// Remove request from pending request map
|
|
mRequestMap.erase( pReqIter );
|
|
|
|
// Extract the underlying request
|
|
const sProtocolRequest & req = mpActiveRequest->mRequest;
|
|
|
|
// Increment attempt count?
|
|
if (req.GetRequests() != INFINITE_REQS)
|
|
{
|
|
// This request isn't an indefinite one, so keep track of each attempt
|
|
mpActiveRequest->mAttempts++;
|
|
}
|
|
|
|
bool bTxSuccess = false;
|
|
|
|
// Encode data for transmission?
|
|
bool bEncoded = false;
|
|
sSharedBuffer * pEncoded = 0;
|
|
pEncoded = EncodeTxData( req.GetSharedBuffer(), bEncoded );
|
|
if (bEncoded == false)
|
|
{
|
|
// Note: no longer asynchronus
|
|
// Send the request data
|
|
bTxSuccess = mpConnection->TxData( req.GetBuffer(),
|
|
req.GetSize() );
|
|
}
|
|
else if (bEncoded == true)
|
|
{
|
|
if (pEncoded != 0 && pEncoded->IsValid() == true)
|
|
{
|
|
// Note: no longer asynchronus
|
|
// Send the request data
|
|
mpActiveRequest->mEncodedSize = pEncoded->GetSize();
|
|
bTxSuccess = mpConnection->TxData( pEncoded->GetBuffer(),
|
|
pEncoded->GetSize() );
|
|
}
|
|
}
|
|
|
|
if (bTxSuccess == true)
|
|
{
|
|
TRACE( "ProcessRequest(): req %lu finished\n", mpActiveRequest->mID );
|
|
TxComplete();
|
|
}
|
|
else
|
|
{
|
|
TxError();
|
|
TRACE( "ProcessRequest(): req finished with a TxError\n" );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
CheckSystemTime (Internal Method)
|
|
|
|
DESCRIPTION:
|
|
Check that system time hasn't moved backwards. Since we use the system
|
|
time for scheduling requests we need to periodically check that the
|
|
user (or system itself) hasn't reset system time backwards, if it has
|
|
then we reschedule everything to the current system time. This disrupts
|
|
the schedule but avoids stranding requests
|
|
|
|
Updates mLastTime
|
|
|
|
SEQUENCING:
|
|
Calling process must have lock on mScheduleMutex
|
|
|
|
RETURN VALUE:
|
|
bool: System time moved backwards?
|
|
===========================================================================*/
|
|
bool cProtocolServer::CheckSystemTime()
|
|
{
|
|
// Assume that time is still marching forward
|
|
bool bAdjust = false;
|
|
|
|
timespec curTime = TimeIn( 0 );
|
|
|
|
if (curTime < mLastTime)
|
|
{
|
|
// Looks like the system clock has been adjusted to an earlier
|
|
// value, go through the current schedule and adjust each timer
|
|
// to reflect the adjustment. This isn't an exact approach but
|
|
// it prevents requests from being stranded which is our goal
|
|
|
|
// Note: set iterators are constant. This means we need to
|
|
// create a set with the new data, we can't modify this one
|
|
|
|
std::set < tSchedule, std::less <tSchedule> > tempSchedule;
|
|
|
|
std::set <tSchedule>::iterator pScheduleIter;
|
|
pScheduleIter = mRequestSchedule.begin();
|
|
|
|
while (pScheduleIter != mRequestSchedule.end())
|
|
{
|
|
tSchedule entry = *pScheduleIter;
|
|
entry.first.tv_sec = curTime.tv_sec;
|
|
entry.first.tv_nsec = curTime.tv_nsec;
|
|
tempSchedule.insert( entry );
|
|
|
|
pScheduleIter++;
|
|
}
|
|
|
|
mRequestSchedule = tempSchedule;
|
|
|
|
// Update mActiveRequestTimeout
|
|
if ( (mpActiveRequest != 0)
|
|
&& (mpActiveRequest->mbWaitingForResponse == true) )
|
|
{
|
|
// Restart active request's timeout
|
|
ULONG mTimeout = mpActiveRequest->mRequest.GetTimeout();
|
|
mActiveRequestTimeout = TimeIn( mTimeout );
|
|
}
|
|
|
|
TRACE( "Time has moved backwards, schedule updated\n" );
|
|
|
|
// Indicate the change
|
|
bAdjust = true;
|
|
}
|
|
|
|
mLastTime.tv_sec = curTime.tv_sec;
|
|
mLastTime.tv_nsec = curTime.tv_nsec;
|
|
|
|
return bAdjust;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
RxComplete (Internal Method)
|
|
|
|
DESCRIPTION:
|
|
Handle completion of receive data operation
|
|
|
|
PARAMETERS:
|
|
status [ I ] - Status of operation
|
|
bytesReceived [ I ] - Number of bytes received
|
|
|
|
SEQUENCING:
|
|
This method is sequenced according to the schedule mutex
|
|
i.e. any other thread that needs to modify the schedule
|
|
will block until this method completes
|
|
|
|
RETURN VALUE:
|
|
None
|
|
===========================================================================*/
|
|
void cProtocolServer::RxComplete(
|
|
DWORD status,
|
|
DWORD bytesReceived )
|
|
{
|
|
if (status != NO_ERROR)
|
|
{
|
|
TRACE( "cProtocolServer::RxComplete() = %lu\n", status );
|
|
}
|
|
|
|
if (mpConnection == 0)
|
|
{
|
|
TRACE( "cProtocolServer::RxComplete() - Not initialized\n" );
|
|
return;
|
|
}
|
|
|
|
// Error with the read
|
|
if (status != NO_ERROR || bytesReceived == 0)
|
|
{
|
|
// Setup the next read
|
|
mpConnection->RxData( mpRxBuffer,
|
|
(ULONG)mRxBufferSize,
|
|
(cIOCallback *)&mRxCallback );
|
|
|
|
return;
|
|
}
|
|
|
|
// Get Schedule Mutex
|
|
if (GetScheduleMutex() == false)
|
|
{
|
|
TRACE( "RxComplete(), unable to get schedule Mutex\n" );
|
|
return;
|
|
}
|
|
|
|
TRACE( "RxComplete() - Entry at %llu\n", GetTickCount() );
|
|
|
|
// Decode data
|
|
bool bAbortTx = false;
|
|
ULONG rspIdx = INVALID_LOG_INDEX;
|
|
bool bRsp = DecodeRxData( bytesReceived, rspIdx, bAbortTx );
|
|
|
|
// Is there an active request that needs to be aborted
|
|
if (mpActiveRequest != 0 && bAbortTx == true)
|
|
{
|
|
// Yes, terminate the transmission and handle the error
|
|
mpConnection->CancelTx();
|
|
TxError();
|
|
}
|
|
// Is there an active request and a valid response?
|
|
else if (mpActiveRequest != 0 && bRsp == true)
|
|
{
|
|
const sProtocolRequest & req = mpActiveRequest->mRequest;
|
|
const cProtocolNotification * pNotifier = req.GetNotifier();
|
|
|
|
// Notify client that response was received
|
|
if (pNotifier != 0)
|
|
{
|
|
pNotifier->Notify( ePROTOCOL_EVT_RSP_RECV,
|
|
(DWORD)mpActiveRequest->mID,
|
|
(DWORD)rspIdx );
|
|
}
|
|
|
|
// Reschedule request as needed
|
|
RescheduleActiveRequest();
|
|
}
|
|
|
|
// Setup the next read
|
|
mpConnection->RxData( mpRxBuffer,
|
|
(ULONG)mRxBufferSize,
|
|
(cIOCallback *)&mRxCallback );
|
|
|
|
TRACE( "RxComplete() - Exit at %llu\n", GetTickCount() );
|
|
|
|
// Unlock schedule mutex
|
|
if (ReleaseScheduleMutex() == false)
|
|
{
|
|
// This should never happen
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
RxTimeout (Internal Method)
|
|
|
|
DESCRIPTION:
|
|
Handle the response timer expiring
|
|
|
|
SEQUENCING:
|
|
Calling process must have lock on mScheduleMutex
|
|
|
|
RETURN VALUE:
|
|
None
|
|
===========================================================================*/
|
|
void cProtocolServer::RxTimeout()
|
|
{
|
|
// No active request?
|
|
if (mpActiveRequest == 0)
|
|
{
|
|
TRACE( "RxTimeout() with no active request\n" );
|
|
ASSERT( 0 );
|
|
}
|
|
|
|
TRACE( "RxTimeout() for req %lu\n", mpActiveRequest->mID );
|
|
|
|
const sProtocolRequest & req = mpActiveRequest->mRequest;
|
|
const cProtocolNotification * pNotifier = req.GetNotifier();
|
|
|
|
// Failure to receive response, notify client
|
|
if (pNotifier != 0)
|
|
{
|
|
pNotifier->Notify( ePROTOCOL_EVT_RSP_ERR,
|
|
(DWORD)mpActiveRequest->mID,
|
|
(DWORD)0 );
|
|
}
|
|
|
|
// Reschedule request as needed
|
|
RescheduleActiveRequest();
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
TxComplete (Internal Method)
|
|
|
|
DESCRIPTION:
|
|
Handle completion of transmit data operation
|
|
|
|
PARAMETERS:
|
|
|
|
SEQUENCING:
|
|
Calling process must have lock on mScheduleMutex
|
|
|
|
RETURN VALUE:
|
|
None
|
|
===========================================================================*/
|
|
void cProtocolServer::TxComplete()
|
|
{
|
|
// No active request?
|
|
if (mpActiveRequest == 0 || mpConnection == 0)
|
|
{
|
|
TRACE( "TxComplete() called with no active request\n" );
|
|
ASSERT( 0 );
|
|
}
|
|
|
|
TRACE( "TxComplete() req %lu started\n", mpActiveRequest->mID );
|
|
|
|
ULONG reqID = mpActiveRequest->mID;
|
|
const sProtocolRequest & req = mpActiveRequest->mRequest;
|
|
const cProtocolNotification * pNotifier = req.GetNotifier();
|
|
|
|
// Notify client of auxiliary data being sent?
|
|
if (mpActiveRequest->mRequiredAuxTxs && mpActiveRequest->mCurrentAuxTx)
|
|
{
|
|
pNotifier->Notify( ePROTOCOL_EVT_AUX_TU_SENT,
|
|
(DWORD)reqID,
|
|
(DWORD)mpActiveRequest->mEncodedSize );
|
|
}
|
|
|
|
// Check for more auxiliary data to transmit
|
|
if (mpActiveRequest->mCurrentAuxTx < mpActiveRequest->mRequiredAuxTxs)
|
|
{
|
|
ULONG auxDataSz = 0;
|
|
const BYTE * pAuxData = req.GetAuxiliaryData( auxDataSz );
|
|
if (auxDataSz > 0 && pAuxData != 0)
|
|
{
|
|
bool bRC = false;
|
|
|
|
// Adjust for current MTU
|
|
pAuxData += (mpActiveRequest->mCurrentAuxTx * MAX_AUX_MTU_SIZE);
|
|
mpActiveRequest->mCurrentAuxTx++;
|
|
|
|
// Last MTU?
|
|
if (mpActiveRequest->mCurrentAuxTx == mpActiveRequest->mRequiredAuxTxs)
|
|
{
|
|
// More than one MTU?
|
|
if (mpActiveRequest->mRequiredAuxTxs > 1)
|
|
{
|
|
auxDataSz = (auxDataSz % MAX_AUX_MTU_SIZE);
|
|
if (auxDataSz == 0)
|
|
{
|
|
auxDataSz = MAX_AUX_MTU_SIZE;
|
|
}
|
|
}
|
|
|
|
if (auxDataSz % MAX_PACKET_SIZE == 0)
|
|
{
|
|
// If last write of unframed write request is divisible
|
|
// by 512, break off last byte and send seperatly.
|
|
TRACE( "TxComplete() Special case, break off last byte\n" );
|
|
|
|
bRC = mpConnection->TxData( pAuxData,
|
|
auxDataSz - 1 );
|
|
|
|
if (bRC == true)
|
|
{
|
|
bRC = mpConnection->TxData( pAuxData + auxDataSz -1,
|
|
1 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bRC = mpConnection->TxData( pAuxData,
|
|
auxDataSz );
|
|
}
|
|
}
|
|
else if (mpActiveRequest->mRequiredAuxTxs > 1)
|
|
{
|
|
auxDataSz = MAX_AUX_MTU_SIZE;
|
|
|
|
bRC = mpConnection->TxData( pAuxData,
|
|
auxDataSz );
|
|
}
|
|
|
|
if (bRC == true)
|
|
{
|
|
mpActiveRequest->mEncodedSize = auxDataSz;
|
|
TxComplete();
|
|
}
|
|
else
|
|
{
|
|
TxError();
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Another successful transmission, add the buffer to the log
|
|
ULONG reqIdx = INVALID_LOG_INDEX;
|
|
|
|
sProtocolBuffer pb( req.GetSharedBuffer() );
|
|
reqIdx = mLog.AddBuffer( pb );
|
|
|
|
// Notify client?
|
|
if (pNotifier != 0)
|
|
{
|
|
pNotifier->Notify( ePROTOCOL_EVT_REQ_SENT, (DWORD)reqID, (DWORD)reqIdx );
|
|
}
|
|
|
|
// Wait for a response?
|
|
if (mpActiveRequest->mRequest.IsTXOnly() == false)
|
|
{
|
|
// We now await the response
|
|
mpActiveRequest->mbWaitingForResponse = true;
|
|
mActiveRequestTimeout = TimeIn( mpActiveRequest->mRequest.GetTimeout() );
|
|
}
|
|
else
|
|
{
|
|
// Reschedule request as needed
|
|
RescheduleActiveRequest();
|
|
}
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
TxError (Internal Method)
|
|
|
|
DESCRIPTION:
|
|
Handle transmit data operation error be either rescheduling the
|
|
request or cleaning it up
|
|
|
|
SEQUENCING:
|
|
Calling process must have lock on mScheduleMutex
|
|
|
|
RETURN VALUE:
|
|
None
|
|
===========================================================================*/
|
|
void cProtocolServer::TxError()
|
|
{
|
|
// No active request?
|
|
if (mpActiveRequest == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ULONG reqID = mpActiveRequest->mID;
|
|
const sProtocolRequest & req = mpActiveRequest->mRequest;
|
|
const cProtocolNotification * pNotifier = req.GetNotifier();
|
|
|
|
// Failure to send request, notify client
|
|
if (pNotifier != 0)
|
|
{
|
|
pNotifier->Notify( ePROTOCOL_EVT_REQ_ERR, (DWORD)reqID, (DWORD)0 );
|
|
}
|
|
|
|
// Reschedule request as needed
|
|
RescheduleActiveRequest();
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
Initialize (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Initialize the protocol server by starting up the schedule thread
|
|
|
|
SEQUENCING:
|
|
This method is sequenced according to the schedule mutex, i.e. any
|
|
other thread that needs to modify the schedule will block until
|
|
this method completes
|
|
|
|
RETURN VALUE:
|
|
bool
|
|
===========================================================================*/
|
|
bool cProtocolServer::Initialize()
|
|
{
|
|
// Assume failure
|
|
bool bRC = false;
|
|
|
|
mbExiting = false;
|
|
|
|
// Get mScheduleMutex
|
|
if (GetScheduleMutex() == true)
|
|
{
|
|
if (mScheduleThreadID == 0)
|
|
{
|
|
// Yes, start thread
|
|
int nRet = pthread_create( &mScheduleThreadID,
|
|
NULL,
|
|
ScheduleThread,
|
|
this );
|
|
if (nRet == 0)
|
|
{
|
|
// Success!
|
|
bRC = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE( "cProtocolServer::Initialize(), unable to aquire ScheduleMutex\n" );
|
|
return false;
|
|
}
|
|
|
|
// Unlock schedule mutex
|
|
if (ReleaseScheduleMutex() == false)
|
|
{
|
|
// This should never happen
|
|
return false;
|
|
}
|
|
|
|
return bRC;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
Exit (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Exit the protocol server by exiting the schedule thread (if necessary)
|
|
|
|
SEQUENCING:
|
|
This method is sequenced according to the schedule mutex, i.e. any
|
|
other thread that needs to modify the schedule will block until
|
|
this method completes
|
|
|
|
RETURN VALUE:
|
|
bool
|
|
===========================================================================*/
|
|
bool cProtocolServer::Exit()
|
|
{
|
|
// Assume failure
|
|
bool bRC = false;
|
|
|
|
if (mScheduleThreadID != 0)
|
|
{
|
|
if (GetScheduleMutex() == false)
|
|
{
|
|
// This should never happen
|
|
return false;
|
|
}
|
|
|
|
// Check that mScheduleTheadID is still not 0
|
|
if (mScheduleThreadID == 0)
|
|
{
|
|
printf( "mScheduleThreadID was zero!!!\n" );
|
|
ReleaseScheduleMutex( false );
|
|
return false;
|
|
}
|
|
|
|
// Set exit event
|
|
mbExiting = true;
|
|
|
|
// Signal a schedule update
|
|
if (mThreadScheduleEvent.Set( 1 ) != 0)
|
|
{
|
|
// This should never happen
|
|
return false;
|
|
}
|
|
|
|
TRACE( "Joining ScheduleThread %lu\n", mScheduleThreadID );
|
|
|
|
// Allow process to continue until it finishes
|
|
int nRet = pthread_join( mScheduleThreadID, NULL );
|
|
if (nRet == ESRCH)
|
|
{
|
|
TRACE( "ScheduleThread has exited already\n" );
|
|
}
|
|
else if (nRet != 0)
|
|
{
|
|
TRACE( "Unable to join ScheduleThread. Error %d: %s\n",
|
|
nRet,
|
|
strerror( nRet ) );
|
|
return false;
|
|
}
|
|
|
|
TRACE( "cProtocolServer::Exit(), completed thread %lu\n",
|
|
(ULONG)mScheduleThreadID );
|
|
|
|
bRC = true;
|
|
|
|
// Release "handle"
|
|
mScheduleThreadID = 0;
|
|
|
|
// Release mutex lock, don't signal ScheduleThread
|
|
if (ReleaseScheduleMutex( false ) == false)
|
|
{
|
|
// This should never happen
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No ScheduleThread
|
|
bRC = true;
|
|
}
|
|
|
|
// Free any allocated requests
|
|
std::map <ULONG, sProtocolReqRsp *>::iterator pReqIter;
|
|
pReqIter = mRequestMap.begin();
|
|
|
|
while (pReqIter != mRequestMap.end())
|
|
{
|
|
sProtocolReqRsp * pReqRsp = pReqIter->second;
|
|
if (pReqRsp != 0)
|
|
{
|
|
delete pReqRsp;
|
|
}
|
|
|
|
pReqIter++;
|
|
}
|
|
|
|
mRequestMap.clear();
|
|
|
|
// Free log
|
|
mLog.Clear();
|
|
|
|
return bRC;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
Connect (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Connect to the given communications port
|
|
|
|
PARAMETERS:
|
|
pPort [ I ] - String pointer representing the device node to
|
|
connect to (IE: /dev/qcqmi0)
|
|
|
|
SEQUENCING:
|
|
None
|
|
|
|
RETURN VALUE:
|
|
bool
|
|
===========================================================================*/
|
|
bool cProtocolServer::Connect( LPCSTR pPort )
|
|
{
|
|
// Assume failure
|
|
bool bRC = false;
|
|
if (pPort == 0 || pPort[0] == 0 || mpConnection == 0)
|
|
{
|
|
return bRC;
|
|
}
|
|
|
|
// Connect to device
|
|
|
|
// Set callback
|
|
mRxCallback.SetServer( this );
|
|
|
|
// Override to initialize port with protocol specific options
|
|
bRC = mpConnection->Connect( pPort );
|
|
if (bRC == true)
|
|
{
|
|
bRC = InitializeComm();
|
|
if (bRC == true)
|
|
{
|
|
// Setup the initial read
|
|
mpConnection->RxData( mpRxBuffer,
|
|
(ULONG)mRxBufferSize,
|
|
(cIOCallback *)&mRxCallback );
|
|
}
|
|
}
|
|
|
|
return bRC;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
Disconnect (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Disconnect from the current communications port
|
|
|
|
SEQUENCING:
|
|
None
|
|
|
|
RETURN VALUE:
|
|
bool
|
|
===========================================================================*/
|
|
bool cProtocolServer::Disconnect()
|
|
{
|
|
// Disconnect
|
|
|
|
if (mpConnection != 0)
|
|
{
|
|
// Cancel any outstanding I/O
|
|
mpConnection->CancelIO();
|
|
}
|
|
|
|
// Empty callback
|
|
mRxCallback.SetServer( 0 );
|
|
|
|
// Cleanup COM port
|
|
CleanupComm();
|
|
|
|
if (mpConnection != 0)
|
|
{
|
|
// Now disconnect
|
|
bool bDis = mpConnection->Disconnect();
|
|
delete mpConnection;
|
|
return bDis;
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
IsConnected (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Are we currently connected to a port?
|
|
|
|
SEQUENCING:
|
|
None
|
|
|
|
RETURN VALUE:
|
|
bool
|
|
===========================================================================*/
|
|
bool cProtocolServer::IsConnected()
|
|
{
|
|
return (mpConnection != 0 && mpConnection->IsConnected());
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
AddRequest (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Add an outgoing protocol request to the protocol server request queue
|
|
|
|
PARAMETERS:
|
|
req [ I ] - Request being added
|
|
|
|
SEQUENCING:
|
|
This method is sequenced according to the schedule mutex, i.e. any
|
|
other thread that needs to modify the schedule will block until
|
|
this method completes
|
|
|
|
RETURN VALUE:
|
|
ULONG - ID of scheduled request (INVALID_REQUEST_ID upon error)
|
|
===========================================================================*/
|
|
ULONG cProtocolServer::AddRequest( const sProtocolRequest & req )
|
|
{
|
|
// Assume failure
|
|
ULONG reqID = INVALID_REQUEST_ID;
|
|
|
|
// Server not configured for sending requests?
|
|
if (IsValid( mTxType ) == false)
|
|
{
|
|
return reqID;
|
|
}
|
|
|
|
// Request type not valid for server?
|
|
if (req.GetType() != mTxType)
|
|
{
|
|
return reqID;
|
|
}
|
|
|
|
// Invalide request?
|
|
if (ValidateRequest( req ) == false)
|
|
{
|
|
return reqID;
|
|
}
|
|
|
|
// Get mScheduleMutex
|
|
if (GetScheduleMutex() == true)
|
|
{
|
|
TRACE( "AddRequest() - Entry at %llu\n", GetTickCount() );
|
|
|
|
// Grab next available request ID
|
|
if (++mLastRequestID == 0)
|
|
{
|
|
mLastRequestID++;
|
|
}
|
|
|
|
reqID = mLastRequestID;
|
|
while (mRequestMap.find( reqID ) != mRequestMap.end())
|
|
{
|
|
reqID++;
|
|
}
|
|
|
|
// Wrap in our internal structure
|
|
sProtocolReqRsp * pReqRsp = 0;
|
|
pReqRsp = new sProtocolReqRsp( req, reqID, MAX_AUX_MTU_SIZE );
|
|
|
|
if (pReqRsp != 0)
|
|
{
|
|
// Add to request map
|
|
mRequestMap[reqID] = pReqRsp;
|
|
|
|
// ... and schedule
|
|
ScheduleRequest( reqID, req.GetSchedule() );
|
|
}
|
|
|
|
TRACE( "AddRequest() - Exit at %llu\n", GetTickCount() );
|
|
|
|
// Unlock schedule mutex
|
|
if (ReleaseScheduleMutex() == false)
|
|
{
|
|
// This should never happen
|
|
return INVALID_REQUEST_ID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE( "cProtocolServer::AddRequest(), unable to get schedule Mutex\n" );
|
|
}
|
|
|
|
return reqID;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
RemoveRequest (Public Method)
|
|
|
|
DESCRIPTION:
|
|
Remove a previously added protocol request
|
|
|
|
SEQUENCING:
|
|
This method is sequenced according to the schedule mutex, i.e. any
|
|
other thread that needs to modify the schedule will block until
|
|
this method completes
|
|
|
|
Note: If a request is being written, it cannot be inturrupted as
|
|
both ProcessRequest and RemoveRequest depend on the ScheduleMutex
|
|
and the write is synchronus. If the request has been written but
|
|
the read has not been triggered it can be removed.
|
|
|
|
PARAMETERS:
|
|
reqID [ I ] - ID of request being removed
|
|
|
|
RETURN VALUE:
|
|
bool
|
|
===========================================================================*/
|
|
bool cProtocolServer::RemoveRequest( ULONG reqID )
|
|
{
|
|
// Assume failure
|
|
bool bRC = false;
|
|
|
|
// Get Schedule Mutex
|
|
if (GetScheduleMutex() == true)
|
|
{
|
|
TRACE( "RemoveRequest() - Entry at %llu\n", GetTickCount() );
|
|
|
|
bRC = HandleRemoveRequest( reqID );
|
|
|
|
TRACE( "RemoveRequest() - Exit at %llu\n", GetTickCount() );
|
|
|
|
// Unlock schedule mutex
|
|
if (ReleaseScheduleMutex() == false)
|
|
{
|
|
// This should never happen
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TRACE( "cProtocolServer::RemoveRequest(), unable to get mScheduleMutex\n" );
|
|
}
|
|
|
|
return bRC;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
GetScheduleMutex (Internal Method)
|
|
|
|
DESCRIPTION:
|
|
Get the schedule mutex. Additionally a check is applied to verify the
|
|
DEADLOCK_TIME was not exceeded
|
|
|
|
SEQUENCING:
|
|
This function will block until the mScheduleMutex is aquired
|
|
|
|
PARAMETERS:
|
|
|
|
RETURN VALUE:
|
|
bool
|
|
===========================================================================*/
|
|
bool cProtocolServer::GetScheduleMutex()
|
|
{
|
|
ULONGLONG nStart = GetTickCount();
|
|
|
|
//TRACE( "Locking Schedule mutex\n" );
|
|
int nRet = pthread_mutex_lock( &mScheduleMutex );
|
|
if (nRet != 0)
|
|
{
|
|
TRACE( "Unable to lock schedule mutex. Error %d: %s\n",
|
|
nRet,
|
|
strerror( nRet ) );
|
|
return false;
|
|
}
|
|
|
|
ULONGLONG nEnd = GetTickCount();
|
|
if (nEnd - nStart > DEADLOCK_TIME)
|
|
{
|
|
TRACE( "Deadlock time exceeded: took %llu ms\n", nEnd - nStart );
|
|
ReleaseScheduleMutex( true );
|
|
return false;
|
|
}
|
|
|
|
//TRACE( "Locked ScheduleMutex\n" );
|
|
return true;
|
|
}
|
|
|
|
/*===========================================================================
|
|
METHOD:
|
|
ReleaseScheduleMutex (Internal Method)
|
|
|
|
DESCRIPTION:
|
|
Release lock on the schedule mutex
|
|
|
|
SEQUENCING:
|
|
Calling process must have lock
|
|
|
|
PARAMETERS:
|
|
bSignalThread [ I ] - Signal Schedule thread as well?
|
|
|
|
RETURN VALUE:
|
|
bool
|
|
===========================================================================*/
|
|
bool cProtocolServer::ReleaseScheduleMutex( bool bSignalThread )
|
|
{
|
|
if (bSignalThread == true)
|
|
{
|
|
if (mThreadScheduleEvent.Set( 1 ) != 0)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int nRet = pthread_mutex_unlock( &mScheduleMutex );
|
|
if (nRet != 0)
|
|
{
|
|
TRACE( "Unable to unlock schedule mutex. Error %d: %s\n",
|
|
nRet,
|
|
strerror( nRet ) );
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|