/*******************************************************************************
**
** Function         NFA_P2pGetLinkInfo
**
** Description      This function is called to get local/remote link MIU and
**                  Well-Known Service list encoded as a 16-bit field of connected LLCP.
**                  NFA_P2P_LINK_INFO_EVT will be returned.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if server or client is not registered
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pGetLinkInfo (tNFA_HANDLE handle)
{
    tNFA_P2P_API_GET_LINK_INFO *p_msg;
    tNFA_HANDLE                 xx;

    P2P_TRACE_API1 ("NFA_P2pGetLinkInfo (): handle:0x%x", handle);

    if (nfa_p2p_cb.llcp_state != NFA_P2P_LLCP_STATE_ACTIVATED)
    {
        P2P_TRACE_ERROR0 ("NFA_P2pGetLinkInfo (): LLCP link is not activated");
        return (NFA_STATUS_FAILED);
    }

    xx = handle & NFA_HANDLE_MASK;

    if (  (xx >= NFA_P2P_NUM_SAP)
        ||(nfa_p2p_cb.sap_cb[xx].p_cback == NULL)  )
    {
        P2P_TRACE_ERROR0 ("NFA_P2pGetLinkInfo (): Handle is invalid or not registered");
        return (NFA_STATUS_BAD_HANDLE);
    }

    if ((p_msg = (tNFA_P2P_API_GET_LINK_INFO *) GKI_getbuf (sizeof (tNFA_P2P_API_GET_LINK_INFO))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_GET_LINK_INFO_EVT;

        p_msg->handle = handle;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}
/*******************************************************************************
**
** Function         NFA_P2pAcceptConn
**
** Description      This function is called to accept a request of data link
**                  connection to a listening SAP on LLCP after receiving
**                  NFA_P2P_CONN_REQ_EVT.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pAcceptConn (tNFA_HANDLE handle,
                               UINT16      miu,
                               UINT8       rw)
{
    tNFA_P2P_API_ACCEPT_CONN *p_msg;
    tNFA_HANDLE               xx;

    P2P_TRACE_API3 ("NFA_P2pAcceptConn (): handle:0x%02X, MIU:%d, RW:%d", handle, miu, rw);

    xx = handle & NFA_HANDLE_MASK;

    if (!(xx & NFA_P2P_HANDLE_FLAG_CONN))
    {
        P2P_TRACE_ERROR0 ("NFA_P2pAcceptConn (): Connection Handle is not valid");
        return (NFA_STATUS_BAD_HANDLE);
    }
    else
    {
        xx &= ~NFA_P2P_HANDLE_FLAG_CONN;
    }

    if (  (xx >= LLCP_MAX_DATA_LINK)
        ||(nfa_p2p_cb.conn_cb[xx].flags == 0)  )
    {
        P2P_TRACE_ERROR0 ("NFA_P2pAcceptConn (): Connection Handle is not valid");
        return (NFA_STATUS_BAD_HANDLE);
    }

    if ((miu < LLCP_DEFAULT_MIU) || (nfa_p2p_cb.local_link_miu < miu))
    {
        P2P_TRACE_ERROR3 ("NFA_P2pAcceptConn (): MIU(%d) must be between %d and %d",
                            miu, LLCP_DEFAULT_MIU, nfa_p2p_cb.local_link_miu);
    }
    else if ((p_msg = (tNFA_P2P_API_ACCEPT_CONN *) GKI_getbuf (sizeof (tNFA_P2P_API_ACCEPT_CONN))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_ACCEPT_CONN_EVT;

        p_msg->conn_handle  = handle;
        p_msg->miu          = miu;
        p_msg->rw           = rw;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}
/*******************************************************************************
**
** Function         nfa_p2p_evt_hdlr
**
** Description      Processing event for NFA P2P
**
**
** Returns          TRUE if p_msg needs to be deallocated
**
*******************************************************************************/
static BOOLEAN nfa_p2p_evt_hdlr (BT_HDR *p_hdr)
{
    BOOLEAN delete_msg = TRUE;
    UINT16  event;

    tNFA_P2P_MSG *p_msg = (tNFA_P2P_MSG *)p_hdr;

#if (BT_TRACE_VERBOSE == TRUE)
    P2P_TRACE_DEBUG2 ("nfa_p2p_evt_hdlr (): LLCP State [%s], Event [%s]",
                       nfa_p2p_llcp_state_code (nfa_p2p_cb.llcp_state),
                       nfa_p2p_evt_code (p_msg->hdr.event));
#else
    P2P_TRACE_DEBUG2 ("nfa_p2p_evt_hdlr (): State 0x%02x, Event 0x%02x",
                       nfa_p2p_cb.llcp_state, p_msg->hdr.event);
#endif

    event = p_msg->hdr.event & 0x00ff;

    /* execute action functions */
    if (event < NFA_P2P_NUM_ACTIONS)
    {
        delete_msg = (*nfa_p2p_action[event]) (p_msg);
    }
    else
    {
        P2P_TRACE_ERROR0 ("Unhandled event");
    }

    return delete_msg;
}
/*******************************************************************************
**
** Function         NFA_P2pDeregister
**
** Description      This function is called to stop listening to a SAP as server
**                  or stop client service on LLCP.
**
** Note:            If this function is called to de-register a server and RF discovery
**                  is started, NFA_StopRfDiscovery()/NFA_RF_DISCOVERY_STOPPED_EVT
**                  should happen before calling this function
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pDeregister (tNFA_HANDLE handle)
{
    tNFA_P2P_API_DEREG *p_msg;
    tNFA_HANDLE         xx;

    P2P_TRACE_API1 ("NFA_P2pDeregister (): handle:0x%02X", handle);

    xx = handle & NFA_HANDLE_MASK;

    if (  (xx >= NFA_P2P_NUM_SAP)
        ||(nfa_p2p_cb.sap_cb[xx].p_cback == NULL)  )
    {
        P2P_TRACE_ERROR0 ("NFA_P2pDeregister (): Handle is invalid or not registered");
        return (NFA_STATUS_BAD_HANDLE);
    }

    if ((p_msg = (tNFA_P2P_API_DEREG *) GKI_getbuf (sizeof (tNFA_P2P_API_DEREG))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_DEREG_EVT;

        p_msg->handle    = handle;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}
/*******************************************************************************
**
** Function         NFA_P2pSetLocalBusy
**
** Description      This function is called to stop or resume incoming data on
**                  connection-oriented transport.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pSetLocalBusy (tNFA_HANDLE conn_handle,
                                 BOOLEAN     is_busy)
{
    tNFA_P2P_API_SET_LOCAL_BUSY *p_msg;
    tNFA_HANDLE                  xx;

    P2P_TRACE_API2 ("NFA_P2pSetLocalBusy (): conn_handle:0x%02X, is_busy:%d", conn_handle, is_busy);

    xx = conn_handle & NFA_HANDLE_MASK;

    if (!(xx & NFA_P2P_HANDLE_FLAG_CONN))
    {
        P2P_TRACE_ERROR0 ("NFA_P2pSetLocalBusy (): Connection Handle is not valid");
        return (NFA_STATUS_BAD_HANDLE);
    }
    else
    {
        xx &= ~NFA_P2P_HANDLE_FLAG_CONN;
    }

    if (  (xx >= LLCP_MAX_DATA_LINK)
        ||(nfa_p2p_cb.conn_cb[xx].flags == 0)  )
    {
        P2P_TRACE_ERROR0 ("NFA_P2pSetLocalBusy (): Connection Handle is not valid");
        return (NFA_STATUS_BAD_HANDLE);
    }

    if ((p_msg = (tNFA_P2P_API_SET_LOCAL_BUSY *) GKI_getbuf (sizeof (tNFA_P2P_API_SET_LOCAL_BUSY))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_SET_LOCAL_BUSY_EVT;

        p_msg->conn_handle = conn_handle;
        p_msg->is_busy     = is_busy;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}
/*******************************************************************************
**
** Function         NFA_P2pDisconnect
**
** Description      This function is called to disconnect an existing or
**                  connecting data link connection.
**
**                  discard any pending data on data link connection if flush is set to TRUE
**
**                  NFA_P2P_DISC_EVT will be returned after data link connection is disconnected
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pDisconnect (tNFA_HANDLE handle, BOOLEAN flush)
{
    tNFA_P2P_API_DISCONNECT *p_msg;
    tNFA_HANDLE              xx;

    P2P_TRACE_API2 ("NFA_P2pDisconnect (): handle:0x%02X, flush=%d", handle, flush);

    xx = handle & NFA_HANDLE_MASK;

    if (xx & NFA_P2P_HANDLE_FLAG_CONN)
    {
        xx &= ~NFA_P2P_HANDLE_FLAG_CONN;

        if (  (xx >= LLCP_MAX_DATA_LINK)
            ||(nfa_p2p_cb.conn_cb[xx].flags == 0)  )
        {
            P2P_TRACE_ERROR0 ("NFA_P2pDisconnect (): Connection Handle is not valid");
            return (NFA_STATUS_BAD_HANDLE);
        }
    }
    else
    {
        P2P_TRACE_ERROR0 ("NFA_P2pDisconnect (): Handle is not valid");
        return (NFA_STATUS_BAD_HANDLE);
    }

    if ((p_msg = (tNFA_P2P_API_DISCONNECT *) GKI_getbuf (sizeof (tNFA_P2P_API_DISCONNECT))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_DISCONNECT_EVT;

        p_msg->conn_handle  = handle;
        p_msg->flush        = flush;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}
/*******************************************************************************
**
** Function         NFA_P2pGetRemoteSap
**
** Description      This function is called to get SAP associated by service name
**                  on connected remote LLCP.
**                  NFA_P2P_SDP_EVT will be returned.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if server or client is not registered
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pGetRemoteSap (tNFA_HANDLE handle,
                                 char        *p_service_name)
{
    tNFA_P2P_API_GET_REMOTE_SAP *p_msg;
    tNFA_HANDLE                  xx;

    P2P_TRACE_API2 ("NFA_P2pGetRemoteSap(): handle:0x%x, SN:<%s>", handle, p_service_name);

    if (nfa_p2p_cb.llcp_state != NFA_P2P_LLCP_STATE_ACTIVATED)
    {
        P2P_TRACE_ERROR0 ("NFA_P2pGetRemoteSap(): LLCP link is not activated");
        return (NFA_STATUS_FAILED);
    }

    xx = handle & NFA_HANDLE_MASK;

    if (  (xx >= NFA_P2P_NUM_SAP)
        ||(nfa_p2p_cb.sap_cb[xx].p_cback == NULL)  )
    {
        P2P_TRACE_ERROR0 ("NFA_P2pGetRemoteSap (): Handle is invalid or not registered");
        return (NFA_STATUS_BAD_HANDLE);
    }

    if ((p_msg = (tNFA_P2P_API_GET_REMOTE_SAP *) GKI_getbuf (sizeof (tNFA_P2P_API_GET_REMOTE_SAP))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_GET_REMOTE_SAP_EVT;

        p_msg->handle = handle;

        BCM_STRNCPY_S (p_msg->service_name, sizeof (p_msg->service_name), p_service_name, LLCP_MAX_SN_LEN);
        p_msg->service_name[LLCP_MAX_SN_LEN] = 0;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}
/*******************************************************************************
**
** Function         NFA_P2pRejectConn
**
** Description      This function is called to reject a request of data link
**                  connection to a listening SAP on LLCP after receiving
**                  NFA_P2P_CONN_REQ_EVT.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pRejectConn (tNFA_HANDLE handle)
{
    tNFA_P2P_API_REJECT_CONN *p_msg;
    tNFA_HANDLE               xx;

    P2P_TRACE_API1 ("NFA_P2pRejectConn (): handle:0x%02X", handle);

    xx = handle & NFA_HANDLE_MASK;

    if (!(xx & NFA_P2P_HANDLE_FLAG_CONN))
    {
        P2P_TRACE_ERROR0 ("NFA_P2pRejectConn (): Connection Handle is not valid");
        return (NFA_STATUS_BAD_HANDLE);
    }
    else
    {
        xx &= ~NFA_P2P_HANDLE_FLAG_CONN;
    }

    if (  (xx >= LLCP_MAX_DATA_LINK)
        ||(nfa_p2p_cb.conn_cb[xx].flags == 0)  )
    {
        P2P_TRACE_ERROR0 ("NFA_P2pRejectConn (): Connection Handle is not valid");
        return (NFA_STATUS_BAD_HANDLE);
    }

    if ((p_msg = (tNFA_P2P_API_REJECT_CONN *) GKI_getbuf (sizeof (tNFA_P2P_API_REJECT_CONN))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_REJECT_CONN_EVT;

        p_msg->conn_handle  = handle;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}
/*******************************************************************************
**
** Function         NFA_P2pConnectByName
**
** Description      This function is called to create a connection-oriented transport
**                  by a service name.
**                  NFA_P2P_CONNECTED_EVT if success
**                  NFA_P2P_DISC_EVT if failed
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if client is not registered
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pConnectByName (tNFA_HANDLE client_handle,
                                  char        *p_service_name,
                                  UINT16      miu,
                                  UINT8       rw)
{
    tNFA_P2P_API_CONNECT *p_msg;
    tNFA_HANDLE           xx;

    P2P_TRACE_API4 ("NFA_P2pConnectByName (): client_handle:0x%x, SN:<%s>, MIU:%d, RW:%d",
                    client_handle, p_service_name, miu, rw);

    xx = client_handle & NFA_HANDLE_MASK;

    if (  (xx >= NFA_P2P_NUM_SAP)
        ||(nfa_p2p_cb.sap_cb[xx].p_cback == NULL)  )
    {
        P2P_TRACE_ERROR0 ("NFA_P2pConnectByName (): Client Handle is not valid");
        return (NFA_STATUS_BAD_HANDLE);
    }

    if (  (miu < LLCP_DEFAULT_MIU)
        ||(nfa_p2p_cb.llcp_state != NFA_P2P_LLCP_STATE_ACTIVATED)
        ||(nfa_p2p_cb.local_link_miu < miu)  )
    {
        P2P_TRACE_ERROR3 ("NFA_P2pConnectByName (): MIU(%d) must be between %d and %d or LLCP link is not activated",
                            miu, LLCP_DEFAULT_MIU, nfa_p2p_cb.local_link_miu);
    }
    else if ((p_msg = (tNFA_P2P_API_CONNECT *) GKI_getbuf (sizeof (tNFA_P2P_API_CONNECT))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_CONNECT_EVT;

        BCM_STRNCPY_S (p_msg->service_name, sizeof (p_msg->service_name), p_service_name, LLCP_MAX_SN_LEN);
        p_msg->service_name[LLCP_MAX_SN_LEN] = 0;

        p_msg->dsap    = LLCP_INVALID_SAP;
        p_msg->miu     = miu;
        p_msg->rw      = rw;
        p_msg->client_handle = client_handle;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}
/*******************************************************************************
**
** Function         NFA_P2pSetLLCPConfig
**
** Description      This function is called to change LLCP config parameters.
**                  Application must call while LLCP is not activated.
**
**                  Parameters descriptions (default value)
**                  - Local Link MIU (LLCP_MIU)
**                  - Option parameter (LLCP_OPT_VALUE)
**                  - Response Waiting Time Index (LLCP_WAITING_TIME)
**                  - Local Link Timeout (LLCP_LTO_VALUE)
**                  - Inactivity Timeout as initiator role (LLCP_INIT_INACTIVITY_TIMEOUT)
**                  - Inactivity Timeout as target role (LLCP_TARGET_INACTIVITY_TIMEOUT)
**                  - Delay SYMM response (LLCP_DELAY_RESP_TIME)
**                  - Data link connection timeout (LLCP_DATA_LINK_CONNECTION_TOUT)
**                  - Delay timeout to send first PDU as initiator (LLCP_DELAY_TIME_TO_SEND_FIRST_PDU)
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pSetLLCPConfig (UINT16  link_miu,
                                  UINT8   opt,
                                  UINT8   wt,
                                  UINT16  link_timeout,
                                  UINT16  inact_timeout_init,
                                  UINT16  inact_timeout_target,
                                  UINT16  symm_delay,
                                  UINT16  data_link_timeout,
                                  UINT16  delay_first_pdu_timeout)
{
    tNFA_P2P_API_SET_LLCP_CFG *p_msg;

    P2P_TRACE_API4 ("NFA_P2pSetLLCPConfig ():link_miu:%d, opt:0x%02X, wt:%d, link_timeout:%d",
                     link_miu, opt, wt, link_timeout);
    P2P_TRACE_API4 ("                       inact_timeout(init:%d, target:%d), symm_delay:%d, data_link_timeout:%d",
                     inact_timeout_init, inact_timeout_target, symm_delay, data_link_timeout);
    P2P_TRACE_API1 ("                       delay_first_pdu_timeout:%d", delay_first_pdu_timeout);

    if (nfa_p2p_cb.llcp_state == NFA_P2P_LLCP_STATE_ACTIVATED)
    {
        P2P_TRACE_ERROR0 ("NFA_P2pSetLLCPConfig (): LLCP link is activated");
        return (NFA_STATUS_FAILED);
    }

    if ((p_msg = (tNFA_P2P_API_SET_LLCP_CFG *) GKI_getbuf (sizeof (tNFA_P2P_API_SET_LLCP_CFG))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_SET_LLCP_CFG_EVT;

        p_msg->link_miu             = link_miu;
        p_msg->opt                  = opt;
        p_msg->wt                   = wt;
        p_msg->link_timeout         = link_timeout;
        p_msg->inact_timeout_init   = inact_timeout_init;
        p_msg->inact_timeout_target = inact_timeout_target;
        p_msg->symm_delay           = symm_delay;
        p_msg->data_link_timeout    = data_link_timeout;
        p_msg->delay_first_pdu_timeout = delay_first_pdu_timeout;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}
/*******************************************************************************
**
** Function         nfa_p2p_discovery_cback
**
** Description      Processing event from discovery callback for listening
**
**
** Returns          None
**
*******************************************************************************/
void nfa_p2p_discovery_cback (tNFA_DM_RF_DISC_EVT event, tNFC_DISCOVER *p_data)
{
    tNFA_CONN_EVT_DATA evt_data;

    P2P_TRACE_DEBUG1 ("nfa_p2p_discovery_cback (): event:0x%02X", event);

    switch (event)
    {
    case NFA_DM_RF_DISC_START_EVT:
        if (p_data->status == NFC_STATUS_OK)
        {
            nfa_p2p_cb.llcp_state    = NFA_P2P_LLCP_STATE_LISTENING;
            nfa_p2p_cb.rf_disc_state = NFA_DM_RFST_DISCOVERY;
        }
        break;

    case NFA_DM_RF_DISC_ACTIVATED_EVT:

        nfa_p2p_cb.rf_disc_state = NFA_DM_RFST_LISTEN_ACTIVE;

        /* notify NFC link activation */
        memcpy (&(evt_data.activated.activate_ntf),
                &(p_data->activate),
                sizeof (tNFC_ACTIVATE_DEVT));
        nfa_dm_conn_cback_event_notify (NFA_ACTIVATED_EVT, &evt_data);

        if ((p_data->activate.protocol        == NFC_PROTOCOL_NFC_DEP)
          &&(p_data->activate.intf_param.type == NFC_INTERFACE_NFC_DEP))
        {
            nfa_p2p_activate_llcp (p_data);

            /* stop timer not to deactivate LLCP link on passive mode */
            nfa_sys_stop_timer (&nfa_p2p_cb.active_listen_restore_timer);
        }
        break;

    case NFA_DM_RF_DISC_DEACTIVATED_EVT:

        if (  (nfa_p2p_cb.rf_disc_state != NFA_DM_RFST_LISTEN_ACTIVE)
            &&(nfa_p2p_cb.rf_disc_state != NFA_DM_RFST_LISTEN_SLEEP)  )
        {
            /* this is not for P2P listen
            ** DM broadcasts deactivaiton event in listen sleep state.
            */
            break;
        }

        /* notify deactivation */
        if (  (p_data->deactivate.type == NFC_DEACTIVATE_TYPE_SLEEP)
            ||(p_data->deactivate.type == NFC_DEACTIVATE_TYPE_SLEEP_AF)  )
        {
            nfa_p2p_cb.rf_disc_state  = NFA_DM_RFST_LISTEN_SLEEP;
            evt_data.deactivated.type = NFA_DEACTIVATE_TYPE_SLEEP;
        }
        else
        {
            nfa_p2p_cb.rf_disc_state  = NFA_DM_RFST_DISCOVERY;
            evt_data.deactivated.type = NFA_DEACTIVATE_TYPE_IDLE;
        }
        nfa_dm_conn_cback_event_notify (NFA_DEACTIVATED_EVT, &evt_data);
        break;

    default:
        P2P_TRACE_ERROR0 ("Unexpected event");
        break;
    }
}