libqmi-qmuxd/gobi-api/GobiAPI_2013-07-31-1347/GobiSampleCM/SampleCM.cpp

1735 lines
46 KiB
C++

/*===========================================================================
FILE:
SampleCM.cpp
DESCRIPTION:
Generic class to act as Sample CM interface
PUBLIC CLASSES AND METHODS:
cSampleCM
Generic class to act as Sample CM interface
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 "SampleCM.h"
#include "GobiCMDLL.h"
#include "GobiCMCallback.h"
#include "GobiConnectionMgmtAPIStructs.h"
#include <sys/inotify.h>
#include <unistd.h>
#include <string>
#include <dirent.h>
#include <iomanip>
// Global pointer for callbacks to reference
class cSampleCM * gpCM;
/*=========================================================================*/
// Free Methods
/*=========================================================================*/
/*===========================================================================
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;
clock_gettime( CLOCK_REALTIME, &curtime );
ULONGLONG outtime = curtime.tv_sec * 1000LL;
outtime += curtime.tv_nsec / 1000000LL;
return outtime;
}
/*===========================================================================
METHOD:
DeviceDetectionThread (Free Method)
DESCRIPTION:
Scans for and detects devices
PARAMETERS:
pData [ I ] - cSampleCM object
RETURN VALUE:
void * - always NULL
===========================================================================*/
void * DeviceDetectionThread( void * pData )
{
cSampleCM * pCM = (cSampleCM *)pData;
if (pCM == NULL)
{
return NULL;
}
// Get inotify handle
int inotify = inotify_init();
if (inotify == -1)
{
TRACE( "inotify_init failed\n" );
return NULL;
}
// Start a watch on the /dev directory
int inotifyFlags = IN_CREATE | IN_MODIFY | IN_DELETE;
int watchD = inotify_add_watch( inotify, "/dev/", inotifyFlags );
if (watchD == -1)
{
TRACE( "inotify_add_watch failed\n" );
close( inotify );
return NULL;
}
// Does a device already exist?
dirent ** ppDevFiles;
// Yes, scandir really takes a triple pointer for its second param
int numDevs = scandir( "/dev/", &ppDevFiles, NULL, NULL );
for (int i = 0; i < numDevs; i++)
{
std::string deviceID = "/dev/";
deviceID += ppDevFiles[i]->d_name;
free( ppDevFiles[i] );
if (deviceID.find( "qcqmi" ) != std::string::npos)
{
pCM->Connect( deviceID.c_str() );
break;
}
}
// Cleanup from scandir
if (numDevs != -1)
{
free( ppDevFiles );
}
else
{
TRACE( "Scandir failed\n" );
}
// Begin async reading
fd_set inputSet, outputSet;
FD_ZERO( &inputSet );
FD_SET( pCM->mDeviceDetectionStopPipe[READING], &inputSet );
FD_SET( inotify, &inputSet );
int largestFD = std::max( pCM->mDeviceDetectionStopPipe[READING], inotify );
while (true)
{
// No FD_COPY() available in android
memcpy( &outputSet, &inputSet, sizeof( fd_set ) );
// Wait for data on either the inotify or the stop pipe
int status = select( largestFD + 1, &outputSet, NULL, NULL, NULL );
if (status <= 0)
{
break;
}
else if (FD_ISSET( pCM->mDeviceDetectionStopPipe[READING], &outputSet ) == true)
{
// Time to close
break;
}
else if (FD_ISSET( inotify, &outputSet ) == true)
{
// Perform a read
BYTE buffer[1024] = { 0 };
int rc = read( inotify, &buffer[0], 1024 );
if (rc < (int)sizeof( inotify_event ))
{
continue;
}
// Typecast the buffer to an inotify event
struct inotify_event * pIEvent = (struct inotify_event *)&buffer[0];
// Check the length of the name
if (pIEvent->len < 5)
{
continue;
}
// Is this a matching device?
if (strncmp( pIEvent->name, "qcqmi", 5 ) != 0)
{
continue;
}
std::string deviceID = "/dev/";
deviceID += pIEvent->name;
if (pIEvent->mask & IN_CREATE
|| pIEvent->mask & IN_MODIFY)
{
pCM->Connect( deviceID.c_str() );
}
if (pIEvent->mask & IN_DELETE)
{
// Was it the connected device which was removed?
if (pCM->mDeviceID.compare( deviceID ) == 0)
{
pCM->Disconnect();
}
}
}
}
// Cleanup
inotify_rm_watch( inotify, watchD );
close( inotify );
return NULL;
}
/*===========================================================================
METHOD:
UpdateNetworkInfoThread (Free Method)
DESCRIPTION:
Updates the network stats every 1s (while device is present)
PARAMETERS:
pData [ I ] - cSampleCM object
RETURN VALUE:
void * - always NULL
===========================================================================*/
void * UpdateNetworkInfoThread( void * pData )
{
cSampleCM * pCM = (cSampleCM*)pData;
if (pCM == NULL)
{
return NULL;
}
// Update once
pCM->CheckConnectedStats();
int rc;
do
{
// Update rates and times every 1s
pCM->UpdateRateDisplay();
pCM->UpdateTimeDisplay();
DWORD temp;
rc = pCM->mUpdateNetworkInfoEvent.Wait( 1000, temp );
} while (rc == ETIME);
return NULL;
}
/*===========================================================================
METHOD:
ParseStats (Free Method)
DESCRIPTION:
Parse the stats file and obtain the TX/RX bytes and connection duration
NOTE: The stats text file consists of one line in the following format:
Total RX Bytes; Total TX Bytes; Total Duration
PARAMETERS:
line [ I ] - Line to parse
lifeTotalRX [ O ] - Total RX bytes
lifeTotalTX [ O ] - Total TX bytes
lifeTotalDuration [ O ] - Total connection duration
RETURN VALUE:
bool
===========================================================================*/
bool ParseStats(
std::istringstream & line,
ULONGLONG & lifeTotalRX,
ULONGLONG & lifeTotalTX,
ULONGLONG & lifeTotalDuration )
{
ULONGLONG temp1, temp2, temp3;
char c1, c2;
// Attempt to parse into temp variables, skipping whitespace
line >> std::skipws >> temp1 >> c1 >> temp2 >> c2 >> temp3;
// Was parsing successful?
if (line.fail() == true)
{
TRACE( "failed to parse stats file\n" );
return false;
}
else
{
TRACE( "read %llu, %llu, %llu", temp1, temp2, temp3 );
lifeTotalRX = temp1;
lifeTotalTX = temp2;
lifeTotalDuration = temp3;
return true;
}
}
/*=========================================================================*/
// cSampleCM Methods
/*=========================================================================*/
/*===========================================================================
METHOD:
~cSampleCM (Public Method)
DESCRIPTION:
Destructor - Stops the data session,
Stops device detection thread,
Writes stats to file
RETURN VALUE:
None
===========================================================================*/
cSampleCM::~cSampleCM()
{
// These functions will most likely fail. That's ok.
OnCancelDataSession();
OnStopDataSession();
Disconnect();
// Close device detection thread, if open
if (mDeviceDetectionStopPipe[WRITING] != -1)
{
if (mDeviceDetectionThreadID != 0)
{
BYTE byte = 1;
write( mDeviceDetectionStopPipe[WRITING], &byte, 1 );
pthread_join( mDeviceDetectionThreadID, NULL );
mDeviceDetectionThreadID = 0;
}
close( mDeviceDetectionStopPipe[WRITING] );
close( mDeviceDetectionStopPipe[READING] );
mDeviceDetectionStopPipe[READING] = -1;
mDeviceDetectionStopPipe[WRITING] = -1;
}
// Write stats to file
std::string config = getenv( "HOME" );
config += "/.GobiSampleCMStats.txt";
int flags = O_CREAT | O_TRUNC | O_WRONLY;
mode_t mode = S_IRUSR | S_IWUSR;
int statsFile = open( config.c_str(), flags, mode );
if (statsFile < 0)
{
TRACE( "Unable to create stats file\n" );
}
else
{
std::ostringstream out;
out << mLifeTotalRX << "; "
<< mLifeTotalTX << "; "
<< mLifeTotalDuration;
int rc = write( statsFile, out.str().c_str(), out.str().size() );
if (rc < 0)
{
TRACE( "Unable to write stats to file\n" );
}
close( statsFile );
}
}
/*===========================================================================
METHOD:
Init (Public Method)
DESCRIPTION:
Initialize GUI, begin waiting for devices
RETURN VALUE:
bool
===========================================================================*/
bool cSampleCM::Init()
{
Disconnect();
// Read in the stats file
std::string config = getenv( "HOME" );
config += "/.GobiSampleCMStats.txt";
int statsFile = open( config.c_str(), O_RDONLY );
if (statsFile < 0)
{
// Non-fatal error
TRACE( "Unable to open config file %s", config.c_str() );
}
else
{
CHAR buf[100];
int len = read( statsFile, &buf[0], 100 );
if (len < 0)
{
// Non-fatal error
TRACE( "failed to read from file\n" );
}
else
{
std::string asString( &buf[0], len );
std::istringstream line( asString );
ULONGLONG lrx, ltx, ld;
if (ParseStats( line, lrx, ltx, ld ) == true)
{
mLifeTotalRX = lrx;
mLifeTotalTX = ltx;
mLifeTotalDuration = ld;
}
}
close( statsFile );
}
// Life totals will just be zeros if the file was not present
std::ostringstream tmp;
tmp << mLifeTotalRX;
SetLifeRx( tmp.str() );
tmp.str( "" );
tmp << mLifeTotalTX;
SetLifeTx( tmp.str() );
tmp.str( "" );
tmp << std::setfill( '0' ) << std::setw( 2 ) << (mLifeTotalDuration / 3600) % 60
<< std::setw( 1 ) << ":"
<< std::setfill( '0' ) << std::setw( 2 ) << (mLifeTotalDuration / 60) % 60
<< std::setw( 1 ) << ":"
<< std::setfill( '0' ) << std::setw( 2 ) << mLifeTotalDuration % 60;
SetLifeDuration( tmp.str() );
// Set the global pointer, used by callbacks
gpCM = this;
// Initialize command pipe
int ret = pipe( mDeviceDetectionStopPipe );
if (ret != 0)
{
// Should never happen, but just in case...
return false;
}
// Begin scanning for devices
ret = pthread_create( &mDeviceDetectionThreadID,
0,
DeviceDetectionThread,
this );
if (ret != 0)
{
return false;
}
return true;
}
/*===========================================================================
METHOD:
Connect (Public Method)
DESCRIPTION:
Initializes the Gobi API to the current device
PARAMETERS:
pInterface [ I ] - Interace to connect to
RETURN VALUE:
ULONG
===========================================================================*/
ULONG cSampleCM::Connect( LPCSTR pInterface )
{
// Are we already connected to a device?
if (mDeviceID.size() != 0)
{
return eGOBI_ERR_MULTIPLE_DEVICES;
}
// Connect to the device
ULONG rc = mGobi.Connect( pInterface );
if (rc != eGOBI_ERR_NONE)
{
TRACE( "GobiConnect error %lu\n", rc );
return rc;
}
UpdateDeviceInfo();
UpdateConnectionInfo();
// Any connection at this point is an external connection
UpdateSessionState( true, mSessionState );
// Success
mDeviceID = pInterface;
return rc;
}
/*===========================================================================
METHOD:
Disconnect (Public Method)
DESCRIPTION:
Calls GobiDisconnect
RETURN VALUE:
ULONG
===========================================================================*/
ULONG cSampleCM::Disconnect()
{
// Stop a data session, if present
OnCancelDataSession();
OnStopDataSession();
mGobi.Disconnect();
// Reset state
mSessionID = 0xFFFFFFFF;
mInitialState = 0xFFFFFFFF;
mDataBearerTech = 0;
// Store life total
mLifeTotalDuration += mTotalDuration;
// Reset all totals
mStartTime = 0;
mPreviousRX = 0;
mPreviousTX = 0;
mTotalRX = 0;
mTotalTX = 0;
mTotalDuration = 0;
SetState( "No Device" );
SetRSSI( "Unknown" );
SetTech( "Unknown" );
SetRx( "Unknown" );
SetTx( "Unknown" );
SetMaxRx( "Unknown" );
SetMaxTx( "Unknown" );
SetRoam( "Unknown" );
SetDuration( "Unknown" );
SetLifeDuration( "Unknown" );
SetLifeRx( "Unknown" );
SetLifeTx( "Unknown" );
SetManufact( "Unknown" );
SetModel( "Unknown" );
SetHardware( "Unknown" );
SetFirmware( "Unknown" );
SetMDN( "Unknown" );
SetMIN( "Unknown" );
SetESN( "Unknown" );
SetMEID( "Unknown" );
SetIMEI( "Unknown" );
SetIMSI( "Unknown" );
return eGOBI_ERR_NONE;
}
/*===========================================================================
ETHOD:
OnStartDataSession (Public Method)
DESCRIPTION:
Starts a data session
PARAMETERS:
pFailureCode [ O ] - Call failure code, if provided
RETURN VALUE:
ULONG
===========================================================================*/
ULONG cSampleCM::OnStartDataSession( ULONG * pFailureCode )
{
mbInitiatedStartDataSession = true;
// Use provided values, if not empty strings
LPCSTR pAPN = (mAPN.size() == 0) ? 0 : mAPN.c_str();
LPCSTR pUsername = (mUsername.size() == 0) ? 0 : mUsername.c_str();
LPCSTR pPassword = (mPassword.size() == 0) ? 0 : mPassword.c_str();
ULONG rc = mGobi.StartDataSession( pAPN,
pUsername,
pPassword,
&mSessionID,
pFailureCode );
if (rc == eGOBI_ERR_NONE)
{
mPreviousRX = 0;
mPreviousTX = 0;
mTotalRX = 0;
mTotalTX = 0;
mTotalDuration = 0;
mStartTime = GetTickCount();
}
else
{
mbInitiatedStartDataSession = false;
}
return rc;
}
/*===========================================================================
METHOD:
CancelDataSession (Public Method)
DESCRIPTION:
Cancels an in progress data session request
RETURN VALUE:
ULONG
===========================================================================*/
ULONG cSampleCM::OnCancelDataSession()
{
return mGobi.CancelDataSession();
}
/*===========================================================================
METHOD:
OnStopDataSession (Public Method)
DESCRIPTION:
Calls WDSStopNetworkInterface
RETURN VALUE:
ULONG
===========================================================================*/
ULONG cSampleCM::OnStopDataSession()
{
if (mSessionID == 0xFFFFFFFF)
{
return eGOBI_ERR_GENERAL;
}
mbInitiatedStopDataSession = true;
mDataBearerTech = 0;
ULONG rc = mGobi.StopDataSession( mSessionID );
mSessionID = 0xFFFFFFFF;
return rc;
}
/*===========================================================================
METHOD:
OnSignalStrengthCBNotification (Internal Method)
DESCRIPTION:
Handle signal strength callback notification
PARAMETERS:
signalStr [ I ] - Signal strength
radioInterface [ I ] - Radio interface
===========================================================================*/
void cSampleCM::OnSignalStrengthCBNotificaion(
INT8 signalStr,
ULONG radioInterface )
{
// Update mServiceSignals
mServiceSignals[radioInterface] = signalStr;
if (mSessionState == eQMIConnectionStatus_Connected)
{
mDataBearerTech = 0;
}
UpdateSignalAndTech();
}
/*===========================================================================
METHOD:
OnSessionStateCBNotification (Internal Method)
DESCRIPTION:
Handle session state callback notification
PARAMETERS:
state [ I ] - Session state
===========================================================================*/
void cSampleCM::OnSessionStateCBNotification( ULONG state )
{
// Was the state change triggered externally?
bool bStateChangeExternal = false;
// Save session state
if (state == eQMIConnectionStatus_Connected)
{
// Started externally
if (mbInitiatedStartDataSession == false)
{
bStateChangeExternal = true;
mStartTime = GetTickCount();
}
}
else
{
mDataBearerTech = 0;
// Stopped externally
if (mbInitiatedStopDataSession == false)
{
// Store all life total variables
mLifeTotalDuration += mTotalDuration;
// Reset all totals
bStateChangeExternal = true;
mStartTime = 0;
mPreviousRX = 0;
mPreviousTX = 0;
mTotalRX = 0;
mTotalTX = 0;
mTotalDuration = 0;
}
}
// Update the session state
UpdateSessionState( bStateChangeExternal, state );
// Update the signal strength and technology
UpdateSignalAndTech();
// Reset to false
mbInitiatedStartDataSession = false;
mbInitiatedStopDataSession = false;
}
/*===========================================================================
METHOD:
OnDataBearerCBNotification (Internal Method)
DESCRIPTION:
Handle data bearer callback notification
PARAMETERS:
dataBearerTech [ I ] - Data bearer technology
===========================================================================*/
void cSampleCM::OnDataBearerCBNotification( ULONG dataBearerTech )
{
if (dataBearerTech != eQMIDataBearerTechnologies_Unknown)
{
mDataBearerTech = dataBearerTech;
UpdateSignalAndTech();
}
}
/*===========================================================================
METHOD:
OnDataCapsNotification (Internal Method)
DESCRIPTION:
Handle data capabilities callback notification
PARAMETERS:
numDataCaps [ I ] - Number of data capabilities
pDataCaps [ I ] - Data Capabilites
===========================================================================*/
void cSampleCM::OnDataCapsNotification(
ULONG numDataCaps,
eQMINASDataServiceCapabilities2 * pDataCaps )
{
// Clear saved data capabilities in order to update
mDataCapabilities.clear();
// Populate list with new capabilities
for (ULONG c = 0; c < numDataCaps; c++)
{
ULONG dataCaps = pDataCaps[c];
if ( (dataCaps != 0xFFFFFFFF)
&& (dataCaps != 0) )
{
mDataCapabilities.push_back( pDataCaps[c] );
}
}
UpdateSignalAndTech();
}
/*===========================================================================
METHOD:
OnByteTotalsNotification (Internal Method)
DESCRIPTION:
Handle byte totals callback notification
PARAMETERS:
rx [ I ] - received bytes
tx [ I ] - transmitted bytes
===========================================================================*/
void cSampleCM::OnByteTotalsNotification( ULONGLONG rx, ULONGLONG tx )
{
mTotalTX = tx;
mTotalRX = rx;
}
/*===========================================================================
METHOD:
UpdateSignalAndTech (Public Method)
DESCRIPTION:
Update the signal strength and technology display
===========================================================================*/
void cSampleCM::UpdateSignalAndTech()
{
std::string radioStr = "Unknown";
std::string ssStr = "Unknown";
ULONG radioVal = eQMINASRadioInterfaces_NoneNoService;
// If connected, use data bearer
if (mDataBearerTech != 0)
{
switch (mDataBearerTech)
{
case eQMIDataBearerTechnologies_CDMA20001x:
radioStr = "CDMA 1xRTT";
radioVal = eQMINASRadioInterfaces_CDMA20001x;
break;
case eQMIDataBearerTechnologies_CDMA20001xEVDORev0:
radioStr = "CDMA 1xEVDO Rev 0";
radioVal = eQMINASRadioInterfaces_CDMA2000HRPD;
break;
case eQMIDataBearerTechnologies_GPRS:
radioStr = "GRPS";
radioVal = eQMINASRadioInterfaces_GSM;
break;
case eQMIDataBearerTechnologies_WCDMA:
radioStr = "WCDMA";
radioVal = eQMINASRadioInterfaces_UMTS;
break;
case eQMIDataBearerTechnologies_CDMA20001xEVDORevA:
radioStr = "CDMA 1xEVDO Rev A";
radioVal = eQMINASRadioInterfaces_CDMA2000HRPD;
break;
case eQMIDataBearerTechnologies_EGPRS:
radioStr = "EDGE";
radioVal = eQMINASRadioInterfaces_GSM;
break;
case eQMIDataBearerTechnologies_HSDPAWCDMA:
radioStr = "HSDPA DL, WCDMA UL";
radioVal = eQMINASRadioInterfaces_UMTS;
break;
case eQMIDataBearerTechnologies_WCDMAHSUPA:
radioStr = "WCDMA DL, HSUPA UL";
radioVal = eQMINASRadioInterfaces_UMTS;
break;
case eQMIDataBearerTechnologies_HSDPAHSUPA:
radioStr = "HSDPA DL, HSUPA UL";
radioVal = eQMINASRadioInterfaces_UMTS;
break;
case eQMIDataBearerTechnologies_LTE:
radioStr = "LTE";
radioVal = eQMINASRadioInterfaces_LTE;
break;
case eQMIDataBearerTechnologies_CDMA2000EHRPD:
radioStr = "CDMA 1xEVDO eHRPD";
radioVal = eQMINASRadioInterfaces_CDMA2000HRPD;
break;
case eQMIDataBearerTechnologies_HSDPAPlusWCDMA:
radioStr = "HSDPA+ DL, WCDMA UL";
radioVal = eQMINASRadioInterfaces_UMTS;
break;
case eQMIDataBearerTechnologies_HSDPAPlusHSUPA:
radioStr = "HSDPA+ DL, HSUPA UL";
radioVal = eQMINASRadioInterfaces_UMTS;
break;
case eQMIDataBearerTechnologies_DualCellHSDPAPlusWCDMA:
radioStr = "Dual Cell HSDPA+ DL, WCDMA UL";
radioVal = eQMINASRadioInterfaces_UMTS;
break;
case eQMIDataBearerTechnologies_DualCellHSDPAPlusHSUPA:
radioStr = "Dual Cell HSDPA+ DL, HSUPA UL";
radioVal = eQMINASRadioInterfaces_UMTS;
break;
}
if ( (radioVal != eQMINASRadioInterfaces_NoneNoService)
&& (mServiceSignals.find( radioVal ) != mServiceSignals.end())
&& (mServiceSignals[radioVal] != SCHAR_MAX) )
{
std::ostringstream temp;
temp << (INT)mServiceSignals[radioVal];
ssStr = temp.str();
}
}
else
{
// When not connected we have to pick the most preferred data capability
eQMINASDataServiceCapabilities2 mostPreferredCap
= (eQMINASDataServiceCapabilities2)0xff;
std::list <ULONG>::const_iterator pIter;
std::list <ULONG>::const_iterator pDataCapsItr;
pIter = mPreferredServices.begin();
bool bDone = false;
while ( (pIter != mPreferredServices.end())
&& (bDone != true) )
{
pDataCapsItr = mDataCapabilities.begin();
while (pDataCapsItr != mDataCapabilities.end())
{
if (*pIter == *pDataCapsItr)
{
mostPreferredCap = (eQMINASDataServiceCapabilities2)*pIter;
bDone = true;
break;
}
pDataCapsItr++;
}
pIter++;
}
// Determine the best radio interface reported
eQMINASRadioInterfaces preferredRadioIf;
preferredRadioIf = eQMINASRadioInterfaces_NoneNoService;
if (mServiceSignals.find( eQMINASRadioInterfaces_LTE ) != mServiceSignals.end())
{
preferredRadioIf = eQMINASRadioInterfaces_LTE;
}
else if (mServiceSignals.find( eQMINASRadioInterfaces_CDMA2000HRPD ) != mServiceSignals.end())
{
preferredRadioIf = eQMINASRadioInterfaces_CDMA2000HRPD;
}
else if (mServiceSignals.find( eQMINASRadioInterfaces_CDMA20001x ) != mServiceSignals.end())
{
preferredRadioIf = eQMINASRadioInterfaces_CDMA20001x;
}
else if (mServiceSignals.find( eQMINASRadioInterfaces_UMTS ) != mServiceSignals.end())
{
preferredRadioIf = eQMINASRadioInterfaces_UMTS;
}
else if (mServiceSignals.find( eQMINASRadioInterfaces_GSM ) != mServiceSignals.end())
{
preferredRadioIf = eQMINASRadioInterfaces_GSM;
}
radioStr = "Unknown";
ssStr = "Unknown";
radioVal = eQMINASRadioInterfaces_NoneNoService;
// Determine sig strength and radio interface to display based on
// most preferred data capabilities
switch (preferredRadioIf)
{
case eQMINASRadioInterfaces_CDMA2000HRPD:
radioStr = "CDMA 1xEVDO";
if ( (mostPreferredCap == eQMINASDataServiceCapabilities2_CDMA1xEVDORevB)
|| (mostPreferredCap == eQMINASDataServiceCapabilities2_CDMA1xEVDORevA)
|| (mostPreferredCap == eQMINASDataServiceCapabilities2_CDMA1xEVDORev0) )
{
radioVal = eQMINASRadioInterfaces_CDMA2000HRPD;
if ( (mServiceSignals.find( radioVal ) != mServiceSignals.end())
&& (mServiceSignals[radioVal] != SCHAR_MAX) )
{
std::ostringstream temp;
temp << (INT)mServiceSignals[radioVal];
ssStr = temp.str();
}
}
break;
case eQMINASRadioInterfaces_CDMA20001x:
radioStr = "CDMA 1xRTT";
if (mostPreferredCap == eQMINASDataServiceCapabilities2_CDMA)
{
radioVal = eQMINASRadioInterfaces_CDMA20001x;
if ( (mServiceSignals.find( radioVal ) != mServiceSignals.end())
&& (mServiceSignals[radioVal] != SCHAR_MAX) )
{
std::ostringstream temp;
temp << (INT)mServiceSignals[radioVal];
ssStr = temp.str();
}
}
break;
case eQMINASRadioInterfaces_UMTS:
radioStr = "WCDMA";
if ( (mostPreferredCap == eQMINASDataServiceCapabilities2_DCHSDPAPlus)
|| (mostPreferredCap == eQMINASDataServiceCapabilities2_HSDPAPlus)
|| (mostPreferredCap == eQMINASDataServiceCapabilities2_HSDPA)
|| (mostPreferredCap == eQMINASDataServiceCapabilities2_HSUPA)
|| (mostPreferredCap == eQMINASDataServiceCapabilities2_WCDMA) )
{
radioVal = eQMINASRadioInterfaces_UMTS;
if ( (mServiceSignals.find( radioVal ) != mServiceSignals.end())
&& (mServiceSignals[radioVal] != SCHAR_MAX) )
{
std::ostringstream temp;
temp << (INT)mServiceSignals[radioVal];
ssStr = temp.str();
}
}
break;
case eQMINASRadioInterfaces_GSM:
radioStr = "GSM";
if ( (mostPreferredCap == eQMINASDataServiceCapabilities2_GPRS)
|| (mostPreferredCap == eQMINASDataServiceCapabilities2_EGPRS)
|| (mostPreferredCap == eQMINASDataServiceCapabilities2_GSM) )
{
radioVal = eQMINASRadioInterfaces_GSM;
if ( (mServiceSignals.find( radioVal ) != mServiceSignals.end())
&& (mServiceSignals[radioVal] != SCHAR_MAX) )
{
std::ostringstream temp;
temp << (INT)mServiceSignals[radioVal];
ssStr = temp.str();
}
}
break;
case eQMINASRadioInterfaces_LTE:
radioStr = "LTE";
if (mostPreferredCap == eQMINASDataServiceCapabilities2_LTE)
{
radioVal = eQMINASRadioInterfaces_LTE;
if ( (mServiceSignals.find( radioVal ) != mServiceSignals.end())
&& (mServiceSignals[radioVal] != SCHAR_MAX) )
{
std::ostringstream temp;
temp << (INT)mServiceSignals[radioVal];
ssStr = temp.str();
}
}
break;
case eQMINASRadioInterfaces_NoneNoService:
if ( (mServiceSignals.find( eQMINASRadioInterfaces_CDMA2000HRPD ) != mServiceSignals.end())
&& (mServiceSignals[eQMINASRadioInterfaces_CDMA2000HRPD] != SCHAR_MAX) )
{
radioStr = "CDMA 1xEVDO";
std::ostringstream temp;
temp << (INT)mServiceSignals[eQMINASRadioInterfaces_CDMA2000HRPD];
ssStr = temp.str();
}
else if ( (mServiceSignals.find( eQMINASRadioInterfaces_CDMA20001x ) != mServiceSignals.end())
&& (mServiceSignals[eQMINASRadioInterfaces_CDMA20001x] != SCHAR_MAX) )
{
radioStr = "CDMA 1xRTT";
std::ostringstream temp;
temp << (INT)mServiceSignals[eQMINASRadioInterfaces_CDMA20001x];
ssStr = temp.str();
}
else if ( (mServiceSignals.find( eQMINASRadioInterfaces_UMTS ) != mServiceSignals.end())
&& (mServiceSignals[eQMINASRadioInterfaces_UMTS] != SCHAR_MAX) )
{
radioStr = "WCDMA";
std::ostringstream temp;
temp << (INT)mServiceSignals[eQMINASRadioInterfaces_UMTS];
ssStr = temp.str();
}
else if ( (mServiceSignals.find( eQMINASRadioInterfaces_GSM ) != mServiceSignals.end())
&& (mServiceSignals[eQMINASRadioInterfaces_GSM] != SCHAR_MAX) )
{
radioStr = "GSM";
std::ostringstream temp;
temp << (INT)mServiceSignals[eQMINASRadioInterfaces_GSM];
ssStr = temp.str();
}
else if ( (mServiceSignals.find( eQMINASRadioInterfaces_LTE ) != mServiceSignals.end())
&& (mServiceSignals[eQMINASRadioInterfaces_LTE] != SCHAR_MAX) )
{
radioStr = "LTE";
std::ostringstream temp;
temp << (INT)mServiceSignals[eQMINASRadioInterfaces_LTE];
ssStr = temp.str();
}
break;
default:
radioStr = "Unknown";
ssStr = "Unknown";
}
}
SetRSSI( ssStr );
SetTech( radioStr );
}
/*===========================================================================
METHOD:
UpdateSessionState (Public Method)
DESCRIPTION:
Update the session state display
Start/stop UpdateNetworkInfo thread
PARAMETERS:
bExternal [ I ] - Was the state change triggered externally
state [ I ] - State of session
RETURN VALUE:
None
===========================================================================*/
void cSampleCM::UpdateSessionState(
bool bExternal,
ULONG state )
{
mSessionState = state;
if (bExternal == true)
{
mInitialState = state;
}
LPCSTR pState = "Unknown";
switch (state)
{
// Disconnected
case 1:
{
pState = "Disconnected";
// Stop the network info thread, if running
if (mUpdateNetworkInfoThreadID != 0)
{
mUpdateNetworkInfoEvent.Set( 0 );
pthread_join( mUpdateNetworkInfoThreadID, NULL );
mUpdateNetworkInfoThreadID = 0;
mLifeTotalDuration += mTotalDuration;
}
// Reset all totals
mPreviousRX = 0;
mPreviousTX = 0;
mTotalRX = 0;
mTotalTX = 0;
mTotalDuration = 0;
mStartTime = 0;
UpdateRateDisplay();
UpdateTimeDisplay();
}
break;
// Connected
case 2:
{
if (bExternal == true)
{
pState = "External Con";
}
else
{
pState = "Connected";
}
// Start the network info thread, if not running
if (mUpdateNetworkInfoThreadID == 0)
{
// Begin updating network info
int rc = pthread_create( &mUpdateNetworkInfoThreadID,
0,
UpdateNetworkInfoThread,
this );
if (rc != 0)
{
TRACE( "error starting network info thread\n" );
}
}
}
break;
// Suspended
case 3:
pState = "Suspended";
break;
// Connecting
case 4:
if (bExternal == true)
{
pState = "Ext Connecting";
}
else
{
pState = "Connecting";
}
break;
}
SetState( pState );
}
/*===========================================================================
METHOD:
UpdateRateDisplay (Public Method)
DESCRIPTION:
Calculate and update the tx, rx rates being displayed
PARAMETERS:
None
RETURN VALUE:
None
===========================================================================*/
void cSampleCM::UpdateRateDisplay()
{
// Update TX/RX Bytes
ULONGLONG deltaRX = 0;
ULONGLONG deltaTX = 0;
// Only update rates if connected
if (mSessionState == eQMIConnectionStatus_Connected)
{
// First time through, don't use the deltas
if (mTotalRX != 0)
{
deltaRX = mTotalRX - mPreviousRX;
}
if (mTotalTX != 0)
{
deltaTX = mTotalTX - mPreviousTX;
}
// Update life total byte variables
mLifeTotalRX += deltaRX;
mLifeTotalTX += deltaTX;
mPreviousRX = mTotalRX;
mPreviousTX = mTotalTX;
}
std::ostringstream tmp;
tmp << deltaRX;
SetRx( tmp.str() );
tmp.str( "" );
tmp << deltaTX;
SetTx( tmp.str() );
tmp.str( "" );
tmp << mLifeTotalRX;
SetLifeRx( tmp.str() );
tmp.str( "" );
tmp << mLifeTotalTX;
SetLifeTx( tmp.str() );
}
/*===========================================================================
METHOD:
UpdateTimeDisplay (Public Method)
DESCRIPTION:
Calculate and update the connection time being displayed
PARAMETERS:
None
RETURN VALUE:
None
===========================================================================*/
void cSampleCM::UpdateTimeDisplay()
{
DWORD elapsedTime = 0;
DWORD lifeTotalTime = 0;
// Update session duration
if ( (mSessionState == eQMIConnectionStatus_Connected)
&& (mStartTime != 0) )
{
// Convert ms to seconds
mTotalDuration = (GetTickCount() - mStartTime) / 1000;
elapsedTime = (DWORD)mTotalDuration;
lifeTotalTime = (DWORD)(mLifeTotalDuration + mTotalDuration);
}
else
{
elapsedTime = 0;
lifeTotalTime = (DWORD)mLifeTotalDuration;
}
std::ostringstream timeStr;
// Format both into hh:mm:ss
// "%02d:%02d:%02d"
timeStr << std::setfill( '0' ) << std::setw( 2 ) << (elapsedTime / 3600) % 60
<< std::setw( 1 ) << ":"
<< std::setfill( '0' ) << std::setw( 2 ) << (elapsedTime / 60) % 60
<< std::setw( 1 ) << ":"
<< std::setfill( '0' ) << std::setw( 2 ) << elapsedTime % 60;
SetDuration( timeStr.str() );
timeStr.str( "" );
timeStr << std::setfill( '0' ) << std::setw( 2 ) << (lifeTotalTime / 3600) % 60
<< std::setw( 1 ) << ":"
<< std::setfill( '0' ) << std::setw( 2 ) << (lifeTotalTime / 60) % 60
<< std::setw( 1 ) << ":"
<< std::setfill( '0' ) << std::setw( 2 ) << lifeTotalTime % 60;
SetLifeDuration( timeStr.str() );
}
/*===========================================================================
METHOD:
CheckConnectedStats (Public Method)
DESCRIPTION:
Check stats which are only valid during a connection
===========================================================================*/
void cSampleCM::CheckConnectedStats()
{
ULONG rc = mGobi.GetDataBearerTechnology( &mDataBearerTech );
if (rc != eGOBI_ERR_NONE || mDataBearerTech == ULONG_MAX)
{
TRACE( "GetDataBearerTechnology error %lu\n", rc );
return;
}
ULONGLONG duration;
rc = mGobi.GetSessionDuration( &duration );
if (rc != eGOBI_ERR_NONE)
{
TRACE( "GetSessionDuration error %lu\n", rc );
return;
}
mStartTime = GetTickCount() - duration;
UpdateSignalAndTech();
}
/*===========================================================================
METHOD:
UpdateDeviceInfo (Public Method)
DESCRIPTION:
Update the device info stats
===========================================================================*/
void cSampleCM::UpdateDeviceInfo()
{
ULONG status = 0;
BYTE strSz1 = 255;
BYTE strSz2 = 255;
BYTE strSz3 = 255;
CHAR str1[255];
CHAR str2[255];
CHAR str3[255];
str1[0] = 0;
str2[0] = 0;
str3[0] = 0;
// Get manufacturer
status = mGobi.GetManufacturer( strSz1, str1 );
if (status != 0)
{
TRACE( "GetManufacturer() = %lu\n", status );
return;
}
if (str1[0] != 0)
{
SetManufact( &str1[0] );
}
// Get model ID
str1[0] = 0;
status = mGobi.GetModelID( strSz1, str1 );
if (status != 0)
{
TRACE( "GetModelID() = %lu\n", status );
return;
}
if (str1[0] != 0)
{
std::ostringstream tmp;
if (strncmp( "88", &str1[0], 2 ) == 0)
{
tmp << "Gobi MDM-1000";
}
else if (strncmp( "12", &str1[0], 2 ) == 0)
{
tmp << "Gobi MDM-2000";
}
else
{
tmp << "Unknown (" << &str1[0] << ")";
}
SetModel( tmp.str() );
}
// Get firmware revision
str1[0] = 0;
status = mGobi.GetFirmwareRevision( strSz1, str1 );
if (status != 0)
{
TRACE( "GetFirmwareRevision() = %lu\n", status );
return;
}
if (str1[0] != 0)
{
SetFirmware( &str1[0] );
}
// Get hardware revision
str1[0] = 0;
status = mGobi.GetHardwareRevision( strSz1, str1 );
if (status != 0)
{
TRACE( "GetHardwareRevision() = %lu\n", status );
return;
}
if (str1[0] != 0)
{
SetHardware( &str1[0] );
}
// Get MDN/MIN
str1[0] = 0;
str2[0] = 0;
status = mGobi.GetVoiceNumber( strSz1, str1, strSz2, str2 );
if (status != 0)
{
if (status == 1016)
{
// Not provisioned
SetMDN( "Not provisioned" );
SetMIN( "Not provisioned" );
}
else
{
TRACE( "GetVoiceNumber() = %lu\n", status );
return;
}
}
if (str1[0] != 0)
{
SetMDN( &str1[0] );
}
if (str2[0] != 0)
{
SetMIN( &str2[0] );
}
// Get ESN/IMEI/MEID
str1[0] = 0;
str2[0] = 0;
str3[0] = 0;
status = mGobi.GetSerialNumbers( strSz1,
str1,
strSz2,
str2,
strSz3,
str3 );
if (status != 0)
{
if (status == 1016)
{
// Not provisioned
SetESN( "Not provisioned" );
SetIMEI( "Not provisioned" );
SetMEID( "Not provisioned" );
}
else
{
TRACE( "GetSerialNumbers() = %lu\n", status );
return;
}
}
if (str1[0] != 0)
{
SetESN( &str1[0] );
}
if (str2[0] != 0)
{
SetIMEI( &str2[0] );
}
if (str3[0] != 0)
{
SetMEID( &str3[0] );
}
// Get IMSI
str1[0] = 0;
status = mGobi.GetIMSI( strSz1, str1 );
if (status != 0)
{
if (status == 1016)
{
// Not provisioned
SetIMSI( "Not provisioned" );
}
else
{
TRACE( "GetIMSI() = %lu\n", status );
return;
}
}
if (str1[0] != 0)
{
SetIMSI( &str1[0] );
}
}
/*===========================================================================
METHOD:
UpdateConnectionInfo (Public Method)
DESCRIPTION:
Update the connection info stats
===========================================================================*/
void cSampleCM::UpdateConnectionInfo()
{
// Re-usable buffer
std::ostringstream tmp;
// Get session state
ULONG rc = mGobi.GetSessionState( &mSessionState );
if (rc != eGOBI_ERR_NONE || mSessionState == ULONG_MAX)
{
TRACE( "GetSessionState error %lu\n", rc );
return;
}
UpdateSessionState( mSessionID != 0xFFFFFFFF, mSessionState );
// If connected, refresh data bearer technology and session duration
if (mSessionState == eQMIConnectionStatus_Connected)
{
CheckConnectedStats();
}
INT8 signalStrengths[MAX_SIGNALS];
ULONG radioInterfaces[MAX_SIGNALS];
// Get the signal strengths
rc = mGobi.GetSignalStrengths( &signalStrengths[0],
&radioInterfaces[0] );
if (rc != eGOBI_ERR_NONE && rc != eGOBI_ERR_NO_SIGNAL)
{
TRACE( "GetSignalStrengths error %lu\n", rc );
return;
}
// Map signal strengths to RadioIf types
for (ULONG s = 0; s < MAX_SIGNALS; s++)
{
INT8 signal = signalStrengths[s];
switch (radioInterfaces[s])
{
case eQMINASRadioInterfaces_NoneNoService:
mServiceSignals[eQMINASRadioInterfaces_NoneNoService] = signal;
break;
case eQMINASRadioInterfaces_CDMA20001x:
mServiceSignals[eQMINASRadioInterfaces_CDMA20001x] = signal;
break;
case eQMINASRadioInterfaces_CDMA2000HRPD:
mServiceSignals[eQMINASRadioInterfaces_CDMA2000HRPD] = signal;
break;
case eQMINASRadioInterfaces_AMPS:
mServiceSignals[eQMINASRadioInterfaces_AMPS] = signal;
break;
case eQMINASRadioInterfaces_GSM:
mServiceSignals[eQMINASRadioInterfaces_GSM] = signal;
break;
case eQMINASRadioInterfaces_UMTS:
mServiceSignals[eQMINASRadioInterfaces_UMTS] = signal;
break;
case eQMINASRadioInterfaces_LTE:
mServiceSignals[eQMINASRadioInterfaces_LTE] = signal;
break;
}
}
ULONG curTX = 0;
ULONG curRX = 0;
ULONG maxTX = 0;
ULONG maxRX = 0;
// Get the connection rate
rc = mGobi.GetConnectionRate( &curTX, &curRX, &maxTX, &maxRX );
if (rc != 0)
{
TRACE( "GetConnectionRate error %lu\n", rc );
return;
}
// Store the max data rates
tmp.str( "" );
tmp << maxRX;
SetMaxRx( tmp.str() );
tmp.str( "" );
tmp << maxTX;
SetMaxTx( tmp.str() );
ULONG dataCapabilities[MAX_DATA_CAPABILITIES];
WORD srvMCC = 0;
WORD srvMNC = 0;
BYTE srvNameSize = 255;
CHAR srvName[255];
srvName[0] = 0;
WORD sid = 0;
WORD nid = 0;
ULONG roam = 0;
// Get the serving network info
rc = mGobi.GetServingNetwork( &dataCapabilities[0],
&srvMCC,
&srvMNC,
srvNameSize,
&srvName[0],
&sid,
&nid,
&roam );
if (rc != 0)
{
TRACE( "GetServingNetwork error %lu\n", rc );
return;
}
// Store data capabilities
for (ULONG c = 0; c < MAX_DATA_CAPABILITIES; c++)
{
if ( (dataCapabilities[c] != ULONG_MAX)
&& (dataCapabilities[c] != 0) )
{
mDataCapabilities.push_back( dataCapabilities[c] );
}
}
if (roam == 0xFFFFFFFF)
{
SetRoam( "Unknown" );
}
else
{
tmp.str( "" );
tmp << roam;
SetRoam( tmp.str() );
}
// Update the signal strength and technology fields
if (mSessionState != eQMIConnectionStatus_Connected)
{
mDataBearerTech = 0;
}
UpdateSignalAndTech();
// Setup callbacks
rc = mGobi.SetWDSEventReportCB( WDSEventReportCallback, 1 );
if (rc != eGOBI_ERR_NONE)
{
TRACE( "SetWDSEventReportCB error %lu\n", rc );
return;
}
rc = mGobi.SetWDSSessionStateCB( WDSSessionStateCallback );
if (rc != eGOBI_ERR_NONE)
{
TRACE( "SetWDSSessionStateCB error %lu\n", rc );
return;
}
BYTE thresSz = 5;
INT8 thres[5] = {-90, -85, -80, -75, -70};
rc = mGobi.SetNASEventReportCB( NASEventReportCallback,
thresSz,
&thres[0] );
if (rc != eGOBI_ERR_NONE)
{
TRACE( "SetNASEventReportCB error %lu\n", rc );
return;
}
rc = mGobi.SetNASServingSystemCB( NASServingSystemCallback );
if (rc != eGOBI_ERR_NONE)
{
TRACE( "SetNASServingSystemCB error %lu\n", rc );
return;
}
}