/*===========================================================================
FILE:
   Gobi3000TranslationWDS.cpp

DESCRIPTION:
   QUALCOMM Translation for Gobi 3000 (WDS Service)

Copyright (c) 2012, Code Aurora Forum. 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 Code Aurora Forum 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 "Gobi3000Translation.h"

/*===========================================================================
METHOD:
   ParseGetSessionState

DESCRIPTION:
   This function returns the state of the current packet data session

PARAMETERS:
   inLen             [ I ] - Length of input buffer
   pIn               [ I ] - Input buffer
   pState            [ O ] - State of the current packet session

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

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

   // Is the TLV large enough?
   if (outLenx01 < sizeof( sWDSGetPacketServiceStatusResponse_Status ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   *pState = pTLVx01->mConnectionStatus;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetSessionDuration

DESCRIPTION:
   This function returns the duration of the current packet data session

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pDuration   [ O ] - Duration of the current packet session

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

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

   // Is the TLV large enough?
   if (outLenx01 < sizeof( sWDSGetDataSessionDurationResponse_Duration ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   *pDuration = pTLVx01->mDataSessionDuration;
   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetDormancyState

DESCRIPTION:
   This function returns the dormancy state of the current packet
   data session (when connected)

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pState      [ O ] - Dormancy state of the current packet session

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

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

   // Is the TLV large enough?
   if (outLenx01 < sizeof( sWDSGetDormancyResponse_DormancyStatus ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   *pState = pTLVx01->mDormancyStatus;
   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetEnhancedAutoconnect

DESCRIPTION:
   This function returns the current autoconnect data session setting

PARAMETERS:
   inLen          [ I ] - Length of input buffer
   pIn            [ I ] - Input buffer
   pSetting       [ O ] - NDIS autoconnect setting
   pRoamSetting   [ O ] - NDIS autoconnect roam setting

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

   *pSetting = 0xffffffff;
   *pRoamSetting = 0xffffffff;

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

   // Is the TLV large enough?
   if (outLenx01 < sizeof( sWDSGetAutoconnectSettingResponse_Autoconnect ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   *pSetting = pTLVx01->mAutoconnectSetting;

   // Find the second TLV (optional)
   const sWDSGetAutoconnectSettingResponse_Roam * pTLVx10;
   ULONG outLenx10;
   rc = GetTLV( inLen, pIn, 0x10, &outLenx10, (const BYTE **)&pTLVx10 );
   if (rc == eGOBI_ERR_NONE)
   {
      // Is the TLV large enough?
      if (outLenx10 < sizeof( sWDSGetAutoconnectSettingResponse_Roam ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pRoamSetting = pTLVx10->mAutoconnectRoamSetting;
   }

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   PackSetEnhancedAutoconnect

DESCRIPTION:
   This function sets the autoconnect data session setting

PARAMETERS:
   pOutLen        [I/O] - Upon input the maximum number of BYTEs pOut can
                          contain, upon output the number of BYTEs copied
                          to pOut
   pOut           [ O ] - Output buffer
   setting        [ I ] - NDIS autoconnect setting
   pRoamSetting   [ I ] - (Optional) NDIS autoconnect roam setting


RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackSetEnhancedAutoconnect(
   ULONG *  pOutLen,
   BYTE *   pOut,
   ULONG    setting,
   ULONG *  pRoamSetting )
{
   // Validate arguments
   if (pOut == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Add setting

   // Check size
   WORD tlvx01Sz = sizeof( sWDSSetAutoconnectSettingRequest_Autoconnect );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSSetAutoconnectSettingRequest_Autoconnect * pTLVx01;
   pTLVx01 = (sWDSSetAutoconnectSettingRequest_Autoconnect*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the value
   pTLVx01->mAutoconnectSetting = (eQMIWDSAutoconnectSettings)setting;

   offset += tlvx01Sz;

   // Add roam setting, if specified
   if (pRoamSetting != 0)
   {
      // Check size
      WORD tlvx10Sz = sizeof( sWDSSetAutoconnectSettingRequest_Roam );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx10Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x10;
      pHeader->mLength = tlvx10Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetAutoconnectSettingRequest_Roam * pTLVx10;
      pTLVx10 = (sWDSSetAutoconnectSettingRequest_Roam*)(pOut + offset);
      memset( pTLVx10, 0, tlvx10Sz );

      // Set the value
      pTLVx10->mAutoconnectRoamSetting = (eQMIWDSAutoconnectRoamSettings)*pRoamSetting;

      offset += tlvx10Sz;
   }

   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   PackSetDefaultProfile

DESCRIPTION:
   This function writes the default profile settings to the device, the
   default profile is used during autoconnect

   pOutLen           [I/O] - Upon input the maximum number of BYTEs pOut can
                             contain, upon output the number of BYTEs copied
                             to pOut
   pOut              [ O ] - Output buffer
   profileType       [ I ] - Profile type being written
   pPDPType          [ I ] - (Optional) PDP type
   pIPAddress        [ I ] - (Optional) Preferred assigned IPv4 address
   pPrimaryDNS       [ I ] - (Optional) Primary DNS IPv4 address
   pSecondaryDNS     [ I ] - (Optional) Secondary DNS IPv4 address
   pAuthentication   [ I ] - (Optional) Authentication algorithm bitmap
   pName             [ I ] - (Optional) The profile name or description
   pAPNName          [ I ] - (Optional) Access point name
   pUsername         [ I ] - (Optional) Username used during authentication
   pPassword         [ I ] - (Optional) Password used during authentication

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackSetDefaultProfile(
   ULONG *                    pOutLen,
   BYTE *                     pOut,
   ULONG                      profileType,
   ULONG *                    pPDPType,
   ULONG *                    pIPAddress,
   ULONG *                    pPrimaryDNS,
   ULONG *                    pSecondaryDNS,
   ULONG *                    pAuthentication,
   CHAR *                     pName,
   CHAR *                     pAPNName,
   CHAR *                     pUsername,
   CHAR *                     pPassword )
{
   // Validate arguments
   if (pOut == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Add profileType

   // Check size
   WORD tlvx01Sz = sizeof( sWDSModifyProfileRequest_ProfileIdentifier );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSModifyProfileRequest_ProfileIdentifier * pTLVx01;
   pTLVx01 = (sWDSModifyProfileRequest_ProfileIdentifier*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the value
   pTLVx01->mProfileType = (eQMIProfileTypes)profileType;
   pTLVx01->mProfileIndex = 1;

   offset += tlvx01Sz;

   // Add name, if specified
   if (pName != 0)
   {
      std::string name( pName );

      // Check size
      WORD tlvx10Sz = (WORD)name.size();
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx10Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x10;
      pHeader->mLength = tlvx10Sz;

      offset += sizeof( sQMIRawContentHeader );

      // Set the value
      memcpy( pOut + offset, name.c_str(), name.size() );

      offset += tlvx10Sz;
   }

   // Add PDP type, if specified
   if (pPDPType != 0)
   {
      // Check size
      WORD tlvx11Sz = sizeof( sWDSModifyProfileRequest_PDPType );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx11Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x11;
      pHeader->mLength = tlvx11Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSModifyProfileRequest_PDPType * pTLVx11;
      pTLVx11 = (sWDSModifyProfileRequest_PDPType*)(pOut + offset);
      memset( pTLVx11, 0, tlvx11Sz );

      // Set the value
      pTLVx11->mPDPType = (eQMIPDPTypes)*pPDPType;

      offset += tlvx11Sz;
   }

   // Add APN Name, if specified
   if (pAPNName != 0)
   {
      std::string apnName( pAPNName );

      // Check size
      WORD tlvx14Sz = (WORD)apnName.size();
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx14Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x14;
      pHeader->mLength = tlvx14Sz;

      offset += sizeof( sQMIRawContentHeader );

      // Set the value
      memcpy( (pOut + offset), apnName.c_str(), apnName.size() );

      offset += tlvx14Sz;
   }

   // Add Primary DNS, if specified
   if (pPrimaryDNS != 0)
   {
      // Check size
      WORD tlvx15Sz = sizeof( sWDSModifyProfileRequest_PrimaryDNS );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx15Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x15;
      pHeader->mLength = tlvx15Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSModifyProfileRequest_PrimaryDNS * pTLVx15;
      pTLVx15 = (sWDSModifyProfileRequest_PrimaryDNS*)(pOut + offset);
      memset( pTLVx15, 0, tlvx15Sz );

      ULONG ip0 = (*pPrimaryDNS & 0x000000FF);
      ULONG ip1 = (*pPrimaryDNS & 0x0000FF00) >> 8;
      ULONG ip2 = (*pPrimaryDNS & 0x00FF0000) >> 16;
      ULONG ip3 = (*pPrimaryDNS & 0xFF000000) >> 24;

      // Set the value
      pTLVx15->mIPV4Address[0] = (INT8)ip0;
      pTLVx15->mIPV4Address[1] = (INT8)ip1;
      pTLVx15->mIPV4Address[2] = (INT8)ip2;
      pTLVx15->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx15Sz;
   }

   // Add Secondary DNS, if specified
   if (pSecondaryDNS != 0)
   {
      // Check size
      WORD tlvx16Sz = sizeof( sWDSModifyProfileRequest_SecondaryDNS );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx16Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x16;
      pHeader->mLength = tlvx16Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSModifyProfileRequest_SecondaryDNS * pTLVx16;
      pTLVx16 = (sWDSModifyProfileRequest_SecondaryDNS*)(pOut + offset);
      memset( pTLVx16, 0, tlvx16Sz );

      ULONG ip0 = (*pSecondaryDNS & 0x000000FF);
      ULONG ip1 = (*pSecondaryDNS & 0x0000FF00) >> 8;
      ULONG ip2 = (*pSecondaryDNS & 0x00FF0000) >> 16;
      ULONG ip3 = (*pSecondaryDNS & 0xFF000000) >> 24;

      // Set the value
      pTLVx16->mIPV4Address[0] = (INT8)ip0;
      pTLVx16->mIPV4Address[1] = (INT8)ip1;
      pTLVx16->mIPV4Address[2] = (INT8)ip2;
      pTLVx16->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx16Sz;
   }

   // Add Username, if specified
   if (pUsername != 0)
   {
      std::string username( pUsername );

      // Check size
      WORD tlvx1BSz = (WORD)username.size();
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx1BSz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x1B;
      pHeader->mLength = tlvx1BSz;

      offset += sizeof( sQMIRawContentHeader );

      // Set the value
      memcpy( (pOut + offset), username.c_str(), username.size() );

      offset += tlvx1BSz;
   }

   // Add Password, if specified
   if (pPassword != 0)
   {
      std::string password( pPassword );

      // Check size
      WORD tlvx1CSz = (WORD)password.size();
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx1CSz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x1C;
      pHeader->mLength = tlvx1CSz;

      offset += sizeof( sQMIRawContentHeader );

      // Set the value
      memcpy( (pOut + offset), password.c_str(), password.size() );

      offset += tlvx1CSz;
   }

   // Add Authentication, if specified
   if (pAuthentication != 0)
   {
      // Check size
      WORD tlvx1DSz = sizeof( sWDSModifyProfileRequest_Authentication );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx1DSz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x1D;
      pHeader->mLength = tlvx1DSz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSModifyProfileRequest_Authentication * pTLVx1D;
      pTLVx1D = (sWDSModifyProfileRequest_Authentication*)(pOut + offset);
      memset( pTLVx1D, 0, tlvx1DSz );

      // Set the value
      pTLVx1D->mEnablePAP = ((*pAuthentication & 0x00000001) != 0);
      pTLVx1D->mEnableCHAP = ((*pAuthentication & 0x00000002) != 0);

      offset += tlvx1DSz;
   }

   // Add IP Address, if specified
   if (pIPAddress != 0)
   {
      // Check size
      WORD tlvx1ESz = sizeof( sWDSModifyProfileRequest_IPAddress );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx1ESz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x1E;
      pHeader->mLength = tlvx1ESz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSModifyProfileRequest_IPAddress * pTLVx1E;
      pTLVx1E = (sWDSModifyProfileRequest_IPAddress*)(pOut + offset);
      memset( pTLVx1E, 0, tlvx1ESz );

      ULONG ip0 = (*pIPAddress & 0x000000FF);
      ULONG ip1 = (*pIPAddress & 0x0000FF00) >> 8;
      ULONG ip2 = (*pIPAddress & 0x00FF0000) >> 16;
      ULONG ip3 = (*pIPAddress & 0xFF000000) >> 24;

      // Set the value
      pTLVx1E->mIPV4Address[0] = (INT8)ip0;
      pTLVx1E->mIPV4Address[1] = (INT8)ip1;
      pTLVx1E->mIPV4Address[2] = (INT8)ip2;
      pTLVx1E->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx1ESz;
   }

   // At least one of the optional parameters must have been set
   if (offset <= sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   PackGetDefaultProfile

DESCRIPTION:
   This function reads the default profile settings from the device, the
   default profile is used during autoconnect

PARAMETERS:
   pOutLen           [I/O] - Upon input the maximum number of BYTEs pOut can
                             contain, upon output the number of BYTEs copied
                             to pOut
   pOut              [ O ] - Output buffer
   profileType       [ I ] - Profile type being read

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackGetDefaultProfile(
   ULONG *                    pOutLen,
   BYTE *                     pOut,
   ULONG                      profileType )
{
      // Validate arguments
   if (pOut == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Add profileType

   // Check size
   WORD tlvx01Sz = sizeof( sWDSGetDefaultSettingsRequest_ProfileType );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSGetDefaultSettingsRequest_ProfileType * pTLVx01;
   pTLVx01 = (sWDSGetDefaultSettingsRequest_ProfileType*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the value
   pTLVx01->mProfileType = (eQMIProfileTypes)profileType;

   offset += tlvx01Sz;

   *pOutLen = offset;
   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetDefaultProfile

DESCRIPTION:
   This function reads the default profile settings from the device, the
   default profile is used during autoconnect

PARAMETERS:
   inLen             [ I ] - Length of input buffer
   pIn               [ I ] - Input buffer
   pPDPType          [ O ] - PDP type
   pIPAddress        [ O ] - Preferred assigned IPv4 address
   pPrimaryDNS       [ O ] - Primary DNS IPv4 address
   pSecondaryDNS     [ O ] - Secondary DNS IPv4 address
   pAuthentication   [ O ] - Authentication algorithm bitmap
   nameSize          [ I ] - The maximum number of characters (including
                             NULL terminator) that the profile name array
                             can contain
   pName             [ O ] - The profile name or description
   apnSize           [ I ] - The maximum number of characters (including
                             NULL terminator) that the APN name array
                             can contain
   pAPNName          [ O ] - Access point name represented as a NULL
                             terminated string (empty string returned when
                             unknown)
   userSize          [ I ] - The maximum number of characters (including
                             NULL terminator) that the username array
                             can contain
   pUsername         [ O ] - Username used during authentication

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetDefaultProfile(
   ULONG                      inLen,
   const BYTE *               pIn,
   ULONG *                    pPDPType,
   ULONG *                    pIPAddress,
   ULONG *                    pPrimaryDNS,
   ULONG *                    pSecondaryDNS,
   ULONG *                    pAuthentication,
   BYTE                       nameSize,
   CHAR *                     pName,
   BYTE                       apnSize,
   CHAR *                     pAPNName,
   BYTE                       userSize,
   CHAR *                     pUsername )
{
   // Validate arguments
   if (pIn == 0
   ||  pPDPType == 0
   ||  pIPAddress == 0
   ||  pPrimaryDNS == 0
   ||  pSecondaryDNS == 0
   ||  pAuthentication == 0
   ||  nameSize == 0
   ||  pName == 0
   ||  apnSize == 0
   ||  pAPNName == 0
   ||  userSize == 0
   ||  pUsername == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Set defaults
   *pPDPType = 0xffffffff;
   *pIPAddress = 0xffffffff;
   *pPrimaryDNS = 0xffffffff;
   *pSecondaryDNS = 0xffffffff;
   *pAuthentication = 0xffffffff;
   pName[0] = 0;
   pAPNName[0] = 0;
   pUsername[0] = 0;

   // Find the name
   const sWDSGetDefaultSettingsResponse_ProfileName * pTLVx10;
   ULONG outLenx10;
   ULONG rc = GetTLV( inLen, pIn, 0x10, &outLenx10, (const BYTE **)&pTLVx10 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (nameSize < outLenx10 + 1)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      memcpy( pName, (const BYTE *)pTLVx10, outLenx10 );

      // Null terminate
      pName[outLenx10] = 0;
   }

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

      *pPDPType = pTLVx11->mPDPType;
   }

   // Find the APN name
   const sWDSGetDefaultSettingsResponse_APNName * pTLVx14;
   ULONG outLenx14;
   rc = GetTLV( inLen, pIn, 0x14, &outLenx14, (const BYTE **)&pTLVx14 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (apnSize < outLenx14 + 1)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      memcpy( pAPNName, (const BYTE *)pTLVx14, outLenx14 );

      // Null terminate
      pAPNName[outLenx14] = 0;
   }

   // Find the Primary DNS
   const sWDSGetDefaultSettingsResponse_PrimaryDNS * pTLVx15;
   ULONG outLenx15;
   rc = GetTLV( inLen, pIn, 0x15, &outLenx15, (const BYTE **)&pTLVx15 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx15 < sizeof( sWDSGetDefaultSettingsResponse_PrimaryDNS ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      ULONG ip0 = pTLVx15->mIPV4Address[0];
      ULONG ip1 = pTLVx15->mIPV4Address[1] << 8;
      ULONG ip2 = pTLVx15->mIPV4Address[2] << 16;
      ULONG ip3 = pTLVx15->mIPV4Address[3] << 24;

      *pPrimaryDNS = (ip0 | ip1 | ip2 | ip3);
   }

   // Find the Secondary DNS
   const sWDSGetDefaultSettingsResponse_SecondaryDNS * pTLVx16;
   ULONG outLenx16;
   rc = GetTLV( inLen, pIn, 0x16, &outLenx16, (const BYTE **)&pTLVx16 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx16 < sizeof( sWDSGetDefaultSettingsResponse_SecondaryDNS ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      ULONG ip0 = pTLVx16->mIPV4Address[0];
      ULONG ip1 = pTLVx16->mIPV4Address[1] << 8;
      ULONG ip2 = pTLVx16->mIPV4Address[2] << 16;
      ULONG ip3 = pTLVx16->mIPV4Address[3] << 24;

      *pSecondaryDNS = (ip0 | ip1 | ip2 | ip3);
   }

   // Find the Username
   const sWDSGetDefaultSettingsResponse_APNName * pTLVx1B;
   ULONG outLenx1B;
   rc = GetTLV( inLen, pIn, 0x1B, &outLenx1B, (const BYTE **)&pTLVx1B );
   if (rc == eGOBI_ERR_NONE)
   {
      if (userSize < outLenx1B + 1)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      memcpy( pAPNName, (const BYTE *)pTLVx1B, outLenx1B );

      // Null terminate
      pAPNName[outLenx1B] = 0;
   }

   // Find the Authentication
   const sWDSGetDefaultSettingsResponse_Authentication * pTLVx1D;
   ULONG outLenx1D;
   rc = GetTLV( inLen, pIn, 0x1D, &outLenx1D, (const BYTE **)&pTLVx1D );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx1D < sizeof( sWDSGetDefaultSettingsResponse_Authentication ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      ULONG pap = pTLVx1D->mEnablePAP;
      ULONG chap = pTLVx1D->mEnableCHAP << 1;

      *pAuthentication = (pap | chap);
   }

   // Find the IP Address
   const sWDSGetDefaultSettingsResponse_IPAddress * pTLVx1E;
   ULONG outLenx1E;
   rc = GetTLV( inLen, pIn, 0x1E, &outLenx1E, (const BYTE **)&pTLVx1E );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx1E < sizeof( sWDSGetDefaultSettingsResponse_IPAddress ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      ULONG ip0 = pTLVx1E->mIPV4Address[0];
      ULONG ip1 = pTLVx1E->mIPV4Address[1] << 8;
      ULONG ip2 = pTLVx1E->mIPV4Address[2] << 16;
      ULONG ip3 = pTLVx1E->mIPV4Address[3] << 24;

      *pIPAddress = (ip0 | ip1 | ip2 | ip3);
   }

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   PackStartDataSession

DESCRIPTION:
   This function activates a packet data session

PARAMETERS:
   pOutLen           [I/O] - Upon input the maximum number of BYTEs pOut can
                             contain, upon output the number of BYTEs copied
                             to pOut
   pOut              [ O ] - Output buffer
   pTechnology       [ I ] - (Optional) Technology bitmap
   pPrimaryDNS       [ I ] - (Optional) Primary DNS IPv4 address
   pSecondaryDNS     [ I ] - (Optional) Secondary DNS IPv4 address
   pPrimaryNBNS      [ I ] - (Optional) Primary NetBIOS NS IPv4 address
   pSecondaryNBNS    [ I ] - (Optional) Secondary NetBIOS NS IPv4 address
   pAPNName          [ I ] - (Optional) Access point name
   pIPAddress        [ I ] - (Optional) Preferred assigned IPv4 address
   pAuthentication   [ I ] - (Optional) Authentication algorithm bitmap
   pUsername         [ I ] - (Optional) Username used during authentication
   pPassword         [ I ] - (Optional) Password used during authentication

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackStartDataSession(
   ULONG *                    pOutLen,
   BYTE *                     pOut,
   ULONG *                    pTechnology,
   ULONG *                    pPrimaryDNS,
   ULONG *                    pSecondaryDNS,
   ULONG *                    pPrimaryNBNS,
   ULONG *                    pSecondaryNBNS,
   CHAR *                     pAPNName,
   ULONG *                    pIPAddress,
   ULONG *                    pAuthentication,
   CHAR *                     pUsername,
   CHAR *                     pPassword )
{
   // Validate arguments
   if (pOut == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   sQMIRawContentHeader * pHeader;
   ULONG offset = 0;

   // Add technology, if specified
   if (pTechnology != 0)
   {
      // Check size
      WORD tlvx30Sz = sizeof( sWDSStartNetworkInterfaceRequest_TechnologyPreference );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx30Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x30;
      pHeader->mLength = tlvx30Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSStartNetworkInterfaceRequest_TechnologyPreference * pTLVx30;
      pTLVx30 = (sWDSStartNetworkInterfaceRequest_TechnologyPreference*)(pOut + offset);
      memset( pTLVx30, 0, tlvx30Sz );

      // Set the value
      pTLVx30->mEnable3GPP = ((*pTechnology & 0x00000001) != 0);
      pTLVx30->mEnable3GPP2 = ((*pTechnology & 0x00000002) != 0);

      offset += tlvx30Sz;
   }

   // Add Primary DNS, if specified
   if (pPrimaryDNS != 0)
   {
      // Check size
      WORD tlvx10Sz = sizeof( sWDSStartNetworkInterfaceRequest_PrimaryDNS );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx10Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x10;
      pHeader->mLength = tlvx10Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSStartNetworkInterfaceRequest_PrimaryDNS * pTLVx10;
      pTLVx10 = (sWDSStartNetworkInterfaceRequest_PrimaryDNS*)(pOut + offset);
      memset( pTLVx10, 0, tlvx10Sz );

      ULONG ip0 = (*pPrimaryDNS & 0x000000FF);
      ULONG ip1 = (*pPrimaryDNS & 0x0000FF00) >> 8;
      ULONG ip2 = (*pPrimaryDNS & 0x00FF0000) >> 16;
      ULONG ip3 = (*pPrimaryDNS & 0xFF000000) >> 24;

      // Set the value
      pTLVx10->mIPV4Address[0] = (INT8)ip0;
      pTLVx10->mIPV4Address[1] = (INT8)ip1;
      pTLVx10->mIPV4Address[2] = (INT8)ip2;
      pTLVx10->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx10Sz;
   }

   // Add Secondary DNS, if specified
   if (pSecondaryDNS != 0)
   {
      // Check size
      WORD tlvx11Sz = sizeof( sWDSStartNetworkInterfaceRequest_SecondaryDNS );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx11Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x11;
      pHeader->mLength = tlvx11Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSStartNetworkInterfaceRequest_SecondaryDNS * pTLVx11;
      pTLVx11 = (sWDSStartNetworkInterfaceRequest_SecondaryDNS*)(pOut + offset);
      memset( pTLVx11, 0, tlvx11Sz );

      ULONG ip0 = (*pSecondaryDNS & 0x000000FF);
      ULONG ip1 = (*pSecondaryDNS & 0x0000FF00) >> 8;
      ULONG ip2 = (*pSecondaryDNS & 0x00FF0000) >> 16;
      ULONG ip3 = (*pSecondaryDNS & 0xFF000000) >> 24;

      // Set the value
      pTLVx11->mIPV4Address[0] = (INT8)ip0;
      pTLVx11->mIPV4Address[1] = (INT8)ip1;
      pTLVx11->mIPV4Address[2] = (INT8)ip2;
      pTLVx11->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx11Sz;
   }

   // Add Primary NBNS, if specified
   if (pPrimaryNBNS != 0)
   {
      // Check size
      WORD tlvx12Sz = sizeof( sWDSStartNetworkInterfaceRequest_PrimaryNBNS );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx12Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x12;
      pHeader->mLength = tlvx12Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSStartNetworkInterfaceRequest_PrimaryNBNS * pTLVx12;
      pTLVx12 = (sWDSStartNetworkInterfaceRequest_PrimaryNBNS*)(pOut + offset);
      memset( pTLVx12, 0, tlvx12Sz );

      ULONG ip0 = (*pPrimaryNBNS & 0x000000FF);
      ULONG ip1 = (*pPrimaryNBNS & 0x0000FF00) >> 8;
      ULONG ip2 = (*pPrimaryNBNS & 0x00FF0000) >> 16;
      ULONG ip3 = (*pPrimaryNBNS & 0xFF000000) >> 24;

      // Set the value
      pTLVx12->mIPV4Address[0] = (INT8)ip0;
      pTLVx12->mIPV4Address[1] = (INT8)ip1;
      pTLVx12->mIPV4Address[2] = (INT8)ip2;
      pTLVx12->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx12Sz;
   }

   // Add Secondary NBNS, if specified
   if (pSecondaryNBNS != 0)
   {
      // Check size
      WORD tlvx13Sz = sizeof( sWDSStartNetworkInterfaceRequest_SecondaryNBNS );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx13Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x13;
      pHeader->mLength = tlvx13Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSStartNetworkInterfaceRequest_SecondaryNBNS * pTLVx13;
      pTLVx13 = (sWDSStartNetworkInterfaceRequest_SecondaryNBNS*)(pOut + offset);
      memset( pTLVx13, 0, tlvx13Sz );

      ULONG ip0 = (*pSecondaryNBNS & 0x000000FF);
      ULONG ip1 = (*pSecondaryNBNS & 0x0000FF00) >> 8;
      ULONG ip2 = (*pSecondaryNBNS & 0x00FF0000) >> 16;
      ULONG ip3 = (*pSecondaryNBNS & 0xFF000000) >> 24;

      // Set the value
      pTLVx13->mIPV4Address[0] = (INT8)ip0;
      pTLVx13->mIPV4Address[1] = (INT8)ip1;
      pTLVx13->mIPV4Address[2] = (INT8)ip2;
      pTLVx13->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx13Sz;
   }

   // Add APN Name, if specified
   if (pAPNName != 0)
   {
      std::string apnName( pAPNName );

      // Check size
      WORD tlvx14Sz = (WORD)apnName.size();
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx14Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x14;
      pHeader->mLength = tlvx14Sz;

      offset += sizeof( sQMIRawContentHeader );

      // Set the value
      memcpy( (pOut + offset), apnName.c_str(), apnName.size() );

      offset += tlvx14Sz;
   }

   // Add IP Address, if specified
   if (pIPAddress != 0)
   {
      // Check size
      WORD tlvx15Sz = sizeof( sWDSStartNetworkInterfaceRequest_IPAddress );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx15Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x15;
      pHeader->mLength = tlvx15Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSStartNetworkInterfaceRequest_IPAddress * pTLVx15;
      pTLVx15 = (sWDSStartNetworkInterfaceRequest_IPAddress*)(pOut + offset);
      memset( pTLVx15, 0, tlvx15Sz );

      ULONG ip0 = (*pIPAddress & 0x000000FF);
      ULONG ip1 = (*pIPAddress & 0x0000FF00) >> 8;
      ULONG ip2 = (*pIPAddress & 0x00FF0000) >> 16;
      ULONG ip3 = (*pIPAddress & 0xFF000000) >> 24;

      // Set the value
      pTLVx15->mIPV4Address[0] = (INT8)ip0;
      pTLVx15->mIPV4Address[1] = (INT8)ip1;
      pTLVx15->mIPV4Address[2] = (INT8)ip2;
      pTLVx15->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx15Sz;
   }

   // Add Authentication, if specified
   if (pAuthentication != 0)
   {
      // Check size
      WORD tlvx16Sz = sizeof( sWDSStartNetworkInterfaceRequest_Authentication );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx16Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x16;
      pHeader->mLength = tlvx16Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSStartNetworkInterfaceRequest_Authentication * pTLVx16;
      pTLVx16 = (sWDSStartNetworkInterfaceRequest_Authentication*)(pOut + offset);
      memset( pTLVx16, 0, tlvx16Sz );

      // Set the value
      pTLVx16->mEnablePAP = ((*pAuthentication & 0x00000001) != 0);
      pTLVx16->mEnableCHAP = ((*pAuthentication & 0x00000002) != 0);

      offset += tlvx16Sz;
   }

   // Add Username, if specified
   if (pUsername != 0)
   {
      std::string username( pUsername );

      // Check size
      WORD tlvx17Sz = (WORD)username.size();
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx17Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x17;
      pHeader->mLength = tlvx17Sz;

      offset += sizeof( sQMIRawContentHeader );

      // Set the value
      memcpy( (pOut + offset), username.c_str(), username.size() );

      offset += tlvx17Sz;
   }

   // Add Password, if specified
   if (pPassword != 0)
   {
      std::string password( pPassword );

      // Check size
      WORD tlvx18Sz = (WORD)password.size();
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx18Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x18;
      pHeader->mLength = tlvx18Sz;

      offset += sizeof( sQMIRawContentHeader );

      // Set the value
      memcpy( (pOut + offset), password.c_str(), password.size() );

      offset += tlvx18Sz;
   }

   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseStartDataSession

DESCRIPTION:
   This function activates a packet data session

PARAMETERS:
   inLen             [ I ] - Length of input buffer
   pIn               [ I ] - Input buffer
   pSessionId        [ O ] - The assigned session ID
   pFailureReason    [ O ] - Upon call failure the failure reason provided

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

   // Check mandatory response
   const sResultCode * pTLVx02;
   ULONG outLenx02;
   ULONG rc = GetTLV( inLen, pIn, 0x02, &outLenx02, (const BYTE **)&pTLVx02 );
   if (rc != eGOBI_ERR_NONE)
   {
      return rc;
   }

   if (outLenx02 < sizeof( sResultCode ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   if (pTLVx02->mQMIResult != eQMIResults_Success)
   {
      rc = pTLVx02->mQMIError + eGOBI_ERR_QMI_OFFSET;
   }

   if (rc != eGOBI_ERR_NONE)
   {
      // Still parse call end reason, if present
      const sWDSStartNetworkInterfaceResponse_CallEndReason * pTLVx10;
      ULONG outLenx10;
      ULONG rc2 = GetTLV( inLen, pIn, 0x10, &outLenx10, (const BYTE **)&pTLVx10 );
      if (rc2 == eGOBI_ERR_NONE)
      {
         if (outLenx10 >= sizeof( sWDSStartNetworkInterfaceResponse_CallEndReason ))
         {
            *pFailureReason = pTLVx10->mCallEnd;
         }
      }

      return rc;
   }

   // Find the Session ID
   const sWDSStartNetworkInterfaceResponse_PacketDataHandle * pTLVx01;
   ULONG outLenx01;
   rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx01 < sizeof( sWDSStartNetworkInterfaceResponse_PacketDataHandle ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pSessionId = pTLVx01->mPacketDataHandle;
   }

   // Session ID is mandatory, if it failed return that error
   return rc;
}

/*===========================================================================
METHOD:
   PackStopDataSession

DESCRIPTION:
   This function stops the current data session

PARAMETERS:
   pOutLen     [I/O] - Upon input the maximum number of BYTEs pOut can
                       contain, upon output the number of BYTEs copied
                       to pOut
   pOut        [ O ] - Output buffer
   sessionId   [ I ] - The ID of the session to terminate

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackStopDataSession(
   ULONG *                    pOutLen,
   BYTE *                     pOut,
   ULONG                      sessionId )
{
   // Validate arguments
   if (pOut == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Add session ID

   // Check size
   WORD tlvx01Sz = sizeof( sWDSStopNetworkInterfaceRequest_PacketDataHandle );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSStopNetworkInterfaceRequest_PacketDataHandle * pTLVx01;
   pTLVx01 = (sWDSStopNetworkInterfaceRequest_PacketDataHandle*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the value
   pTLVx01->mPacketDataHandle = sessionId;

   offset += tlvx01Sz;
   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   PackGetIPAddress

DESCRIPTION:
   This function returns the current packet data session IP address

PARAMETERS:
   pOutLen     [I/O] - Upon input the maximum number of BYTEs pOut can
                       contain, upon output the number of BYTEs copied
                       to pOut
   pOut        [ O ] - Output buffer

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackGetIPAddress(
   ULONG *                    pOutLen,
   BYTE *                     pOut )
{
   // Validate arguments
   if (pOut == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Request the settings

   // Check size
   WORD tlvx10Sz = sizeof( sWDSGetCurrentSettingsRequest_RequestedSettings );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx10Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x10;
   pHeader->mLength = tlvx10Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSGetCurrentSettingsRequest_RequestedSettings * pTLVx10;
   pTLVx10 = (sWDSGetCurrentSettingsRequest_RequestedSettings*)(pOut + offset);
   memset( pTLVx10, 0, tlvx10Sz );

   // Set the value
   pTLVx10->mIPAddress = true;

   offset += tlvx10Sz;
   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetIPAddress

DESCRIPTION:
   This function returns the current packet data session IP address

PARAMETERS:
   inLen             [ I ] - Length of input buffer
   pIn               [ I ] - Input buffer
   pIPAddress        [ O ] - Assigned IPv4 address

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

   // Find the IP Address
   const sWDSGetDefaultSettingsResponse_IPAddress * pTLVx1E;
   ULONG outLenx1E;
   ULONG rc = GetTLV( inLen, pIn, 0x1E, &outLenx1E, (const BYTE **)&pTLVx1E );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx1E < sizeof( sWDSGetDefaultSettingsResponse_IPAddress ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      ULONG ip0 = pTLVx1E->mIPV4Address[0];
      ULONG ip1 = pTLVx1E->mIPV4Address[1] << 8;
      ULONG ip2 = pTLVx1E->mIPV4Address[2] << 16;
      ULONG ip3 = pTLVx1E->mIPV4Address[3] << 24;

      *pIPAddress = (ip0 | ip1 | ip2 | ip3);
   }

   // If no IP address is found, fail
   return rc;
}

/*===========================================================================
METHOD:
   ParseGetConnectionRate

DESCRIPTION:
   This function returns connection rate information for the packet data
   connection

PARAMETERS:
   inLen                   [ I ] - Length of input buffer
   pIn                     [ I ] - Input buffer
   pCurrentChannelTXRate   [ O ] - Current channel TX rate (bps)
   pCurrentChannelRXRate   [ O ] - Current channel RX rate (bps)
   pMaxChannelTXRate       [ O ] - Maximum channel TX rate (bps)
   pMaxChannelRXRate       [ O ] - Maximum channel RX rate (bps)

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetConnectionRate(
   ULONG                      inLen,
   const BYTE *               pIn,
   ULONG *                    pCurrentChannelTXRate,
   ULONG *                    pCurrentChannelRXRate,
   ULONG *                    pMaxChannelTXRate,
   ULONG *                    pMaxChannelRXRate )
{
   // Validate arguments
   if (pIn == 0
   ||  pCurrentChannelTXRate == 0
   ||  pCurrentChannelRXRate == 0
   ||  pMaxChannelTXRate == 0
   ||  pMaxChannelRXRate == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Find the rates
   const sWDSGetChannelRatesResponse_ChannelRates * pTLVx01;
   ULONG outLenx01;
   ULONG rc = GetTLV( inLen, pIn, 0x01, &outLenx01, (const BYTE **)&pTLVx01 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx01 < sizeof( sWDSGetChannelRatesResponse_ChannelRates ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      // Get the values
      *pCurrentChannelTXRate = pTLVx01->mChannelTXRatebps;
      *pCurrentChannelRXRate = pTLVx01->mChannelRXRatebps;
      *pMaxChannelTXRate = pTLVx01->mMaxChannelTXRatebps;
      *pMaxChannelRXRate = pTLVx01->mMaxChannelRXRatebps;
   }

   // If no rates are found, fail
   return rc;
}

/*===========================================================================
METHOD:
   PackGetPacketStatus

DESCRIPTION:
   This function returns the packet data transfer statistics since the start
   of the current packet data session

PARAMETERS:
   pOutLen     [I/O] - Upon input the maximum number of BYTEs pOut can
                       contain, upon output the number of BYTEs copied
                       to pOut
   pOut        [ O ] - Output buffer

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackGetPacketStatus(
   ULONG *                    pOutLen,
   BYTE *                     pOut )
{
   // Validate arguments
   if (pOut == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Request the settings

   // Check size
   WORD tlvx01Sz = sizeof( sWDSGetPacketStatisticsRequest_PacketStatsMask );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSGetPacketStatisticsRequest_PacketStatsMask * pTLVx01;
   pTLVx01 = (sWDSGetPacketStatisticsRequest_PacketStatsMask*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the values
   pTLVx01->mReportTXPacketSuccesses = true;
   pTLVx01->mReportRXPacketSuccesses = true;
   pTLVx01->mReportTXPacketErrors = true;
   pTLVx01->mReportRXPacketErrors = true;
   pTLVx01->mReportTXOverflows = true;
   pTLVx01->mReportRXOverflows = true;

   offset += tlvx01Sz;
   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetPacketStatus

DESCRIPTION:
   This function returns the packet data transfer statistics since the start
   of the current packet data session

PARAMETERS:
   inLen                [ I ] - Length of input buffer
   pIn                  [ I ] - Input buffer
   pTXPacketSuccesses   [ O ] - Packets transmitted without error
   pRXPacketSuccesses   [ O ] - Packets received without error
   pTXPacketErrors      [ O ] - Outgoing packets with framing errors
   pRXPacketErrors      [ O ] - Incoming packets with framing errors
   pTXPacketOverflows   [ O ] - Packets dropped because TX buffer overflowed
   pRXPacketOverflows   [ O ] - Packets dropped because RX buffer overflowed

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetPacketStatus(
   ULONG                      inLen,
   const BYTE *               pIn,
   ULONG *                    pTXPacketSuccesses,
   ULONG *                    pRXPacketSuccesses,
   ULONG *                    pTXPacketErrors,
   ULONG *                    pRXPacketErrors,
   ULONG *                    pTXPacketOverflows,
   ULONG *                    pRXPacketOverflows )
{
   // Validate arguments
   if (pIn == 0
   ||  pTXPacketSuccesses == 0
   ||  pRXPacketSuccesses == 0
   ||  pTXPacketErrors == 0
   ||  pRXPacketErrors == 0
   ||  pTXPacketOverflows == 0
   ||  pRXPacketOverflows == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // NOTE: All TLVs are required.  If any fail then all fail

   // Find the TX packet sucesses
   const sWDSGetPacketStatisticsResponse_TXPacketSuccesses * pTLVx10;
   ULONG outLenx10;
   ULONG rc = GetTLV( inLen, pIn, 0x10, &outLenx10, (const BYTE **)&pTLVx10 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx10 < sizeof( sWDSGetPacketStatisticsResponse_TXPacketSuccesses ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }
   }
   else
   {
      return rc;
   }

   // Find the RX packet sucesses
   const sWDSGetPacketStatisticsResponse_RXPacketSuccesses * pTLVx11;
   ULONG outLenx11;
   rc = GetTLV( inLen, pIn, 0x11, &outLenx11, (const BYTE **)&pTLVx11 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx11 < sizeof( sWDSGetPacketStatisticsResponse_RXPacketSuccesses ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }
   }
   else
   {
      return rc;
   }

   // Find the TX packet errors
   const sWDSGetPacketStatisticsResponse_TXPacketErrors * pTLVx12;
   ULONG outLenx12;
   rc = GetTLV( inLen, pIn, 0x12, &outLenx12, (const BYTE **)&pTLVx12 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx12 < sizeof( sWDSGetPacketStatisticsResponse_TXPacketErrors ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }
   }
   else
   {
      return rc;
   }

   // Find the RX packet errors
   const sWDSGetPacketStatisticsResponse_RXPacketErrors * pTLVx13;
   ULONG outLenx13;
   rc = GetTLV( inLen, pIn, 0x13, &outLenx13, (const BYTE **)&pTLVx13 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx13 < sizeof( sWDSGetPacketStatisticsResponse_RXPacketErrors ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }
   }
   else
   {
      return rc;
   }

   // Find the TX packet overflows
   const sWDSGetPacketStatisticsResponse_TXOverflows * pTLVx14;
   ULONG outLenx14;
   rc = GetTLV( inLen, pIn, 0x14, &outLenx14, (const BYTE **)&pTLVx14 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx14 < sizeof( sWDSGetPacketStatisticsResponse_TXOverflows ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }
   }
   else
   {
      return rc;
   }

   // Find the RX packet overflows
   const sWDSGetPacketStatisticsResponse_RXOverflows * pTLVx15;
   ULONG outLenx15;
   rc = GetTLV( inLen, pIn, 0x15, &outLenx15, (const BYTE **)&pTLVx15 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx15 < sizeof( sWDSGetPacketStatisticsResponse_RXOverflows ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }
   }
   else
   {
      return rc;
   }

   // Populate the statistics
   *pTXPacketSuccesses = pTLVx10->mTXPacketSuccesses;
   *pRXPacketSuccesses = pTLVx11->mRXPacketSuccesses;
   *pTXPacketErrors = pTLVx12->mTXPacketErrors;
   *pRXPacketErrors = pTLVx13->mRXPacketErrors;
   *pTXPacketOverflows = pTLVx14->mTXOverflows;
   *pRXPacketOverflows = pTLVx15->mRXOverflows;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   PackGetByteTotals

DESCRIPTION:
   This function returns the RX/TX byte counts since the start of the
   current packet data session

PARAMETERS:
   pOutLen     [I/O] - Upon input the maximum number of BYTEs pOut can
                       contain, upon output the number of BYTEs copied
                       to pOut
   pOut        [ O ] - Output buffer

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackGetByteTotals(
   ULONG *                    pOutLen,
   BYTE *                     pOut )
{
   // Validate arguments
   if (pOut == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Request the settings

   // Check size
   WORD tlvx01Sz = sizeof( sWDSGetPacketStatisticsRequest_PacketStatsMask );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSGetPacketStatisticsRequest_PacketStatsMask * pTLVx01;
   pTLVx01 = (sWDSGetPacketStatisticsRequest_PacketStatsMask*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the values
   pTLVx01->mTXByteTotal = true;
   pTLVx01->mRXByteTotal = true;

   offset += tlvx01Sz;
   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetByteTotals

DESCRIPTION:
   This function returns the RX/TX byte counts since the start of the
   current packet data session

PARAMETERS:
   inLen          [ I ] - Length of input buffer
   pIn            [ I ] - Input buffer
   pTXTotalBytes  [ O ] - Bytes transmitted without error
   pRXTotalBytes  [ O ] - Bytes received without error

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

   // NOTE: All TLVs are required.  If any fail then all fail

   // Find the TX bytes
   const sWDSGetPacketStatisticsResponse_TXBytes * pTLVx19;
   ULONG outLenx19;
   ULONG rc = GetTLV( inLen, pIn, 0x19, &outLenx19, (const BYTE **)&pTLVx19 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx19 < sizeof( sWDSGetPacketStatisticsResponse_TXBytes ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }
   }
   else
   {
      return rc;
   }

   // Find the RX bytes
   const sWDSGetPacketStatisticsResponse_RXBytes * pTLVx1A;
   ULONG outLenx1A;
   rc = GetTLV( inLen, pIn, 0x1A, &outLenx1A, (const BYTE **)&pTLVx1A );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx1A < sizeof( sWDSGetPacketStatisticsResponse_RXBytes ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }
   }
   else
   {
      return rc;
   }

   // Populate the statistics
   *pTXTotalBytes = pTLVx19->mTXByteTotal;
   *pRXTotalBytes = pTLVx1A->mRXByteTotal;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   PackSetMobileIP

DESCRIPTION:
   This function sets the current mobile IP setting

PARAMETERS:
   pOutLen     [I/O] - Upon input the maximum number of BYTEs pOut can
                       contain, upon output the number of BYTEs copied
                       to pOut
   pOut        [ O ] - Output buffer
   mode        [ I ] - Desired mobile IP setting

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackSetMobileIP(
   ULONG *              pOutLen,
   BYTE *               pOut,
   ULONG                mode )
{
   // Validate arguments
   if (pOut == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Set the mode

   // Check size
   WORD tlvx01Sz = sizeof( sWDSSetMIPModeRequest_MobileIPMode );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSSetMIPModeRequest_MobileIPMode * pTLVx01;
   pTLVx01 = (sWDSSetMIPModeRequest_MobileIPMode*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the values
   pTLVx01->mMIPMode = (eQMIMobileIPModes)mode;

   offset += tlvx01Sz;
   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetMobileIP

DESCRIPTION:
   This function gets the current mobile IP setting

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pMode       [ O ] - Current mobile IP setting

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

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

      *pMode = pTLVx01->mMIPMode;
   }

   return rc;
}

/*===========================================================================
METHOD:
   PackSetActiveMobileIPProfile

DESCRIPTION:
   This function sets the active mobile IP profile index

PARAMETERS:
   pOutLen     [I/O] - Upon input the maximum number of BYTEs pOut can
                       contain, upon output the number of BYTEs copied
                       to pOut
   pOut        [ O ] - Output buffer
   pSPC        [ I ] - Six digit service programming code
   index       [ I ] - Desired mobile IP profile index

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackSetActiveMobileIPProfile(
   ULONG *              pOutLen,
   BYTE *               pOut,
   CHAR *               pSPC,
   BYTE                 index )
{
   // Validate arguments
   if (pOut == 0 || pSPC == 0 || pSPC[0] == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   std::string spc( pSPC );
   if (spc.size() > 6)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   if (spc.find_first_not_of( "0123456789" ) != std::string::npos )
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Check size
   WORD tlvx01Sz = sizeof( sWDSSetActiveMIPProfileRequest_Index );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSSetActiveMIPProfileRequest_Index * pTLVx01;
   pTLVx01 = (sWDSSetActiveMIPProfileRequest_Index*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the values
   memcpy( &pTLVx01->mSPC[0], spc.c_str(), spc.size() );
   pTLVx01->mProfileIndex = index;

   offset += tlvx01Sz;
   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetActiveMobileIPProfile

DESCRIPTION:
   This function gets the the active mobile IP profile index

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pIndex      [ O ] - Active mobile IP profile index

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

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

      *pIndex = pTLVx01->mProfileIndex;
   }

   return rc;
}

/*===========================================================================
METHOD:
   PackSetMobileIPProfile

DESCRIPTION:
   This function sets the specified mobile IP profile settings

PARAMETERS:
   pOutLen        [I/O] - Upon input the maximum number of BYTEs pOut can
                          contain, upon output the number of BYTEs copied
                          to pOut
   pOut           [ O ] - Output buffer
   pSPC           [ I ] - Six digit service programming code
   index          [ I ] - Mobile IP profile ID
   pEnabled       [ I ] - (Optional) Enable MIP profile?
   pAddress       [ I ] - (Optional) Home IPv4 address
   pPrimaryHA     [ I ] - (Optional) Primary home agent IPv4 address
   pSecondaryHA   [ I ] - (Optional) Secondary home agent IPv4 address
   bRevTunneling  [ I ] - (Optional) Enable reverse tunneling?
   pNAI           [ I ] - (Optional) Network access identifier string
   pHASPI         [ I ] - (Optional) HA security parameter index
   pAAASPI        [ I ] - (Optional) AAA security parameter index
   pMNHA          [ I ] - (Optional) MN-HA string
   pMNAAA         [ I ] - (Optional) MN-AAA string

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackSetMobileIPProfile(
   ULONG *                    pOutLen,
   BYTE *                     pOut,
   CHAR *                     pSPC,
   BYTE                       index,
   BYTE *                     pEnabled,
   ULONG *                    pAddress,
   ULONG *                    pPrimaryHA,
   ULONG *                    pSecondaryHA,
   BYTE *                     pRevTunneling,
   CHAR *                     pNAI,
   ULONG *                    pHASPI,
   ULONG *                    pAAASPI,
   CHAR *                     pMNHA,
   CHAR *                     pMNAAA )
{
   // Validate arguments
   if (pOut == 0 || pSPC == 0 || pSPC[0] == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   std::string spc( pSPC );
   if (spc.size() > 6)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   if (spc.find_first_not_of( "0123456789" ) != std::string::npos )
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Check size
   WORD tlvx01Sz = sizeof( sWDSSetMIPProfileRequest_Index );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSSetMIPProfileRequest_Index * pTLVx01;
   pTLVx01 = (sWDSSetMIPProfileRequest_Index*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the values
   memcpy( &pTLVx01->mSPC[0], spc.c_str(), spc.size() );
   pTLVx01->mProfileIndex = index;

   offset += tlvx01Sz;

   // Add Enabled, if specified
   if (pEnabled != 0)
   {
      // Check size
      WORD tlvx10Sz = sizeof( sWDSSetMIPProfileRequest_State );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx10Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x10;
      pHeader->mLength = tlvx10Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPProfileRequest_State * pTLVx10;
      pTLVx10 = (sWDSSetMIPProfileRequest_State*)(pOut + offset);
      memset( pTLVx10, 0, tlvx10Sz );

      // Set the value
      pTLVx10->mEnabled = (*pEnabled == 0 ? 0 : 1);

      offset += tlvx10Sz;
   }

   // Add Home Address, if specified
   if (pAddress != 0)
   {
      // Check size
      WORD tlvx11Sz = sizeof( sWDSSetMIPProfileRequest_HomeAddress );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx11Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x11;
      pHeader->mLength = tlvx11Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPProfileRequest_HomeAddress * pTLVx11;
      pTLVx11 = (sWDSSetMIPProfileRequest_HomeAddress*)(pOut + offset);
      memset( pTLVx11, 0, tlvx11Sz );

      ULONG ip0 = (*pAddress & 0x000000FF);
      ULONG ip1 = (*pAddress & 0x0000FF00) >> 8;
      ULONG ip2 = (*pAddress & 0x00FF0000) >> 16;
      ULONG ip3 = (*pAddress & 0xFF000000) >> 24;

      // Set the value
      pTLVx11->mIPV4Address[0] = (INT8)ip0;
      pTLVx11->mIPV4Address[1] = (INT8)ip1;
      pTLVx11->mIPV4Address[2] = (INT8)ip2;
      pTLVx11->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx11Sz;
   }

   // Add Primary Home Agent Address, if specified
   if (pPrimaryHA != 0)
   {
      // Check size
      WORD tlvx12Sz = sizeof( sWDSSetMIPProfileRequest_PrimaryHomeAgentAddress );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx12Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x12;
      pHeader->mLength = tlvx12Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPProfileRequest_PrimaryHomeAgentAddress * pTLVx12;
      pTLVx12 = (sWDSSetMIPProfileRequest_PrimaryHomeAgentAddress*)(pOut + offset);
      memset( pTLVx12, 0, tlvx12Sz );

      ULONG ip0 = (*pPrimaryHA & 0x000000FF);
      ULONG ip1 = (*pPrimaryHA & 0x0000FF00) >> 8;
      ULONG ip2 = (*pPrimaryHA & 0x00FF0000) >> 16;
      ULONG ip3 = (*pPrimaryHA & 0xFF000000) >> 24;

      // Set the value
      pTLVx12->mIPV4Address[0] = (INT8)ip0;
      pTLVx12->mIPV4Address[1] = (INT8)ip1;
      pTLVx12->mIPV4Address[2] = (INT8)ip2;
      pTLVx12->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx12Sz;
   }

   // Add Secondary Home Agent Address, if specified
   if (pSecondaryHA != 0)
   {
      // Check size
      WORD tlvx13Sz = sizeof( sWDSSetMIPProfileRequest_SecondaryHomeAgentAddress );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx13Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x13;
      pHeader->mLength = tlvx13Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPProfileRequest_SecondaryHomeAgentAddress * pTLVx13;
      pTLVx13 = (sWDSSetMIPProfileRequest_SecondaryHomeAgentAddress*)(pOut + offset);
      memset( pTLVx13, 0, tlvx13Sz );

      ULONG ip0 = (*pSecondaryHA & 0x000000FF);
      ULONG ip1 = (*pSecondaryHA & 0x0000FF00) >> 8;
      ULONG ip2 = (*pSecondaryHA & 0x00FF0000) >> 16;
      ULONG ip3 = (*pSecondaryHA & 0xFF000000) >> 24;

      // Set the value
      pTLVx13->mIPV4Address[0] = (INT8)ip0;
      pTLVx13->mIPV4Address[1] = (INT8)ip1;
      pTLVx13->mIPV4Address[2] = (INT8)ip2;
      pTLVx13->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx13Sz;
   }

   // Add reverse tunneling, if specified
   if (pRevTunneling != 0)
   {
      // Check size
      WORD tlvx14Sz = sizeof( sWDSSetMIPProfileRequest_ReverseTunneling );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx14Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x14;
      pHeader->mLength = tlvx14Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPProfileRequest_ReverseTunneling * pTLVx14;
      pTLVx14 = (sWDSSetMIPProfileRequest_ReverseTunneling*)(pOut + offset);
      memset( pTLVx14, 0, tlvx14Sz );

      // Set the value
      pTLVx14->mReverseTunneling = (*pRevTunneling == 0 ? 0 : 1);

      offset += tlvx14Sz;
   }

   // Add NAI, if specified
   if (pNAI != 0)
   {
      std::string nai( pNAI );

      // Check size
      WORD tlvx15Sz = (WORD)nai.size();
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx15Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x15;
      pHeader->mLength = tlvx15Sz;

      offset += sizeof( sQMIRawContentHeader );

      // Set the value
      memcpy( (pOut + offset), nai.c_str(), nai.size() );

      offset += tlvx15Sz;
   }

   // Add HA SPI, if specified
   if (pHASPI != 0)
   {
      // Check size
      WORD tlvx16Sz = sizeof( sWDSSetMIPProfileRequest_HASPI );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx16Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x16;
      pHeader->mLength = tlvx16Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPProfileRequest_HASPI * pTLVx16;
      pTLVx16 = (sWDSSetMIPProfileRequest_HASPI*)(pOut + offset);
      memset( pTLVx16, 0, tlvx16Sz );

      // Set the value
      pTLVx16->mHASPI = *pHASPI;

      offset += tlvx16Sz;
   }

   // Add AAA SPI, if specified
   if (pAAASPI != 0)
   {
      // Check size
      WORD tlvx17Sz = sizeof( sWDSSetMIPProfileRequeste_AAASPI );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx17Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x17;
      pHeader->mLength = tlvx17Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPProfileRequeste_AAASPI * pTLVx17;
      pTLVx17 = (sWDSSetMIPProfileRequeste_AAASPI*)(pOut + offset);
      memset( pTLVx17, 0, tlvx17Sz );

      // Set the value
      pTLVx17->mAAASPI = *pAAASPI;

      offset += tlvx17Sz;
   }

   // Add MN-HA key, if specified
   if (pMNHA != 0)
   {
      std::string mnha( pMNHA );

      // Check size
      WORD tlvx18Sz = (WORD)mnha.size();
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx18Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x18;
      pHeader->mLength = tlvx18Sz;

      offset += sizeof( sQMIRawContentHeader );

      // Set the value
      memcpy( (pOut + offset), mnha.c_str(), mnha.size() );

      offset += tlvx18Sz;
   }

   // Add MN-AAA key, if specified
   if (pMNHA != 0)
   {
      std::string mnaaa( pMNAAA );

      // Check size
      WORD tlvx19Sz = (WORD)mnaaa.size();
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx19Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x19;
      pHeader->mLength = tlvx19Sz;

      offset += sizeof( sQMIRawContentHeader );

      // Set the value
      memcpy( (pOut + offset), mnaaa.c_str(), mnaaa.size() );

      offset += tlvx19Sz;
   }

   // At least one of the optional parameters must have been set
   if (offset <= sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   PackGetMobileIPProfile

DESCRIPTION:
   This function gets the specified mobile IP profile settings

PARAMETERS:
   pOutLen        [I/O] - Upon input the maximum number of BYTEs pOut can
                          contain, upon output the number of BYTEs copied
                          to pOut
   pOut           [ O ] - Output buffer
   index          [ I ] - Mobile IP profile ID

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackGetMobileIPProfile(
   ULONG *                    pOutLen,
   BYTE *                     pOut,
   BYTE                       index )
{
   // Validate arguments
   if (pOut == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Check size
   WORD tlvx01Sz = sizeof( sWDSGetMIPProfileRequest_Index );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSGetMIPProfileRequest_Index * pTLVx01;
   pTLVx01 = (sWDSGetMIPProfileRequest_Index*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the values
   pTLVx01->mProfileIndex = index;

   offset += tlvx01Sz;
   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetMobileIPProfile

DESCRIPTION:
   This function gets the specified mobile IP profile settings

PARAMETERS:
   inLen          [ I ] - Length of input buffer
   pIn            [ I ] - Input buffer
   pEnabled       [ O ] - Mobile IP profile enabled?
   pAddress       [ O ] - Home IPv4 address
   pPrimaryHA     [ O ] - Primary home agent IPv4 address
   pSecondaryHA   [ O ] - Secondary home agent IPv4 address
   pRevTunneling  [ O ] - Reverse tunneling enabled?
   naiSize        [ I ] - The maximum number of characters (including NULL
                          terminator) that the NAI array can contain
   pNAI           [ O ] - Network access identifier string
   pHASPI         [ O ] - HA security parameter index
   pAAASPI        [ O ] - AAA security parameter index
   pHAState       [ O ] - HA key state
   pAAAState      [ O ] - AAA key state

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetMobileIPProfile(
   ULONG                      inLen,
   const BYTE *               pIn,
   BYTE *                     pEnabled,
   ULONG *                    pAddress,
   ULONG *                    pPrimaryHA,
   ULONG *                    pSecondaryHA,
   BYTE *                     pRevTunneling,
   BYTE                       naiSize,
   CHAR *                     pNAI,
   ULONG *                    pHASPI,
   ULONG *                    pAAASPI,
   ULONG *                    pHAState,
   ULONG *                    pAAAState )
{
   // Validate arguments
   if (pIn == 0
   ||  pEnabled == 0
   ||  pAddress == 0
   ||  pPrimaryHA == 0
   ||  pSecondaryHA == 0
   ||  pRevTunneling == 0
   ||  naiSize == 0
   ||  pNAI == 0
   ||  pHASPI == 0
   ||  pAAASPI == 0
   ||  pHAState == 0
   ||  pAAAState == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Assume errors
   *pEnabled = 0xff;
   *pAddress = 0xffffffff;
   *pPrimaryHA = 0xffffffff;
   *pSecondaryHA = 0xffffffff;
   *pRevTunneling = 0xff;
   *pHASPI = 0xffffffff;
   *pAAASPI = 0xffffffff;
   *pHAState = 0xffffffff;
   *pAAAState = 0xffffffff;

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

      *pEnabled = pTLVx10->mEnabled;
   }

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

      ULONG ip0 = pTLVx11->mIPV4Address[0];
      ULONG ip1 = pTLVx11->mIPV4Address[1] << 8;
      ULONG ip2 = pTLVx11->mIPV4Address[2] << 16;
      ULONG ip3 = pTLVx11->mIPV4Address[3] << 24;

      *pAddress = (ip0 | ip1 | ip2 | ip3);
   }

   // Find the Primary Home Agent Address
   const sWDSGetMIPProfileResponse_PrimaryHomeAgentAddress * pTLVx12;
   ULONG outLenx12;
   rc = GetTLV( inLen, pIn, 0x12, &outLenx12, (const BYTE **)&pTLVx12 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx12 < sizeof( sWDSGetMIPProfileResponse_PrimaryHomeAgentAddress ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      ULONG ip0 = pTLVx12->mIPV4Address[0];
      ULONG ip1 = pTLVx12->mIPV4Address[1] << 8;
      ULONG ip2 = pTLVx12->mIPV4Address[2] << 16;
      ULONG ip3 = pTLVx12->mIPV4Address[3] << 24;

      *pPrimaryHA = (ip0 | ip1 | ip2 | ip3);
   }

   // Find the Secondary Home Agent Address
   const sWDSGetMIPProfileResponse_SecondaryHomeAgentAddress * pTLVx13;
   ULONG outLenx13;
   rc = GetTLV( inLen, pIn, 0x13, &outLenx13, (const BYTE **)&pTLVx13 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx13 < sizeof( sWDSGetMIPProfileResponse_SecondaryHomeAgentAddress ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      ULONG ip0 = pTLVx13->mIPV4Address[0];
      ULONG ip1 = pTLVx13->mIPV4Address[1] << 8;
      ULONG ip2 = pTLVx13->mIPV4Address[2] << 16;
      ULONG ip3 = pTLVx13->mIPV4Address[3] << 24;

      *pSecondaryHA = (ip0 | ip1 | ip2 | ip3);
   }

   // Find the Reverse tunneling, if enabled
   const sWDSGetMIPProfileResponse_ReverseTunneling * pTLVx14;
   ULONG outLenx14;
   rc = GetTLV( inLen, pIn, 0x14, &outLenx14, (const BYTE **)&pTLVx14 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx10 < sizeof( sWDSGetMIPProfileResponse_ReverseTunneling ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pRevTunneling = pTLVx14->mReverseTunneling;
   }

   // Find the NAI, if enabled
   const sWDSGetMIPProfileResponse_NAI * pTLVx15;
   ULONG outLenx15;
   rc = GetTLV( inLen, pIn, 0x15, &outLenx15, (const BYTE **)&pTLVx15 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (naiSize < outLenx15 + 1)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      memcpy( pNAI, (const BYTE *)pTLVx15, outLenx15 );

      // Null terminate
      pNAI[outLenx15] = 0;
   }

   // Find the HA SPI
   const sWDSGetMIPProfileResponse_HASPI * pTLVx16;
   ULONG outLenx16;
   rc = GetTLV( inLen, pIn, 0x16, &outLenx16, (const BYTE **)&pTLVx16 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx16 < sizeof( sWDSGetMIPProfileResponse_HASPI ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pHASPI = pTLVx16->mHASPI;
   }

   // Find the AAA SPI
   const sWDSGetMIPProfileResponse_AAASPI * pTLVx17;
   ULONG outLenx17;
   rc = GetTLV( inLen, pIn, 0x17, &outLenx17, (const BYTE **)&pTLVx17 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx17 < sizeof( sWDSGetMIPProfileResponse_AAASPI ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pAAASPI = pTLVx17->mAAASPI;
   }

   // Find the HA state
   const sWDSGetMIPProfileResponse_HAState * pTLVx1A;
   ULONG outLenx1A;
   rc = GetTLV( inLen, pIn, 0x1A, &outLenx1A, (const BYTE **)&pTLVx1A );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx1A < sizeof( sWDSGetMIPProfileResponse_HAState ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pHAState = pTLVx1A->mKeyState;
   }

   // Find the AAA state
   const sWDSGetMIPProfileResponse_AAAState * pTLVx1B;
   ULONG outLenx1B;
   rc = GetTLV( inLen, pIn, 0x1B, &outLenx1B, (const BYTE **)&pTLVx1B );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx1B < sizeof( sWDSGetMIPProfileResponse_AAAState ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pAAAState = pTLVx1B->mKeyState;
   }

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   PackSetMobileIPParameters

DESCRIPTION:
   This function sets the specified mobile IP parameters

PARAMETERS:
   pOutLen           [I/O] - Upon input the maximum number of BYTEs pOut can
                             contain, upon output the number of BYTEs copied
                             to pOut
   pOut              [ O ] - Output buffer
   pSPC              [ I ] - Six digit service programming code
   pMode             [ I ] - (Optional) Desired mobile IP setting
   pRetryLimit       [ I ] - (Optional) Retry attempt limit
   pRetryInterval    [ I ] - (Optional) Retry attempt interval
   pReRegPeriod      [ I ] - (Optional) Re-registration period
   pReRegTraffic     [ I ] - (Optional) Re-registration only with traffic?
   pHAAuthenticator  [ I ] - (Optional) MH-HA authenticator calculator?
   pHA2002bis        [ I ] - (Optional) MH-HA RFC 2002bis authentication?

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackSetMobileIPParameters(
   ULONG *                    pOutLen,
   BYTE *                     pOut,
   CHAR *                     pSPC,
   ULONG *                    pMode,
   BYTE *                     pRetryLimit,
   BYTE *                     pRetryInterval,
   BYTE *                     pReRegPeriod,
   BYTE *                     pReRegTraffic,
   BYTE *                     pHAAuthenticator,
   BYTE *                     pHA2002bis )
{
   // Validate arguments
   if (pOut == 0 || pSPC == 0 || pSPC[0] == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   std::string spc( pSPC );
   if (spc.size() > 6)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   if (spc.find_first_not_of( "0123456789" ) != std::string::npos )
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   // Check size
   WORD tlvx01Sz = sizeof( sWDSSetMIPParametersRequest_SPC );
   if (*pOutLen < sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_BUFFER_SZ;
   }

   sQMIRawContentHeader * pHeader = (sQMIRawContentHeader*)pOut;
   pHeader->mTypeID = 0x01;
   pHeader->mLength = tlvx01Sz;

   ULONG offset = sizeof( sQMIRawContentHeader );

   sWDSSetMIPParametersRequest_SPC * pTLVx01;
   pTLVx01 = (sWDSSetMIPParametersRequest_SPC*)(pOut + offset);
   memset( pTLVx01, 0, tlvx01Sz );

   // Set the values
   memcpy( &pTLVx01->mSPC[0], spc.c_str(), spc.size() );

   offset += tlvx01Sz;

   // Add Mode, if specified
   if (pMode != 0)
   {
      // Check size
      WORD tlvx10Sz = sizeof( sWDSSetMIPParametersRequest_MobileIPMode );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx10Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x10;
      pHeader->mLength = tlvx10Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPParametersRequest_MobileIPMode * pTLVx10;
      pTLVx10 = (sWDSSetMIPParametersRequest_MobileIPMode*)(pOut + offset);
      memset( pTLVx10, 0, tlvx10Sz );

      // Set the value
      pTLVx10->mMIPMode = (eQMIMobileIPModes)*pMode;

      offset += tlvx10Sz;
   }

   // Add Retry Limit, if specified
   if (pRetryLimit != 0)
   {
      // Check size
      WORD tlvx11Sz = sizeof( sWDSSetMIPParametersRequest_RetryAttemptLimit );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx11Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x11;
      pHeader->mLength = tlvx11Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPParametersRequest_RetryAttemptLimit * pTLVx11;
      pTLVx11 = (sWDSSetMIPParametersRequest_RetryAttemptLimit*)(pOut + offset);
      memset( pTLVx11, 0, tlvx11Sz );

      // Set the value
      pTLVx11->mRetryAttemptLimit = *pRetryLimit;

      offset += tlvx11Sz;
   }

   // Add Retry interval, if specified
   if (pRetryInterval != 0)
   {
      // Check size
      WORD tlvx12Sz = sizeof( sWDSSetMIPParametersRequest_RetryAttemptInterval );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx12Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x12;
      pHeader->mLength = tlvx12Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPParametersRequest_RetryAttemptInterval * pTLVx12;
      pTLVx12 = (sWDSSetMIPParametersRequest_RetryAttemptInterval*)(pOut + offset);
      memset( pTLVx12, 0, tlvx12Sz );

      // Set the value
      pTLVx12->mRetryAttemptInterval = *pRetryInterval;

      offset += tlvx12Sz;
   }

   // Add Re-registration period, if specified
   if (pReRegPeriod != 0)
   {
      // Check size
      WORD tlvx13Sz = sizeof( sWDSSetMIPParametersRequest_ReRegistrationPeriod );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx13Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x13;
      pHeader->mLength = tlvx13Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPParametersRequest_ReRegistrationPeriod * pTLVx13;
      pTLVx13 = (sWDSSetMIPParametersRequest_ReRegistrationPeriod*)(pOut + offset);
      memset( pTLVx13, 0, tlvx13Sz );

      // Set the value
      pTLVx13->mReRegistrationPeriod = *pReRegPeriod;

      offset += tlvx13Sz;
   }

   // Add Re-registration on traffic flag, if specified
   if (pReRegTraffic != 0)
   {
      // Check size
      WORD tlvx14Sz = sizeof( sWDSSetMIPParametersRequest_ReRegistrationOnlyWithTraffic );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx14Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x14;
      pHeader->mLength = tlvx14Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPParametersRequest_ReRegistrationOnlyWithTraffic * pTLVx14;
      pTLVx14 = (sWDSSetMIPParametersRequest_ReRegistrationOnlyWithTraffic*)(pOut + offset);
      memset( pTLVx14, 0, tlvx14Sz );

      // Set the value
      pTLVx14->mReRegistrationOnlyWithTraffic = (*pReRegTraffic == 0 ? 0 : 1);

      offset += tlvx14Sz;
   }

   // Add HA authenticator flag, if specified
   if (pHAAuthenticator != 0)
   {
      // Check size
      WORD tlvx15Sz = sizeof( sWDSSetMIPParametersRequest_MNHAAuthenticatorCalculator );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx15Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x15;
      pHeader->mLength = tlvx15Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPParametersRequest_MNHAAuthenticatorCalculator * pTLVx15;
      pTLVx15 = (sWDSSetMIPParametersRequest_MNHAAuthenticatorCalculator*)(pOut + offset);
      memset( pTLVx15, 0, tlvx15Sz );

      // Set the value
      pTLVx15->mMNHAAuthenticatorCalculator = (*pHAAuthenticator == 0 ? 0 : 1);

      offset += tlvx15Sz;
   }

   // Add HA RFC2002bis authentication flag, if specified
   if (pHA2002bis != 0)
   {
      // Check size
      WORD tlvx16Sz = sizeof( sWDSSetMIPParametersRequest_MNHARFC2002BISAuthentication );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx16Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x16;
      pHeader->mLength = tlvx16Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetMIPParametersRequest_MNHARFC2002BISAuthentication * pTLVx16;
      pTLVx16 = (sWDSSetMIPParametersRequest_MNHARFC2002BISAuthentication*)(pOut + offset);
      memset( pTLVx16, 0, tlvx16Sz );

      // Set the value
      pTLVx16->mMNHARFC2002BISAuthentication = (*pHA2002bis == 0 ? 0 : 1);

      offset += tlvx16Sz;
   }

   // At least one of the optional parameters must have been set
   if (offset <= sizeof( sQMIRawContentHeader ) + tlvx01Sz)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetMobileIPParameters

DESCRIPTION:
   This function gets the mobile IP parameters

PARAMETERS:
   inLen             [ I ] - Length of input buffer
   pIn               [ I ] - Input buffer
   pMode             [ O ] - Current mobile IP setting
   pRetryLimit       [ O ] - Retry attempt limit
   pRetryInterval    [ O ] - Retry attempt interval
   pReRegPeriod      [ O ] - Re-registration period
   pReRegTraffic     [ O ] - Re-registration only with traffic?
   pHAAuthenticator  [ O ] - MH-HA authenticator calculator?
   pHA2002bis        [ O ] - MH-HA RFC 2002bis authentication?

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG ParseGetMobileIPParameters(
   ULONG                      inLen,
   const BYTE *               pIn,
   ULONG *                    pMode,
   BYTE *                     pRetryLimit,
   BYTE *                     pRetryInterval,
   BYTE *                     pReRegPeriod,
   BYTE *                     pReRegTraffic,
   BYTE *                     pHAAuthenticator,
   BYTE *                     pHA2002bis )
{
   // Validate arguments
   if (pIn == 0
   ||  pMode == 0
   ||  pRetryLimit == 0
   ||  pRetryInterval == 0
   ||  pReRegPeriod == 0
   ||  pReRegTraffic == 0
   ||  pHAAuthenticator == 0
   ||  pHA2002bis == 0)
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   *pMode = 0xffffffff;
   *pRetryLimit = 0xff;
   *pRetryInterval = 0xff;
   *pReRegPeriod = 0xff;
   *pReRegTraffic = 0xff;
   *pHAAuthenticator = 0xff;
   *pHA2002bis = 0xff;

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

      *pMode = pTLVx10->mMIPMode;
   }

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

      *pRetryLimit = pTLVx11->mRetryAttemptLimit;
   }

   // Find the Retry Interval
   const sWDSGetMIPParametersResponse_RetryAttemptInterval * pTLVx12;
   ULONG outLenx12;
   rc = GetTLV( inLen, pIn, 0x12, &outLenx12, (const BYTE **)&pTLVx12 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx12 < sizeof( sWDSGetMIPParametersResponse_RetryAttemptInterval ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pRetryInterval = pTLVx12->mRetryAttemptInterval;
   }

   // Find the Re-registration period
   const sWDSGetMIPParametersResponse_ReRegistrationPeriod * pTLVx13;
   ULONG outLenx13;
   rc = GetTLV( inLen, pIn, 0x13, &outLenx13, (const BYTE **)&pTLVx13 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx13 < sizeof( sWDSGetMIPParametersResponse_ReRegistrationPeriod ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pReRegPeriod = pTLVx13->mReRegistrationPeriod;
   }

   // Find the Re-register on traffic flag
   const sWDSGetMIPParametersResponse_ReRegistrationOnlyWithTraffic * pTLVx14;
   ULONG outLenx14;
   rc = GetTLV( inLen, pIn, 0x14, &outLenx14, (const BYTE **)&pTLVx14 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx14 < sizeof( sWDSGetMIPParametersResponse_ReRegistrationOnlyWithTraffic ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pReRegTraffic = pTLVx14->mReRegistrationOnlyWithTraffic;
   }

   // Find the HA authenticator
   const sWDSGetMIPParametersResponse_MNHAAuthenticatorCalculator * pTLVx15;
   ULONG outLenx15;
   rc = GetTLV( inLen, pIn, 0x15, &outLenx15, (const BYTE **)&pTLVx15 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx15 < sizeof( sWDSGetMIPParametersResponse_MNHAAuthenticatorCalculator ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pHAAuthenticator = pTLVx15->mMNHAAuthenticatorCalculator;
   }

   // Find the HA RFC2002bis authentication flag
   const sWDSGetMIPParametersResponse_MNHARFC2002BISAuthentication * pTLVx16;
   ULONG outLenx16;
   rc = GetTLV( inLen, pIn, 0x16, &outLenx16, (const BYTE **)&pTLVx16 );
   if (rc == eGOBI_ERR_NONE)
   {
      if (outLenx16 < sizeof( sWDSGetMIPParametersResponse_MNHARFC2002BISAuthentication ))
      {
         return eGOBI_ERR_MALFORMED_RSP;
      }

      *pHA2002bis = pTLVx16->mMNHARFC2002BISAuthentication;
   }

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetLastMobileIPError

DESCRIPTION:
   This function gets the last mobile IP error

PARAMETERS:
   inLen       [ I ] - Length of input buffer
   pIn         [ I ] - Input buffer
   pError      [ O ] - Last mobile IP error

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

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

   // Is the TLV large enough?
   if (outLenx01 < sizeof( sWDSGetLastMIPStatusResponse_Status ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   *pError = pTLVx01->mLastMIPStatus;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   PackSetDNSSettings

DESCRIPTION:
   This function sets the DNS settings for the device

PARAMETERS:
   pOutLen           [I/O] - Upon input the maximum number of BYTEs pOut can
                             contain, upon output the number of BYTEs copied
                             to pOut
   pOut              [ O ] - Output buffer
   pPrimaryDNS       [ I ] - (Optional) Primary DNS IPv4 address
   pSecondaryDNS     [ I ] - (Optional) Secondary DNS IPv4 address

RETURN VALUE:
   ULONG - Return code
===========================================================================*/
ULONG PackSetDNSSettings(
   ULONG *                    pOutLen,
   BYTE *                     pOut,
   ULONG *                    pPrimaryDNS,
   ULONG *                    pSecondaryDNS )
{
   // Validate arguments
   // At least one must be specified
   if (pOut == 0 || (pPrimaryDNS == 0 && pSecondaryDNS == 0))
   {
      return eGOBI_ERR_INVALID_ARG;
   }

   sQMIRawContentHeader * pHeader;
   ULONG offset = 0;

   // Add Primary DNS, if specified
   if (pPrimaryDNS != 0)
   {
      // Check size
      WORD tlvx10Sz = sizeof( sWDSSetDNSSettingRequest_PrimaryDNS );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx10Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x10;
      pHeader->mLength = tlvx10Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetDNSSettingRequest_PrimaryDNS * pTLVx10;
      pTLVx10 = (sWDSSetDNSSettingRequest_PrimaryDNS*)(pOut + offset);
      memset( pTLVx10, 0, tlvx10Sz );

      ULONG ip0 = (*pPrimaryDNS & 0x000000FF);
      ULONG ip1 = (*pPrimaryDNS & 0x0000FF00) >> 8;
      ULONG ip2 = (*pPrimaryDNS & 0x00FF0000) >> 16;
      ULONG ip3 = (*pPrimaryDNS & 0xFF000000) >> 24;

      // Set the value
      pTLVx10->mIPV4Address[0] = (INT8)ip0;
      pTLVx10->mIPV4Address[1] = (INT8)ip1;
      pTLVx10->mIPV4Address[2] = (INT8)ip2;
      pTLVx10->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx10Sz;
   }

   // Add Secondary DNS, if specified
   if (pSecondaryDNS != 0)
   {
      // Check size
      WORD tlvx11Sz = sizeof( sWDSSetDNSSettingRequest_SecondaryDNS );
      if (*pOutLen < offset + sizeof( sQMIRawContentHeader ) + tlvx11Sz)
      {
         return eGOBI_ERR_BUFFER_SZ;
      }

      pHeader = (sQMIRawContentHeader*)(pOut + offset);
      pHeader->mTypeID = 0x11;
      pHeader->mLength = tlvx11Sz;

      offset += sizeof( sQMIRawContentHeader );

      sWDSSetDNSSettingRequest_SecondaryDNS * pTLVx11;
      pTLVx11 = (sWDSSetDNSSettingRequest_SecondaryDNS*)(pOut + offset);
      memset( pTLVx11, 0, tlvx11Sz );

      ULONG ip0 = (*pSecondaryDNS & 0x000000FF);
      ULONG ip1 = (*pSecondaryDNS & 0x0000FF00) >> 8;
      ULONG ip2 = (*pSecondaryDNS & 0x00FF0000) >> 16;
      ULONG ip3 = (*pSecondaryDNS & 0xFF000000) >> 24;

      // Set the value
      pTLVx11->mIPV4Address[0] = (INT8)ip0;
      pTLVx11->mIPV4Address[1] = (INT8)ip1;
      pTLVx11->mIPV4Address[2] = (INT8)ip2;
      pTLVx11->mIPV4Address[3] = (INT8)ip3;

      offset += tlvx11Sz;
   }

   *pOutLen = offset;

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetDNSSettings

DESCRIPTION:
   This function gets the DNS settings for the device

PARAMETERS:
   inLen             [ I ] - Length of input buffer
   pIn               [ I ] - Input buffer
   pPrimaryDNS       [ O ] - Primary DNS IPv4 address
   pSecondaryDNS     [ O ] - Secondary DNS IPv4 address

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

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

      ULONG ip0 = pTLVx10->mIPV4Address[0];
      ULONG ip1 = pTLVx10->mIPV4Address[1] << 8;
      ULONG ip2 = pTLVx10->mIPV4Address[2] << 16;
      ULONG ip3 = pTLVx10->mIPV4Address[3] << 24;

      *pPrimaryDNS = (ip0 | ip1 | ip2 | ip3);
   }

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

      ULONG ip0 = pTLVx11->mIPV4Address[0];
      ULONG ip1 = pTLVx11->mIPV4Address[1] << 8;
      ULONG ip2 = pTLVx11->mIPV4Address[2] << 16;
      ULONG ip3 = pTLVx11->mIPV4Address[3] << 24;

      *pSecondaryDNS = (ip0 | ip1 | ip2 | ip3);
   }

   return eGOBI_ERR_NONE;
}

/*===========================================================================
METHOD:
   ParseGetDataBearerTechnology

DESCRIPTION:
   This function retrieves the current data bearer technology (only
   valid when connected)

PARAMETERS:
   inLen          [ I ] - Length of input buffer
   pIn            [ I ] - Input buffer
   pDataCaps      [ O ] - The data bearer technology

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

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

   // Is the TLV large enough?
   if (outLenx01 < sizeof( sWDSGetDataBearerTechnologyResponse_Technology ))
   {
      return eGOBI_ERR_MALFORMED_RSP;
   }

   *pDataBearer = pTLVx01->mDataBearerTechnology;

   return eGOBI_ERR_NONE;
}
