/*=============================================================================

FILE: OEMSock.c

SERVICES: OEM Functions for Network Services

GENERAL DESCRIPTION:
A sample implementation of the OEMSock functions declared in
OEMSock.h that are used in the implementation of AEENet.
  
PUBLIC CLASSES AND STATIC FUNCTIONS:

  OEMNet_Open
  OEMNet_Close
  OEMNet_SetSocketWaiter
  OEMNet_SetNetWaiter
  OEMNet_SetMTPDWaiter
  OEMNet_SetQoSWaiter
  OEMNet_RegisterToMTPD
  OEMNet_DeRegisterFromMTPD
  OEMNet_InstallSocketEventHandler
  OEMNet_PPPOpen
  OEMNet_PPPClose
  OEMNet_PPPState
  OEMNet_MyIPAddr
  OEMNet_GetUrgent
  OEMNet_RequestQoS
  OEMNet_ReleaseQoS
  OEMNet_GetQoSFlowSpec
  OEMNet_GetQoSStatus
  OEMNet_QoSGoActive
  OEMNet_SetQoSDormancyTimeout
  OEMNet_GetQoSEventInfo
  OEMSocket_Open
  OEMSocket_Connect
  OEMSocket_Bind
  OEMSocket_Close
  OEMSocket_AsyncSelect
  OEMSocket_GetNextEvent
  OEMSocket_Write
  OEMSocket_Writev
  OEMSocket_Read
  OEMSocket_Readv
  OEMSocket_SendTo
  OEMSocket_RecvFrom


        Copyright © 1999-2002 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
=============================================================================*/
#include <stdlib.h>

/* necessary BREW includes */
#include "OEMSock_QoSSpec.h" // must be included before "OEMSock_priv.h"

#include "OEMSock.h"
#include "OEMSock_priv.h"       // OEM_PROVIDES_*, etc.
#include "OEMNotify.h"
#include "OEMConfig.h"
#include "OEMHeap.h"
#include "OEMOS.h"
#include "AEE_OEM.h"
#include "AEE_OEMDispatch.h"
#include "AEE_OEMNet.h"
#include "AEEComdef.h"
#include "AEEStdLib.h"
#include "AEEError.h"

//
// Conditional Debug Output
//

#define FARF_EVENT             0
#define FARF_STATE             0
#define FARF_FAILOVER_3G_TO_2G 0

#ifdef FARF
#undef FARF
#endif /* #ifdef FARF */

#if defined(AEE_SIMULATOR)
#define FARF_ALWAYS  1
#else
#define FARF_ALWAYS  0
#endif

#define FARF(x, p)  if (1 == FARF_##x) DBGPRINTF p 

#define ASSERT_FARF(x) if (!(x)) { FARF(ALWAYS,("assertion %s failed, "__FILE__":%d", #x, __LINE__)); }

#define AEE_SVC_RESUME_IF(x) if (x) { AEE_SVC_RESUME(x); }

// TBD: what is the optimal QoS queue event size ?
#define QOS_EVENT_QUEUE_SIZE 6

/* =======================================================================
       
   ======================================================================= */

typedef enum {
   NET_FAKE_NONE,
   NET_FAKE_COMING_UP,
   NET_FAKE_GOING_DOWN
} OEMNetTransitionStates;

typedef struct OEMNetPPPState
{
   // We fake the transition states for two reasons: 1) DMSS does not
   // always update state synchronously (e.g. OPENING occurs sometime
   // after we call dss_pppopen()), and 2) DMSS does not callback for
   // transition states (e.g. PHYS_LINK_COMING_UP)
   OEMNetTransitionStates nFakeNetState;
   OEMNetTransitionStates nFakePhysLinkState;

#if defined(FAILOVER_3G_TO_2G)
   unsigned bTrying3G:1;  /* set when we call openppp and 
                             QCMDR is set to SO33_PREF, cleared
                             when we re-set MDR mode, in netstatus, 
                             after a 3g call fails */
   unsigned bRestore3G:1; /* set if we discover the normal mode
                             of operation is 3G */
#endif /* #if defined(FAILOVER_3G_TO_2G) */

} OEMNetPPPState;


#ifdef OEM_PROVIDES_QOS
/* =======================================================================
   OEMNetQoSEventData
   ======================================================================= */

typedef struct OEMNetQoSEventData OEMNetQoSEventData;

struct OEMNetQoSEventData {
   OEMNetQoSEventData  *pNext;

   dss_qos_handle_type                          handle;
   dss_iface_ioctl_event_enum_type              event;
   dss_iface_ioctl_extended_info_code_enum_type infoCode;
};
#endif // OEM_PROVIDES_QOS


/* =======================================================================
   OEMNet
   ======================================================================= */

typedef struct OEMNet OEMNet;
typedef struct OEMSocket OEMSocket;

typedef struct
{
   // list of open nets
   OEMNet* pNets;

} OEMNetList;

struct OEMNet
{
   OEMNet*                 pNext;      // linked into OEMNetList

   sint15                  sAppID;     // AMSS application ID

   // list of open sockets
   OEMSocket*              pSockets;

   /* keeps track of funky states in dmss  */
   OEMNetPPPState          PPPState;

   AEECallback*            pcbNetWaiter;
   AEECallback*            pcbSocketWaiter;
   AEECallback*            pcbMTPDWaiter; 
   AEECallback*            pcbQoSWaiter;

   // socket event handler pfn for intercepting DS socket events,
   // typically used by QCT code.
   PFNNOTIFY               pfnSocketEventHandlerInterceptor;

   // Timers
   AEECallback             cbReEnableCallDialogs;

   dss_iface_ioctl_mt_handle_type mtpdpHandle;
   flg                     bMTpdpHandleValid;

   flg                     bCDMA_SN:1;
#ifdef FEATURE_BREW_NET_SIG
   flg                     bNetEvt:1;
   flg                     bSocketEvt:1;
   flg                     bMTPDEvt:1;
   flg                     bQoSEvt:1;
#endif

#ifdef OEM_PROVIDES_QOS
   /* QoS event queue */
   OEMNetQoSEventData      qeBuffers[QOS_EVENT_QUEUE_SIZE];
   OEMNetQoSEventData     *pqeFreeBuffers;
   OEMNetQoSEventData     *pqeReadyBuffers;
#endif // OEM_PROVIDES_QOS
};


/* =======================================================================
   OEMSocket
   ======================================================================= */

struct OEMSocket
{
   OEMSocket        *pNext;     // linked into OEMNet
   OEMNet           *pNet;      // parent network

   int16             sockfd;    // DMSS socket descriptor

   flg               bStream:1;
   flg               bLoopback:1; // don't fake waking for loopback
   flg               bListen:1; // socket in listen mode

#if defined(DSS_FAKE_CLOSE)
   flg               bFakeCloseEvent:1; // fake close timer fired

   // Timers
   AEECallback       cbFakeClose;
#endif // defined(DSS_FAKE_CLOSE)

   // are we shut down?
   int               nShutdown; 
};

/*===========================================================================

             Globals

===========================================================================*/

static OEMNetList gNetList;


/*===========================================================================

                      LOCAL FUNCTION DEFINITIONS

 ===========================================================================*/
static void    OEMNet_NonInterceptedSocketEventOccurred(void *pv);
static void    OEMNet_InterceptedSocketEventOccurred(void *pv);
static boolean OEMNet_IsSocketInterceptorInstalled(OEMNet* pNet);

 /*===========================================================================

                      FUNCTION DEFINITIONS

 ===========================================================================*/


 /*===========================================================================

                      OEM NET FUNCTION DEFINITIONS

 ===========================================================================*/


/*===========================================================================

FUNCTION OEMNet_New

DESCRIPTION
 Allocate a net related data structure and remember it

===========================================================================*/
static OEMNet* OEMNet_New(void)
{
   OEMNet* me;

   me = sys_malloc(sizeof(OEMNet));
   if ((OEMNet*)0 == me) {
      return (OEMNet*)0;
   }

   // insert at front of linked list
   me->pNext = gNetList.pNets;
   gNetList.pNets = me;

   me->sAppID = DSS_ERROR;

   me->bMTpdpHandleValid = FALSE;

#ifdef OEM_PROVIDES_QOS
   // Initialize the QoS free buffers.
   {
      OEMNetQoSEventData **ppqe = &me->pqeFreeBuffers;
      int                  index;

      // Initialize the callinfo buffers.
      for( index = 0; index < QOS_EVENT_QUEUE_SIZE; ++index ) {
         *ppqe = &me->qeBuffers[index];
         ppqe = &((*ppqe)->pNext);
      }
   }
#endif // OEM_PROVIDES_QOS

   return me;
}

/*===========================================================================

FUNCTION OEMNet_Delete

DESCRIPTION
 Forget and free a net related data structure

===========================================================================*/
static void OEMNet_Delete(OEMNet* me)
{
   OEMNet* ps;
   OEMNet* pPrev;

   // remove from linked list
   pPrev = (OEMNet*)0;
   for (ps = gNetList.pNets; ps; ps = ps->pNext) {

      if (me == ps) {
         if ((OEMNet*)0 == pPrev) {
            gNetList.pNets = ps->pNext;
         } else {
            pPrev->pNext = ps->pNext;
         }
         break;
      }

      pPrev = ps;
   }
   
   sys_free(ps);
}

/*===========================================================================

FUNCTION DSSToAEE_Err

DESCRIPTION
  Converts a DMSS error code to the corresponding AEE error code.
  
DEPENDENCIES
  none
===========================================================================*/
static int16 DSSToAEE_Err( int16 nMSMErr)
{
/*===========================================================================
===========================================================================*/

   //Store mapping between MSM Error Codes and AEE Error codes.
   static const struct {
      int16          from; // MSM Error Code
      int16          to; // AEE Error Code
   } map[] =
   {
      {DSS_SUCCESS            , AEE_NET_SUCCESS              },
      {DS_EBADF               , AEE_NET_EBADF                },
      {DS_EFAULT              , AEE_NET_EFAULT               },
      {DS_EWOULDBLOCK         , AEE_NET_EWOULDBLOCK          },
      {DS_EAFNOSUPPORT        , AEE_NET_EAFNOSUPPORT         },
      {DS_EPROTOTYPE          , AEE_NET_EPROTOTYPE           },
      {DS_ESOCKNOSUPPORT      , AEE_NET_ESOCKNOSUPPORT       },
      {DS_EPROTONOSUPPORT     , AEE_NET_EPROTONOSUPPORT      },
      {DS_EMFILE              , AEE_NET_EMFILE               },
      {DS_EOPNOTSUPP          , AEE_NET_EOPNOTSUPP           },
      {DS_EADDRINUSE          , AEE_NET_EADDRINUSE           },
      {DS_EADDRREQ            , AEE_NET_EADDRREQ             },
      {DS_EINPROGRESS         , AEE_NET_EINPROGRESS          },
      {DS_EISCONN             , AEE_NET_EISCONN              },
      {DS_EIPADDRCHANGED      , AEE_NET_EIPADDRCHANGED       },
      {DS_ENOTCONN            , AEE_NET_ENOTCONN             },
      {DS_ECONNREFUSED        , AEE_NET_ECONNREFUSED         },
      {DS_ETIMEDOUT           , AEE_NET_ETIMEDOUT            },
      {DS_ECONNRESET          , AEE_NET_ECONNRESET           },
      {DS_ECONNABORTED        , AEE_NET_ECONNABORTED         },
      {DS_EPIPE               , AEE_NET_EPIPE                },
      {DS_ENETDOWN            , AEE_NET_ENETDOWN             },
      {DS_EMAPP               , AEE_NET_EMAPP                },
      {DS_EBADAPP             , AEE_NET_ENETDOWN             },
      {DS_SOCKEXIST           , AEE_NET_SOCKEXIST            },
      {DS_EINVAL              , AEE_NET_EINVAL               },
      {DS_ENOMEM              , AEE_NET_ENOMEM               },
      {DS_EMSGSIZE            , AEE_NET_EMSGSIZE             },
      {DS_EMSGTRUNC           , AEE_NET_EMSGSIZE             },
      {DS_ESHUTDOWN           , AEE_NET_ESHUTDOWN            },
      {DS_ENOPROTOOPT         , AEE_NET_ENOPROTOOPT          },
      {DS_ENETISCONN          , AEE_NET_ENETISCONN           },
      {DS_ENETINPROGRESS      , AEE_NET_ENETINPROGRESS       },
      {DS_ENETNONET           , AEE_NET_ENETNONET            },
      {DS_ENETCLOSEINPROGRESS , AEE_NET_ENETCLOSEINPROGRESS  },
      {DS_ENETEXIST           , AEE_NET_ENETEXIST            }
   };

   int   i;
   int16 AEE_Err = AEE_NET_GENERAL_FAILURE;

   for (i = 0; i < ARRAY_SIZE(map); i++) {
      if (nMSMErr == map[i].from) {
         AEE_Err = map[i].to;
         break;
      }
   }

   return AEE_Err;
}

OEMNet* OEMNet_FromAppID(sint15 sAppID)
{
   OEMNet* pNet;

   for (pNet = gNetList.pNets; (OEMNet*)0 != pNet; pNet = pNet->pNext) {
      if (sAppID == pNet->sAppID) {
         break;
      }
   }

   return pNet;
}

void OEMNet_ProcessSocketEvent(OEMNet* pNet)
{
   if (OEMNet_IsSocketInterceptorInstalled(pNet)) {
      OEMNet_InterceptedSocketEventOccurred(pNet);
   } else {
      OEMNet_NonInterceptedSocketEventOccurred(pNet);
   }
}

void OEMNet_ProcessNetEvent(OEMNet* pNet)
{
   // when this callback fires, we can probably trust the chipset's state
   FARF(STATE,("FakeNetState: NONE"));
   pNet->PPPState.nFakeNetState = NET_FAKE_NONE;
   
   FARF(EVENT,("NET netevent"));
   AEE_SVC_RESUME_IF(pNet->pcbNetWaiter);
}

void OEMNet_ProcessMTPDEvent(OEMNet* pNet)
{
   FARF(EVENT,("NET MT-PD event"));
   AEE_SVC_RESUME_IF(pNet->pcbMTPDWaiter);
}

void OEMNet_ProcessQoSEvent(OEMNet* pNet)
{
   FARF(EVENT,("NET QoS event"));
   AEE_SVC_RESUME_IF(pNet->pcbQoSWaiter);
}


#ifdef FEATURE_BREW_NET_SIG
void OEMNet_SignalHandler(void)
{
   OEMNet*pNet = gNetList.pNets;
   
   while (pNet)
   {
      if (TRUE == pNet->bSocketEvt) {
         pNet->bSocketEvt = FALSE;
         OEMNet_ProcessSocketEvent(pNet);
      } 
      if (TRUE == pNet->bNetEvt) {
         pNet->bNetEvt = FALSE;
         OEMNet_ProcessNetEvent(pNet);
      }
      if (TRUE == pNet->bMTPDEvt) {
         pNet->bMTPDEvt = FALSE;
         OEMNet_ProcessMTPDEvent(pNet);
      }

      if (TRUE == pNet->bQoSEvt) {
         pNet->bQoSEvt = FALSE;
         OEMNet_ProcessQoSEvent(pNet);
      }

      pNet = pNet->pNext;
   }
}
#endif

/* parameter shim for the openlib callbacks */
static void OEMNet_NetEventOccurred(void *pv) 
{
   sint15* psAppID = (sint15*)pv;
   OEMNet* pNet = OEMNet_FromAppID(*psAppID);

   if ((OEMNet*)0 != pNet) {
#ifdef FEATURE_BREW_NET_SIG
      pNet->bNetEvt = TRUE;
      OEMOS_NetSignalDispatch();
#else
      OEMNet_ProcessNetEvent(pNet);
#endif
   }
}

//
// callback function for MT PDP events
//
static void OEMNet_MTPDEventOccurred
(
  dss_iface_ioctl_event_enum_type          event,
  dss_iface_ioctl_event_info_union_type    event_info,
  void                                     *user_data,
  sint15                                   app_id,
  dss_iface_id_type                        iface_id
)
{
   OEMNet* pNet = OEMNet_FromAppID(app_id);

   if ((OEMNet*)0 != pNet) {
#ifdef FEATURE_BREW_NET_SIG
      pNet->bMTPDEvt = TRUE;
      OEMOS_NetSignalDispatch();
#else
      OEMNet_ProcessMTPDEvent(pNet);
#endif
   }
}

// handle a non intercepted socket event
static void OEMNet_NonInterceptedSocketEventOccurred(void *pv)
{
   OEMNet* pNet = (OEMNet*)pv;
   AEE_SVC_RESUME_IF(pNet->pcbSocketWaiter);
}

// handle an intercepted socket event
static void OEMNet_InterceptedSocketEventOccurred(void *pv)
{
   OEMNet* pNet = (OEMNet*)pv;
   pNet->pfnSocketEventHandlerInterceptor(pv);
}

// check if a socket interceptor has been installed
static boolean OEMNet_IsSocketInterceptorInstalled(OEMNet* pNet)
{
   return (pNet->pfnSocketEventHandlerInterceptor != NULL);
}

/* parameter shim for the openlib callbacks */
static void OEMNet_SocketEventOccurred(void *pv)
{
   sint15* psAppID = (sint15*)pv;
   OEMNet* pNet = OEMNet_FromAppID(*psAppID);

   if ((OEMNet*)0 != pNet) {
#ifdef FEATURE_BREW_NET_SIG
      pNet->bSocketEvt = TRUE;
      OEMOS_NetSignalDispatch();
#else
      OEMNet_ProcessSocketEvent(pNet);
#endif
   }
}

/*===========================================================================
FUNCTION: OEMNet_CancelNetWaiter

DESCRIPTION
  Cancel the net waiter by removing itself from the net instance
===========================================================================*/
static void OEMNet_CancelNetWaiter(AEECallback *pcb)
{
   OEMNet* pNet = (OEMNet*)pcb->pCancelData;
   pNet->pcbNetWaiter = NULL;
}

/*===========================================================================
FUNCTION: OEMNet_CancelSocketWaiter

DESCRIPTION
  Cancel the socket waiter by removing itself from the net instance
===========================================================================*/
static void OEMNet_CancelSocketWaiter(AEECallback *pcb)
{
   OEMNet* pNet = (OEMNet*)pcb->pCancelData;
   pNet->pcbSocketWaiter = NULL;
}

/*===========================================================================
FUNCTION: OEMNet_CancelMTPDWaiter

DESCRIPTION
  Cancel the MT PD waiter by removing itself from the net instance
===========================================================================*/
static void OEMNet_CancelMTPDWaiter(AEECallback *pcb)
{
   OEMNet* pNet = (OEMNet*)pcb->pCancelData;
   pNet->pcbMTPDWaiter = NULL;
}

/*===========================================================================
FUNCTION: OEMNet_CancelQoSWaiter

DESCRIPTION
  Cancel the QoS waiter by removing itself from the net instance
===========================================================================*/
static void OEMNet_CancelQoSWaiter(AEECallback *pcb)
{
   OEMNet* pNet = (OEMNet*)pcb->pCancelData;
   pNet->pcbQoSWaiter = NULL;
}


// callback function for interface events
//
// NOTE: the fifth parameter (name) changed types from
// ps_iface_name_enum_type to dss_iface_ioctl_name_enum_type somewhere
// between 6100 releases 3055 and 4040.  Since this parameter is not
// used in the reference implementation, this difference can be simply
// cast away.
//
static void OEMNet_IfaceEventCallback
(
  dss_iface_ioctl_event_enum_type          event,
  dss_iface_ioctl_event_info_union_type    event_info,
  void                                     *user_data,
  sint15                                   app_id,
  dss_iface_id_type                        iface_id
)
{
   OEMNet* pNet = OEMNet_FromAppID(app_id);

   if ((OEMNet*)0 != pNet) {
   switch (event) {

      case DSS_IFACE_IOCTL_PHYS_LINK_DOWN_EV:
         // when this event is received, we can probably trust the chipset's state
         FARF(STATE,("FakePhyLinkState: NONE"));
            pNet->PPPState.nFakePhysLinkState = NET_FAKE_NONE;
         FARF(EVENT,("phys link down event"));
         break;

      case DSS_IFACE_IOCTL_PHYS_LINK_UP_EV:
         // when this event is received, we can probably trust the chipset's state
         FARF(STATE,("FakePhyLinkState: NONE"));
            pNet->PPPState.nFakePhysLinkState = NET_FAKE_NONE;
         FARF(EVENT,("phys link up event"));
         break;

      case DSS_IFACE_IOCTL_ADDR_CHANGED_EV:
         FARF(EVENT,("addr changed event"));
         break;

      default:
         return;
   }

      AEE_SVC_RESUME_IF(pNet->pcbNetWaiter);
   }
}

// (de)register event callback for an interface event
static int OEMNet_RegisterIfaceEventCallback(OEMNet* pNet, dss_iface_ioctl_event_enum_type event, boolean bRegister)
{
   dss_iface_ioctl_ev_cb_type ev_cb;
   int ret;
   int16 ndssErr;
   dss_iface_id_type iface_id;

   // the following cast is needed as one of the enum types changed
   // names in middle of the 6100 series of releases...see note
   // above OEMNet_IfaceEventCallback() for more details.
   ev_cb.event_cb = (dss_iface_ioctl_event_cb)OEMNet_IfaceEventCallback;
   ev_cb.event = event;
   ev_cb.user_data_ptr = NULL;
   ev_cb.app_id = pNet->sAppID;
      
   if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) == DSS_IFACE_INVALID_ID) {
      return AEE_NET_ENETNONET;
   }

   ret = oemdss_iface_ioctl(iface_id,
                            bRegister ? DSS_IFACE_IOCTL_REG_EVENT_CB : DSS_IFACE_IOCTL_DEREG_EVENT_CB,
                            &ev_cb,
                            &ndssErr);
   if (DSS_SUCCESS != ret) {
      return DSSToAEE_Err(ndssErr);
   }

   return AEE_NET_SUCCESS;
}

static void OEMNet_ReEnableCallDialogs(void *unused)
{
   (void)unused;
   OEM_Notify(OEMNTF_SHOW_CALL_DIALOGS, TRUE); // OK to show call dialogs again
}

/*===========================================================================

FUNCTION OEMNet_Open

DESCRIPTION

  Opens up the network.

  This function is called from the context of the socket client's task.

===========================================================================*/
OEMCONTEXT OEMNet_Open( int          nNetwork,
                        AEECallback *pcbNetWaiter,
                        AEECallback *pcbSocketWaiter,
                        AEECallback *pcbQoSWaiter,
                        int16       *err)
{
   int16 ndssErr;
   OEMNet* pNet;

   switch (nNetwork&0xffff) {
      case AEE_NETWORK_CDMA_SN:
          break;

#ifdef FEATURE_DATA_BCMCS
      case AEE_NETWORK_CDMA_BCMCS:
         break;
#endif // FEATURE_DATA_BCMCS

#if defined(FEATURE_DATA_UMTS)
      case AEE_NETWORK_UMTS(0):
      {
         boolean bValid;
         ds_umts_pdp_profile_status_etype status;
         
         status = ds_umts_get_pdp_profile_context_info_is_valid((byte)(nNetwork >> 16), &bValid);

         if ((DS_UMTS_PDP_SUCCESS != status) || !bValid) {
            *err = AEE_NET_ENETNONET;
            return (OEMCONTEXT)AEE_NET_ERROR;
         }

         break;
      }
#endif // defined(FEATURE_DATA_UMTS)

      default:
         *err = AEE_NET_ENETNONET;
         return (OEMCONTEXT)AEE_NET_ERROR;
   }

   pNet = OEMNet_New();
   if ((OEMNet*)0 == pNet) {
      *err = AEE_NET_EMFILE;
      return (OEMCONTEXT)AEE_NET_ERROR;
   }

   OEMNet_SetNetWaiter(pNet, pcbNetWaiter);
   OEMNet_SetSocketWaiter(pNet, pcbSocketWaiter);
   OEMNet_SetQoSWaiter(pNet, pcbQoSWaiter);
   OEMNet_SetMTPDWaiter(pNet, NULL);

   // Does the network library need to be opened?
   if (DSS_ERROR == pNet->sAppID) {  
      int ret;
      dss_net_policy_info_type policy_info;

      pNet->sAppID = dss_open_netlib(OEMNet_NetEventOccurred,
                                     OEMNet_SocketEventOccurred,
                                     &ndssErr );
      if (DSS_ERROR == pNet->sAppID) {
         OEMNet_Delete(pNet);
         *err = DSSToAEE_Err(ndssErr);
         return (OEMCONTEXT)AEE_NET_ERROR;
      }

      // Initialize to defaults
      dss_init_net_policy_info(&policy_info);

      switch (nNetwork&0xffff) {
         case AEE_NETWORK_CDMA_SN:
         {
            policy_info.iface.kind = DSS_IFACE_NAME;
            policy_info.iface.info.name = DSS_IFACE_CDMA_SN;

            // Allow certain ops for only for CDMA Service Network
            pNet->bCDMA_SN = TRUE;
            break;
         }
#ifdef FEATURE_DATA_BCMCS
         case AEE_NETWORK_CDMA_BCMCS:
         {
            policy_info.iface.kind = DSS_IFACE_NAME;
            policy_info.iface.info.name = DSS_IFACE_CDMA_BCAST;
            break;
         }
#endif // FEATURE_DATA_BCMCS
#if defined(FEATURE_DATA_UMTS)
         case AEE_NETWORK_UMTS(0):
         {
            policy_info.iface.kind = DSS_IFACE_NAME;
            policy_info.iface.info.name = DSS_IFACE_UMTS;
            policy_info.umts.pdp_profile_num = (nNetwork>>16)&0xffff;
            break;
         }
#endif // defined(FEATURE_DATA_UMTS)
         default:
            break;
      }

      if (DSS_SUCCESS != dss_set_app_net_policy(pNet->sAppID, &policy_info, &ndssErr)) {
         OEMNet_Delete(pNet);
         *err = DSSToAEE_Err(ndssErr);
         return (OEMCONTEXT)AEE_NET_ERROR;
      }
   
      // register an event callback for ip address changes
      ret = OEMNet_RegisterIfaceEventCallback(pNet, DSS_IFACE_IOCTL_ADDR_CHANGED_EV, TRUE);
      if (DSS_SUCCESS != ret) {
         FARF(ALWAYS, ("OEMNet_RegisterIfaceEventCallback failed! %d", ret));
      }

      CALLBACK_Init(&pNet->cbReEnableCallDialogs, OEMNet_ReEnableCallDialogs, 0);
   }

   return (OEMCONTEXT)pNet;
}

/*===========================================================================

FUNCTION OEMNet_Close

DESCRIPTION

  Closes the specified network library.  All sockets must have
  been closed for the network, prior to closing.

===========================================================================*/
int16 OEMNet_Close
(
 OEMCONTEXT netd
)
{
   OEMNet* pNet = (OEMNet*)netd;

   if ((OEMNet*)0 != pNet) {
   int16 ndssErr;

      if (DSS_ERROR != pNet->sAppID) {

         CALLBACK_Cancel(&pNet->cbReEnableCallDialogs);

         // deregister for ip address changes
         (void)OEMNet_RegisterIfaceEventCallback(pNet, DSS_IFACE_IOCTL_ADDR_CHANGED_EV, FALSE);

         // remove the event handlers
         OEMNet_SetNetWaiter(pNet, NULL);
         OEMNet_SetSocketWaiter(pNet, NULL);
         OEMNet_DeRegisterFromMTPD(pNet);
         OEMNet_SetQoSWaiter(pNet, NULL);

         pNet->pfnSocketEventHandlerInterceptor = NULL;

         if (DSS_SUCCESS != dss_close_netlib(pNet->sAppID, &ndssErr)) {
            return DSSToAEE_Err(ndssErr);
         }
   
         // don't want these anymore
         FARF(STATE,("Fake*State: NONE"));
         pNet->PPPState.nFakeNetState = NET_FAKE_NONE;
         pNet->PPPState.nFakePhysLinkState = NET_FAKE_NONE;
         pNet->sAppID = DSS_ERROR;
      }

      OEMNet_Delete(pNet);
   }

   return AEE_NET_SUCCESS;
}


/*===========================================================================

FUNCTION OEMNet_SetSocketWaiter

DESCRIPTION

  Registers a new socket waiter callback

===========================================================================*/
void OEMNet_SetSocketWaiter(OEMCONTEXT netd, AEECallback *pcbWaiter)
{
   OEMNet* pNet = (OEMNet*)netd;

   if (NULL != pcbWaiter) {
      CALLBACK_Cancel(pcbWaiter);
      pcbWaiter->pfnCancel = OEMNet_CancelSocketWaiter;
      pcbWaiter->pCancelData = pNet;
   }

   if (NULL != pNet->pcbSocketWaiter) {
      CALLBACK_Cancel(pNet->pcbSocketWaiter);
   }

   pNet->pcbSocketWaiter = pcbWaiter;
}


/*===========================================================================

FUNCTION OEMNet_SetNetWaiter

DESCRIPTION

  Registers a new network waiter callback

===========================================================================*/
void OEMNet_SetNetWaiter(OEMCONTEXT netd, AEECallback *pcbWaiter)
{
   OEMNet* pNet = (OEMNet*)netd;

   if (NULL != pcbWaiter) {
      CALLBACK_Cancel(pcbWaiter);
      pcbWaiter->pfnCancel = OEMNet_CancelNetWaiter;
      pcbWaiter->pCancelData = pNet;
   }

   if (NULL != pNet->pcbNetWaiter) {
      CALLBACK_Cancel(pNet->pcbNetWaiter);
   }

   pNet->pcbNetWaiter = pcbWaiter;
}


/*===========================================================================

FUNCTION OEMNet_SetMTPDWaiter

DESCRIPTION

  Registers a new MT packet data waiter callback

===========================================================================*/
void OEMNet_SetMTPDWaiter(OEMCONTEXT netd, AEECallback *pcbWaiter)
{
   OEMNet *pNet = (OEMNet*)netd;

   if (NULL != pcbWaiter) {
      CALLBACK_Cancel(pcbWaiter);
      pcbWaiter->pfnCancel = OEMNet_CancelMTPDWaiter;
      pcbWaiter->pCancelData = pNet;
   }

   if (NULL != pNet->pcbMTPDWaiter) {
      CALLBACK_Cancel(pNet->pcbMTPDWaiter);
   }

   pNet->pcbMTPDWaiter = pcbWaiter;
}

/*===========================================================================

FUNCTION OEMNet_SetQoSWaiter

DESCRIPTION

  Registers a new QoS event waiter callback

===========================================================================*/
void OEMNet_SetQoSWaiter(OEMCONTEXT netd, AEECallback *pcbWaiter)
{
   OEMNet *pNet = (OEMNet*)netd;

   if (NULL != pcbWaiter) {
      CALLBACK_Cancel(pcbWaiter);
      pcbWaiter->pfnCancel = OEMNet_CancelQoSWaiter;
      pcbWaiter->pCancelData = pNet;
   }

   if (NULL != pNet->pcbQoSWaiter) {
      CALLBACK_Cancel(pNet->pcbQoSWaiter);
   }

   pNet->pcbQoSWaiter = pcbWaiter;
}


/*===========================================================================

FUNCTION OEMNet_RegisterToMTPD

DESCRIPTION

   Sets the MT packet data waiter and then registers to MT packet data events.

===========================================================================*/
int16 OEMNet_RegisterToMTPD(OEMCONTEXT netd, AEECallback *pcbWaiter)
{
   OEMNet *pNet = (OEMNet*)netd;
   sint15  ndssErr;
   dss_iface_id_type iface_id;
   dss_iface_ioctl_mt_reg_cb_type mtpdp_reg_cb;

   OEMNet_SetMTPDWaiter(pNet, pcbWaiter);

   // if oemnet is already registered for MT PDP, then do nothing ...
   if (pNet->bMTpdpHandleValid) {
      return AEE_NET_SUCCESS;
   }

   // The following cast is needed as the dss_iface_ioctl_event_cb type 
   // changed somewhere in AMSS releases.
   // In older AMSS releases, MT PDP is not supported and this event
   // callback will never be used.
   mtpdp_reg_cb.event_cb      = (dss_iface_ioctl_event_cb)OEMNet_MTPDEventOccurred;
   mtpdp_reg_cb.app_id        = pNet->sAppID;
   mtpdp_reg_cb.handle        = &pNet->mtpdpHandle;
   mtpdp_reg_cb.user_data_ptr = pNet;
   
   if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) != DSS_IFACE_INVALID_ID) {
      int ret = oemdss_iface_ioctl(iface_id,
                                   DSS_IFACE_IOCTL_MT_REG_CB,
                                   &mtpdp_reg_cb,
                                   &ndssErr);
      if (DSS_SUCCESS != ret) {
         return DSSToAEE_Err(ndssErr);
      }
   } else {
      return AEE_NET_ENETNONET; 
   }
   
   pNet->bMTpdpHandleValid = TRUE;
   return AEE_NET_SUCCESS;
}


/*===========================================================================

FUNCTION OEMNet_DeRegisterFromMTPD

DESCRIPTION

   Clears the MT packet data waiter and then de-registers from MT packet data
   events

===========================================================================*/
int16 OEMNet_DeRegisterFromMTPD(OEMCONTEXT netd)
{
   OEMNet *pNet = (OEMNet*)netd;
   sint15  ndssErr;
   dss_iface_id_type iface_id;
   dss_iface_ioctl_mt_dereg_cb_type mtpdp_dereg_cb;

   // remove waiter
   OEMNet_SetMTPDWaiter(pNet, NULL);

   // if oemnet is not registered for MT PDP, then do nothing ...
   if (!pNet->bMTpdpHandleValid) {
      return AEE_NET_SUCCESS;
   }

   mtpdp_dereg_cb.app_id = pNet->sAppID;
   mtpdp_dereg_cb.handle = pNet->mtpdpHandle;
   
   if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) != DSS_IFACE_INVALID_ID) {
      int ret = oemdss_iface_ioctl(iface_id,
                                   DSS_IFACE_IOCTL_MT_DEREG_CB,
                                   &mtpdp_dereg_cb,
                                   &ndssErr);
      if (DSS_SUCCESS != ret) {
         return DSSToAEE_Err(ndssErr);
      }
   } else {
      return AEE_NET_ENETNONET;
   }

   pNet->bMTpdpHandleValid = FALSE;
   pNet->mtpdpHandle = 0; /* set to a known value */

   return AEE_NET_SUCCESS;
}


/*===========================================================================

FUNCTION OEMNet_InstallSocketEventHandler

DESCRIPTION
  Installs a socket event handler that will be called whenever a
  socket event occurs.  The previously installed event handler will be
  returned, and should be called if the event is not consumed.

  An installed socket handler cannot be deinstalled.

===========================================================================*/
PFNNOTIFY OEMNet_InstallSocketEventHandler(OEMCONTEXT netd,
                                           PFNNOTIFY pfnSocketEventHandler)
{
   OEMNet* pNet = (OEMNet*)netd;
   PFNNOTIFY prevInterceptor = pNet->pfnSocketEventHandlerInterceptor;

   pNet->pfnSocketEventHandlerInterceptor = pfnSocketEventHandler;

   if (prevInterceptor == NULL) {
      // if no prev Interceptor, then return a shim for the OEMNet's
      // socket handler callback
      prevInterceptor = &OEMNet_NonInterceptedSocketEventOccurred;
   }
   
   return prevInterceptor;
}


static void OEMNet_MonitorPhysLink(OEMNet* pNet, boolean bRegister)
{
   (void)OEMNet_RegisterIfaceEventCallback(pNet, DSS_IFACE_IOCTL_PHYS_LINK_DOWN_EV, bRegister);
   (void)OEMNet_RegisterIfaceEventCallback(pNet, DSS_IFACE_IOCTL_PHYS_LINK_UP_EV, bRegister);
}

// Call dss_pppopen() for use by both OEMNet_PPPOpen() and OEMNet_ReconcileFailover()
static int16 OEMNet_PPPOpen2
(
   OEMNet* pNet
)
{ 
   int16 ndssErr;
   int16   nRet;

   if (DSS_SUCCESS != dss_pppopen(pNet->sAppID, &ndssErr)) {
      nRet = DSSToAEE_Err(ndssErr);
   } else {
      nRet = AEE_NET_SUCCESS;
   }

   // CR31550: if a non-Brew app is currently closing the network, 
   // some AMSS builds will return "close in progress" error.
   // Brew should wait, and try again after the network has closed.
   if (nRet == AEE_NET_ENETCLOSEINPROGRESS) {
      nRet = AEE_NET_EWOULDBLOCK;
   }
   else if ((nRet == AEE_NET_SUCCESS) || (nRet == AEE_NET_EWOULDBLOCK)) {
      // Tell the system not to open any call related dialogs...
      CALLBACK_Cancel(&pNet->cbReEnableCallDialogs);
      OEM_Notify(OEMNTF_SHOW_CALL_DIALOGS, FALSE);
      
      if (AEE_NET_EWOULDBLOCK == nRet) {
         // fake out state 
         FARF(STATE,("FAKE_PPP_OPENING"));
         pNet->PPPState.nFakeNetState = NET_FAKE_COMING_UP;

         AEE_SVC_RESUME_IF(pNet->pcbNetWaiter);
      }

      // Watch for dormancy
      OEMNet_MonitorPhysLink(pNet, TRUE);
   }

   return nRet;
}

/*===========================================================================

FUNCTION OEMNet_PPPOpen

DESCRIPTION
  Starts up the network subsystem (CDMA data service and PPP) over the Um 
  interface for all sockets.  
  
DEPENDENCIES
  dss_pppopen() cannot be called by the application if the network is in the 
  process of closing. The network layer cannot queue the open request until 
  the close is completely finished.  Therefore, the application should wait 
  for the net_callback_fn() to be called (after dss_pppclose() has 
  completed), before making a call to dss_pppopen().  Note that a valid
  application ID must be specified as a parameter, obtained by a successful
  return of dss_open_netlib().

===========================================================================*/
int16 OEMNet_PPPOpen
(
   OEMCONTEXT netd
)
{ 
   OEMNet* pNet = (OEMNet*)netd;

   // can't call dss_pppopen() while the network is still closing
   if (NET_PPP_CLOSING == OEMNet_PPPState(pNet)) {
      FARF(ALWAYS,("# PPPOpen: PPP still closing - Abort!"));
      return AEE_NET_GENERAL_FAILURE;
   }

#if defined(FAILOVER_3G_TO_2G)
   {
      boolean bAllow;

      // By default, the failover is disabled
      if (SUCCESS != OEM_GetConfig(CFGI_ALLOW_3G_2G_FAILOVER,
                                   (void *)&bAllow,sizeof(bAllow))) {
         bAllow = FALSE;
      }

      if (bAllow) {
         int ret;
         dss_iface_ioctl_707_mdr_type mdrMode;
         sint15 ndssErr;
         dss_iface_id_type iface_id;
      
         if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) != DSS_IFACE_INVALID_ID) {
            ret = oemdss_iface_ioctl(iface_id,
                                  DSS_IFACE_IOCTL_707_GET_MDR,
                                  &mdrMode,
                                  &ndssErr);
         if (DSS_SUCCESS == ret) {
            FARF(FAILOVER_3G_TO_2G,("MDR mode %d",mdrMode));

            if (DS_MDR_MODE_SO33_PREF == mdrMode) {
                  pNet->PPPState.bTrying3G = TRUE;
            }

         } else {
            FARF(FAILOVER_3G_TO_2G,("get MDR mode failed!"));
         }
         } else {
            FARF(FAILOVER_3G_TO_2G,("get iface_id failed!"));
      }
      }
   }
#endif /* #if defined(FAILOVER_3G_TO_2G) */

   return OEMNet_PPPOpen2(pNet);
}

int16 OEMNet_SetPPPAuth(OEMCONTEXT netd,
                        const char *cpszAuth)
{
   return EUNSUPPORTED;
}

int16 OEMNet_GetPPPAuth(OEMCONTEXT netd,
                        char *pszAuth, int *pnLen)
{
   return EUNSUPPORTED;
}

// Data dedicated transmission mode for hybrid HDR (MSM5500/6500)
int16 OEMNet_SetDDTMPref(OEMCONTEXT netd,
                         boolean bOn)
{
#if defined(FEATURE_DDTM_CNTL)
#ifdef SYS_DDTM_MAX_SO_LIST_SIZE
   sys_srv_opt_type so_list[1] = { (sys_srv_opt_type)CAI_SO_NULL };
#endif
   cm_client_status_e_type nStatus;
   cm_client_id_type nClient;
   boolean bRet;

   nStatus = cm_client_init(CM_CLIENT_TYPE_FLOATING3, &nClient);
   if (CM_CLIENT_OK != nStatus) {
      return EFAILED;
   }

   // Don't worry about specifying a callback, because 1) this will make this
   // operation asynchronous, and 2)the cm code has no failure cases
   // specific to this command
   bRet = cm_ph_cmd_ddtm_pref(
			NULL, 
			NULL, 
			nClient,
			bOn ? CM_DDTM_PREF_ON : CM_DDTM_PREF_OFF
#ifdef SYS_DDTM_MAX_SO_LIST_SIZE
         ,SYS_DDTM_ACT_MASK_IGN_SO_PAGES,
	      CM_DDTM_SO_LIST_ACT_DELETE,
        	sizeof(so_list),
         so_list
#endif
			);
   if (FALSE == bRet) {
      (void)cm_client_release(nClient);
      return EFAILED;
   }

   (void)cm_client_release(nClient); // only fails in case of invalid client id
   return SUCCESS;
#else
   return EUNSUPPORTED;
#endif
}


/*===========================================================================

FUNCTION OEMNet_PPPClose

DESCRIPTION
  Initiates termination to bring down PPP and the traffic channel.  Upon
  successful close of the network subsystem, invokes the network callback
  function.
                         
DEPENDENCIES
  None.
===========================================================================*/
int16 OEMNet_PPPClose
(
   OEMCONTEXT netd
)
{
   OEMNet* pNet = (OEMNet*)netd;
   int16 ndssErr;

#if defined(FAILOVER_3G_TO_2G)
   pNet->PPPState.bTrying3G = FALSE;
#endif /* #if defined(FAILOVER_3G_TO_2G) */

   if ((DSS_SUCCESS != dss_pppclose(pNet->sAppID, &ndssErr)) &&
       (DS_ENETNONET != ndssErr)) {

      if (DS_EWOULDBLOCK == ndssErr) {
         // fake out state
         FARF(STATE,("FAKE_PPP_CLOSING"));
         pNet->PPPState.nFakeNetState = NET_FAKE_GOING_DOWN;

         AEE_SVC_RESUME_IF(pNet->pcbNetWaiter);
      }

      return DSSToAEE_Err(ndssErr);
   } 

   // Don't watch for dormancy
   OEMNet_MonitorPhysLink(pNet, FALSE);

   // Tell the system it can open any call related dialogs...
   AEE_SetSvcTimerCallback(3000, &pNet->cbReEnableCallDialogs);

   return AEE_NET_SUCCESS;
}

/*===========================================================================
  called to reconcile NET_PPP_OPEN with physical link state
  ===========================================================================*/
static NetState OEMNet_ReconcilePhysLink(OEMNet* pNet, NetState ns)
{
   dss_iface_id_type iface_id;

   if (NET_PPP_OPEN != ns) {
      return ns;
   }

   // this only fails if the interface is not up for this appid...
   if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) != DSS_IFACE_INVALID_ID) {
      int ret;
      dss_iface_ioctl_phys_link_state_type phys_link_state;
      sint15 ndssErr;
      
      ret = oemdss_iface_ioctl(iface_id,
                               DSS_IFACE_IOCTL_GET_PHYS_LINK_STATE,
                               &phys_link_state,
                               &ndssErr);
      if (DSS_SUCCESS == ret) {
         switch (phys_link_state) {
            case PHYS_LINK_UP:
               if (NET_FAKE_GOING_DOWN == pNet->PPPState.nFakePhysLinkState) {
                  /* have called force dormancy, haven't been called back */
                  FARF(STATE,("Faking sleeping"));
                  ns = NET_PPP_SLEEPING;
               }
               break;
            case PHYS_LINK_DOWN:
               if (NET_FAKE_COMING_UP == pNet->PPPState.nFakePhysLinkState) {
                  /* have called wakeup, haven't been called back */
                  FARF(STATE,("Faking waking"));
                  ns = NET_PPP_WAKING;
               } else {
                  ns = NET_PPP_ASLEEP;
               }
               break;
            case PHYS_LINK_COMING_UP:
               ns = NET_PPP_WAKING;
               break;
            case PHYS_LINK_GOING_DOWN:
               ns = NET_PPP_SLEEPING;
               break;
         }
      }
   }

   return ns;
}

static NetState OEMNet_NetStatus(OEMNet* pNet)
{
   int16    nstat;
   NetState ns;

   dss_netstatus(pNet->sAppID,&nstat);

   switch (nstat) {
      case DS_ENETNONET:
         ns = NET_PPP_CLOSED;
         break;

      case DS_ENETISCONN:
         ns = NET_PPP_OPEN;
         break;

      case DS_ENETINPROGRESS:
         ns = NET_PPP_OPENING;
         break;

      case DS_ENETCLOSEINPROGRESS:
         ns = NET_PPP_CLOSING;
         break;

      default:
         ns = NET_INVALID_STATE;
         break;
   }

   FARF(STATE,("dmss state %s",
               ns==NET_PPP_CLOSED?"closed":
               ns==NET_PPP_OPEN?"open":
               ns==NET_PPP_OPENING?"opening":
               ns==NET_PPP_CLOSING?"closing":"invalid"));
               

   if (NET_PPP_CLOSED == ns && 
       NET_FAKE_COMING_UP == pNet->PPPState.nFakeNetState) {
      FARF(STATE,("Faking opening"));
      /* We know we're opening even if the DS layer reports "CLOSED"
         for a while after dss_pppopen(). */
      ns = NET_PPP_OPENING;
   } else if (NET_PPP_OPEN == ns && 
              NET_FAKE_GOING_DOWN == pNet->PPPState.nFakeNetState) {
      FARF(STATE,("Faking closing"));
      /* We know we're closing even if the DS layer reports "OPEN"
         for a while after dss_pppclose(). */
      ns = NET_PPP_CLOSING;
   } else {
      // we can probably trust the chipset's state now
      FARF(STATE,("FakeNetState: NONE"));
      pNet->PPPState.nFakeNetState = NET_FAKE_NONE;
   }

   return ns;
}

/* ====================================================================
   fakes out state if we're doing app-level failover, returns TRUE
   if you should re-call netstatus, returns FALSE otherwise
   
   *pns might get changed
   ==================================================================== */
static NetState OEMNet_ReconcileFailover(OEMNet* pNet, NetState ns)
{
#if !defined(FAILOVER_3G_TO_2G)
   return ns;
#else /* #if !defined(FAILOVER_3G_TO_2G) */

   if (pNet->PPPState.bTrying3G) {

      /* closing, set to opening, wait until we're all the way closed... */
      if (ns == NET_PPP_CLOSING) {
         FARF(FAILOVER_3G_TO_2G,("3g try failing"));

         return NET_PPP_OPENING;
      }

      if (ns == NET_PPP_CLOSED) {
         dss_iface_id_type iface_id;
         int ret;
         dss_iface_ioctl_707_mdr_type mdrMode = DS_MDR_MODE_NO_MDR;
         sint15 ndssErr;
         
         FARF(FAILOVER_3G_TO_2G,("3g try failed"));
         /* closed, set to opening, retry open 
            after setting MDR mode to 2G... */
         
         pNet->PPPState.bTrying3G = FALSE;         
      
         if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) != DSS_IFACE_INVALID_ID) {
            ret = oemdss_iface_ioctl(iface_id,
                                  DSS_IFACE_IOCTL_707_SET_MDR,
                                  &mdrMode,
                                  &ndssErr);
         if (DSS_SUCCESS == ret) {
            /* retry, ignore return value.... */
            FARF(FAILOVER_3G_TO_2G,("retry 2g..."));

               pNet->PPPState.bRestore3G = TRUE;
            
            // Just do the bottom half of OEMNet_PPPOpen()
               OEMNet_PPPOpen2(pNet);

            /* re-derive netstate */
               return OEMNet_NetStatus(pNet);

         } else {
            FARF(FAILOVER_3G_TO_2G,("set MDR mode failed!"));
         }
         } else {
            FARF(FAILOVER_3G_TO_2G,("get iface_id failed!"));
         }
      }
   }

   if (ns == NET_PPP_OPEN) {
      /* open, not trying anymore */
      pNet->PPPState.bTrying3G = FALSE;
   }

   if (pNet->PPPState.bRestore3G && 
       ((ns == NET_PPP_OPEN) || ns == NET_PPP_CLOSED)) {
      dss_iface_id_type iface_id;
      int ret;
      dss_iface_ioctl_707_mdr_type mdrMode = DS_MDR_MODE_SO33_PREF;
      sint15 ndssErr;
      
      /* We've finally got the 'net up, and need to restore the MDR mode.
         This API call doesn't affect the current data call */

      if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) != DSS_IFACE_INVALID_ID) {
         ret = oemdss_iface_ioctl(iface_id,
                               DSS_IFACE_IOCTL_707_SET_MDR,
                               &mdrMode,
                               &ndssErr);
      if (DSS_SUCCESS == ret) {
         FARF(FAILOVER_3G_TO_2G,("restored 3g..."));
            pNet->PPPState.bRestore3G = FALSE;
         } else {
            FARF(FAILOVER_3G_TO_2G,("set MDR mode failed!"));
         }
      } else {
         FARF(FAILOVER_3G_TO_2G,("get iface_id failed!"));
      }
   }

   return ns;
#endif /* #if !defined(FAILOVER_3G_TO_2G) */
}


/*===========================================================================

FUNCTION OEMNet_PPPState

DESCRIPTION
  Returns the state of the PPP connection.
  
DEPENDENCIES

===========================================================================*/
NetState OEMNet_PPPState
(
   OEMCONTEXT netd
)
{
   OEMNet* pNet = (OEMNet*)netd;
   NetState  ns;

   ns = OEMNet_NetStatus(pNet);

   /* reconcile state with failover state */
   ns = OEMNet_ReconcileFailover(pNet, ns);
   
   /* see if open really means one of the dormancy states */
   ns = OEMNet_ReconcilePhysLink(pNet, ns);

   FARF(STATE,("oemnet state %s",
               ns==NET_PPP_CLOSED?"closed":
               ns==NET_PPP_OPEN?"open":
               ns==NET_PPP_OPENING?"opening":
               ns==NET_PPP_SLEEPING?"sleeping":
               ns==NET_PPP_WAKING?"waking":
               ns==NET_PPP_ASLEEP?"asleep":
               ns==NET_PPP_OPENING?"opening":
               ns==NET_PPP_CLOSING?"closing":"invalid"));

   return  ns;
}

/*===========================================================================

FUNCTION OEMNet_MyIPAddr

DESCRIPTION

  Returns the IP address of the active session.

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
int16 OEMNet_MyIPAddr
(
   OEMCONTEXT  netd,
   INAddr     *addr                 /* IP address: network byte order */
)
{
   OEMNet* pNet = (OEMNet*)netd;
   int ret;
   sint15 ndssErr;
   dss_iface_id_type iface_id;
   ip_addr_type ip_addr;

   // this only fails if the interface is not up for this appid...
   if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) == DSS_IFACE_INVALID_ID) {
      return AEE_NET_ENETNONET;
   }
      
      ret = oemdss_iface_ioctl(iface_id,
                               DSS_IFACE_IOCTL_GET_IPV4_ADDR,
                               &ip_addr,
                               &ndssErr);
      if (DSS_SUCCESS != ret) {
         return DSSToAEE_Err(ndssErr);
      }
   
   if (!IP_ADDR_IS_VALID(ip_addr)) {
      return AEE_NET_ENETNONET;
   }
   
   *addr = IP_ADDR_V4(ip_addr);
   return AEE_NET_SUCCESS;
}


/*===========================================================================

FUNCTION OEMNet_GetUrgent

DESCRIPTION

  Discovers status of urgent (short data burst) support

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS

===========================================================================*/
int16 OEMNet_GetUrgent
(
   OEMCONTEXT netd,
   AEEUDPUrgent *pUrg
) 
{
#if defined(SDB_SUPPORTED)
   OEMNet* pNet = (OEMNet*)netd;
   dss_iface_id_type iface_id;
   int ret;
   sint15 ndssErr;
   dss_iface_ioctl_707_sdb_support_query_type query = {0,0};

   if (!pUrg) {
      return EBADPARM;
   }
 
   // needs to match SDB sendto flags
   query.flags = MSG_FAST_EXPEDITE;

   // this only fails if the interface is not up for this appid...
   if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) == DSS_IFACE_INVALID_ID) {
      return AEE_NET_ENETNONET;
   }

   ret = oemdss_iface_ioctl(iface_id,
                            DSS_IFACE_IOCTL_707_SDB_SUPPORT_QUERY,
                            &query,
                            &ndssErr);
   if (DSS_SUCCESS != ret) {
      return DSSToAEE_Err(ndssErr);
   }

   pUrg->bUrgentSupported = query.can_do_sdb;

// DBM limit 255 - IPv4 header size 20 - UDP header size 8
// Note that this protocol-defined limit is merely theoretical. The
// actual limit is imposed by the physical layer in terms of acceptable
// data rates and access probe duration, and by the signaling layer's
// need to add other headers to the data burst message.
#define MAX_UDP_VIA_SDB 222

   pUrg->nUrgentLimit = pUrg->bUrgentSupported ? MAX_UDP_VIA_SDB : 0;
   return AEE_NET_SUCCESS;
#endif // defined(SDB_SUPPORTED)
   return EUNSUPPORTED;
}

/*===========================================================================

FUNCTION OEMNet_GetNativeDescriptor

DESCRIPTION
  Returns the underlying network descriptor

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.

===========================================================================*/
int16 OEMNet_GetNativeDescriptor
(
   OEMCONTEXT netd,
   uint32               *pdwDesc
) 
{
   OEMNet* pNet = (OEMNet*)netd;

   if ((uint32*)0 == pdwDesc) {
      return AEE_NET_EINVAL;
   }

   *pdwDesc = pNet->sAppID;

   return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION OEMNet_GetDefaultNetwork

DESCRIPTION
   Returns the default network (AEE_NETWORK_*)

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.

===========================================================================*/
int16 OEMNet_GetDefaultNetwork
(
   int *pnNetwork
) 
{
   oemdss_iface_ioctl_all_ifaces_type iface_info;
   sint15 ndssErr;
   int ret;
   uint16 i;

   if ((int*)0 == pnNetwork) {
      return AEE_NET_EINVAL;
   }

   ret = oemdss_iface_ioctl(0,
                            DSS_IFACE_IOCTL_GET_ALL_IFACES,
                            &iface_info,
                            &ndssErr);
   if (DSS_SUCCESS != ret) {
      return DSSToAEE_Err(ndssErr);
   }

   // Look for an ENABLED interface that we know about
   for (i = 0; i < iface_info.number_of_ifaces; ++i) {
      switch ((iface_info.ifaces[i]>>16)&0xffff) {
         case DSS_IFACE_CDMA_SN:
         {
            *pnNetwork = AEE_NETWORK_CDMA_SN;
            return AEE_NET_SUCCESS;
         }
#if defined(FEATURE_DATA_UMTS)
         case DSS_IFACE_UMTS:
         {
            byte profile_number = 1; // default default
            byte configured_default;
            ds_umts_pdp_profile_status_etype status;

            // See if there is a configured_default configured in the PDP database
            status = ds_umts_get_pdp_profile_num_for_sock_calls(&configured_default);
            if (DS_UMTS_PDP_SUCCESS == status) {
               profile_number = configured_default;
            }
            *pnNetwork = AEE_NETWORK_UMTS(profile_number);
            return AEE_NET_SUCCESS;
         }
#endif // defined(FEATURE_DATA_UMTS)
         default:
            continue;
      }
   }

   return AEE_NET_ENETNONET;
}

/*===========================================================================

FUNCTION OEMNet_GetUMTSCount

DESCRIPTION
   Returns the number of PDP contexts.

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.

===========================================================================*/
int16 OEMNet_GetUMTSCount
(
   uint16    *pwCount
) 
{
#if defined(FEATURE_DATA_UMTS)

   if ((uint16*)0 == pwCount) {
      return AEE_NET_EINVAL;
   }

   *pwCount = DS_UMTS_MAX_PDP_PROFILE_NUM;

   return AEE_NET_SUCCESS;
#endif // defined(FEATURE_DATA_UMTS)
   return EUNSUPPORTED;
}

/*===========================================================================

FUNCTION OEMNet_GetUMTSInfo

DESCRIPTION
  Returns the requested PDP context information.

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.

===========================================================================*/
int16 OEMNet_GetUMTSInfo
(
   uint16       wNumber,
   AEEUMTSInfo *pInfo
)
{
#if defined(FEATURE_DATA_UMTS)
   ds_umts_pdp_profile_status_etype status;
   byte profile_number;
   char profile_name[DS_UMTS_MAX_PROFILE_NAME_LEN+1];

   if ((AEEUMTSInfo*)0 == pInfo) {
      return AEE_NET_EINVAL;
   }
   profile_number = (byte)wNumber;

   status = ds_umts_get_pdp_profile_context_info_is_valid(profile_number, &pInfo->bValid);
   if (DS_UMTS_PDP_SUCCESS != status) {
      return AEE_NET_ERROR;
   }
    
   status = ds_umts_get_pdp_profile_name(profile_number, (byte *)profile_name, sizeof(profile_name));
   if (DS_UMTS_PDP_SUCCESS != status) {
      return AEE_NET_ERROR;
   }

   STRTOWSTR(profile_name, pInfo->szName, sizeof(pInfo->szName));
   pInfo->szName[ARRAY_SIZE(pInfo->szName)-1] = 0;

   return AEE_NET_SUCCESS;
#endif // defined(FEATURE_DATA_UMTS)
   return EUNSUPPORTED;
}

/*===========================================================================

FUNCTION OEMNet_SetDormancyTimeout

DESCRIPTION
  Set the IS-707 (cdma2000 1x) packet data dormancy timeout value 
  (idle timeout after which mobile should enter dormancy)

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.

===========================================================================*/
int16 OEMNet_SetDormancyTimeout
(
   OEMCONTEXT netd,
   uint8      ucSecs
)
{
   OEMNet*           pNet = (OEMNet*)netd;
   uint32            dssSecs = ucSecs;
   int16             ndssErr;
   int               ret;

   // dormancy timeout is only valid for the CDMA Service Network
   if (TRUE != pNet->bCDMA_SN) {
      return EUNSUPPORTED;
   }

   ret = oemdss_iface_ioctl(DSS_IFACE_GET_ID3(DSS_IFACE_CDMA_SN,0,0),
                            DSS_IFACE_IOCTL_707_SET_DORM_TIMER,
                            &dssSecs,
                            &ndssErr);

   if (DSS_SUCCESS != ret) {
      return DSSToAEE_Err(ndssErr);
   }

   return AEE_NET_SUCCESS;
}


int16 OEMNet_NameServers
(
   OEMCONTEXT netd,
  INAddr *ainaAddrs,
  int    *pnNumAddrs
)
{
   OEMNet* pNet = (OEMNet*)netd;
   INAddr aina[4]; /* 4's the max for me! */
   int    i = 0;
   INAddr ina = 0;

   /* special internal testing value, means use IP1 as the only one! */
   OEM_GetConfig(CFGI_DNS_IP2, (void*) &ina, sizeof(ina));

   if ((INAddr)0xffeeeeff == ina) {
      /* return IP1 unconditionally */
      ina = 0;
      OEM_GetConfig(CFGI_DNS_IP1, (void*) &ina, sizeof(ina));
      aina[i++] = ina;
   } else {
#if defined(IPCP_DNS_SUPPORTED)
      int ret;
      sint15 ndssErr;
      dss_iface_id_type iface_id;

      // this only fails if the interface is not up for this appid...
      if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) != DSS_IFACE_INVALID_ID) {
         ip_addr_type ip_addr;

         ret = oemdss_iface_ioctl(iface_id,
                                  DSS_IFACE_IOCTL_GET_IPV4_PRIM_DNS_ADDR,
                                  &ip_addr,
                                  &ndssErr);
         if ((DSS_SUCCESS == ret) &&
             IP_ADDR_IS_VALID(ip_addr)) {
            aina[i++] = IP_ADDR_V4(ip_addr);
         }

         ret = oemdss_iface_ioctl(iface_id,
                                  DSS_IFACE_IOCTL_GET_IPV4_SECO_DNS_ADDR,
                                  &ip_addr,
                                  &ndssErr);
         if ((DSS_SUCCESS == ret) &&
             IP_ADDR_IS_VALID(ip_addr)) {
            aina[i++] = IP_ADDR_V4(ip_addr);
         }
      }
#endif /* #if defined(IPCP_DNS_SUPPORTED) */
      ina = 0;
      OEM_GetConfig(CFGI_DNS_IP1, (void*) &ina, sizeof(ina));
      if ((0 != ina) && ((INAddr)-1 != ina)) {
         aina[i++] = ina;
      }

      ina = 0;
      OEM_GetConfig(CFGI_DNS_IP2, (void*) &ina, sizeof(ina));
      if ((0 != ina) && ((INAddr)-1 != ina)) {
         aina[i++] = ina;
      }
   }

   if ((INAddr *)0 != ainaAddrs) {
      i = MIN(*pnNumAddrs, i);
      MEMMOVE(ainaAddrs,aina,i*sizeof(INAddr));
   }
   *pnNumAddrs = i;

   return SUCCESS;
}

#if defined(RLP3_CFG_SUPPORTED)
static AEERLP3Cfg *DSSIFaceIOCtlToAEE_RLP3Cfg(const dss_iface_ioctl_707_rlp_opt_type *pfrom,
                                              AEERLP3Cfg *pto)
{
   int i;

   for (i = 0; i < ARRAY_SIZE(pto->aucFwdNaksPerRound); i++) {
      pto->aucFwdNaksPerRound[i] = pfrom->naks_per_round_fwd[i];
      pto->aucRevNaksPerRound[i] = pfrom->naks_per_round_rev[i];
   }
   pto->ucFwdNakRounds = pfrom->nak_rounds_fwd;
   pto->ucRevNakRounds = pfrom->nak_rounds_rev;

   return pto;
}

static dss_iface_ioctl_707_rlp_opt_type *AEEToDSSIFaceIOCtl_RLP3Cfg(const AEERLP3Cfg *pfrom,
                                                              dss_iface_ioctl_707_rlp_opt_type *pto)
{
   int i;

   pto->rscb_index = DSRLP_FIXED_SR_ID_INDEX;
   for (i = 0; i < ARRAY_SIZE(pto->naks_per_round_fwd); i++) {
      pto->naks_per_round_fwd[i] = pfrom->aucFwdNaksPerRound[i];
      pto->naks_per_round_rev[i] = pfrom->aucRevNaksPerRound[i];
   }
   pto->nak_rounds_fwd = pfrom->ucFwdNakRounds;
   pto->nak_rounds_rev = pfrom->ucRevNakRounds;

   return pto;
}

static dss_iface_ioctl_type AEEToDSSIFaceIOCtl_IOCtlName(int16 nOptName, boolean bGet)
{
   switch (nOptName) {
      case INET_OPT_DEF_RLP3:
         return bGet ? DSS_IFACE_IOCTL_707_GET_RLP_ALL_DEF_NAK : DSS_IFACE_IOCTL_707_SET_RLP_ALL_DEF_NAK;
      case INET_OPT_CUR_RLP3:
         return bGet ? DSS_IFACE_IOCTL_707_GET_RLP_ALL_CURR_NAK : DSS_IFACE_IOCTL_707_SET_RLP_ALL_CURR_NAK;
      case INET_OPT_NEG_RLP3:
         return bGet ? DSS_IFACE_IOCTL_707_GET_RLP_ALL_NEG_NAK : DSS_IFACE_IOCTL_MIN;
      default:
         return DSS_IFACE_IOCTL_MIN; /* bad val */
   }
}
#endif /* #if defined(RLP3_CFG_SUPPORTED) */


int16 OEMNet_GetRLP3Cfg
(
   OEMCONTEXT  netd,
  int16 nOptName,
  AEERLP3Cfg *prlp3
) 
{
#if defined(RLP3_CFG_SUPPORTED)
   OEMNet* pNet = (OEMNet*)netd;
   int ret;
   dss_iface_ioctl_type ioctl_name;
   dss_iface_ioctl_707_rlp_opt_type optval;
   sint15 ndssErr;
      
   // RLP config is only valid for the CDMA Service Network
   if (TRUE != pNet->bCDMA_SN) {
      return EUNSUPPORTED;
   }

   ZEROAT(&optval);
   optval.rscb_index = DSRLP_FIXED_SR_ID_INDEX;

   ioctl_name = AEEToDSSIFaceIOCtl_IOCtlName(nOptName, TRUE);
   if (DSS_IFACE_IOCTL_MIN == ioctl_name) {
      return AEE_NET_EINVAL;
   }

   ret = oemdss_iface_ioctl(DSS_IFACE_GET_ID3(DSS_IFACE_CDMA_SN,0,0),
                            ioctl_name,
                            &optval,
                            &ndssErr);
   if (DSS_SUCCESS != ret) {
      return DSSToAEE_Err(ndssErr);
   }

   DSSIFaceIOCtlToAEE_RLP3Cfg(&optval,prlp3);
   return DSS_SUCCESS;
#endif /* if defined(RLP3_CFG_SUPPORTED) */
   return EUNSUPPORTED;
}

int16 OEMNet_SetRLP3Cfg
(
   OEMCONTEXT        netd,
  int16 nOptName,
  const AEERLP3Cfg *prlp3
)
{
#if defined(RLP3_CFG_SUPPORTED)
   OEMNet* pNet = (OEMNet*)netd;
   int ret;
   dss_iface_ioctl_type ioctl_name;
   dss_iface_ioctl_707_rlp_opt_type optval;
   sint15 ndssErr;
      
   // RLP config is only valid for the CDMA Service Network
   if (TRUE != pNet->bCDMA_SN) {
      return EUNSUPPORTED;
   }

   AEEToDSSIFaceIOCtl_RLP3Cfg(prlp3,&optval);

   ioctl_name = AEEToDSSIFaceIOCtl_IOCtlName(nOptName, FALSE);
   if (DSS_IFACE_IOCTL_MIN == ioctl_name) {
      return AEE_NET_EINVAL;
   }

   ret = oemdss_iface_ioctl(DSS_IFACE_GET_ID3(DSS_IFACE_CDMA_SN,0,0),
                            ioctl_name,
                            &optval,
                            &ndssErr);
   if (DSS_SUCCESS != ret) {
      return DSSToAEE_Err(ndssErr);
   }

   return DSS_SUCCESS;
#endif /* if defined(RLP3_CFG_SUPPORTED) */
   return EUNSUPPORTED;
}


 /*===========================================================================

                      OEM SOCKET FUNCTION DEFINITIONS

 ===========================================================================*/


/*===========================================================================

FUNCTION OEMSocket_New

DESCRIPTION
 Allocate a socket related data structure and remember it

===========================================================================*/
static OEMSocket* OEMSocket_New(OEMNet* pNet)
{
   OEMSocket* me;

   me = sys_malloc(sizeof(OEMSocket));
   if ((OEMSocket*)0 == me) {
      return (OEMSocket*)0;
   }

   // insert at front of linked list
   me->pNext = pNet->pSockets;
   pNet->pSockets = me;

   me->pNet = pNet;

   return me;
}

/*===========================================================================

FUNCTION OEMSocket_Delete

DESCRIPTION
 Forget and free a socket related data structure

===========================================================================*/
static void OEMSocket_Delete(OEMSocket* me)
{
   OEMNet* pNet = me->pNet;
   OEMSocket* ps;
   OEMSocket* pPrev;

   // remove from linked list
   pPrev = (OEMSocket*)0;
   for (ps = pNet->pSockets; ps; ps = ps->pNext) {

      if (me == ps) {
         if ((OEMSocket*)0 == pPrev) {
            pNet->pSockets = ps->pNext;
         } else {
            pPrev->pNext = ps->pNext;
         }
         break;
      }

      pPrev = ps;
   }
   
   sys_free(ps);
}

/*===========================================================================

Function:  Set_sockaddr

Description:

Prototype:

Parameters:

Return Value:
   
Comments:  None

Side Effects: None

===========================================================================*/
static void Set_sockaddr( struct sockaddr_in *sockaddr,
                          INAddr             addr,
                          INPort             port )
{
   sockaddr->sin_family       = AF_INET;
   sockaddr->sin_port         = port;
   sockaddr->sin_addr.s_addr  = addr;
}

/*===========================================================================
  called by socket I/O methods
  ===========================================================================*/
static __inline void OEMSocket_FakeWaking(OEMSocket* me, int16 ndssErr)
{
   NetState ns;
   
   // If the I/O operation did not block, we are most likely already up
   if (DS_EWOULDBLOCK != ndssErr) {
      return;
   }

   // Are we waking, either real or fake?
   ns = OEMNet_PPPState(me->pNet);
   switch (ns) {
      case NET_PPP_WAKING:      // real waking
         break;

      case NET_PPP_ASLEEP:      // fake waking
         FARF(STATE,("FAKE_PPP_WAKING"));
         if (TRUE != me->bLoopback) {
            me->pNet->PPPState.nFakePhysLinkState = NET_FAKE_COMING_UP;
         }
         break;

      default:                  // not waking
         return;
   }

   AEE_SVC_RESUME_IF(me->pNet->pcbNetWaiter);
}

#if defined(DSS_FAKE_CLOSE)
// The short term timer has expired, so fake
// a close event to get around the DMSS "not
// always sending close events" bug
static void OEMSocket_FakeClose(void *pv)
{
   OEMSocket* me = (OEMSocket*)pv;
   OEMNet* pNet = me->pNet;

   me->bFakeCloseEvent = TRUE;

   if (OEMNet_IsSocketInterceptorInstalled(pNet)) {
      OEMNet_InterceptedSocketEventOccurred(pNet);
   } else {
      OEMNet_NonInterceptedSocketEventOccurred(pNet);
   }
}
#endif // defined(DSS_FAKE_CLOSE)

/*===========================================================================

FUNCTION OEMSocket_Open

DESCRIPTION
 Create a socket and related data structures, and return a socket descriptor.

===========================================================================*/
OEMCONTEXT OEMSocket_Open( OEMCONTEXT  netd,
                           NetSocket   type,
                           int16      *err)
{
   OEMNet* pNet = (OEMNet*)netd;
   int16 sockfd;
   OEMSocket* me;
   int16 ndssErr;
   byte dmss_type;
   byte dmss_proto;

   switch (type) {
      case AEE_SOCK_STREAM:
         dmss_type = SOCK_STREAM;
         dmss_proto = IPPROTO_TCP;
         break;
      case AEE_SOCK_DGRAM:  
         dmss_type = SOCK_DGRAM;
         dmss_proto = IPPROTO_UDP;
         break;
      default:
         *err = AEE_NET_EOPNOTSUPP;
         return (OEMCONTEXT)AEE_NET_ERROR;
   }

   me = OEMSocket_New(pNet);
   if ((OEMSocket*)0 == me) {
      *err = AEE_NET_EMFILE;
      return (OEMCONTEXT)AEE_NET_ERROR;
   }

   sockfd = dss_socket(pNet->sAppID, AF_INET, dmss_type, dmss_proto, &ndssErr);
   if (DSS_ERROR == sockfd) {
      OEMSocket_Delete(me);
      *err = DSSToAEE_Err(ndssErr);
      return (OEMCONTEXT)AEE_NET_ERROR;
   } else {
      *err = AEE_NET_SUCCESS;
   }

   me->sockfd = sockfd;
   if (AEE_SOCK_STREAM == type) {
      me->bStream = TRUE;
   }

#if defined(DSS_FAKE_CLOSE)
   CALLBACK_Init(&me->cbFakeClose, OEMSocket_FakeClose, me);
#endif // defined(DSS_FAKE_CLOSE)

   return (OEMCONTEXT)me;
}

/*===========================================================================g

FUNCTION OEMSocket_Connect

DESCRIPTION
  For TCP, attempts to establish the TCP connection.  Upon
  successful connection, calls the socket callback function asserting that
  the DS_WRITE_EVENT is TRUE.  The function must receive
  (as a parameter) a valid socket descriptor, implying a previous successful
  call to dss_socket().


===========================================================================*/
int16 OEMSocket_Connect
(
   OEMCONTEXT            sockd,        /* Socket descriptor */
   INAddr                addr,         /* destination address */
   INPort                port          /* destination port */
)
{
   OEMSocket*          me = (OEMSocket*)sockd;
   int16               ndssErr;
   struct sockaddr_in  sockaddr;                     /* destination address */

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   if (HTONL(AEE_INADDR_LOOPBACK) == addr) {
#if defined(OEM_PROVIDES_LOOPBACK)
      me->bLoopback = TRUE;
#else
      return AEE_NET_EOPNOTSUPP;
#endif
   }

   Set_sockaddr( &sockaddr, addr, port );

   if (DSS_SUCCESS != 
       dss_connect(me->sockfd, (struct sockaddr *) &sockaddr, 
                   sizeof(sockaddr), &ndssErr)) {
      OEMSocket_FakeWaking(me, ndssErr);
      return DSSToAEE_Err(ndssErr);
   }
   
   return AEE_NET_SUCCESS;
}


/*===========================================================================

FUNCTION OEMSocket_Bind

DESCRIPTION
  For all client sockets, attaches a local address and port value to the 
  socket.  If the call is not explicitly issued, the socket will implicitly
  bind in calls to dss_connect() or dss_sendto().  Note that this function
  does not support binding a local IP address, but rather ONLY a local port
  number.  The local IP address is assigned automatically by the sockets 
  library.  The function must receive (as a parameter) a valid socket 
  descriptor, implying a previous successful call to dss_socket().

===========================================================================*/
int16 OEMSocket_Bind
(
   OEMCONTEXT            sockd,            /* socket descriptor */
   INAddr                addr,             /* IP addr: network byte order */
   INPort                port              /* port number: network byte order */
)
{
   OEMSocket*          me = (OEMSocket*)sockd;
   int16               ndssErr;
   struct sockaddr_in  localaddr;                     /* destination address */

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   // These bugs are fixed in newer versions of DMSS
   // that support listen and accept...
#if !defined(OEM_PROVIDES_SERVER_SOCKETS)
   // we don't support bind with TCP sockets,
   // dsssocket crashes if we try to bind(), then close()
   if (me->bStream) {
      return AEE_NET_EOPNOTSUPP;
   }

   // wildcard port not supported
   if (0 == port) {
      return AEE_NET_EOPNOTSUPP;
   }
#endif // !defined(OEM_PROVIDES_SERVER_SOCKETS)

   Set_sockaddr( &localaddr, addr, port );
   
   if (DSS_SUCCESS != 
       dss_bind( me->sockfd, (struct sockaddr *) &localaddr, 
                 sizeof(localaddr), &ndssErr)) {
      return DSSToAEE_Err(ndssErr);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION OEMSocket_Close

DESCRIPTION
  Non-blocking close of a socket.  Performs all necessary clean-up of data 
  structures and frees the socket for re-use.  For TCP initiates the active 
  close for connection termination.  Once TCP has closed, the DS_CLOSE_EVENT 
  will become TRUE, and the application can call dss_close() again to free 
  the socket for re-use.  UDP sockets also need to call this to 
  clean-up the socket and free it for re-use.
                         
===========================================================================*/
int16 OEMSocket_Close
(
   OEMCONTEXT        sockd                /* socket descriptor */
)
{
   OEMSocket* me = (OEMSocket*)sockd;
   int16  ndssErr;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   if (DSS_SUCCESS != dss_close(me->sockfd, &ndssErr)) {
      int16 err = DSSToAEE_Err(ndssErr);

#if defined(DSS_FAKE_CLOSE)
      // 1/25/02 - DMSS Issue - Need to set a short-term timer here if
      //  the close did not complete.  If we dont we may/will have cases
      // where sockets sit out there "forever" without us being notified
      // that they moved to closed.
      if (AEE_NET_EWOULDBLOCK == err) {
         AEE_SetSvcTimerCallback(500, &me->cbFakeClose);
      }
#endif // defined(DSS_FAKE_CLOSE)

      return err;
   }

#if defined(DSS_FAKE_CLOSE)
   // If bFakeCloseEvent is set, then the timer has fired but the fake close
   // event has not yet been generated, so just unset bFakeCloseEvent.
   // Else if the timer is registered but not yet fired, cancel the timer.
   if (me->bFakeCloseEvent) {
      me->bFakeCloseEvent = FALSE;
   } else if (CALLBACK_IsQueued(&me->cbFakeClose)) {
      CALLBACK_Cancel(&me->cbFakeClose);
   }
#endif // defined(DSS_FAKE_CLOSE)

   OEMSocket_Delete(me);

   return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION OEMSocket_AsyncSelect

DESCRIPTION
  Enables the events to be notified about through the asynchronous 
  notification mechanism.  Application specifies a bitmask of events that it
  is interested in, for which it will receive asynchronous notification via
  its application callback function.  This function also performs a real-time
  check to determine if any of the events have already occurred, and if so
  invokes the application callback.

===========================================================================*/
int16 OEMSocket_AsyncSelect
(
   OEMCONTEXT            sockd,              /* socket descriptor */
   int32                 interest_mask       /* bitmask of events to set */
) {
   OEMSocket* me = (OEMSocket*)sockd;
   int16      ndssErr;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   // mutate READ into ACCEPT for listening sockets
   if (me->bListen &&
       interest_mask & DS_READ_EVENT) {
      interest_mask &= ~DS_READ_EVENT;
      interest_mask |= DS_ACCEPT_EVENT;
   }

   if (DSS_SUCCESS != 
       dss_async_select( me->sockfd, interest_mask, &ndssErr)) {
      return DSSToAEE_Err(ndssErr);
   }
   
   return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION OEMSocket_GetNextEvent

DESCRIPTION
  This function performs a real-time check to determine if any of the events 
  of interest specified in the socket control block's event mask have 
  occurred.  It also clears any bits in the event mask that have occurred.
  The application must re-enable these events through a subsequent call to 
  dss_async_select().  The application passes in a pointer to a single 
  socket descriptor to determine if any events have occurred for that socket.

 ===========================================================================*/
int32 OEMSocket_GetNextEvent
( 
   OEMCONTEXT            *psockd,                /* socket descriptor */
   int16                 *err
) {
   OEMSocket  *ps;
   int16       ndssErr = DSS_SUCCESS;
   int32       nRet = 0;

   ps = (OEMSocket *)*psockd;

   if ( ((OEMSocket *)AEE_NET_ERROR == ps) || (NULL == ps) ) {
      return AEE_NET_EBADF;
   }

   nRet = dss_getnextevent(ps->pNet->sAppID, &ps->sockfd, &ndssErr);
      
   if (nRet != DSS_ERROR) {

      // mutate ACCEPT into READ for listening sockets
      if (ps->bListen && nRet & DS_ACCEPT_EVENT) {
         nRet &= ~DS_ACCEPT_EVENT;
         nRet |= DS_READ_EVENT;
      }
      
#if defined(DSS_FAKE_CLOSE)
      // fake a close event?
      if (ps->bFakeCloseEvent) {
         ps->bFakeCloseEvent = FALSE;
         nRet |= DS_CLOSE_EVENT;
      }
#endif // defined(DSS_FAKE_CLOSE)
   }
   
   *err = DSSToAEE_Err(ndssErr);
   return nRet;
}

/*===========================================================================

FUNCTION OEMSocket_Write

DESCRIPTION
  Sends specified number of bytes in the buffer over the TCP transport.

===========================================================================*/
int32 OEMSocket_Write
(
   OEMCONTEXT         sockd,      /* socket descriptor */
   const byte        *buffer,     /* user buffer from which to copy data */
   uint32             nbytes,     /* number of bytes to be written to socket */
   int16             *err         /* error condition value */
) {
   OEMSocket* me = (OEMSocket*)sockd;
   int16      Ret;
   int16      ndssErr = DSS_SUCCESS;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      *err = AEE_NET_EBADF;
      return AEE_NET_ERROR;
   }

   // are we shutdown?
   if ((AEE_SHUTDOWN_WR+1) & me->nShutdown) {
      *err = AEE_NET_ESHUTDOWN;
      return AEE_NET_ERROR;
   }
      
   Ret = dss_write(me->sockfd, (byte*)buffer, (uint16)MIN(MAX_UINT16,nbytes), &ndssErr);
   
   /* dss returns DSS_SUCCESS on EOF!! luckily, ndssErr is set to EEOF */
   if (Ret == 0 && nbytes != 0) { 
      Ret = AEE_NET_ERROR;
   }

   OEMSocket_FakeWaking(me, ndssErr);
   *err = DSSToAEE_Err(ndssErr);
   return Ret;
}

/*===========================================================================

FUNCTION OEMSocket_Writev

DESCRIPTION
  Provides the gather write variant of the dss_write() call, which 
  allows the application to write from non-contiguous buffers.    Sends 
  specified number of bytes in the buffer over the TCP transport.

===========================================================================*/
int32 OEMSocket_Writev
(
   OEMCONTEXT            sockd,          /* socket descriptor */
   const SockIOBlock     iov[],          /* array of data buffers from which to copy data */
   uint16                iovcount,       /* number of array items */
   int16                 *err
) {
   OEMSocket* me = (OEMSocket*)sockd;
   int16      Ret;
   int16      ndssErr = DSS_SUCCESS;
 
   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      *err = AEE_NET_EBADF;
      return AEE_NET_ERROR;
   }

   // are we shutdown?
   if ((AEE_SHUTDOWN_WR+1) & me->nShutdown) {
      *err = AEE_NET_ESHUTDOWN;
      return AEE_NET_ERROR;
   }

   {
      uint16 i;
      
      for (i = 0; i < iovcount; i++) {
         if (iov[i].wLen != 0) {
            break;
         }
      }

      Ret = dss_writev(me->sockfd, (struct iovec*)iov, iovcount, &ndssErr);

      /* dss returns DSS_SUCCESS on EOF!! luckily, ndssErr is set to EEOF */
      if (Ret == 0 && i != iovcount) { 
         Ret = AEE_NET_ERROR;
      }
   }

   OEMSocket_FakeWaking(me, ndssErr);
   *err = DSSToAEE_Err(ndssErr);
   return Ret;
}

/*===========================================================================

FUNCTION OEMSocket_Read

DESCRIPTION
  Reads specified number of bytes into buffer from the TCP transport.

===========================================================================*/
int32 OEMSocket_Read
(
   OEMCONTEXT            sockd,      /* socket descriptor */
   byte                 *buffer,     /* user buffer to which to copy data */
   uint32                nbytes,     /* number of bytes to be read from socket */
   int16                 *err
) {
   OEMSocket* me = (OEMSocket*)sockd;
   int16      ndssErr = DSS_SUCCESS;
   int32      Ret;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      *err = AEE_NET_EBADF;
      return AEE_NET_ERROR;
   }

   // are we shutdown?
   if ((AEE_SHUTDOWN_RD+1) & me->nShutdown) {
      *err = AEE_NET_ESHUTDOWN;
      return AEE_NET_ERROR;
   }

   Ret = dss_read(me->sockfd, buffer, (uint16)MIN(MAX_UINT16,nbytes), &ndssErr);

   *err = DSSToAEE_Err(ndssErr);
   return Ret; 
}

/*===========================================================================

FUNCTION OEMSocket_Readv

DESCRIPTION
  Provides the scatter read variant of the dss_read() call, which 
  allows the application to read into non-contiguous buffers.    Reads
  specified number of bytes into the buffer from the TCP transport.

===========================================================================*/
int32 OEMSocket_Readv
(
   OEMCONTEXT            sockd,     /* socket descriptor */
   SockIOBlock           iov[],     /* array of data buffers to copy data into */
   uint16                iovcount,  /* number of array items */
   int16                 *err
) {
   OEMSocket* me = (OEMSocket*)sockd;
   int32      Ret;
   int16      ndssErr = DSS_SUCCESS;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      *err = AEE_NET_EBADF;
      return AEE_NET_ERROR;
   }

   // are we shutdown?
   if ((AEE_SHUTDOWN_RD+1) & me->nShutdown) {
      *err = AEE_NET_ESHUTDOWN;
      return AEE_NET_ERROR;
   }

   Ret = dss_readv(me->sockfd, (struct iovec*)iov, iovcount, &ndssErr);

   *err = DSSToAEE_Err(ndssErr);
   return Ret;
}

/*===========================================================================

FUNCTION OEMSocket_SendTo

DESCRIPTION
  Sends 'nbytes' bytes in the buffer over the UDP transport.
===========================================================================*/
int32 OEMSocket_SendTo
(
   OEMCONTEXT            sockd,     /* socket descriptor */
   const byte           *buffer,    /* user buffer from which to copy the data */
   uint32                nbytes,    /* number of bytes to be written */
   uint32                flags,     /* data transport options */
   INAddr                addr,      /* IP address: network byte order */
   INPort                port,      /* port number: network byte order */
   int16                 *err
) {
   OEMSocket* me = (OEMSocket*)sockd;
   struct sockaddr_in toaddr;                          /* destination address */
   int32 Ret;
   int16 ndssErr = DSS_SUCCESS;
   uint32 ds_flags = 0;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      *err = AEE_NET_EBADF;
      return AEE_NET_ERROR;
   }

   if (HTONL(AEE_INADDR_LOOPBACK) == addr) {
#if defined(OEM_PROVIDES_LOOPBACK)
      me->bLoopback = TRUE;
#else
      *err = AEE_NET_EOPNOTSUPP;
      return AEE_NET_ERROR;
#endif
   }

   if (flags & ISOCKET_FLAG_SENDTO_URGENT) {
      // Request use of short data burst over enhanced access channel
      ds_flags |= MSG_FAST_EXPEDITE;
      flags &= ~ISOCKET_FLAG_SENDTO_URGENT;
   }

   // Should have translated all flags across so anything left is unsupported
   if (flags) {
      *err = AEE_NET_EOPNOTSUPP;
      return AEE_NET_ERROR;
   }

   Set_sockaddr( &toaddr, addr, port );
   Ret = dss_sendto(me->sockfd, (byte*)buffer, (uint16)MIN(MAX_UINT16,nbytes), ds_flags, 
                    (struct sockaddr*)&toaddr, sizeof(toaddr), &ndssErr);

   OEMSocket_FakeWaking(me, ndssErr);
   *err = DSSToAEE_Err(ndssErr);
   return Ret;
}

/*===========================================================================

FUNCTION OEMSocket_RecvFrom

DESCRIPTION
  Reads 'nbytes' bytes in the buffer from the UDP transport.  Fills in
  address structure with values from who sent the data.
===========================================================================*/
int32 OEMSocket_RecvFrom
(
   OEMCONTEXT        sockd,                         /* socket descriptor */
   byte             *buffer,  /* user buffer from which to copy the data */
   uint32            nbytes,            /* number of bytes to be written */
   uint32            flags,             /* unused */
   INAddr           *addr,             /* IP address: network byte order */
   INPort           *port,            /* port number: network byte order */
   int16            *err
) {
   OEMSocket* me = (OEMSocket*)sockd;
   struct sockaddr_in fromaddr;
   uint16 fromaddr_size;
   int32 Ret;
   int16 ndssErr = DSS_SUCCESS;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      *err = AEE_NET_EBADF;
      return AEE_NET_ERROR;
   }

   Set_sockaddr( &fromaddr, *addr, *port );
   fromaddr_size = sizeof( fromaddr );
   Ret = dss_recvfrom(me->sockfd, buffer, (uint16)MIN(MAX_UINT16,nbytes), flags, 
                      (struct sockaddr*)&fromaddr, &fromaddr_size, &ndssErr);
   *addr = fromaddr.sin_addr.s_addr;
   *port = fromaddr.sin_port;

   *err = DSSToAEE_Err(ndssErr);
   return Ret;
}

/*===========================================================================

FUNCTION OEMSocket_GetSockName

DESCRIPTION
  Returns the local IP and port of a socket.

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.

===========================================================================*/
int16 OEMSocket_GetSockName
(
   OEMCONTEXT            sockd,                       /* Socket descriptor */
   INAddr               *addr,           /* IP address: network byte order */
   INPort               *port           /* port number: network byte order */
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   struct sockaddr_in sa;
   uint16 salen = sizeof(sa);
   int16 ndssErr;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }
      
   if (DSS_SUCCESS != 
       dss_getsockname(me->sockfd,(struct sockaddr *)&sa,
                       &salen,&ndssErr)) {
      return DSSToAEE_Err(ndssErr);
   }

   *port = sa.sin_port;
   *addr = sa.sin_addr.s_addr;

   return AEE_NET_SUCCESS;   
}

/*===========================================================================

FUNCTION OEMSocket_GetPeerName

DESCRIPTION
  Returns the IP and port of a connected peer.

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.

===========================================================================*/
int16 OEMSocket_GetPeerName
(
   OEMCONTEXT            sockd,                       /* Socket descriptor */
   INAddr               *addr,           /* IP address: network byte order */
   INPort               *port           /* port number: network byte order */
) {
   OEMSocket* me = (OEMSocket*)sockd;
   struct sockaddr_in sa;
   uint16 salen = sizeof(sa);
   int16 ndssErr;
      
   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   if (DSS_SUCCESS != 
       dss_getpeername(me->sockfd,(struct sockaddr *)&sa,
                       &salen,&ndssErr)) {
      return DSSToAEE_Err(ndssErr);
   }
   
   *port = sa.sin_port;
   *addr = sa.sin_addr.s_addr;

   return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION OEMSocket_Listen

DESCRIPTION
  Places a socket in listening mode.

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.

===========================================================================*/
int16 OEMSocket_Listen
(
   OEMCONTEXT            sockd,                       /* Socket descriptor */
   int16                 backlog
) {
   OEMSocket* me = (OEMSocket*)sockd;
   int16 ndssErr;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   // CR20046: DMSS preallocates OEM sockets for the entire
   // backlog, so a backlog of 3 will consume 4 OEM sockets for
   // each listening socket, easily resulting in EMFILE for other
   // sockets/apps.  Silently cap the backlog to 1 to minimize this
   // problem.  Given the slower nature of wireless networks, a
   // large backlog is not really all that useful anyhow...
   if (1 < backlog) {
      backlog = 1;
   }

   if (DSS_SUCCESS != 
       dss_listen( me->sockfd, backlog, &ndssErr)) {
      return DSSToAEE_Err(ndssErr);
   }
      
   me->bListen = TRUE;
   return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION OEMSocket_Accept

DESCRIPTION
  Returns a newly connected socket.

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.
 
===========================================================================*/
OEMCONTEXT OEMSocket_Accept
(
   OEMCONTEXT            sockd,                /* Listen Socket descriptor */
   int16                *err
) {
   OEMSocket* me = (OEMSocket*)sockd;
   int16 sockfd;
   struct sockaddr remoteaddr;
   uint16 addrlen = sizeof(remoteaddr);
   int16 ndssErr;
   OEMSocket* ps;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      *err = AEE_NET_EBADF;
      return (OEMCONTEXT)AEE_NET_ERROR;
   }

   ps = OEMSocket_New(me->pNet);
   if ((OEMSocket*)0 == ps) {
      *err = AEE_NET_EMFILE;
      return (OEMCONTEXT)AEE_NET_ERROR;
   }

   sockfd = dss_accept(me->sockfd, &remoteaddr, &addrlen, &ndssErr);
   if (DSS_ERROR == sockfd) {
      OEMSocket_Delete(ps);
      *err = DSSToAEE_Err(ndssErr);
      return (OEMCONTEXT)AEE_NET_ERROR;
   }
      
   ps->sockfd = sockfd;

   return ps;
}

/*===========================================================================

FUNCTION OEMSocket_Shutdown

DESCRIPTION
  Performs a half-close on a socket.

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.

===========================================================================*/
int16 OEMSocket_Shutdown
(
   OEMCONTEXT            sockd,                       /* Socket descriptor */
   int32                 how
) {
   OEMSocket* me = (OEMSocket*)sockd;
   int16 ndssErr;
   uint16 ndssHow;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   // map AEE_SHUT_* to DSS_SHUT_*
   switch (how) {
      case AEE_SHUTDOWN_RD:
         if (me->nShutdown & (AEE_SHUTDOWN_RD+1)) {
            return AEE_NET_ESHUTDOWN;
         }
         ndssHow = DSS_SHUT_RD;
         break;
      case AEE_SHUTDOWN_WR:
         if (me->nShutdown & (AEE_SHUTDOWN_WR+1)) {
            return AEE_NET_ESHUTDOWN;
         }
         ndssHow = DSS_SHUT_WR;
         break;
      case AEE_SHUTDOWN_RDWR:
         if (me->nShutdown & ((AEE_SHUTDOWN_RD+1)|(AEE_SHUTDOWN_WR+1))) {
            return AEE_NET_ESHUTDOWN;
         }
         ndssHow = DSS_SHUT_RDWR;
         break;
      default:
         return AEE_NET_EINVAL;
   }

   if (DSS_SUCCESS != 
       dss_shutdown( me->sockfd, (uint16)ndssHow, &ndssErr)) {
      return DSSToAEE_Err(ndssErr);
   }
      
   // OK, now we are shutdown
   me->nShutdown |= (how+1);

   return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION OEMSocket_GetNativeDescriptor

DESCRIPTION
  Returns the underlying socket descriptor

DEPENDENCIES
  None.

RETURN VALUE

SIDE EFFECTS
  None.

===========================================================================*/
int16 OEMSocket_GetNativeDescriptor(
  OEMCONTEXT            sockd,         /* socket descriptor */
  uint32               *pdwDesc
) {
   OEMSocket* me = (OEMSocket*)sockd;

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   if ((uint32*)0 == pdwDesc) {
      return AEE_NET_EINVAL;
   }

   *pdwDesc = me->sockfd;

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_SetKeepAlive

DESCRIPTION
  Sets the underlying socket keepalive setting
===========================================================================*/
int16 OEMSocket_SetKeepAlive(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean               bKeepAlive
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   int nFlag = bKeepAlive ? 1 : 0;
   uint32 len = sizeof(nFlag); 
   
   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_setsockopt(me->sockfd,
                        DSS_SOL_SOCKET,
                        DSS_SO_KEEPALIVE,
                        &nFlag,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_SetLinger

DESCRIPTION
  Sets the underlying socket linger setting
===========================================================================*/
int16 OEMSocket_SetLinger(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean               bOn,          // linger on or off
  uint32                dwLinger      // linger time, in seconds
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   dss_so_linger_type dss_so_linger;
   uint32 len = sizeof(dss_so_linger);

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   dss_so_linger.l_onoff = (int)bOn;
   dss_so_linger.l_linger = 1000*dwLinger; // milliseconds

   ret = dss_setsockopt(me->sockfd,
                        DSS_SOL_SOCKET,
                        DSS_SO_LINGER,
                        &dss_so_linger,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_SetRcvBuf

DESCRIPTION
  Sets the underlying socket receive buffer size
===========================================================================*/
int16 OEMSocket_SetRcvBuf(
  OEMCONTEXT            sockd,         /* socket descriptor */
  int                   nRcvBuf
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   uint32 len = sizeof(nRcvBuf);

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   // check for valid range of receive buffer
   if ( nRcvBuf < DSS_MIN_RCVBUF)
   {
      DBGPRINTF("OEMSocket_SetRcvBuf change buffer size from %d to %d", 
                nRcvBuf, DSS_MIN_RCVBUF);
      nRcvBuf = DSS_MIN_RCVBUF;
   }
   else if ( nRcvBuf > DSS_MAX_RCVBUF)
   {
      DBGPRINTF("OEMSocket_SetRcvBuf change buffer size from %d to %d", 
                nRcvBuf, DSS_MAX_RCVBUF);
         
      nRcvBuf = DSS_MAX_RCVBUF;
   }

   ret = dss_setsockopt(me->sockfd,
                        DSS_SOL_SOCKET,
                        DSS_SO_RCVBUF,
                        &nRcvBuf,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_SetSndBuf

DESCRIPTION
  Sets the underlying socket send buffer size
===========================================================================*/
int16 OEMSocket_SetSndBuf(
  OEMCONTEXT            sockd,         /* socket descriptor */
  int                   nSndBuf
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   uint32 len = sizeof(nSndBuf);

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   // check for valid range of send buffer
   if ( nSndBuf < DSS_MIN_SNDBUF)
   {
      DBGPRINTF("OEMSocket_SetSndBuf change buffer size from %d to %d", 
                nSndBuf, DSS_MIN_SNDBUF);
      nSndBuf = DSS_MIN_SNDBUF;
   }
   else if ( nSndBuf > DSS_MAX_SNDBUF)
   {
      DBGPRINTF("OEMSocket_SetSndBuf change buffer size from %d to %d", 
                nSndBuf, DSS_MAX_SNDBUF);
         
      nSndBuf = DSS_MAX_SNDBUF;
   }

   ret = dss_setsockopt(me->sockfd,
                        DSS_SOL_SOCKET,
                        DSS_SO_SNDBUF,
                        &nSndBuf,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_SetNoDelay

DESCRIPTION
  Sets the underlying socket Nagle delay setting
===========================================================================*/
int16 OEMSocket_SetNoDelay(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean               bNoDelay
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   int nFlag = bNoDelay ? 1 : 0;
   uint32 len = sizeof(nFlag); 

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_setsockopt(me->sockfd,
                        DSS_IPPROTO_TCP,
                        DSS_TCP_NODELAY,
                        &nFlag,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_SetDelayedAck

DESCRIPTION
  Sets the underlying socket Delayed Ack setting
===========================================================================*/
int16 OEMSocket_SetDelayedAck(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean               bDelayedAck
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   int nFlag = bDelayedAck ? 1 : 0;
   uint32 len = sizeof(nFlag); 

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_setsockopt(me->sockfd,
                        DSS_IPPROTO_TCP,
                        DSS_TCP_DELAYED_ACK,
                        &nFlag,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_SetSACK

DESCRIPTION
  Sets the underlying socket SACK setting
===========================================================================*/
int16 OEMSocket_SetSACK(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean               bSACK
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   int nFlag = bSACK ? 1 : 0;
   uint32 len = sizeof(nFlag); 

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_setsockopt(me->sockfd,
                        DSS_IPPROTO_TCP,
                        DSS_TCP_SACK,
                        &nFlag,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_SetTimeStamp

DESCRIPTION
  Sets the underlying socket TCP time stamp setting
===========================================================================*/
int16 OEMSocket_SetTimeStamp(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean               bTimeStamp
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   int nFlag = bTimeStamp ? 1 : 0;
   uint32 len = sizeof(nFlag); 

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_setsockopt(me->sockfd,
                        DSS_IPPROTO_TCP,
                        DSS_TCP_TIME_STAMP,
                        &nFlag,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_SetMembership

DESCRIPTION
  Sets the underlying socket membership setting
===========================================================================*/
int16 OEMSocket_SetMembership(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean               bAdd,          // Add or Drop membership
  INAddr                nMCast         // multicast group to join/leave
) 
{
#ifdef FEATURE_DATA_BCMCS
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   struct in_addr mcast_addr;
   uint32 len = sizeof(mcast_addr);

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   mcast_addr.s_addr = nMCast;

   ret = dss_setsockopt(me->sockfd,
                        DSS_IPPROTO_IP,
                        bAdd ? DSS_BCMCS_JOIN : DSS_BCMCS_LEAVE,
                        &mcast_addr,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
#endif // def FEATURE_DATA_BCMCS
   return EUNSUPPORTED;
}

/*===========================================================================
FUNCTION: OEMSocket_GetKeepAlive

DESCRIPTION
  Returns the underlying socket keepalive setting
===========================================================================*/
int16 OEMSocket_GetKeepAlive(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean              *pbKeepAlive
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   int nFlag;
   uint32 len = sizeof(nFlag); 
      
   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_getsockopt(me->sockfd,
                        DSS_SOL_SOCKET,
                        DSS_SO_KEEPALIVE,
                        &nFlag,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   *pbKeepAlive = nFlag ? TRUE : FALSE;
   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_GetLinger

DESCRIPTION
  Returns the underlying socket linger setting
===========================================================================*/
int16 OEMSocket_GetLinger(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean              *pbOn,          // linger on or off
  uint32               *pdwLinger      // linger time, in seconds
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   dss_so_linger_type dss_so_linger;
   uint32 len = sizeof(dss_so_linger);

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_getsockopt(me->sockfd,
                        DSS_SOL_SOCKET,
                        DSS_SO_LINGER,
                        &dss_so_linger,
                        &len,
                        &dss_errno);

   *pbOn = (boolean)dss_so_linger.l_onoff;
   *pdwLinger = dss_so_linger.l_linger/1000; // seconds
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }


   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_GetRcvBuf

DESCRIPTION
  Returns the underlying socket receive buffer size
===========================================================================*/
int16 OEMSocket_GetRcvBuf(
  OEMCONTEXT            sockd,         /* socket descriptor */
  int                  *pnRcvBuf
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   uint32 len = sizeof(*pnRcvBuf);

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_getsockopt(me->sockfd,
                        DSS_SOL_SOCKET,
                        DSS_SO_RCVBUF,
                        pnRcvBuf,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_GetSndBuf

DESCRIPTION
  Returns the underlying socket send buffer size
===========================================================================*/
int16 OEMSocket_GetSndBuf(
  OEMCONTEXT            sockd,         /* socket descriptor */
  int                  *pnSndBuf
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   uint32 len = sizeof(*pnSndBuf);

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_getsockopt(me->sockfd,
                        DSS_SOL_SOCKET,
                        DSS_SO_SNDBUF,
                        pnSndBuf,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_GetNoDelay

DESCRIPTION
  Returns the underlying socket Nagle delay setting
===========================================================================*/
int16 OEMSocket_GetNoDelay(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean              *pbNoDelay
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   int nFlag;
   uint32 len = sizeof(nFlag); 

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_getsockopt(me->sockfd,
                        DSS_IPPROTO_TCP,
                        DSS_TCP_NODELAY,
                        &nFlag,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   *pbNoDelay = nFlag ? TRUE : FALSE;
   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_GetDelayedAck

DESCRIPTION
  Returns the underlying socket Delayed Ack setting
===========================================================================*/
int16 OEMSocket_GetDelayedAck(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean              *pbDelayedAck
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   int nFlag;
   uint32 len = sizeof(nFlag); 

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_getsockopt(me->sockfd,
                        DSS_IPPROTO_TCP,
                        DSS_TCP_DELAYED_ACK,
                        &nFlag,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   *pbDelayedAck = nFlag ? TRUE : FALSE;
   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_GetSACK

DESCRIPTION
  Returns the underlying socket SACK setting
===========================================================================*/
int16 OEMSocket_GetSACK(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean              *pbSACK
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   int nFlag;
   uint32 len = sizeof(nFlag); 

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_getsockopt(me->sockfd,
                        DSS_IPPROTO_TCP,
                        DSS_TCP_SACK,
                        &nFlag,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   *pbSACK = nFlag ? TRUE : FALSE;
   return AEE_NET_SUCCESS;
}

/*===========================================================================
FUNCTION: OEMSocket_GetTimeStamp

DESCRIPTION
  Returns the underlying socket TCP time stamp setting
===========================================================================*/
int16 OEMSocket_GetTimeStamp(
  OEMCONTEXT            sockd,         /* socket descriptor */
  boolean              *pbTimeStamp
) 
{
   OEMSocket* me = (OEMSocket*)sockd;
   sint15 ret;
   sint15 dss_errno;
   int nFlag;
   uint32 len = sizeof(nFlag); 

   if ((OEMSocket*)AEE_NET_ERROR == me ||
       (OEMSocket*)0 == me) {
      return AEE_NET_EBADF;
   }

   ret = dss_getsockopt(me->sockfd,
                        DSS_IPPROTO_TCP,
                        DSS_TCP_TIME_STAMP,
                        &nFlag,
                        &len,
                        &dss_errno);
   if (DSS_ERROR == ret) {
      return DSSToAEE_Err(dss_errno);
   }

   *pbTimeStamp = nFlag ? TRUE : FALSE;
   return AEE_NET_SUCCESS;
}

/*===========================================================================

                      OEM NET QOS FUNCTION DEFINITIONS

 ===========================================================================*/

#ifdef OEM_PROVIDES_QOS

/*===========================================================================

FUNCTION TranslateAMSSFlowToOEM

DESCRIPTION
  Translate AMSS Flow spec into an OEM Flow spec
  
===========================================================================*/
uint16 TranslateAMSSFlowToOEM(ip_flow_type *pAMSSFlow, oem_ip_flow_type *pOemFlow)
{
    pOemFlow->field_mask = 0;
    pOemFlow->err_mask = pAMSSFlow->err_mask;
    
    if (pAMSSFlow->field_mask & IPFLOW_MASK_TRF_CLASS) {
       switch (pAMSSFlow->trf_class)
       {
       case IP_TRF_CLASS_CONVERSATIONAL:
          pOemFlow->trf_class = OEM_IP_TRF_CLASS_CONVERSATIONAL;
          break;

       case IP_TRF_CLASS_STREAMING:
          pOemFlow->trf_class = OEM_IP_TRF_CLASS_STREAMING;
          break;

       case IP_TRF_CLASS_INTERACTIVE:
          pOemFlow->trf_class = OEM_IP_TRF_CLASS_INTERACTIVE;
          break;

       case IP_TRF_CLASS_BACKGROUND:
          pOemFlow->trf_class = OEM_IP_TRF_CLASS_BACKGROUND;
          break;

       default:
          return AEE_NET_EBADOPTVAL;
       }

       pOemFlow->field_mask |= OEM_IPFLOW_MASK_TRF_CLASS;
    }
    
    if (pAMSSFlow->field_mask & IPFLOW_MASK_DATA_RATE) {

       switch (pAMSSFlow->data_rate.format_type)
       {
          case DATA_RATE_FORMAT_MIN_MAX_TYPE:
             pOemFlow->data_rate.format_type = OEM_DATA_RATE_FORMAT_MIN_MAX_TYPE;

             if (sizeof(pOemFlow->data_rate.format.min_max) 
                 != sizeof(pAMSSFlow->data_rate.format.min_max)) {
                return AEE_NET_EBADOPTLEN;
             }

             memcpy(&pOemFlow->data_rate.format.min_max, 
                    &pAMSSFlow->data_rate.format.min_max, 
                    sizeof(pAMSSFlow->data_rate.format.min_max));
             break;

          case DATA_RATE_FORMAT_TOKEN_BUCKET_TYPE:
             pOemFlow->data_rate.format_type = OEM_DATA_RATE_FORMAT_TOKEN_BUCKET_TYPE;

             if (sizeof(pOemFlow->data_rate.format.token_bucket) 
                 != sizeof(pAMSSFlow->data_rate.format.token_bucket)) {
                return AEE_NET_EBADOPTLEN;
             }

             memcpy(&pOemFlow->data_rate.format.token_bucket, 
                    &pAMSSFlow->data_rate.format.token_bucket, 
                    sizeof(pAMSSFlow->data_rate.format.token_bucket));
             break;

          default:
             return AEE_NET_EBADOPTVAL;
       }

       pOemFlow->field_mask |= OEM_IPFLOW_MASK_DATA_RATE;
    }

    if (pAMSSFlow->field_mask & IPFLOW_MASK_LATENCY) {

       pOemFlow->latency = pAMSSFlow->latency;
       pOemFlow->field_mask |= OEM_IPFLOW_MASK_LATENCY;
    }

    if (pAMSSFlow->field_mask & IPFLOW_MASK_LATENCY_VAR) {
       pOemFlow->latency_var = pAMSSFlow->latency_var;
       pOemFlow->field_mask |= OEM_IPFLOW_MASK_LATENCY_VAR;
    }

    if (pAMSSFlow->field_mask & IPFLOW_MASK_PKT_ERR_RATE) {

       if (sizeof(pOemFlow->pkt_err_rate) 
           != sizeof(pAMSSFlow->pkt_err_rate)) {
          return AEE_NET_EBADOPTLEN;
       }

       memcpy(&pOemFlow->pkt_err_rate, 
              &pAMSSFlow->pkt_err_rate, 
              sizeof(pAMSSFlow->pkt_err_rate));

       pOemFlow->field_mask |= OEM_IPFLOW_MASK_PKT_ERR_RATE;
    }

    if (pAMSSFlow->field_mask & IPFLOW_MASK_MIN_POLICED_PKT_SIZE) {
       pOemFlow->min_policed_pkt_size = pAMSSFlow->min_policed_pkt_size;
       pOemFlow->field_mask |= OEM_IPFLOW_MASK_MIN_POLICED_PKT_SIZE;
    }

    if (pAMSSFlow->field_mask & IPFLOW_MASK_MAX_ALLOWED_PKT_SIZE) {
       pOemFlow->max_allowed_pkt_size = pAMSSFlow->max_allowed_pkt_size;
       pOemFlow->field_mask |= OEM_IPFLOW_MASK_MAX_ALLOWED_PKT_SIZE;
    }

    if (pAMSSFlow->field_mask & IPFLOW_MASK_UMTS_RES_BER) {
       switch (pAMSSFlow->umts_params.res_ber)
       {
       case UMTS_RES_BIT_ERR_RATE1:
          pOemFlow->umts_params.res_ber = OEM_UMTS_RES_BIT_ERR_RATE1;
          break;

       case UMTS_RES_BIT_ERR_RATE2:
          pOemFlow->umts_params.res_ber = OEM_UMTS_RES_BIT_ERR_RATE2;
          break;

       case UMTS_RES_BIT_ERR_RATE3:
          pOemFlow->umts_params.res_ber = OEM_UMTS_RES_BIT_ERR_RATE3;
          break;

       case UMTS_RES_BIT_ERR_RATE4:
          pOemFlow->umts_params.res_ber = OEM_UMTS_RES_BIT_ERR_RATE4;
          break;

       case UMTS_RES_BIT_ERR_RATE5:
          pOemFlow->umts_params.res_ber = OEM_UMTS_RES_BIT_ERR_RATE5;
          break;

       case UMTS_RES_BIT_ERR_RATE6:
          pOemFlow->umts_params.res_ber = OEM_UMTS_RES_BIT_ERR_RATE6;
          break;

       case UMTS_RES_BIT_ERR_RATE7:
          pOemFlow->umts_params.res_ber = OEM_UMTS_RES_BIT_ERR_RATE7;
          break;

       case UMTS_RES_BIT_ERR_RATE8:
          pOemFlow->umts_params.res_ber = OEM_UMTS_RES_BIT_ERR_RATE8;
          break;

       case UMTS_RES_BIT_ERR_RATE9:
          pOemFlow->umts_params.res_ber = OEM_UMTS_RES_BIT_ERR_RATE9;
          break;

       case UMTS_RES_BIT_ERR_RATE10:
          pOemFlow->umts_params.res_ber = OEM_UMTS_RES_BIT_ERR_RATE10;
          break;

       default:
          return AEE_NET_EBADOPTVAL;
       }

       pOemFlow->field_mask |= OEM_IPFLOW_MASK_UMTS_RES_BER;
    }

   if (pAMSSFlow->field_mask & IPFLOW_MASK_UMTS_TRF_PRI) {
       switch (pAMSSFlow->umts_params.trf_pri)
       {
       case UMTS_TRF_HANDLING_PRI1:
          pOemFlow->umts_params.trf_pri = OEM_UMTS_TRF_HANDLING_PRI1;
          break;

       case UMTS_TRF_HANDLING_PRI2:
          pOemFlow->umts_params.trf_pri = OEM_UMTS_TRF_HANDLING_PRI2;
          break;

       case UMTS_TRF_HANDLING_PRI3:
          pOemFlow->umts_params.trf_pri = OEM_UMTS_TRF_HANDLING_PRI3;
          break;

       default:
          return AEE_NET_EBADOPTVAL;
       }

       pOemFlow->field_mask |= OEM_IPFLOW_MASK_UMTS_TRF_PRI;
    }

    if (pAMSSFlow->field_mask & IPFLOW_MASK_CDMA_PROFILE_ID) {
       pOemFlow->cdma_params.profile_id = pAMSSFlow->cdma_params.profile_id;
       pOemFlow->field_mask |= OEM_IPFLOW_MASK_CDMA_PROFILE_ID;
    }

    return SUCCESS;
}

/*===========================================================================

FUNCTION TranslateOEMFlowToAMSS

DESCRIPTION
  Translate OEM Flow spec into an AMSS Flow spec
                         
===========================================================================*/
uint16 TranslateOEMFlowToAMSS(oem_ip_flow_type *pOemFlow, ip_flow_type *pAMSSFlow)
{
    if (pOemFlow->field_mask & OEM_IPFLOW_MASK_TRF_CLASS) {
       switch (pOemFlow->trf_class)
       {
       case OEM_IP_TRF_CLASS_CONVERSATIONAL:
          pAMSSFlow->trf_class = IP_TRF_CLASS_CONVERSATIONAL;
          break;

       case OEM_IP_TRF_CLASS_STREAMING:
          pAMSSFlow->trf_class = IP_TRF_CLASS_STREAMING;
          break;

       case OEM_IP_TRF_CLASS_INTERACTIVE:
          pAMSSFlow->trf_class = IP_TRF_CLASS_INTERACTIVE;
          break;

       case OEM_IP_TRF_CLASS_BACKGROUND:
          pAMSSFlow->trf_class = IP_TRF_CLASS_BACKGROUND;
          break;

       default:
          return AEE_NET_EBADOPTVAL;
       }

       pAMSSFlow->field_mask |= IPFLOW_MASK_TRF_CLASS;
    }
    
    if (pOemFlow->field_mask & OEM_IPFLOW_MASK_DATA_RATE) {

       switch (pOemFlow->data_rate.format_type)
       {
          case OEM_DATA_RATE_FORMAT_MIN_MAX_TYPE:
             pAMSSFlow->data_rate.format_type = DATA_RATE_FORMAT_MIN_MAX_TYPE;

             if (sizeof(pAMSSFlow->data_rate.format.min_max) 
                 != sizeof(pOemFlow->data_rate.format.min_max)) {
                return AEE_NET_EBADOPTLEN;
             }

             memcpy(&pAMSSFlow->data_rate.format.min_max, 
                    &pOemFlow->data_rate.format.min_max, 
                    sizeof(pOemFlow->data_rate.format.min_max));
             break;

          case OEM_DATA_RATE_FORMAT_TOKEN_BUCKET_TYPE:
             pAMSSFlow->data_rate.format_type = DATA_RATE_FORMAT_TOKEN_BUCKET_TYPE;

             if (sizeof(pAMSSFlow->data_rate.format.token_bucket) 
                 != sizeof(pOemFlow->data_rate.format.token_bucket)) {
                return AEE_NET_EBADOPTLEN;
             }

             memcpy(&pAMSSFlow->data_rate.format.token_bucket, 
                    &pOemFlow->data_rate.format.token_bucket, 
                    sizeof(pOemFlow->data_rate.format.token_bucket));
             break;

          default:
             return AEE_NET_EBADOPTVAL;
       }

       pAMSSFlow->field_mask |= IPFLOW_MASK_DATA_RATE;
    }

    if (pOemFlow->field_mask & OEM_IPFLOW_MASK_LATENCY) {

       pAMSSFlow->latency = pOemFlow->latency;
       pAMSSFlow->field_mask |= IPFLOW_MASK_LATENCY;
    }

    if (pOemFlow->field_mask & OEM_IPFLOW_MASK_LATENCY_VAR) {
       pAMSSFlow->latency_var = pOemFlow->latency_var;
       pAMSSFlow->field_mask |= IPFLOW_MASK_LATENCY_VAR;
    }

    if (pOemFlow->field_mask & OEM_IPFLOW_MASK_PKT_ERR_RATE) {

       if (sizeof(pAMSSFlow->pkt_err_rate) 
           != sizeof(pOemFlow->pkt_err_rate)) {
          return AEE_NET_EBADOPTLEN;
       }

       memcpy(&pAMSSFlow->pkt_err_rate, 
              &pOemFlow->pkt_err_rate, 
              sizeof(pOemFlow->pkt_err_rate));

       pAMSSFlow->field_mask |= IPFLOW_MASK_PKT_ERR_RATE;
    }

    if (pOemFlow->field_mask & OEM_IPFLOW_MASK_MIN_POLICED_PKT_SIZE) {
       pAMSSFlow->min_policed_pkt_size = pOemFlow->min_policed_pkt_size;
       pAMSSFlow->field_mask |= IPFLOW_MASK_MIN_POLICED_PKT_SIZE;
    }

    if (pOemFlow->field_mask & OEM_IPFLOW_MASK_MAX_ALLOWED_PKT_SIZE) {
       pAMSSFlow->max_allowed_pkt_size = pOemFlow->max_allowed_pkt_size;
       pAMSSFlow->field_mask |= IPFLOW_MASK_MAX_ALLOWED_PKT_SIZE;
    }

    if (pOemFlow->field_mask & OEM_IPFLOW_MASK_UMTS_RES_BER) {
       switch (pOemFlow->umts_params.res_ber)
       {
       case OEM_UMTS_RES_BIT_ERR_RATE1:
          pAMSSFlow->umts_params.res_ber = UMTS_RES_BIT_ERR_RATE1;
          break;

       case OEM_UMTS_RES_BIT_ERR_RATE2:
          pAMSSFlow->umts_params.res_ber = UMTS_RES_BIT_ERR_RATE2;
          break;

       case OEM_UMTS_RES_BIT_ERR_RATE3:
          pAMSSFlow->umts_params.res_ber = UMTS_RES_BIT_ERR_RATE3;
          break;

       case OEM_UMTS_RES_BIT_ERR_RATE4:
          pAMSSFlow->umts_params.res_ber = UMTS_RES_BIT_ERR_RATE4;
          break;

       case OEM_UMTS_RES_BIT_ERR_RATE5:
          pAMSSFlow->umts_params.res_ber = UMTS_RES_BIT_ERR_RATE5;
          break;

       case OEM_UMTS_RES_BIT_ERR_RATE6:
          pAMSSFlow->umts_params.res_ber = UMTS_RES_BIT_ERR_RATE6;
          break;

       case OEM_UMTS_RES_BIT_ERR_RATE7:
          pAMSSFlow->umts_params.res_ber = UMTS_RES_BIT_ERR_RATE7;
          break;

       case OEM_UMTS_RES_BIT_ERR_RATE8:
          pAMSSFlow->umts_params.res_ber = UMTS_RES_BIT_ERR_RATE8;
          break;

       case OEM_UMTS_RES_BIT_ERR_RATE9:
          pAMSSFlow->umts_params.res_ber = UMTS_RES_BIT_ERR_RATE9;
          break;

       case OEM_UMTS_RES_BIT_ERR_RATE10:
          pAMSSFlow->umts_params.res_ber = UMTS_RES_BIT_ERR_RATE10;
          break;

       default:
          return AEE_NET_EBADOPTVAL;
       }

       pAMSSFlow->field_mask |= IPFLOW_MASK_UMTS_RES_BER;
    }

   if (pOemFlow->field_mask & OEM_IPFLOW_MASK_UMTS_TRF_PRI) {
       switch (pOemFlow->umts_params.trf_pri)
       {
       case OEM_UMTS_TRF_HANDLING_PRI1:
          pAMSSFlow->umts_params.trf_pri = UMTS_TRF_HANDLING_PRI1;
          break;

       case OEM_UMTS_TRF_HANDLING_PRI2:
          pAMSSFlow->umts_params.trf_pri = UMTS_TRF_HANDLING_PRI2;
          break;

       case OEM_UMTS_TRF_HANDLING_PRI3:
          pAMSSFlow->umts_params.trf_pri = UMTS_TRF_HANDLING_PRI3;
          break;

       default:
          return AEE_NET_EBADOPTVAL;
       }

       pAMSSFlow->field_mask |= IPFLOW_MASK_UMTS_TRF_PRI;
    }

    if (pOemFlow->field_mask & OEM_IPFLOW_MASK_CDMA_PROFILE_ID) {
       pAMSSFlow->cdma_params.profile_id = pOemFlow->cdma_params.profile_id;
       pAMSSFlow->field_mask |= IPFLOW_MASK_CDMA_PROFILE_ID;
    }

    return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION TranslateOEMFilterToAMSS

DESCRIPTION
  Translate OEM Filter to AMSS Filter

===========================================================================*/
int16 TranslateOEMFilterToAMSS(oem_ip_filter_type *pOemFilter, 
                               ip_filter_type     *pAMSSFilter)
{
   switch (pOemFilter->ip_vsn) {
      case OEM_IP_V4:
         pAMSSFilter->ip_vsn = IP_V4;
         break;

      default:
         return AEE_NET_EBADOPTVAL;
   }

   if (pOemFilter->ip_hdr.v4.field_mask & OEM_IPFLTR_MASK_IP4_SRC_ADDR) {

      if (sizeof(pAMSSFilter->ip_hdr.v4.src) 
          != sizeof(pOemFilter->ip_hdr.v4.src)) {
         return AEE_NET_EBADOPTLEN;
      }

      memcpy(&pAMSSFilter->ip_hdr.v4.src, 
             &pOemFilter->ip_hdr.v4.src, 
             sizeof(pOemFilter->ip_hdr.v4.src));

      pAMSSFilter->ip_hdr.v4.field_mask |= IPFLTR_MASK_IP4_SRC_ADDR;
   }

   if (pOemFilter->ip_hdr.v4.field_mask & OEM_IPFLTR_MASK_IP4_DST_ADDR) {

      if (sizeof(pAMSSFilter->ip_hdr.v4.dst) 
          != sizeof(pOemFilter->ip_hdr.v4.dst)) {
         return AEE_NET_EBADOPTLEN;
      }

      memcpy(&pAMSSFilter->ip_hdr.v4.dst, 
             &pOemFilter->ip_hdr.v4.dst, 
             sizeof(pOemFilter->ip_hdr.v4.dst));

      pAMSSFilter->ip_hdr.v4.field_mask |= IPFLTR_MASK_IP4_DST_ADDR;
   }

  if (pOemFilter->ip_hdr.v4.field_mask & OEM_IPFLTR_MASK_IP4_TOS) {

      if (sizeof(pAMSSFilter->ip_hdr.v4.tos) 
          != sizeof(pOemFilter->ip_hdr.v4.tos)) {
         return AEE_NET_EBADOPTLEN;
      }

      memcpy(&pAMSSFilter->ip_hdr.v4.tos, 
             &pOemFilter->ip_hdr.v4.tos, 
             sizeof(pOemFilter->ip_hdr.v4.tos));

      pAMSSFilter->ip_hdr.v4.field_mask |= IPFLTR_MASK_IP4_TOS;
   }

   if (pOemFilter->ip_hdr.v4.field_mask & OEM_IPFLTR_MASK_IP4_NEXT_HDR_PROT) {

      switch (pOemFilter->ip_hdr.v4.next_hdr_prot) {

         case OEM_IPPROTO_TCP:
            if (pOemFilter->next_prot_hdr.tcp.field_mask & OEM_IPFLTR_MASK_TCP_SRC_PORT) {
               if (sizeof(pAMSSFilter->next_prot_hdr.tcp.src) 
                   != sizeof(pOemFilter->next_prot_hdr.tcp.src)) {
                  return AEE_NET_EBADOPTLEN;
               }

               memcpy(&pAMSSFilter->next_prot_hdr.tcp.src, 
                      &pOemFilter->next_prot_hdr.tcp.src, 
                      sizeof(pOemFilter->next_prot_hdr.tcp.src));

               pAMSSFilter->next_prot_hdr.tcp.field_mask |= IPFLTR_MASK_TCP_SRC_PORT;
            }

            if (pOemFilter->next_prot_hdr.tcp.field_mask & OEM_IPFLTR_MASK_IP4_DST_ADDR) {
               if (sizeof(pAMSSFilter->next_prot_hdr.tcp.dst) 
                   != sizeof(pOemFilter->next_prot_hdr.tcp.dst)) {
                  return AEE_NET_EBADOPTLEN;
               }

               memcpy(&pAMSSFilter->next_prot_hdr.tcp.dst, 
                      &pOemFilter->next_prot_hdr.tcp.dst, 
                      sizeof(pOemFilter->next_prot_hdr.tcp.dst));

               pAMSSFilter->next_prot_hdr.tcp.field_mask |= IPFLTR_MASK_IP4_DST_ADDR;
            }

            break;

         case OEM_IPPROTO_UDP:
            if (pOemFilter->next_prot_hdr.udp.field_mask & OEM_IPFLTR_MASK_UDP_SRC_PORT) {
               if (sizeof(pAMSSFilter->next_prot_hdr.udp.src)
                   != sizeof(pOemFilter->next_prot_hdr.udp.src)) {
                  return AEE_NET_EBADOPTLEN;
               }

               memcpy(&pAMSSFilter->next_prot_hdr.udp.src, 
                      &pOemFilter->next_prot_hdr.udp.src, 
                      sizeof(pOemFilter->next_prot_hdr.udp.src));

               pAMSSFilter->next_prot_hdr.udp.field_mask |= IPFLTR_MASK_UDP_SRC_PORT;
            }

            if (pOemFilter->next_prot_hdr.udp.field_mask & OEM_IPFLTR_MASK_IP4_DST_ADDR) {
               if (sizeof(pAMSSFilter->next_prot_hdr.udp.dst) 
                   != sizeof(pOemFilter->next_prot_hdr.udp.dst)) {
                  return AEE_NET_EBADOPTLEN;
               }

               memcpy(&pAMSSFilter->next_prot_hdr.udp.dst, 
                      &pOemFilter->next_prot_hdr.udp.dst, 
                      sizeof(pOemFilter->next_prot_hdr.udp.dst));

               pAMSSFilter->next_prot_hdr.udp.field_mask |= IPFLTR_MASK_IP4_DST_ADDR;
            }

            break;

         default:
            return AEE_NET_EBADOPTVAL;
      }

      pAMSSFilter->ip_hdr.v4.field_mask |= IPFLTR_MASK_IP4_NEXT_HDR_PROT;
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION TranslateOEMFilterSpecToAMSS

DESCRIPTION
  Translate OEM Filter Spec to AMSS Filter Spec

===========================================================================*/
int16 TranslateOEMFilterSpecToAMSS(oem_ip_filter_spec_type *pOemFilterSpec, 
                                   ip_filter_spec_type     *pAMSSFilterSpec)
{
   int16 result;
   uint8 index;

   pAMSSFilterSpec->list_ptr = 
      sys_malloc(pOemFilterSpec->num_filters * sizeof(ip_filter_type));

   if (!pAMSSFilterSpec->list_ptr) {
      return AEE_NET_ENOMEM;
   }

   for (index = 0; index < pOemFilterSpec->num_filters; ++index) {
      result = TranslateOEMFilterToAMSS(&pOemFilterSpec->list_ptr[index], 
                                        &pAMSSFilterSpec->list_ptr[index]);
      if (result != AEE_NET_SUCCESS) {
         sys_free(pAMSSFilterSpec->list_ptr);
         return result;
      }
   }

   pAMSSFilterSpec->num_filters = pOemFilterSpec->num_filters;
   return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION TranslateOEMQoSSpecToAMSS

DESCRIPTION
  Translate OEM QoS Spec to AMSS QoS Spec

===========================================================================*/
int16 TranslateOEMQoSSpecToAMSS(oem_qos_spec_type *pOemQoSSpec, 
                                    qos_spec_type *pAMSSQoSSpec)
{
   int16 result;

   if ((pOemQoSSpec->field_mask & OEM_QOS_MASK_RX_FLOW) || 
       (pOemQoSSpec->field_mask & OEM_QOS_MASK_RX_MIN_FLOW) ) {
      result = TranslateOEMFilterSpecToAMSS(&pOemQoSSpec->rx.fltr_template,
                                            &pAMSSQoSSpec->rx.fltr_template);
      if (result != AEE_NET_SUCCESS) {
         return result;
      }
   }

   if (pOemQoSSpec->field_mask & OEM_QOS_MASK_RX_FLOW) {
      result = TranslateOEMFlowToAMSS(&pOemQoSSpec->rx.flow_template.req_flow,
                                      &pAMSSQoSSpec->rx.flow_template.req_flow);
      if (result != AEE_NET_SUCCESS) {
         return result;
      }
      pAMSSQoSSpec->field_mask |= QOS_MASK_RX_FLOW;
   }

   if (pOemQoSSpec->field_mask & OEM_QOS_MASK_RX_MIN_FLOW) {
      result = TranslateOEMFlowToAMSS(&pOemQoSSpec->rx.flow_template.min_req_flow,
                                      &pAMSSQoSSpec->rx.flow_template.min_req_flow);
      if (result != AEE_NET_SUCCESS) {
         return result;
      }
      pAMSSQoSSpec->field_mask |= QOS_MASK_RX_MIN_FLOW;
   }

   if ((pOemQoSSpec->field_mask & OEM_QOS_MASK_TX_FLOW) || 
       (pOemQoSSpec->field_mask & OEM_QOS_MASK_TX_MIN_FLOW) ) {
      result = TranslateOEMFilterSpecToAMSS(&pOemQoSSpec->tx.fltr_template,
                                            &pAMSSQoSSpec->tx.fltr_template);
      if (result != AEE_NET_SUCCESS) {
         return result;
      }
   }

   if (pOemQoSSpec->field_mask & OEM_QOS_MASK_TX_FLOW) {
      result = TranslateOEMFlowToAMSS(&pOemQoSSpec->tx.flow_template.req_flow,
                                      &pAMSSQoSSpec->tx.flow_template.req_flow);
      if (result != AEE_NET_SUCCESS) {
         return result;
      }
      pAMSSQoSSpec->field_mask |= QOS_MASK_TX_FLOW;
   }

   if (pOemQoSSpec->field_mask & OEM_QOS_MASK_TX_MIN_FLOW) {
      result = TranslateOEMFlowToAMSS(&pOemQoSSpec->tx.flow_template.min_req_flow,
                                      &pAMSSQoSSpec->tx.flow_template.min_req_flow);
      if (result != AEE_NET_SUCCESS) {
         return result;
      }
      pAMSSQoSSpec->field_mask |= QOS_MASK_TX_MIN_FLOW;
   }

   return AEE_NET_SUCCESS;
}

/*===========================================================================

FUNCTION InitAMSSSpec

DESCRIPTION
  Initialize an AMSS QoS Spec to default/initial values

===========================================================================*/
void InitAMSSSpec(qos_spec_type *pAMSSQoSSpec)
{
   // init filter number to zero, for easy memory cleanup based on the filter
   // number, when translation fails
   pAMSSQoSSpec->rx.fltr_template.num_filters = 0;
   pAMSSQoSSpec->tx.fltr_template.num_filters = 0;
}

/*===========================================================================

FUNCTION FreeAMSSSpecContents

DESCRIPTION
  free an AMSS QoS Spec's dynamically allocated buffers

===========================================================================*/
void FreeAMSSSpecContents(qos_spec_type *pAMSSQoSSpec)
{
   if (0 != pAMSSQoSSpec->rx.fltr_template.num_filters) {
      sys_free(pAMSSQoSSpec->rx.fltr_template.list_ptr);
   }

   if (0 != pAMSSQoSSpec->tx.fltr_template.num_filters) {
      sys_free(pAMSSQoSSpec->tx.fltr_template.list_ptr);
   }
}

/*===========================================================================

FUNCTION SetOEMQoSSpecErrorMaskFromAMSS

DESCRIPTION
  Set an OEM QoS Spec's error mask fields by coping them from an AMSS QoS Spec
                         
===========================================================================*/
void SetOEMQoSSpecErrorMaskFromAMSS(oem_qos_spec_type *pOemQoSSpec,
                                    qos_spec_type     *pAMSSQoSSpec)
{
   int index;

   // handle RX direction
   if (pAMSSQoSSpec->field_mask & QOS_MASK_RX_FLOW) {
      pOemQoSSpec->rx.flow_template.req_flow.err_mask =
         pAMSSQoSSpec->rx.flow_template.req_flow.err_mask;
   }

   if (pAMSSQoSSpec->field_mask & QOS_MASK_RX_MIN_FLOW) {
      pOemQoSSpec->rx.flow_template.min_req_flow.err_mask =
         pAMSSQoSSpec->rx.flow_template.min_req_flow.err_mask;
   }

   // handle RX filter list
   if ((pAMSSQoSSpec->field_mask & QOS_MASK_RX_FLOW) || 
       (pAMSSQoSSpec->field_mask & QOS_MASK_RX_MIN_FLOW) ) {

      for (index = 0; index < pAMSSQoSSpec->rx.fltr_template.num_filters; ++index) {
         pOemQoSSpec->rx.fltr_template.list_ptr[index].ip_hdr.v4.err_mask = 
            pAMSSQoSSpec->rx.fltr_template.list_ptr[index].ip_hdr.v4.err_mask;

         if (IPPROTO_TCP == pAMSSQoSSpec->rx.fltr_template.list_ptr[index].ip_hdr.v4.next_hdr_prot) {
            pOemQoSSpec->rx.fltr_template.list_ptr[index].next_prot_hdr.tcp.err_mask = 
               pAMSSQoSSpec->rx.fltr_template.list_ptr[index].next_prot_hdr.tcp.err_mask;
         }

         if (IPPROTO_UDP == pAMSSQoSSpec->rx.fltr_template.list_ptr[index].ip_hdr.v4.next_hdr_prot) {
            pOemQoSSpec->rx.fltr_template.list_ptr[index].next_prot_hdr.udp.err_mask = 
               pAMSSQoSSpec->rx.fltr_template.list_ptr[index].next_prot_hdr.udp.err_mask;
         }
      }
   }

   // handle TX direction
   if (pAMSSQoSSpec->field_mask & QOS_MASK_TX_FLOW) {
      pOemQoSSpec->tx.flow_template.req_flow.err_mask = 
         pAMSSQoSSpec->tx.flow_template.req_flow.err_mask;
   }

   if (pAMSSQoSSpec->field_mask & QOS_MASK_TX_MIN_FLOW) {
      pOemQoSSpec->tx.flow_template.min_req_flow.err_mask = 
         pAMSSQoSSpec->tx.flow_template.min_req_flow.err_mask;
   }

   // handle TX filter list
   if ((pAMSSQoSSpec->field_mask & QOS_MASK_TX_FLOW) || 
       (pAMSSQoSSpec->field_mask & QOS_MASK_TX_MIN_FLOW) ) {

      for (index = 0; index < pAMSSQoSSpec->tx.fltr_template.num_filters; ++index) {
         pOemQoSSpec->tx.fltr_template.list_ptr[index].ip_hdr.v4.err_mask = 
            pAMSSQoSSpec->tx.fltr_template.list_ptr[index].ip_hdr.v4.err_mask;

         if (IPPROTO_TCP == pAMSSQoSSpec->tx.fltr_template.list_ptr[index].ip_hdr.v4.next_hdr_prot) {
            pOemQoSSpec->tx.fltr_template.list_ptr[index].next_prot_hdr.tcp.err_mask = 
               pAMSSQoSSpec->tx.fltr_template.list_ptr[index].next_prot_hdr.tcp.err_mask;
         }

         if (IPPROTO_UDP == pAMSSQoSSpec->tx.fltr_template.list_ptr[index].ip_hdr.v4.next_hdr_prot) {
            pOemQoSSpec->tx.fltr_template.list_ptr[index].next_prot_hdr.udp.err_mask = 
               pAMSSQoSSpec->tx.fltr_template.list_ptr[index].next_prot_hdr.udp.err_mask;
         }
      }
   }
}

/*===========================================================================
 Insert a buffer into a QoS event list (at the head of the list)
===========================================================================*/
static void PutQoSEventBufAtHead( OEMNetQoSEventData **ppqeListHead, OEMNetQoSEventData *pqeBuffer )
{
   pqeBuffer->pNext = *ppqeListHead;
   *ppqeListHead = pqeBuffer;
}

/*===========================================================================
 Insert a buffer into a QoS event list (at the tail of the list)
===========================================================================*/
static void PutQoSEventBufAtTail( OEMNetQoSEventData **ppqeListHead, OEMNetQoSEventData *pqeBuffer )
{
   while ( *ppqeListHead ) {
      ppqeListHead = &((*ppqeListHead)->pNext);
   }
   *ppqeListHead = pqeBuffer;
}

/*===========================================================================
 Retrieve a buffer from a QoS event list (from the head of the list)
===========================================================================*/
static OEMNetQoSEventData* GetQoSEventBufFromHead( OEMNetQoSEventData **ppqeListHead )
{
   OEMNetQoSEventData *pqeResult = *ppqeListHead;

   if ( pqeResult ) {
      *ppqeListHead = pqeResult->pNext;
      pqeResult->pNext = NULL;
   }

   return pqeResult;
}

//
// callback function for QoS events
//
static void OEMNet_QoSEventOccurred
(
   dss_iface_ioctl_event_enum_type        event,
   dss_iface_ioctl_event_info_union_type  event_info,
   void                                  *user_data,
   sint15                                 app_id,
   dss_iface_id_type                      iface_id
)
{
   OEMNet *pNet = OEMNet_FromAppID(app_id); // is this safe ?

   if ((OEMNet*)0 != pNet) {

      OEMNetQoSEventData *pqeBuffer;

      INTLOCK();
      pqeBuffer = GetQoSEventBufFromHead(&pNet->pqeFreeBuffers);
      INTFREE();

      if ( pqeBuffer ) {

         pqeBuffer->handle = event_info.qos_info.handle;
         pqeBuffer->infoCode = event_info.qos_info.info_code;
         pqeBuffer->event = event;

         INTLOCK();
         PutQoSEventBufAtTail(&pNet->pqeReadyBuffers, pqeBuffer);
         INTFREE();
      } else {
         FARF(ALWAYS, ("QoS free Queue is empty! %d", pNet));
      }

#ifdef FEATURE_BREW_NET_SIG
      pNet->bQoSEvt = TRUE;
      OEMOS_NetSignalDispatch();
#else
      OEMNet_ProcessQoSEvent(pNet);
#endif
   }
}

#endif // OEM_PROVIDES_QOS

/*===========================================================================

FUNCTION OEMNet_RequestQoS

DESCRIPTION
  Request a QoS.
                         
DEPENDENCIES
  None.
===========================================================================*/
int16 OEMNet_RequestQoS
(
   OEMCONTEXT           netd,
   oem_qos_spec_type   *pQoS,
   oem_qos_handle_type *pHandle
)
{
#ifdef OEM_PROVIDES_QOS

   OEMNet                           *pNet = (OEMNet*)netd;
   uint16                            ret;
   sint15                            ndssErr;
   dss_iface_id_type                 iface_id;
   dss_iface_ioctl_qos_request_type *pqos_Request;

   if ((NULL == pQoS) || (NULL == pHandle)) {
      return AEE_NET_EINVAL;
   }

   if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) == DSS_IFACE_INVALID_ID) {
      return AEE_NET_ENETNONET;
   }

   pqos_Request = sys_malloc(sizeof(dss_iface_ioctl_qos_request_type)); 
   if (!pqos_Request) {
      return AEE_NET_ENOMEM;
   }

   InitAMSSSpec(&pqos_Request->qos);

   ret = TranslateOEMQoSSpecToAMSS(pQoS, &pqos_Request->qos);
   if (AEE_NET_SUCCESS != ret) {
      FARF(ALWAYS, ("QoSSpec translation failed! n:%d", pNet));
      FreeAMSSSpecContents(&pqos_Request->qos);
      sys_free(pqos_Request);
      return ret;
   }

   // The following cast is needed as the dss_iface_ioctl_event_cb type 
   // changed somewhere in AMSS releases.
   // In older AMSS releases, QoS is not supported and this callback
   // will never be used.
   pqos_Request->cback_fn  = (dss_iface_ioctl_event_cb)OEMNet_QoSEventOccurred;
   pqos_Request->user_data = pNet;
   pqos_Request->app_id    = pNet->sAppID;
   
   ret = oemdss_iface_ioctl(iface_id,
                            DSS_IFACE_IOCTL_QOS_REQUEST,
                            pqos_Request,
                            &ndssErr);

   if (DSS_SUCCESS == ret) {
      *pHandle = pqos_Request->handle;
      ret = AEE_NET_SUCCESS;
   } else {
      SetOEMQoSSpecErrorMaskFromAMSS(pQoS, &pqos_Request->qos);
      ret = DSSToAEE_Err(ndssErr);
   }

   FreeAMSSSpecContents(&pqos_Request->qos);
   sys_free(pqos_Request);

   return ret;

#else 
   return EUNSUPPORTED;
#endif // OEM_PROVIDES_QOS
}

/*===========================================================================

FUNCTION OEMNet_ReleaseQoS

DESCRIPTION
  Release a granted QoS.
                         
DEPENDENCIES
  None.
===========================================================================*/
int16 OEMNet_ReleaseQoS
(
   OEMCONTEXT          netd,
   oem_qos_handle_type handle
)
{
#ifdef OEM_PROVIDES_QOS

   OEMNet                          *pNet = (OEMNet*)netd;
   sint15                           ndssErr;
   dss_iface_id_type                iface_id;
   dss_iface_ioctl_qos_release_type qos_release;

   qos_release.handle = handle;
   
   if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) != DSS_IFACE_INVALID_ID) {
      int ret = oemdss_iface_ioctl(iface_id,
                                   DSS_IFACE_IOCTL_QOS_RELEASE,
                                   &qos_release,
                                   &ndssErr);
      if (DSS_SUCCESS != ret) {
         return DSSToAEE_Err(ndssErr);
      }
   } else {
      return AEE_NET_ENETNONET; 
   }

   return AEE_NET_SUCCESS;

#else 
   return EUNSUPPORTED;
#endif // OEM_PROVIDES_QOS
}

/*===========================================================================

FUNCTION OEMNet_GetQoSFlowSpec

DESCRIPTION
  Retrieve the flow specification of a granted Quality of Service instance,
  identified by its handle value 
                         
DEPENDENCIES
  None.
===========================================================================*/
int16 OEMNet_GetQoSFlowSpec
(
   OEMCONTEXT           netd,
   oem_qos_handle_type  handle,
   oem_ip_flow_type    *pRxFlow,
   oem_ip_flow_type    *pTxFlow
)
{
#ifdef OEM_PROVIDES_QOS

   OEMNet                                *pNet = (OEMNet*)netd;
   int16                                  ret;
   sint15                                 ndssErr;
   dss_iface_id_type                      iface_id;
   dss_iface_ioctl_qos_get_flow_spec_type qos_getFlowSpec;

   if ((NULL == pRxFlow) || (NULL == pTxFlow)) {
      return AEE_NET_EINVAL;
   }

   qos_getFlowSpec.handle = handle;
   
   if ((iface_id = oemdss_get_iface_id(pNet->sAppID)) != DSS_IFACE_INVALID_ID) {
      int ret = oemdss_iface_ioctl(iface_id,
                                   DSS_IFACE_IOCTL_QOS_GET_FLOW_SPEC,
                                   &qos_getFlowSpec,
                                   &ndssErr);
      if (DSS_SUCCESS != ret) {
         return DSSToAEE_Err(ndssErr);
      }
   } else {
      return AEE_NET_ENETNONET;
   }

   ret = TranslateAMSSFlowToOEM(&qos_getFlowSpec.rx_flow, pRxFlow);
   if (AEE_NET_SUCCESS != ret) {
      FARF(ALWAYS, ("QoSflow rx translation failed! n:%d h:%d", pNet, handle));
      return ret;
   }

   ret = TranslateAMSSFlowToOEM(&qos_getFlowSpec.tx_flow, pTxFlow);
   if (AEE_NET_SUCCESS != ret) {
      FARF(ALWAYS, ("QoSflow tx translation failed! n:%d h:%d", pNet, handle));
      return ret;
   }

   return AEE_NET_SUCCESS;

#else 
   return EUNSUPPORTED;
#endif // OEM_PROVIDES_QOS
}

/*===========================================================================

FUNCTION OEMNet_GetQoSStatus

DESCRIPTION
  Returns the status of a QoS instace
                         
DEPENDENCIES
  None.
===========================================================================*/
uint16 OEMNet_GetQoSStatus
(
   OEMCONTEXT          netd,
   oem_qos_handle_type handle
)
{
#ifdef OEM_PROVIDES_QOS

   dss_iface_id_type  iface_id;
   uint16             ret = AEE_QOS_STATUS_NOT_AVAILABLE;

   // this only fails if the interface is not up for this appid...
   if ((iface_id = dss_get_iface_id_by_qos_handle(handle)) != DSS_IFACE_INVALID_ID) {     
      dss_iface_ioctl_phys_link_state_type phys_link_state;
      sint15 ndssErr;

      ret = oemdss_iface_ioctl(iface_id,
                               DSS_IFACE_IOCTL_GET_PHYS_LINK_STATE,
                               &phys_link_state,
                               &ndssErr);
      if (DSS_SUCCESS == ret) {
         switch (phys_link_state) {
            case PHYS_LINK_UP:
               ret = AEE_QOS_STATUS_ACTIVE;
               break;

            case PHYS_LINK_DOWN:
               ret = AEE_QOS_STATUS_INACTIVE;
               break;

            case PHYS_LINK_COMING_UP:
               ret = AEE_QOS_STATUS_GOING_ACTIVE;
               break;

            case PHYS_LINK_GOING_DOWN:
               ret = AEE_QOS_STATUS_GOING_INACTIVE;
               break;
         }
      }
   }

   return ret;

#else 
   return EUNSUPPORTED;
#endif // OEM_PROVIDES_QOS
}

/*===========================================================================

FUNCTION OEMNet_QoSGoActive

DESCRIPTION
  Tries to make a QoS instance go active
                         
DEPENDENCIES
  None.
===========================================================================*/
uint16 OEMNet_QoSGoActive
(
   OEMCONTEXT          netd,
   oem_qos_handle_type handle
)
{
#ifdef OEM_PROVIDES_QOS

   dss_iface_id_type iface_id;
   uint16            ret;
   sint15            ndssErr;

   if ((iface_id = dss_get_iface_id_by_qos_handle(handle)) == DSS_IFACE_INVALID_ID) {
      return AEE_NET_ENETNONET;
   }
      
   ret = oemdss_iface_ioctl(iface_id,
                            DSS_IFACE_IOCTL_GO_ACTIVE,
                            NULL,
                            &ndssErr);

   if (DSS_SUCCESS != ret) {
      return DSSToAEE_Err(ndssErr);
   }

   return AEE_NET_SUCCESS;

#else 
   return EUNSUPPORTED;
#endif // OEM_PROVIDES_QOS
}

/*===========================================================================

FUNCTION OEMNet_SetQoSDormancyTimeout

DESCRIPTION
   Set a QoS inactivity timeout value (idle timeout after which the QoS 
   should enter dormancy)
                         
DEPENDENCIES
  None.
===========================================================================*/
uint16 OEMNet_SetQoSDormancyTimeout
(
   OEMCONTEXT          netd,
   oem_qos_handle_type handle,
   uint8               ucSecs
)
{
#ifdef OEM_PROVIDES_QOS

   uint32             dssSecs = ucSecs;
   dss_iface_id_type  iface_id;
   int16              ndssErr;
   int                ret;

   if ((iface_id = dss_get_iface_id_by_qos_handle(handle)) == DSS_IFACE_INVALID_ID) {
      return AEE_NET_ENETNONET;
   }

   ret = oemdss_iface_ioctl(iface_id,
                            DSS_IFACE_IOCTL_707_SET_DORM_TIMER,
                            &dssSecs,
                            &ndssErr);

   if (DSS_SUCCESS != ret) {
      return DSSToAEE_Err(ndssErr);
   }

   return AEE_NET_SUCCESS;

#else 
   return EUNSUPPORTED;
#endif // OEM_PROVIDES_QOS
}

/*===========================================================================

FUNCTION OEMNet_GetQoSEventInfo

DESCRIPTION
  Retrieve QoS event information

DEPENDENCIES
  None.
===========================================================================*/
int32 OEMNet_GetQoSEventInfo(
   OEMCONTEXT           netd,
   oem_qos_handle_type *pHandle,
   AEEQoSEvent         *pEvent,
   AEEQoSInfoCode      *pInfoCode
)
{
#ifdef OEM_PROVIDES_QOS

   OEMNetQoSEventData *pqeBuffer;
   OEMNet             *pNet = (OEMNet*)netd;
   int32               ret;

   if ((NULL == pHandle) || (NULL == pEvent) || (NULL == pInfoCode)) {
      return AEE_NET_EINVAL;
   }

   INTLOCK();
   pqeBuffer = GetQoSEventBufFromHead(&pNet->pqeReadyBuffers);
   INTFREE();

   if (pqeBuffer) {

      ret = AEE_NET_SUCCESS;
      *pHandle = (oem_qos_handle_type)pqeBuffer->handle;
      
      switch (pqeBuffer->event)
      {
         case DSS_IFACE_IOCTL_QOS_AVAILABLE_EV:
            *pEvent = AEE_QOS_AVAILABLE_EV;
            break;

         case DSS_IFACE_IOCTL_QOS_AVAILABLE_MODIFIED_EV:
            *pEvent = AEE_QOS_AVAILABLE_MODIFIED_EV;
            break;

         case DSS_IFACE_IOCTL_QOS_AVAILABLE_DEACTIVATED_EV:
            *pEvent = AEE_QOS_AVAILABLE_DEACTIVATED_EV;
            break;

         case DSS_IFACE_IOCTL_QOS_UNAVAILABLE_EV:
            *pEvent = AEE_QOS_UNAVAILABLE_EV;
            break;

         default:
            *pEvent = -1;
            ret = AEE_NET_ERROR; /* consider this an unusable event! */
            FARF(ALWAYS, ("unknown QoS event value: %d", pqeBuffer->event));
            break;
      }
      
      switch (pqeBuffer->infoCode)
      {
         case DSS_IFACE_IOCTL_EIC_NOT_SPECIFIED:
            *pInfoCode = AEE_QOS_INFO_CODE_NOT_SPECIFIED;
            break;

         case DSS_IFACE_IOCTL_EIC_QOS_NOT_SUPPORTED:
            *pInfoCode = AEE_QOS_INFO_CODE_NOT_SUPPORTED;
            break;

         case DSS_IFACE_IOCTL_EIC_QOS_NOT_AVAILABLE:
            *pInfoCode = AEE_QOS_INFO_CODE_NOT_AVAILABLE;
            break;

         case DSS_IFACE_IOCTL_EIC_QOS_NOT_GUARANTEED:
            *pInfoCode = AEE_QOS_INFO_CODE_NOT_GUARANTEED;
            break;

         case DSS_IFACE_IOCTL_EIC_QOS_INVALID_PARAMS:
            *pInfoCode = AEE_QOS_INFO_CODE_INVALID_PARAMS;
            break;

         case DSS_IFACE_IOCTL_EIC_QOS_INTERNAL_ERROR:
            *pInfoCode = AEE_QOS_INFO_CODE_INTERNAL_ERROR;
            break;

         case DSS_IFACE_IOCTL_EIC_QOS_INSUFFICIENT_NET_RESOURCES:
            *pInfoCode = AEE_QOS_INFO_CODE_INSUFFICIENT_NET_RESOURCES;
            break;

         case DSS_IFACE_IOCTL_EIC_QOS_INSUFFICIENT_LOCAL_RESOURCES:
            *pInfoCode = AEE_QOS_INFO_CODE_INSUFFICIENT_LOCAL_RESOURCES;
            break;

         default:
            *pInfoCode = AEE_QOS_INFO_CODE_NOT_SPECIFIED;
            FARF(ALWAYS, ("unknown QoS info code value: %d", pqeBuffer->infoCode));
            break;
      }

      INTLOCK();
      PutQoSEventBufAtHead(&pNet->pqeFreeBuffers, pqeBuffer);
      INTFREE();
   } else {
      ret = AEE_NET_ERROR;
   }

   return ret;

#else 
   return EUNSUPPORTED;
#endif // OEM_PROVIDES_QOS
}





