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

FILE: OEMDSS.c

SERVICES: OEM DSS backwards compatibility layer

GENERAL DESCRIPTION:
Definitions, typedefs, etc. for newer DSS functions that do not exist in older distributions
  
PUBLIC CLASSES AND STATIC FUNCTIONS:
   dss_getsockname
   dss_getpeername
   dss_get_iface_id
   dss_iface_ioctl
   dss_shutdown
   dss_listen
   dss_accept

INITIALIZATION AND SEQUENCING REQUIREMENTS:
   None

        Copyright © 2003 QUALCOMM Incorporated.
               All Rights Reserved.
            QUALCOMM Proprietary/GTDR
=============================================================================*/

#if !defined(T_MSM6100) && !defined(T_MSM6250)
#include "dsiface.h"
#endif // !defined(T_MSM6100) && !defined(T_MSM6250)

#include "OEMSock_priv.h"
#include "OEMSock.h"
#include "OEMTAPI.h"
#include "OEMConfig.h"
#include "AEEError.h"
#include "AEEStdLib.h"
#include "AEE_OEMNet.h"
#include "AEE_OEMDispatch.h"
#include "AEE_OEM.h"

//
// Conditional Debug Output
//

#define FARF_EVENT             0
#define FARF_TAPISTATE         0
#define FARF_CLOSE_OFFLINE_PPP 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__)); }


typedef struct
{
#if !defined(T_MSM6100) && !defined(T_MSM6250)
   OEMTAPI*                   piTAPI; /* monitors call status for dormancy, 
                                         dropped calls, etc. */
   AEECallback                cbFreeTAPI; // exit handler

   boolean                    bUp; // physical link is currently up
   dss_iface_ioctl_ev_cb_type cb;  // OEMNet interface events callback 
   AEECallback                cbCloseOfflinePPP; // offline timer
#endif // !defined(T_MSM6100) && !defined(T_MSM6250)

} DSSNet;

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

             Globals

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

static DSSNet gNet;

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

                      FUNCTION DEFINITIONS

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

#if !defined(OEM_PROVIDES_SERVER_SOCKETS)

/*===========================================================================
FUNCTION DSS_LISTEN()
===========================================================================*/
sint15 dss_listen
(
  sint15 sockfd,                                      /* Socket descriptor */
  sint15 backlog,                      /* Number of connections to backlog */
  sint15 *dss_errno                               /* error condition value */
)
{
   *dss_errno = DS_EOPNOTSUPP;
   return DSS_ERROR;
}

/*===========================================================================
FUNCTION DSS_ACCEPT()
===========================================================================*/
sint15 dss_accept 
(
  sint15 sockfd,                                      /* Socket descriptor */
  struct sockaddr *remoteaddr,                       /* new remote address */
  uint16 *addrlen,                                   /* length of servaddr */
  sint15 *dss_errno                               /* error condition value */
)
{
   *dss_errno = DS_EOPNOTSUPP;
   return DSS_ERROR;
}

#endif // !defined(OEM_PROVIDES_SERVER_SOCKETS)

#if !defined(T_MSM6100) && !defined(T_MSM6250)

/*===========================================================================
FUNCTION DSS_GETSOCKNAME()
===========================================================================*/
sint15 dss_getsockname
(
  sint15           sockfd,                  /* socket descriptor           */
  struct sockaddr* addr,                    /* address of the socket       */
  uint16*          addrlen,                 /* address length              */
  sint15*          dss_errno                /* error number                */
)
{
   struct scb_s  *pScbType;
   struct sockaddr_in* psin = (struct sockaddr_in*)addr;

   if ((pScbType = dssocki_sockfdtoscb(sockfd)) == NULL) {
      return AEE_NET_EBADF;
   }

   if (*addrlen < sizeof(struct sockaddr_in)) {
      return AEE_NET_EFAULT;
   }
   *addrlen = sizeof(struct sockaddr_in);

   psin->sin_family = AF_INET;
   psin->sin_port = HTONS(pScbType->localname.port);
   psin->sin_addr.s_addr = (uint32)HTONL(pScbType->localname.address);

   return DSS_SUCCESS;   
}

/*===========================================================================
FUNCTION DSS_GETPEERNAME()
===========================================================================*/
sint15 dss_getpeername
(
  sint15           sockfd,                  /* socket descriptor           */
  struct sockaddr* addr,                    /* address of the socket       */
  uint16*          addrlen,                 /* address length              */
  sint15*          dss_errno                /* error number                */
)
{
   struct scb_s    *pScbType;
   struct sockaddr_in* psin = (struct sockaddr_in*)addr;
      
   if ((pScbType = dssocki_sockfdtoscb(sockfd)) == NULL) {
      return AEE_NET_EBADF;
   }
      
   if (*addrlen < sizeof(struct sockaddr_in)) {
      return AEE_NET_EFAULT;
   }
   *addrlen = sizeof(struct sockaddr_in);

   psin->sin_family = AF_INET;
   psin->sin_port = HTONS(pScbType->servname.port);

   if (0 == psin->sin_port) {
      return AEE_NET_ENOTCONN;
   }

   psin->sin_addr.s_addr = (uint32)HTONL(pScbType->servname.address);

   return DSS_SUCCESS;   
}

/*===========================================================================
FUNCTION DSS_GET_IFACE_ID()
===========================================================================*/
int dss_get_iface_id
(
  dss_id_info_type id,
  dss_iface_ioctl_id_type *iface_id 
)
{
   switch (id.type) {
      case DSS_APP_ID:
         iface_id->name = DSS_IFACE_CDMA_SN;
         iface_id->instance = id.info.app_id;
         return 0;

      case DSS_SOCK_FD:         // currently not used
      default:
         return -1;
   }
}

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

 ===========================================================================*/
static void OEMDSS_CloseOfflinePPP(void *unused)
{   
   FARF(CLOSE_OFFLINE_PPP,("close offline PPP"));
   AEENetList_ClosePPP(); 
}

/*===========================================================================
  This is a static callback from OEMTAPI.c, invoked every time the CM
  task notifies TAPI about a state change (call, system, or phone)
 ===========================================================================*/
void OEMDSS_OnCallStatus(void)
{
   TAPIStatus ts;

   FARF(EVENT,("OCS netevent"));

   // Get the status according to TAPI
   if (gNet.piTAPI && SUCCESS == OEMTAPI_GetStatus(gNet.piTAPI,&ts)) {
      boolean bOffline = (PS_OFFLINE == ts.state) || ((PS_IDLE == ts.state) && !ts.bDigital);
      boolean bUp = (PS_CONVERSATION == ts.state) && ts.bData;

      // has the phys link state changed?
      if (bUp != gNet.bUp) {
         dss_iface_ioctl_ev_cb_type* pCb = &gNet.cb;

         // notify registered callback
         if (pCb->event_cb) {
            dss_iface_ioctl_event_info_union_type u;
            dss_iface_ioctl_event_enum_type event;

            event = bUp ? DSS_IFACE_IOCTL_PHYS_LINK_UP_EV : DSS_IFACE_IOCTL_PHYS_LINK_DOWN_EV;
            u.phys_link_state_info = bUp ? PHYS_LINK_UP : PHYS_LINK_DOWN; 
            pCb->event_cb(event, u, pCb->user_data_ptr, pCb->app_id,
                          DSS_IFACE_IOCTL_CDMA_SN, 0);
         }

         // remember new state
         gNet.bUp = bUp;
      }

      // 5100 (at least) has a bug where PPP will get stuck if digital
      // service is lost.  If the call state has gone offline, start a
      // timer to close PPP, unless the call state comes back online
      if (bOffline &&
          !CALLBACK_IsQueued(&gNet.cbCloseOfflinePPP)) {
         uint32 dwOfflinePPPTimeout;

         // Default to 60 seconds
         if (SUCCESS != OEM_GetConfig(CFGI_OFFLINE_PPP_TIMEOUT,
                                      (void *)&dwOfflinePPPTimeout, sizeof(dwOfflinePPPTimeout))) {
            dwOfflinePPPTimeout = 60000;
         }

         FARF(CLOSE_OFFLINE_PPP,("set offline PPP timer"));
         CALLBACK_Init(&gNet.cbCloseOfflinePPP, OEMDSS_CloseOfflinePPP, 0);
         AEE_SetSvcTimerCallback(dwOfflinePPPTimeout, &gNet.cbCloseOfflinePPP);
      } else if (CALLBACK_IsQueued(&gNet.cbCloseOfflinePPP) &&
                 bUp) {
         FARF(CLOSE_OFFLINE_PPP,("cancel offline PPP timer"));
         CALLBACK_Cancel(&gNet.cbCloseOfflinePPP);
      }
   }
}

static void OEMDSS_FreeTAPI(void *p)
{
   (void)p;

   CALLBACK_Cancel(&gNet.cbFreeTAPI);

   if ((OEMTAPI *)0 != gNet.piTAPI) {
      OEMTAPI_Destroy(gNet.piTAPI);
      gNet.piTAPI = 0;
   }
}

static void OEMDSS_SetupTAPI(void)
{
   if (((OEMTAPI *)0 == gNet.piTAPI) &&
       (SUCCESS == OEMTAPI_New(&gNet.piTAPI))) {
      /* Register for notification on AEE_Exit()... */
      CALLBACK_Init(&gNet.cbFreeTAPI, OEMDSS_FreeTAPI, 0);
      AEE_RegisterSvcSystemCallback(&gNet.cbFreeTAPI, AEE_SVC_SCB_AEE_EXIT);
   }
}

static sint15 DSIFaceToDSSIFaceIOCtl_Err(dsiface_error_type err)
{
/*===========================================================================
===========================================================================*/

   //Store mapping between dsiface Error Codes and dss_iface_ioctl Error codes.
   static const struct {
      dsiface_error_type from;  // dsiface Error Code
      sint15              to;   // dss_iface_ioctl Error Code
   } map[] =
   {
      {DSIFACE_BAD_OPT_NAME   , DS_EINVAL   },
      {DSIFACE_BAD_IFACE      , DS_EBADF    },
      {DSIFACE_BAD_PARAM      , DS_EFAULT   },
      {DSIFACE_BAD_LEN        , DS_EFAULT   },
      {DSIFACE_BAD_ARG_VAL    , DS_EFAULT   },
      {DSIFACE_SUCCESS        , DSS_SUCCESS }
   };

   int   i;
   sint15 DSS_Err = DSS_ERROR;

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

   return DSS_Err;
}

/*===========================================================================
FUNCTION DSS_IFACE_IOCTL()
===========================================================================*/
int dss_iface_ioctl
( 
  dss_iface_ioctl_id_type  *id_ptr,
  dss_iface_ioctl_type     ioctl_name,
  void                     *argval_ptr,
  sint15                   *dss_errno
)
{
   switch (ioctl_name) {

      case DSS_IFACE_IOCTL_REG_EVENT_CB:
      case DSS_IFACE_IOCTL_DEREG_EVENT_CB:
      {
         dss_iface_ioctl_ev_cb_type* pCb = (dss_iface_ioctl_ev_cb_type*)argval_ptr;
         
         // there is no equivalent for this event
         if (pCb->event == DSS_IFACE_IOCTL_ADDR_CHANGED_EV) {
            return DSS_SUCCESS;
         } else if (pCb->event == DSS_IFACE_IOCTL_PHYS_LINK_DOWN_EV) { // implies UP_EV, as well
            if (DSS_IFACE_IOCTL_REG_EVENT_CB == ioctl_name) {
               gNet.cb = *pCb;
               OEMDSS_SetupTAPI();
            } else {
               memset(&gNet.cb, 0, sizeof(gNet.cb));
               OEMDSS_FreeTAPI(0);
            }
            return DSS_SUCCESS;
         } else {
            *dss_errno = DS_EOPNOTSUPP;
            return DSS_ERROR;
         }
      }

      case DSS_IFACE_IOCTL_GET_PHYS_LINK_STATE:
      {
         TAPIStatus ts;
         phys_link_state_type* pState = (phys_link_state_type*)argval_ptr;
 
         if (!argval_ptr) {
            *dss_errno = DS_EFAULT;
            return DSS_ERROR;
         }
         
         if (((OEMTAPI *)0 != gNet.piTAPI) && 
             (SUCCESS == OEMTAPI_GetStatus(gNet.piTAPI,&ts))) {

            FARF(TAPISTATE,("TAPISTATE: %s",
                            PS_ORIG==ts.state?"orig":
                            PS_INCOMING==ts.state?"incoming":
                            PS_OFFLINE==ts.state?"offline":
                            PS_IDLE==ts.state?"idle":
                            PS_CONVERSATION==ts.state?"conversation":"what the??"));

            /* check phone state */
            switch (ts.state) {

               case PS_OFFLINE:
               case PS_IDLE:
                  *pState = PHYS_LINK_DOWN;
                  break;

               case PS_ORIG:
               case PS_INCOMING:
                  if (!ts.bData) {
                     *pState = PHYS_LINK_DOWN;
                  } else { 
                     *pState = PHYS_LINK_COMING_UP;
                  }
                  break;

               case PS_CONVERSATION:
                  if (!ts.bData) {
                     *pState = PHYS_LINK_DOWN;
                  } else { 
                     *pState = PHYS_LINK_UP;
                  }
                  break;

               default: 
                  *dss_errno = DS_EFAULT;
                  return DSS_ERROR;
            }

            return DSS_SUCCESS;
         } else {
            *dss_errno = DS_EFAULT;
            return DSS_ERROR;
         }
      }

      case DSS_IFACE_IOCTL_707_SDB_SUPPORT_QUERY:
      {
         dss_iface_ioctl_707_sdb_support_query_type* pQuery =
            (dss_iface_ioctl_707_sdb_support_query_type*)argval_ptr;

         pQuery->can_do_sdb = FALSE;
         return DSS_SUCCESS;
      }

      case DSS_IFACE_IOCTL_GET_IPV4_ADDR:
      {
         ip_addr_type* pIpAddr = (ip_addr_type*)argval_ptr;

         IP_ADDR_V4(*pIpAddr) = HTONL(dsiface_get_ip_addr(IFACE_UM_PPP_STACK));

         return DSS_SUCCESS;
      }

      case DSS_IFACE_IOCTL_GET_IPV4_PRIM_DNS_ADDR:
      {
         ip_addr_type* pIpAddr = (ip_addr_type*)argval_ptr;
         INAddr ina = 0;

         ina = dsiface_get_dns_addr(IFACE_UM_PPP_STACK, IFACE_IPv4_DNS_PRIMARY);
         if ((0 == ina) || ((INAddr)-1 == ina)) {
            *dss_errno = DS_EFAULT;
            return DSS_ERROR;
         }
         
         IP_ADDR_V4(*pIpAddr) = HTONL(ina);
         return DSS_SUCCESS;
      }

      case DSS_IFACE_IOCTL_GET_IPV4_SECO_DNS_ADDR:
      {
         ip_addr_type* pIpAddr = (ip_addr_type*)argval_ptr;
         INAddr ina = 0;

         ina = dsiface_get_dns_addr(IFACE_UM_PPP_STACK, IFACE_IPv4_DNS_SECONDARY);
         if ((0 == ina) || ((INAddr)-1 == ina)) {
            *dss_errno = DS_EFAULT;
            return DSS_ERROR;
         }

         IP_ADDR_V4(*pIpAddr) = HTONL(ina);
         return DSS_SUCCESS;
      }

      case DSS_IFACE_IOCTL_707_GET_MDR:
      {
         dss_iface_ioctl_707_mdr_type* pMdrMode = (dss_iface_ioctl_707_mdr_type*)argval_ptr;
         int mdrMode;
         int nModeLen = sizeof(mdrMode);
         
         if (DSIFACE_SUCCESS != dsiface_get_opt(IFACE_UM_PPP_STACK,
                                                DSIFACE_OPT_MDR,
                                                &mdrMode,
                                                &nModeLen)) {
            *dss_errno = DS_EFAULT;
            return DSS_ERROR;
         }

         *pMdrMode = mdrMode;
         return DSS_SUCCESS;
      }

      case DSS_IFACE_IOCTL_707_SET_MDR:
      {
         dss_iface_ioctl_707_mdr_type* pMdrMode = (dss_iface_ioctl_707_mdr_type*)argval_ptr;
         int mdrMode = *pMdrMode;
         int nModeLen = sizeof(mdrMode);
         
         if (DSIFACE_SUCCESS != dsiface_set_opt(IFACE_UM_PPP_STACK,
                                                DSIFACE_OPT_MDR,
                                                &mdrMode,
                                                nModeLen)) {
            *dss_errno = DS_EFAULT;
            return DSS_ERROR;
         }

         return DSS_SUCCESS;
      }

      case DSS_IFACE_IOCTL_707_GET_RLP_ALL_CURR_NAK:
      {
         dss_iface_ioctl_707_rlp_opt_type* pOptval = (dss_iface_ioctl_707_rlp_opt_type*)argval_ptr;
         dsiface_error_type err;
         int noptlen = sizeof(*pOptval);

         err = dsiface_get_opt(IFACE_UM_PPP_STACK,
                               DSIFACE_OPT_RLP_ALL_CUR_NAK,
                               pOptval,
                               &noptlen);

         return DSIFaceToDSSIFaceIOCtl_Err(err);
      }

      case DSS_IFACE_IOCTL_707_SET_RLP_ALL_CURR_NAK:
      {
         dss_iface_ioctl_707_rlp_opt_type* pOptval = (dss_iface_ioctl_707_rlp_opt_type*)argval_ptr;
         dsiface_error_type err;
         int noptlen = sizeof(*pOptval);

         err = dsiface_set_opt(IFACE_UM_PPP_STACK,
                               DSIFACE_OPT_RLP_ALL_CUR_NAK,
                               pOptval,
                               noptlen);

         return DSIFaceToDSSIFaceIOCtl_Err(err);
      }

      case DSS_IFACE_IOCTL_707_GET_RLP_ALL_DEF_NAK:
      {
         dss_iface_ioctl_707_rlp_opt_type* pOptval = (dss_iface_ioctl_707_rlp_opt_type*)argval_ptr;
         dsiface_error_type err;
         int noptlen = sizeof(*pOptval);

         err = dsiface_get_opt(IFACE_UM_PPP_STACK,
                               DSIFACE_OPT_RLP_ALL_DEF_NAK,
                               pOptval,
                               &noptlen);

         return DSIFaceToDSSIFaceIOCtl_Err(err);
      }

      case DSS_IFACE_IOCTL_707_SET_RLP_ALL_DEF_NAK:
      {
         dss_iface_ioctl_707_rlp_opt_type* pOptval = (dss_iface_ioctl_707_rlp_opt_type*)argval_ptr;
         dsiface_error_type err;
         int noptlen = sizeof(*pOptval);

         err = dsiface_set_opt(IFACE_UM_PPP_STACK,
                               DSIFACE_OPT_RLP_ALL_DEF_NAK,
                               pOptval,
                               noptlen);

         return DSIFaceToDSSIFaceIOCtl_Err(err);
      }

      case DSS_IFACE_IOCTL_707_GET_RLP_ALL_NEG_NAK:
      {
         dss_iface_ioctl_707_rlp_opt_type* pOptval = (dss_iface_ioctl_707_rlp_opt_type*)argval_ptr;
         dsiface_error_type err;
         int noptlen = sizeof(*pOptval);

         err = dsiface_get_opt(IFACE_UM_PPP_STACK,
                               DSIFACE_OPT_RLP_ALL_NEG_NAK,
                               pOptval,
                               &noptlen);

         return DSIFaceToDSSIFaceIOCtl_Err(err);
      }

      case DSS_IFACE_IOCTL_707_SET_DORM_TIMER:
      {
         unsigned int* pTimeout = (unsigned int*)argval_ptr;
         dsiface_error_type err;

         int dwTimeout = *pTimeout;
         int noptlen = sizeof(dwTimeout);

         // check MDR mode. change dormancy timer only if MDR is set to SO33.
         // in other configurations (at least for async data), mobile originated
         // dormancy causes problems so leave dormancy timeout as is (=0 => no dormancy)
         int mdrMode;
         int nModeLen = sizeof(mdrMode);
         
         if (DSIFACE_SUCCESS != dsiface_get_opt(IFACE_UM_PPP_STACK,
                                                DSIFACE_OPT_MDR,
                                                &mdrMode,
                                                &nModeLen)) {
            *dss_errno = DS_EFAULT;
            return DSS_ERROR;
         }

         if (mdrMode != DS_MDR_MODE_SO33_PREF) {
            return DSS_SUCCESS;
         }
         
         err = dsiface_set_opt(IFACE_UM_PPP_STACK,
                               DSIFACE_OPT_DORM_TIMER,
                               &dwTimeout,
                               noptlen);

         return DSIFaceToDSSIFaceIOCtl_Err(err);
      }

      case DSS_IFACE_IOCTL_GET_ALL_IFACES:
      {
         dss_iface_ioctl_all_ifaces_type* pIFaceInfo = (dss_iface_ioctl_all_ifaces_type*)argval_ptr;

         pIFaceInfo->ifaces[0].name = DSS_IFACE_CDMA_SN;
         pIFaceInfo->ifaces[0].instance = 0;
         pIFaceInfo->ifaces[1].name = DSS_IFACE_LO;
         pIFaceInfo->ifaces[1].instance = 0;
         pIFaceInfo->number_of_ifaces = 2;
         return DSS_SUCCESS;
      }

      default:
         *dss_errno = DS_EINVAL;
         return DSS_ERROR;
   }
}

/*===========================================================================
FUNCTION DSS_SHUTDOWN()
===========================================================================*/
sint15 dss_shutdown
(
  sint15           sockfd,                  /* socket descriptor           */
  uint16           how,                     /* what action to perform      */
  sint15*          dss_errno                /* error number                */
)
{
   *dss_errno = DS_EOPNOTSUPP;
   return DSS_ERROR;
}

#endif // !defined(T_MSM6100) && !defined(T_MSM6250)
