/******************************************************************************
 *
 *  Copyright (C) 2010-2014 Broadcom Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/


/******************************************************************************
 *
 *  NFA interface to LLCP
 *
 ******************************************************************************/
#include <string.h>
#include "nfc_api.h"
#include "nfa_sys.h"
#include "nfa_sys_int.h"
#include "llcp_defs.h"
#include "llcp_api.h"
#include "nfa_p2p_api.h"
#include "nfa_p2p_int.h"

/*****************************************************************************
**  Constants
*****************************************************************************/

/*******************************************************************************
**
** Function         NFA_P2pRegisterServer
**
** Description      This function is called to listen to a SAP as server on LLCP.
**
**                  NFA_P2P_REG_SERVER_EVT will be returned with status and handle.
**
**                  If server_sap is set to NFA_P2P_ANY_SAP, then NFA will allocate
**                  a SAP between LLCP_LOWER_BOUND_SDP_SAP and LLCP_UPPER_BOUND_SDP_SAP
**                  Otherwise, server_sap must be between (LLCP_SDP_SAP + 1) and
**                  LLCP_UPPER_BOUND_SDP_SAP
**
**                  link_type : NFA_P2P_LLINK_TYPE and/or NFA_P2P_DLINK_TYPE
**
** Note:            If 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_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pRegisterServer (UINT8              server_sap,
                                   tNFA_P2P_LINK_TYPE link_type,
                                   char               *p_service_name,
                                   tNFA_P2P_CBACK     *p_cback)
{
    tNFA_P2P_API_REG_SERVER *p_msg;

    P2P_TRACE_API3 ("NFA_P2pRegisterServer (): server_sap:0x%02x, link_type:0x%x, SN:<%s>",
                     server_sap, link_type, p_service_name);

    if (  (server_sap != NFA_P2P_ANY_SAP)
        &&((server_sap <= LLCP_SAP_SDP) ||(server_sap > LLCP_UPPER_BOUND_SDP_SAP))  )
    {
        P2P_TRACE_ERROR2 ("NFA_P2pRegisterServer (): server_sap must be between %d and %d",
                          LLCP_SAP_SDP + 1, LLCP_UPPER_BOUND_SDP_SAP);
        return (NFA_STATUS_FAILED);
    }
    else if (  ((link_type & NFA_P2P_LLINK_TYPE) == 0x00)
             &&((link_type & NFA_P2P_DLINK_TYPE) == 0x00)  )
    {
        P2P_TRACE_ERROR1 ("NFA_P2pRegisterServer(): link type (0x%x) must be specified", link_type);
        return (NFA_STATUS_FAILED);
    }

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

        p_msg->server_sap = server_sap;
        p_msg->link_type  = link_type;

        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->p_cback = p_cback;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}

/*******************************************************************************
**
** Function         NFA_P2pRegisterClient
**
** Description      This function is called to register a client service on LLCP.
**
**                  NFA_P2P_REG_CLIENT_EVT will be returned with status and handle.
**
**                  link_type : NFA_P2P_LLINK_TYPE and/or NFA_P2P_DLINK_TYPE
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pRegisterClient (tNFA_P2P_LINK_TYPE link_type,
                                   tNFA_P2P_CBACK     *p_cback)
{
    tNFA_P2P_API_REG_CLIENT *p_msg;

    P2P_TRACE_API1 ("NFA_P2pRegisterClient (): link_type:0x%x", link_type);

    if (  ((link_type & NFA_P2P_LLINK_TYPE) == 0x00)
        &&((link_type & NFA_P2P_DLINK_TYPE) == 0x00)  )
    {
        P2P_TRACE_ERROR1 ("NFA_P2pRegisterClient (): link type (0x%x) must be specified", link_type);
        return (NFA_STATUS_FAILED);
    }

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

        p_msg->p_cback   = p_cback;
        p_msg->link_type = link_type;

        nfa_sys_sendmsg (p_msg);

        return (NFA_STATUS_OK);
    }

    return (NFA_STATUS_FAILED);
}

/*******************************************************************************
**
** 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_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_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_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_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_P2pConnectBySap
**
** Description      This function is called to create a connection-oriented transport
**                  by a SAP.
**                  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_P2pConnectBySap (tNFA_HANDLE client_handle,
                                 UINT8       dsap,
                                 UINT16      miu,
                                 UINT8       rw)
{
    tNFA_P2P_API_CONNECT *p_msg;
    tNFA_HANDLE           xx;

    P2P_TRACE_API4 ("NFA_P2pConnectBySap (): client_handle:0x%x, DSAP:0x%02X, MIU:%d, RW:%d",
                    client_handle, dsap, 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_P2pConnectBySap (): 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_P2pConnectBySap (): 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;

        p_msg->service_name[LLCP_MAX_SN_LEN] = 0;

        p_msg->dsap    = dsap;
        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_P2pSendUI
**
** Description      This function is called to send data on connectionless
**                  transport.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**                  NFA_STATUS_BAD_LENGTH if data length is more than remote link MIU
**                  NFA_STATUS_CONGESTED  if congested
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pSendUI (tNFA_HANDLE handle,
                           UINT8       dsap,
                           UINT16      length,
                           UINT8      *p_data)
{
    tNFA_P2P_API_SEND_UI *p_msg;
    tNFA_STATUS           ret_status = NFA_STATUS_FAILED;
    tNFA_HANDLE           xx;

    P2P_TRACE_API3 ("NFA_P2pSendUI (): handle:0x%X, DSAP:0x%02X, length:%d", handle, dsap, length);

    GKI_sched_lock ();

    xx = handle & NFA_HANDLE_MASK;

    if (  (xx >= NFA_P2P_NUM_SAP)
        ||(nfa_p2p_cb.sap_cb[xx].p_cback == NULL))
    {
        P2P_TRACE_ERROR1 ("NFA_P2pSendUI (): Handle (0x%X) is not valid", handle);
        ret_status = NFA_STATUS_BAD_HANDLE;
    }
    else if (length > nfa_p2p_cb.remote_link_miu)
    {
        P2P_TRACE_ERROR3 ("NFA_P2pSendUI (): handle:0x%X, length(%d) must be less than remote link MIU(%d)",
                           handle, length, nfa_p2p_cb.remote_link_miu);
        ret_status = NFA_STATUS_BAD_LENGTH;
    }
    else if (nfa_p2p_cb.sap_cb[xx].flags & NFA_P2P_SAP_FLAG_LLINK_CONGESTED)
    {
        P2P_TRACE_WARNING1 ("NFA_P2pSendUI (): handle:0x%X, logical data link is already congested",
                             handle);
        ret_status = NFA_STATUS_CONGESTED;
    }
    else if (LLCP_IsLogicalLinkCongested ((UINT8)xx,
                                          nfa_p2p_cb.sap_cb[xx].num_pending_ui_pdu,
                                          nfa_p2p_cb.total_pending_ui_pdu,
                                          nfa_p2p_cb.total_pending_i_pdu))
    {
        nfa_p2p_cb.sap_cb[xx].flags |= NFA_P2P_SAP_FLAG_LLINK_CONGESTED;

        P2P_TRACE_WARNING1 ("NFA_P2pSendUI(): handle:0x%X, logical data link is congested",
                             handle);
        ret_status = NFA_STATUS_CONGESTED;
    }
    else if ((p_msg = (tNFA_P2P_API_SEND_UI *) GKI_getbuf (sizeof(tNFA_P2P_API_SEND_UI))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_SEND_UI_EVT;

        p_msg->handle  = handle;
        p_msg->dsap    = dsap;

        if ((p_msg->p_msg = (BT_HDR *) GKI_getpoolbuf (LLCP_POOL_ID)) != NULL)
        {
            p_msg->p_msg->len    = length;
            p_msg->p_msg->offset = LLCP_MIN_OFFSET;
            memcpy (((UINT8*) (p_msg->p_msg + 1) + p_msg->p_msg->offset), p_data, length);

            /* increase number of tx UI PDU which is not processed by NFA for congestion control */
            nfa_p2p_cb.sap_cb[xx].num_pending_ui_pdu++;
            nfa_p2p_cb.total_pending_ui_pdu++;
            nfa_sys_sendmsg (p_msg);

            ret_status = NFA_STATUS_OK;
        }
        else
        {
            GKI_freebuf (p_msg);

            nfa_p2p_cb.sap_cb[xx].flags |= NFA_P2P_SAP_FLAG_LLINK_CONGESTED;
            ret_status = NFA_STATUS_CONGESTED;
        }
    }

    GKI_sched_unlock ();

    return (ret_status);
}

/*******************************************************************************
**
** Function         NFA_P2pReadUI
**
** Description      This function is called to read data on connectionless
**                  transport when receiving NFA_P2P_DATA_EVT with NFA_P2P_LLINK_TYPE.
**
**                  - Remote SAP who sent UI PDU is returned.
**                  - Information of UI PDU up to max_data_len is copied into p_data.
**                  - If more information of UI PDU or more UI PDU in queue then more
**                    is returned to TRUE.
**                  - Information of next UI PDU is not concatenated.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**
*******************************************************************************/
tNFA_STATUS NFA_P2pReadUI (tNFA_HANDLE handle,
                           UINT32      max_data_len,
                           UINT8       *p_remote_sap,
                           UINT32      *p_data_len,
                           UINT8       *p_data,
                           BOOLEAN     *p_more)
{
    tNFA_STATUS ret_status;
    tNFA_HANDLE xx;

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

    GKI_sched_lock ();

    xx = handle & NFA_HANDLE_MASK;

    if (  (xx >= NFA_P2P_NUM_SAP)
        ||(nfa_p2p_cb.sap_cb[xx].p_cback == NULL)  )
    {
        P2P_TRACE_ERROR1 ("NFA_P2pReadUI (): Handle (0x%X) is not valid", handle);
        ret_status = NFA_STATUS_BAD_HANDLE;
    }
    else
    {
        *p_more = LLCP_ReadLogicalLinkData ((UINT8)xx,
                                            max_data_len,
                                            p_remote_sap,
                                            p_data_len,
                                            p_data);
        ret_status = NFA_STATUS_OK;
    }

    GKI_sched_unlock ();

    return (ret_status);
}

/*******************************************************************************
**
** Function         NFA_P2pFlushUI
**
** Description      This function is called to flush data on connectionless
**                  transport.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**
*******************************************************************************/
tNFA_STATUS NFA_P2pFlushUI (tNFA_HANDLE handle,
                            UINT32      *p_length)
{
    tNFA_STATUS ret_status;
    tNFA_HANDLE xx;

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

    GKI_sched_lock ();

    xx = handle & NFA_HANDLE_MASK;

    if (  (xx >= NFA_P2P_NUM_SAP)
        ||(nfa_p2p_cb.sap_cb[xx].p_cback == NULL)  )
    {
        P2P_TRACE_ERROR1 ("NFA_P2pFlushUI (): Handle (0x%X) is not valid", handle);
        ret_status = NFA_STATUS_BAD_HANDLE;
        *p_length  = 0;
    }
    else
    {
        *p_length  = LLCP_FlushLogicalLinkRxData ((UINT8)xx);
        ret_status = NFA_STATUS_OK;
    }

    GKI_sched_unlock ();

    return (ret_status);
}

/*******************************************************************************
**
** Function         NFA_P2pSendData
**
** Description      This function is called to send data on connection-oriented
**                  transport.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**                  NFA_STATUS_BAD_LENGTH if data length is more than remote MIU
**                  NFA_STATUS_CONGESTED  if congested
**                  NFA_STATUS_FAILED otherwise
**
*******************************************************************************/
tNFA_STATUS NFA_P2pSendData (tNFA_HANDLE handle,
                             UINT16      length,
                             UINT8      *p_data)
{
    tNFA_P2P_API_SEND_DATA *p_msg;
    tNFA_STATUS            ret_status = NFA_STATUS_FAILED;
    tNFA_HANDLE            xx;

    P2P_TRACE_API2 ("NFA_P2pSendData (): handle:0x%X, length:%d", handle, length);

    GKI_sched_lock ();

    xx = handle & NFA_HANDLE_MASK;
    xx &= ~NFA_P2P_HANDLE_FLAG_CONN;

    if (  (!(handle & NFA_P2P_HANDLE_FLAG_CONN))
        ||(xx >= LLCP_MAX_DATA_LINK)
        ||(nfa_p2p_cb.conn_cb[xx].flags == 0)  )
    {
        P2P_TRACE_ERROR1 ("NFA_P2pSendData (): Handle(0x%X) is not valid", handle);
        ret_status = NFA_STATUS_BAD_HANDLE;
    }
    else if (nfa_p2p_cb.conn_cb[xx].flags & NFA_P2P_CONN_FLAG_REMOTE_RW_ZERO)
    {
        P2P_TRACE_ERROR1 ("NFA_P2pSendData (): handle:0x%X, Remote set RW to 0 (flow off)", handle);
        ret_status = NFA_STATUS_FAILED;
    }
    else if (nfa_p2p_cb.conn_cb[xx].remote_miu < length)
    {
        P2P_TRACE_ERROR2 ("NFA_P2pSendData (): handle:0x%X, Data more than remote MIU(%d)",
                           handle, nfa_p2p_cb.conn_cb[xx].remote_miu);
        ret_status = NFA_STATUS_BAD_LENGTH;
    }
    else if (nfa_p2p_cb.conn_cb[xx].flags & NFA_P2P_CONN_FLAG_CONGESTED)
    {
        P2P_TRACE_WARNING1 ("NFA_P2pSendData (): handle:0x%X, data link connection is already congested",
                            handle);
        ret_status = NFA_STATUS_CONGESTED;
    }
    else if (LLCP_IsDataLinkCongested (nfa_p2p_cb.conn_cb[xx].local_sap,
                                       nfa_p2p_cb.conn_cb[xx].remote_sap,
                                       nfa_p2p_cb.conn_cb[xx].num_pending_i_pdu,
                                       nfa_p2p_cb.total_pending_ui_pdu,
                                       nfa_p2p_cb.total_pending_i_pdu))
    {
        nfa_p2p_cb.conn_cb[xx].flags |= NFA_P2P_CONN_FLAG_CONGESTED;

        P2P_TRACE_WARNING1 ("NFA_P2pSendData (): handle:0x%X, data link connection is congested",
                            handle);
        ret_status = NFA_STATUS_CONGESTED;
    }
    else if ((p_msg = (tNFA_P2P_API_SEND_DATA *) GKI_getbuf (sizeof(tNFA_P2P_API_SEND_DATA))) != NULL)
    {
        p_msg->hdr.event = NFA_P2P_API_SEND_DATA_EVT;

        p_msg->conn_handle  = handle;

        if ((p_msg->p_msg = (BT_HDR *) GKI_getpoolbuf (LLCP_POOL_ID)) != NULL)
        {
            p_msg->p_msg->len    = length;
            p_msg->p_msg->offset = LLCP_MIN_OFFSET;
            memcpy (((UINT8*) (p_msg->p_msg + 1) + p_msg->p_msg->offset), p_data, length);

            /* increase number of tx I PDU which is not processed by NFA for congestion control */
            nfa_p2p_cb.conn_cb[xx].num_pending_i_pdu++;
            nfa_p2p_cb.total_pending_i_pdu++;
            nfa_sys_sendmsg (p_msg);

            ret_status = NFA_STATUS_OK;
        }
        else
        {
            GKI_freebuf (p_msg);
            nfa_p2p_cb.conn_cb[xx].flags |= NFA_P2P_CONN_FLAG_CONGESTED;
            ret_status = NFA_STATUS_CONGESTED;
        }
    }

    GKI_sched_unlock ();

    return (ret_status);
}

/*******************************************************************************
**
** Function         NFA_P2pReadData
**
** Description      This function is called to read data on connection-oriented
**                  transport when receiving NFA_P2P_DATA_EVT with NFA_P2P_DLINK_TYPE.
**
**                  - Information of I PDU is copied into p_data up to max_data_len.
**                  - If more information of I PDU or more I PDU in queue, then more
**                    is returned to TRUE.
**                  - Information of next I PDU is not concatenated.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**
*******************************************************************************/
tNFA_STATUS NFA_P2pReadData (tNFA_HANDLE handle,
                             UINT32      max_data_len,
                             UINT32      *p_data_len,
                             UINT8       *p_data,
                             BOOLEAN     *p_more)
{
    tNFA_STATUS ret_status;
    tNFA_HANDLE xx;

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

    GKI_sched_lock ();

    xx = handle & NFA_HANDLE_MASK;
    xx &= ~NFA_P2P_HANDLE_FLAG_CONN;

    if (  (!(handle & NFA_P2P_HANDLE_FLAG_CONN))
        ||(xx >= LLCP_MAX_DATA_LINK)
        ||(nfa_p2p_cb.conn_cb[xx].flags == 0)  )
    {
        P2P_TRACE_ERROR1 ("NFA_P2pReadData (): Handle(0x%X) is not valid", handle);
        ret_status = NFA_STATUS_BAD_HANDLE;
    }
    else
    {
        *p_more = LLCP_ReadDataLinkData (nfa_p2p_cb.conn_cb[xx].local_sap,
                                         nfa_p2p_cb.conn_cb[xx].remote_sap,
                                         max_data_len,
                                         p_data_len,
                                         p_data);
        ret_status = NFA_STATUS_OK;
    }

    GKI_sched_unlock ();

    return (ret_status);
}

/*******************************************************************************
**
** Function         NFA_P2pFlushData
**
** Description      This function is called to flush data on connection-oriented
**                  transport.
**
** Returns          NFA_STATUS_OK if successfully initiated
**                  NFA_STATUS_BAD_HANDLE if handle is not valid
**
*******************************************************************************/
tNFA_STATUS NFA_P2pFlushData (tNFA_HANDLE handle,
                              UINT32      *p_length)
{
    tNFA_STATUS ret_status;
    tNFA_HANDLE xx;

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

    GKI_sched_lock ();

    xx = handle & NFA_HANDLE_MASK;
    xx &= ~NFA_P2P_HANDLE_FLAG_CONN;

    if (  (!(handle & NFA_P2P_HANDLE_FLAG_CONN))
        ||(xx >= LLCP_MAX_DATA_LINK)
        ||(nfa_p2p_cb.conn_cb[xx].flags == 0)  )
    {
        P2P_TRACE_ERROR1 ("NFA_P2pFlushData (): Handle(0x%X) is not valid", handle);
        ret_status = NFA_STATUS_BAD_HANDLE;
    }
    else
    {
        *p_length = LLCP_FlushDataLinkRxData (nfa_p2p_cb.conn_cb[xx].local_sap,
                                              nfa_p2p_cb.conn_cb[xx].remote_sap);
        ret_status = NFA_STATUS_OK;
    }

    GKI_sched_unlock ();

    return (ret_status);
}

/*******************************************************************************
**
** 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_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_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_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_P2pGetLLCPConfig
**
** Description      This function is called to read LLCP config parameters.
**
**                  Parameters descriptions
**                  - Local Link MIU
**                  - Option parameter
**                  - Response Waiting Time Index
**                  - Local Link Timeout
**                  - Inactivity Timeout as initiator role
**                  - Inactivity Timeout as target role
**                  - Delay SYMM response
**                  - Data link connection timeout
**                  - Delay timeout to send first PDU as initiator
**
** Returns          None
**
*******************************************************************************/
void NFA_P2pGetLLCPConfig (UINT16 *p_link_miu,
                           UINT8  *p_opt,
                           UINT8  *p_wt,
                           UINT16 *p_link_timeout,
                           UINT16 *p_inact_timeout_init,
                           UINT16 *p_inact_timeout_target,
                           UINT16 *p_symm_delay,
                           UINT16 *p_data_link_timeout,
                           UINT16 *p_delay_first_pdu_timeout)
{
    LLCP_GetConfig (p_link_miu,
                    p_opt,
                    p_wt,
                    p_link_timeout,
                    p_inact_timeout_init,
                    p_inact_timeout_target,
                    p_symm_delay,
                    p_data_link_timeout,
                    p_delay_first_pdu_timeout);

    P2P_TRACE_API4 ("NFA_P2pGetLLCPConfig () link_miu:%d, opt:0x%02X, wt:%d, link_timeout:%d",
                     *p_link_miu, *p_opt, *p_wt, *p_link_timeout);
    P2P_TRACE_API4 ("                       inact_timeout(init:%d, target:%d), symm_delay:%d, data_link_timeout:%d",
                     *p_inact_timeout_init, *p_inact_timeout_target, *p_symm_delay, *p_data_link_timeout);
    P2P_TRACE_API1 ("                       delay_first_pdu_timeout:%d", *p_delay_first_pdu_timeout);

}

/*******************************************************************************
**
** Function         NFA_P2pSetTraceLevel
**
** Description      This function sets the trace level for P2P.  If called with
**                  a value of 0xFF, it simply returns the current trace level.
**
** Returns          The new or current trace level
**
*******************************************************************************/
UINT8 NFA_P2pSetTraceLevel (UINT8 new_level)
{
    if (new_level != 0xFF)
        nfa_p2p_cb.trace_level = new_level;

    return (nfa_p2p_cb.trace_level);
}