/*===========================================================================
METHOD:
   ParseGetVoiceNumber

DESCRIPTION:
   This function returns the voice number in use by the device

PARAMETERS:
   inLen             [ I ] - Length of input buffer
   pIn               [ I ] - Input buffer
   voiceNumberSize   [ I ] - The maximum number of characters (including NULL
                             terminator) that the voice number array can
                             contain
   pVoiceNumber      [ O ] - Voice number (MDN or ISDN) string
   minSize           [ I ] - The maximum number of characters (including NULL
                             terminator) that the MIN array can contain
   pMIN              [ O ] - MIN string (empty string returned when MIN is
                             not supported/programmed)

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetVoiceNumber(
   ULONG             inLen,
   const BYTE *      pIn,
   BYTE              voiceNumberSize,
   CHAR *            pVoiceNumber,
   BYTE              minSize,
   CHAR *            pMIN )
{
   // Validate arguments
   if (pIn == 0
   ||  voiceNumberSize == 0 || pVoiceNumber == 0
   ||  minSize == 0 || pMIN == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Assume failure
   *pVoiceNumber = 0;
   *pMIN = 0;

   // Find the Voice number
   // sDMSGetDeviceVoiceNumberResponse_VoiceNumber only contains this
   const CHAR * pTLVx01;
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   // Space to perform the copy?
   if (voiceNumberSize < outLenx01 + 1)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   memcpy( pVoiceNumber, pTLVx01, outLenx01 );
   pVoiceNumber[outLenx01] = 0;

   // Find the Mobile ID (optional)
   // sDMSGetDeviceVoiceNumberResponse_MobileIDNumber only contains this
   const CHAR * pTLVx10;
   ULONG outLenx10;
   rc = GetTLV( inLen, pIn, 0x10, &outLenx10, (const BYTE **)&pTLVx10 );
   if (rc == eGOBI_ERR_NONE)
   {
      // Space to perform the copy?
      if (minSize < outLenx10 + 1)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      memcpy( pMIN, pTLVx10, outLenx10 );
      pMIN[outLenx10] = 0;
   }

   return eGOBI_ERR_NONE;
}
/*===========================================================================
METHOD:
   ParseGetOfflineReason

DESCRIPTION:
   This function returns the reason why the operating mode of the device
   is currently offline

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pReasonMask [ O ] - Bitmask of offline reasons
   pbPlatform  [ O ] - Offline due to being platform retricted?

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetOfflineReason(
   ULONG             inLen,
   const BYTE *      pIn,
   ULONG *           pReasonMask,
   ULONG *           pbPlatform )
{
   // Validate arguments
   if (pIn == 0 || pReasonMask == 0 || pbPlatform == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Assume failure
   *pReasonMask = 0;
   *pbPlatform = 0;

   // Find the reason mask (optional)
   const sDMSGetOperatingModeResponse_OfflineReason * pTLVx10;
   ULONG outLenx10;
   ULONG rc = GetTLV( inLen, pIn, 0x10, &outLenx10, (const BYTE **)&pTLVx10 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx10 < sizeof( sDMSGetOperatingModeResponse_OfflineReason ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      // Copy the bitmask to pReasonMask
      *pReasonMask = *(WORD*)pTLVx10;
   }

   // Find the platform restriction (optional)
   const sDMSGetOperatingModeResponse_PlatformRestricted * pTLVx11;
   ULONG outLenx11;
   rc = GetTLV( inLen, pIn, 0x11, &outLenx11, (const BYTE **)&pTLVx11 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx11 < sizeof( sDMSGetOperatingModeResponse_PlatformRestricted ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      // Copy the value
      *pbPlatform = pTLVx11->mPlatformRestricted;
   }

   return eGOBI_ERR_NONE;
}
/*===========================================================================
METHOD:
   ParseGetPRLVersion

DESCRIPTION:
   This function returns the version of the active Preferred Roaming List
   (PRL) in use by the device

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pPRLVersion [ O ] - The PRL version number

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetPRLVersion(
   ULONG             inLen,
   const BYTE *      pIn,
   WORD *            pPRLVersion )
{
   // Validate arguments
   if (pIn == 0 || pPRLVersion == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Find the state
   const sDMSGetPRLVersionResponse_PRLVersion * pTLVx01;
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   if (outLenx01 < sizeof( sDMSGetPRLVersionResponse_PRLVersion ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   *pPRLVersion = pTLVx01->mPRLVersion;

   return eGOBI_ERR_NONE;
}
/*===========================================================================
METHOD:
   ParseGetHardwareRevision

DESCRIPTION:
   This function returns the device hardware revision

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   stringSize  [ I ] - The maximum number of characters (including NULL
                       terminator) that the string array can contain
   pString     [ O ] - NULL terminated string

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetHardwareRevision(
   ULONG             inLen,
   const BYTE *      pIn,
   BYTE              stringSize,
   CHAR *            pString )
{
   // Validate arguments
   if (pIn == 0 || pString == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Find the hardware revision
   // sDMSGetHardwareRevisionResponse_HardwareRevision only contains this
   const CHAR * pTLVx01;
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   // Space to perform the copy?
   if (stringSize < outLenx01 + 1)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   memcpy( pString, pTLVx01, outLenx01 );
   pString[outLenx01] = 0;

   return eGOBI_ERR_NONE;
}
/*===========================================================================
METHOD:
   ParseQueryLock

DESCRIPTION:
   This function sets the user lock state maintained by the device

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pState      [ O ] - Current lock state

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseQueryLock(
   ULONG             inLen,
   const BYTE *      pIn,
   ULONG *           pState )
{
   // Validate arguments
   if (pIn == 0 || pState == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Find the state
   const sDMSGetLockStateResponse_LockState * pTLVx01;
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   if (outLenx01 < sizeof( sDMSGetLockStateResponse_LockState ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   *pState = pTLVx01->mLockState;

   return eGOBI_ERR_NONE;
}
/*===========================================================================
METHOD:
   ParseGetNetworkTime

DESCRIPTION:
   This function returns the current time of the device

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pTimeCount  [ O ] - Count of 1.25ms that have elapsed from the start
                       of GPS time (Jan 6, 1980)
   pTimeSource [ O ] - Source of the timestamp

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetNetworkTime(
   ULONG             inLen,
   const BYTE *      pIn,
   ULONGLONG *       pTimeCount,
   ULONG *           pTimeSource )
{
   // Validate arguments
   if (pIn == 0 || pTimeCount == 0 || pTimeSource == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Find the reason mask
   const sDMSGetTimestampResponse_Timestamp * pTLVx01;
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   if (outLenx01 < sizeof( sDMSGetTimestampResponse_Timestamp ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   // Get the values
   *pTimeCount = pTLVx01->mTimestamp;
   // mSource is of type eQMIDMSTimestampSources
   *pTimeSource = pTLVx01->mSource;

   return eGOBI_ERR_NONE;
}
/*===========================================================================
METHOD:
   ParseGetPower

DESCRIPTION:
   This function returns the operating mode of the device

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pPowerMode  [ O ] - Current operating mode

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetPower(
   ULONG             inLen,
   const BYTE *      pIn,
   ULONG *           pPowerMode )
{
   // Validate arguments
   if (pIn == 0 || pPowerMode == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Assume failure
   *pPowerMode = 0xffffffff;

   // Find the mode
   const sDMSGetOperatingModeResponse_OperatingMode * pTLVx01;
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   if (outLenx01 < sizeof( sDMSGetOperatingModeResponse_OperatingMode ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   *pPowerMode = pTLVx01->mOperatingMode;

   return eGOBI_ERR_NONE;
}
/*===========================================================================
METHOD:
   ParseGetERIFile

DESCRIPTION:
   This command returns the ERI file that is stored in EFS on the device

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pFileSize   [I/O] - Upon input the maximum number of bytes that the file
                       contents array can contain.  Upon successful output
                       the actual number of bytes written to the file contents
                       array
   pFile       [ O ] - The file contents

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetERIFile(
   ULONG             inLen,
   const BYTE *      pIn,
   ULONG *           pFileSize,
   BYTE *            pFile )
{
   // Validate arguments
   if (pIn == 0 || pFileSize == 0 || *pFileSize == 0 || pFile == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Assume failure
   ULONG maxFileSize = *pFileSize;
   *pFileSize = 0;

   // Find the state
   const sDMSReadERIDataResponse_UserData * pTLVx01;
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   if (outLenx01 < sizeof( sDMSReadERIDataResponse_UserData ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   ULONG fileSz = pTLVx01->mDataLength;
   const BYTE * pInFile;

   // Verify there is room for the array in the TLV
   if (outLenx01 < sizeof( sDMSReadERIDataResponse_UserData )
                  + sizeof( BYTE ) * fileSz)
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   // Space to copy into?
   if (fileSz > maxFileSize)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   // Align to the first array element
   pInFile = (const BYTE *)pTLVx01 
           + sizeof( sDMSReadERIDataResponse_UserData );

   // Perform the copy
   memcpy( pFile, pInFile, fileSz );
   *pFileSize = fileSz;

   return eGOBI_ERR_NONE;
}
Example #9
0
/*===========================================================================
METHOD:
   QMIDMSGetMEIDResp (Public Method)

DESCRIPTION:
   Parse the QMI DMS Get Serial Numbers Resp

PARAMETERS
   pBuffer         [ I ] - Buffer to be parsed
   buffSize        [ I ] - Size of pBuffer
   pMEID           [ O ] - Device MEID
   meidSize        [ I ] - Size of MEID buffer (at least 14)

RETURN VALUE:
   int - 0 for success
         Negative errno for error
===========================================================================*/
int QMIDMSGetMEIDResp(
   void *   pBuffer,
   u16      buffSize,
   char *   pMEID,
   int      meidSize )
{
   int result;

   // Ignore QMUX and SDU
   u8 offset = sizeof( sQMUX ) + 3;

   if (pBuffer == 0 || buffSize < offset || meidSize < 14)
   {
      return -ENOMEM;
   }

   pBuffer = pBuffer + offset;
   buffSize -= offset;

   result = GetQMIMessageID( pBuffer, buffSize );
   if (result != 0x25)
   {
      return -EFAULT;
   }

   result = ValidQMIMessage( pBuffer, buffSize );
   if (result != 0)
   {
      return -EFAULT;
   }

   result = GetTLV( pBuffer, buffSize, 0x12, (void*)pMEID, 14 );
   if (result != 14)
   {
      return -EFAULT;
   }

   return 0;
}
Example #10
0
/*===========================================================================
METHOD:
   QMICTLGetClientIDResp (Public Method)

DESCRIPTION:
   Parse the QMI CTL Get Client ID Resp

PARAMETERS
   pBuffer         [ I ] - Buffer to be parsed
   buffSize        [ I ] - Size of pBuffer
   pClientID       [ 0 ] - Recieved client ID

RETURN VALUE:
   int - 0 for success
         Negative errno for error
===========================================================================*/
int QMICTLGetClientIDResp(
   void * pBuffer,
   u16    buffSize,
   u16 *  pClientID )
{
   int result;
   
   // Ignore QMUX and SDU
   //    QMI CTL SDU is 2 bytes, not 3
   u8 offset = sizeof( sQMUX ) + 2;

   if (pBuffer == 0 || buffSize < offset)
   {
      return -ENOMEM;
   }

   pBuffer = pBuffer + offset;
   buffSize -= offset;

   result = GetQMIMessageID( pBuffer, buffSize );
   if (result != 0x22)
   {
      return -EFAULT;
   }

   result = ValidQMIMessage( pBuffer, buffSize );
   if (result != 0)
   {
      return -EFAULT;
   }

   result = GetTLV( pBuffer, buffSize, 0x01, pClientID, 2 );
   if (result != 2)
   {
      return -EFAULT;
   }

   return 0;
}
Example #11
0
/*===========================================================================
METHOD:
   ValidQMIMessage (Public Method)

DESCRIPTION:
   Check mandatory TLV in a QMI message

   QMI Message shall NOT include SDU

PARAMETERS
   pQMIMessage    [ I ] - QMI Message buffer
   messageLen     [ I ] - Size of QMI Message buffer

RETURN VALUE:
   int - 0 for success (no error)
         Negative errno for error
         Positive for QMI error code
===========================================================================*/
int ValidQMIMessage(
   void *   pQMIMessage,
   u16      messageLen )
{
   char mandTLV[4];

   if (GetTLV( pQMIMessage, messageLen, 2, &mandTLV[0], 4 ) == 4)
   {
      // Found TLV
      if (*(u16 *)&mandTLV[0] != 0)
      {
         return *(u16 *)&mandTLV[2];
      }
      else
      {
         return 0;
      }
   }
   else
   {
      return -ENOMSG;
   }
}      
/*===========================================================================
METHOD:
   ParseGetIMSI

DESCRIPTION:
   This function returns the device IMSI

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   stringSize  [ I ] - The maximum number of characters (including NULL
                       terminator) that the string array can contain
   pString     [ O ] - NULL terminated string

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetIMSI(
   ULONG             inLen,
   const BYTE *      pIn,
   BYTE              stringSize,
   CHAR *            pString )
{
   // Validate arguments
   if (pIn == 0 ||  stringSize == 0 || pString == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Assume failure
   *pString = 0;

   // Find the IMSI
   // sDMSGetDeviceVoiceNumberResponse_IMSI only contains this
   const CHAR * pTLVx01;
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   // Space to perform the copy?
   if (stringSize < outLenx01 + 1)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   memcpy( pString, pTLVx01, outLenx01 );
   pString[outLenx01] = 0;

   return eGOBI_ERR_NONE;
}
Example #13
0
/*===========================================================================
METHOD:
   QMIWDSEventResp (Public Method)

DESCRIPTION:
   Parse the QMI WDS Set Event Report Resp/Indication or
      QMI WDS Get PKG SRVC Status Resp/Indication

   Return parameters will only be updated if value was received

PARAMETERS
   pBuffer         [ I ] - Buffer to be parsed
   buffSize        [ I ] - Size of pBuffer
   pTXOk           [ O ] - Number of transmitted packets without errors
   pRXOk           [ O ] - Number of recieved packets without errors
   pTXErr          [ O ] - Number of transmitted packets with framing errors
   pRXErr          [ O ] - Number of recieved packets with framing errors
   pTXOfl          [ O ] - Number of transmitted packets dropped due to overflow
   pRXOfl          [ O ] - Number of recieved packets dropped due to overflow
   pTXBytesOk      [ O ] - Number of transmitted bytes without errors
   pRXBytesOk      [ O ] - Number of recieved bytes without errors
   pbLinkState     [ 0 ] - Is the link active?
   pbReconfigure   [ 0 ] - Must interface be reconfigured? (reset IP address)

RETURN VALUE:
   int - 0 for success
         Negative errno for error
===========================================================================*/
int QMIWDSEventResp(
   void *   pBuffer,
   u16      buffSize,
   u32 *    pTXOk,
   u32 *    pRXOk,
   u32 *    pTXErr,
   u32 *    pRXErr,
   u32 *    pTXOfl,
   u32 *    pRXOfl,
   u64 *    pTXBytesOk,
   u64 *    pRXBytesOk,
   bool *   pbLinkState,
   bool *   pbReconfigure )
{
   int result;
   u8 pktStatusRead[2];

   // Ignore QMUX and SDU
   u8 offset = sizeof( sQMUX ) + 3;

   if (pBuffer == 0 
   || buffSize < offset
   || pTXOk == 0
   || pRXOk == 0
   || pTXErr == 0
   || pRXErr == 0
   || pTXOfl == 0
   || pRXOfl == 0
   || pTXBytesOk == 0
   || pRXBytesOk == 0
   || pbLinkState == 0
   || pbReconfigure == 0 )
   {
      return -ENOMEM;
   }

   pBuffer = pBuffer + offset;
   buffSize -= offset;

   // Note: Indications.  No Mandatory TLV required

   result = GetQMIMessageID( pBuffer, buffSize );
   // QMI WDS Set Event Report Resp
   if (result == 0x01)
   {
      // TLV's are not mandatory
      GetTLV( pBuffer, buffSize, 0x10, (void*)pTXOk, 4 );
      GetTLV( pBuffer, buffSize, 0x11, (void*)pRXOk, 4 );
      GetTLV( pBuffer, buffSize, 0x12, (void*)pTXErr, 4 );
      GetTLV( pBuffer, buffSize, 0x13, (void*)pRXErr, 4 );
      GetTLV( pBuffer, buffSize, 0x14, (void*)pTXOfl, 4 );
      GetTLV( pBuffer, buffSize, 0x15, (void*)pRXOfl, 4 );
      GetTLV( pBuffer, buffSize, 0x19, (void*)pTXBytesOk, 8 );
      GetTLV( pBuffer, buffSize, 0x1A, (void*)pRXBytesOk, 8 );
   }
   // QMI WDS Get PKG SRVC Status Resp
   else if (result == 0x22)
   {
      result = GetTLV( pBuffer, buffSize, 0x01, &pktStatusRead[0], 2 );
      // 1 or 2 bytes may be received
      if (result >= 1)
      {
         if (pktStatusRead[0] == 0x02)
         {
            *pbLinkState = true;
         }
         else
         {
            *pbLinkState = false;
         }
      }
      if (result == 2)
      {
         if (pktStatusRead[1] == 0x01)
         {
            *pbReconfigure = true;
         }
         else
         {
            *pbReconfigure = false;
         }
      }
      
      if (result < 0)
      {
         return result;
      }
   }
   else
   {
      return -EFAULT;
   }

   return 0;
}
/*===========================================================================
METHOD:
   ParseGetDeviceCapabilities

DESCRIPTION:
   This function gets device capabilities

PARAMETERS:
   inLen                   [ I ] - Length of input buffer
   pIn                     [ I ] - Input buffer
   pMaxTXChannelRate       [ O ] - Maximum transmission rate (bps)
   pMaxRXChannelRate       [ O ] - Maximum reception rate (bps)
   pDataServiceCapability  [ O ] - CS/PS data service capability
   pSimCapability          [ O ] - Device SIM support
   pRadioIfacesSize        [I/O] - Upon input the maximum number of elements
                                   that the radio interfaces can contain.
                                   Upon successful output the actual number
                                   of elements in the radio interface array
   pRadioIfaces            [ O ] - The radio interface array

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetDeviceCapabilities(
   ULONG             inLen,
   const BYTE *      pIn,
   ULONG *           pMaxTXChannelRate,
   ULONG *           pMaxRXChannelRate,
   ULONG *           pDataServiceCapability,
   ULONG *           pSimCapability,
   ULONG *           pRadioIfacesSize,
   BYTE *            pRadioIfaces )
{
   // Validate arguments
   if (pIn == 0
   ||  pMaxTXChannelRate == 0
   ||  pMaxRXChannelRate == 0
   ||  pDataServiceCapability == 0
   ||  pSimCapability == 0
   ||  pRadioIfacesSize == 0
   ||  *pRadioIfacesSize == 0
   ||  pRadioIfaces == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   ULONG maxRadioIfaces = (ULONG)*pRadioIfacesSize;

   // Assume failure
   *pRadioIfacesSize = 0;

   const sDMSGetDeviceCapabilitiesResponse_Capabilities * pTLVx01;
   ULONG structSzx01 = sizeof( sDMSGetDeviceCapabilitiesResponse_Capabilities );
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   if (outLenx01 < structSzx01)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   // Populate the variables
   *pMaxTXChannelRate = pTLVx01->mMaxTXRatebps;
   *pMaxRXChannelRate = pTLVx01->mMaxRXRatebps;
   *pDataServiceCapability = pTLVx01->mDataServiceCapability;
   
   // SIM capability should be treated as a boolean, even though it's not
   *pSimCapability = (pTLVx01->mSIMSupported == 0 ? 0 : 1);

   ULONG activeRadioIfaces = pTLVx01->mRadioInterfaceCount;
   if (activeRadioIfaces > maxRadioIfaces)
   {
      activeRadioIfaces = maxRadioIfaces;
   }

   const eQMIDMSRadioInterfaces * pInRadioInterfaces;

   // Verify there is room for the array in the TLV
   if (outLenx01 < structSzx01
                 + sizeof( eQMIDMSRadioInterfaces ) * activeRadioIfaces)
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   // Align to the first array element
   pInRadioInterfaces = (const eQMIDMSRadioInterfaces *)
                        ((const BYTE *)pTLVx01 + structSzx01);

   ULONG * pOutRadioIfaces = (ULONG *)pRadioIfaces;
   for (ULONG r = 0; r < activeRadioIfaces; r++)
   {
      *pOutRadioIfaces = *pInRadioInterfaces;
      pOutRadioIfaces++;
      pInRadioInterfaces++;
   }

   *pRadioIfacesSize = activeRadioIfaces;
   return eGOBI_ERR_NONE;
}
/*===========================================================================
METHOD:
   ParseGetSerialNumbers

DESCRIPTION:
   This command returns all serial numbers assigned to the device

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   esnSize     [ I ] - The maximum number of characters (including NULL
                       terminator) that the ESN array can contain
   pESNString  [ O ] - ESN string (empty string returned when ESN is
                       not supported/programmed)
   imeiSize    [ I ] - The maximum number of characters (including NULL
                       terminator) that the IMEI array can contain
   pIMEIString [ O ] - IMEI string (empty string returned when IMEI is
                       not supported/programmed)
   meidSize    [ I ] - The maximum number of characters (including NULL
                       terminator) that the MEID array can contain
   pMEIDString [ O ] - MEID string (empty string returned when MEID is
                       not supported/programmed)

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetSerialNumbers(
   ULONG             inLen,
   const BYTE *      pIn,
   BYTE              esnSize,
   CHAR *            pESNString,
   BYTE              imeiSize,
   CHAR *            pIMEIString,
   BYTE              meidSize,
   CHAR *            pMEIDString )
{
   // Validate arguments
   if (pIn == 0
   ||  esnSize == 0 || pESNString == 0
   ||  imeiSize == 0 || pIMEIString == 0
   ||  meidSize == 0 || pMEIDString == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Assume failure
   *pESNString = 0;
   *pIMEIString = 0;
   *pMEIDString = 0;

   // Find the ESN
   // sDMSGetDeviceSerialNumbersResponse_ESN only contains this
   const CHAR * pTLVx10;
   ULONG outLenx10;
   ULONG rc = GetTLV( inLen, pIn, 0x10, &outLenx10, (const BYTE **)&pTLVx10 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   // Space to perform the copy?
   if (esnSize < outLenx10 + 1)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   memcpy( pESNString, pTLVx10, outLenx10 );
   pESNString[outLenx10] = 0;

   // Find the IMEI
   // sDMSGetDeviceSerialNumbersResponse_IMEI only contains this
   const CHAR * pTLVx11;
   ULONG outLenx11;
   rc = GetTLV( inLen, pIn, 0x11, &outLenx11, (const BYTE **)&pTLVx11 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   // Space to perform the copy?
   if (imeiSize < outLenx11 + 1)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   memcpy( pIMEIString, pTLVx11, outLenx11 );
   pIMEIString[outLenx11] = 0;

   // Find the MEID
   // sDMSGetDeviceSerialNumbersResponse_MEID only contains this
   const CHAR * pTLVx12;
   ULONG outLenx12;
   rc = GetTLV( inLen, pIn, 0x12, &outLenx12, (const BYTE **)&pTLVx12 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   // Space to perform the copy?
   if (meidSize < outLenx12 + 1)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   memcpy( pMEIDString, pTLVx12, outLenx12 );
   pMEIDString[outLenx12] = 0;

   return eGOBI_ERR_NONE;
}
/*===========================================================================
METHOD:
   ParseGetFirmwareRevisions

DESCRIPTION:
   This function returns the device firmware (AMSS, boot, and PRI)
   revisions

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   amssSize    [ I ] - The maximum number of characters (including NULL
                       terminator) that the AMSS string array can contain
   pAMSSString [ O ] - NULL terminated AMSS revision string
   bootSize    [ I ] - The maximum number of characters (including NULL
                       terminator) that the boot string array can contain
   pBootString [ O ] - NULL terminated boot code revision string
   priSize     [ I ] - The maximum number of characters (including NULL
                       terminator) that the PRI string array can contain
   pPRIString  [ O ] - NULL terminated PRI revision string

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetFirmwareRevisions(
   ULONG             inLen,
   const BYTE *      pIn,
   BYTE              amssSize,
   CHAR *            pAMSSString,
   BYTE              bootSize,
   CHAR *            pBootString,
   BYTE              priSize,
   CHAR *            pPRIString )
{
   // Validate arguments
   if (pIn == 0
   ||  amssSize == 0 || pAMSSString == 0
   ||  bootSize == 0 || pBootString == 0
   ||  priSize == 0 || pPRIString == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Assume failure
   *pAMSSString = 0;
   *pBootString = 0;
   *pPRIString = 0;

   // Find the AMSS version
   // sDMSGetDeviceRevisionResponse_Revision only contains this
   const CHAR * pTLVx01;
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   // Space to perform the copy?
   if (amssSize < outLenx01 + 1)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   memcpy( pAMSSString, pTLVx01, outLenx01 );
   pAMSSString[outLenx01] = 0;

   // Find the Boot version
   // sDMSGetDeviceRevisionResponse_BootCodeRevision only contains this
   const CHAR * pTLVx10;
   ULONG outLenx10;
   rc = GetTLV( inLen, pIn, 0x10, &outLenx10, (const BYTE **)&pTLVx10 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   // Space to perform the copy?
   if (bootSize < outLenx10 + 1)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   memcpy( pBootString, pTLVx10, outLenx10 );
   pBootString[outLenx10] = 0;

   // The PRI version is returned by ParseGetFirmwareRevision()
   rc = ParseGetFirmwareRevision( inLen, pIn, priSize, pPRIString );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   return eGOBI_ERR_NONE;
}