NFC_NCIHALx_AR2000.09.00.02_OpnSrc
diff --git a/src/nfc/llcp/llcp_dlc.cc b/src/nfc/llcp/llcp_dlc.cc
new file mode 100644
index 0000000..7f0ae4f
--- /dev/null
+++ b/src/nfc/llcp/llcp_dlc.cc
@@ -0,0 +1,1427 @@
+/******************************************************************************
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ *  This file contains the LLCP Data Link Connection Management
+ *
+ ******************************************************************************/
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <base/logging.h>
+
+#include "bt_types.h"
+#include "gki.h"
+#include "llcp_defs.h"
+#include "llcp_int.h"
+#include "nfc_int.h"
+
+using android::base::StringPrintf;
+
+static tLLCP_STATUS llcp_dlsm_idle(tLLCP_DLCB* p_dlcb, tLLCP_DLC_EVENT event,
+                                   void* p_data);
+static tLLCP_STATUS llcp_dlsm_w4_remote_resp(tLLCP_DLCB* p_dlcb,
+                                             tLLCP_DLC_EVENT event,
+                                             void* p_data);
+static tLLCP_STATUS llcp_dlsm_w4_local_resp(tLLCP_DLCB* p_dlcb,
+                                            tLLCP_DLC_EVENT event,
+                                            void* p_data);
+static tLLCP_STATUS llcp_dlsm_connected(tLLCP_DLCB* p_dlcb,
+                                        tLLCP_DLC_EVENT event, void* p_data);
+static tLLCP_STATUS llcp_dlsm_w4_remote_dm(tLLCP_DLCB* p_dlcb,
+                                           tLLCP_DLC_EVENT event);
+static std::string llcp_dlsm_get_state_name(tLLCP_DLC_STATE state);
+static std::string llcp_dlsm_get_event_name(tLLCP_DLC_EVENT event);
+
+extern bool nfc_debug_enabled;
+extern unsigned char appl_dta_mode_flag;
+
+/*******************************************************************************
+**
+** Function         llcp_dlsm_execute
+**
+** Description      This function executes the state machine for data link
+**                  connection.
+**
+** Returns          tLLCP_STATUS
+**
+*******************************************************************************/
+tLLCP_STATUS llcp_dlsm_execute(tLLCP_DLCB* p_dlcb, tLLCP_DLC_EVENT event,
+                               void* p_data) {
+  tLLCP_STATUS status;
+
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("DLC (0x%02X) - state: %s, evt: %s", p_dlcb->local_sap,
+                      llcp_dlsm_get_state_name(p_dlcb->state).c_str(),
+                      llcp_dlsm_get_event_name(event).c_str());
+
+  switch (p_dlcb->state) {
+    case LLCP_DLC_STATE_IDLE:
+      status = llcp_dlsm_idle(p_dlcb, event, p_data);
+      break;
+
+    case LLCP_DLC_STATE_W4_REMOTE_RESP:
+      status = llcp_dlsm_w4_remote_resp(p_dlcb, event, p_data);
+      break;
+
+    case LLCP_DLC_STATE_W4_LOCAL_RESP:
+      status = llcp_dlsm_w4_local_resp(p_dlcb, event, p_data);
+      break;
+
+    case LLCP_DLC_STATE_CONNECTED:
+      status = llcp_dlsm_connected(p_dlcb, event, p_data);
+      break;
+
+    case LLCP_DLC_STATE_W4_REMOTE_DM:
+      status = llcp_dlsm_w4_remote_dm(p_dlcb, event);
+      break;
+
+    default:
+      status = LLCP_STATUS_FAIL;
+      break;
+  }
+
+  return status;
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlsm_idle
+**
+** Description      Data link connection is in idle state
+**
+** Returns          tLLCP_STATUS
+**
+*******************************************************************************/
+static tLLCP_STATUS llcp_dlsm_idle(tLLCP_DLCB* p_dlcb, tLLCP_DLC_EVENT event,
+                                   void* p_data) {
+  tLLCP_STATUS status = LLCP_STATUS_SUCCESS;
+  tLLCP_SAP_CBACK_DATA data;
+  tLLCP_CONNECTION_PARAMS* p_params;
+
+  switch (event) {
+    case LLCP_DLC_EVENT_API_CONNECT_REQ:
+
+      /* upper layer requests to create data link connection */
+      p_params = (tLLCP_CONNECTION_PARAMS*)p_data;
+
+      status = llcp_util_send_connect(p_dlcb, p_params);
+
+      if (status == LLCP_STATUS_SUCCESS) {
+        p_dlcb->local_miu = p_params->miu;
+        p_dlcb->local_rw = p_params->rw;
+
+        /* wait for response from peer device */
+        p_dlcb->state = LLCP_DLC_STATE_W4_REMOTE_RESP;
+
+        nfc_start_quick_timer(&p_dlcb->timer, NFC_TTYPE_LLCP_DATA_LINK,
+                              (uint32_t)(llcp_cb.lcb.data_link_timeout *
+                                         QUICK_TIMER_TICKS_PER_SEC) /
+                                  1000);
+      }
+      break;
+
+    case LLCP_DLC_EVENT_PEER_CONNECT_IND:
+
+      /* peer device requests to create data link connection */
+      p_params = (tLLCP_CONNECTION_PARAMS*)p_data;
+
+      if (p_params->miu > llcp_cb.lcb.peer_miu) {
+        LOG(WARNING) << StringPrintf(
+            "Peer sent data link MIU bigger than peer's "
+            "link MIU");
+        p_params->miu = llcp_cb.lcb.peer_miu;
+      }
+
+      data.connect_ind.event = LLCP_SAP_EVT_CONNECT_IND;
+      data.connect_ind.remote_sap = p_dlcb->remote_sap;
+      data.connect_ind.local_sap = p_dlcb->local_sap;
+      data.connect_ind.miu = p_params->miu;
+      data.connect_ind.rw = p_params->rw;
+      data.connect_ind.p_service_name = p_params->sn;
+      data.connect_ind.server_sap = p_dlcb->local_sap;
+
+      p_dlcb->remote_miu = p_params->miu;
+      p_dlcb->remote_rw = p_params->rw;
+
+      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+          "Remote MIU:%d, RW:%d", p_dlcb->remote_miu, p_dlcb->remote_rw);
+
+      /* wait for response from upper layer */
+      p_dlcb->state = LLCP_DLC_STATE_W4_LOCAL_RESP;
+
+      nfc_start_quick_timer(&p_dlcb->timer, NFC_TTYPE_LLCP_DATA_LINK,
+                            (uint32_t)(llcp_cb.lcb.data_link_timeout *
+                                       QUICK_TIMER_TICKS_PER_SEC) /
+                                1000);
+
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+
+      break;
+
+    default:
+      LOG(ERROR) << StringPrintf("Unexpected event");
+      status = LLCP_STATUS_FAIL;
+      break;
+  }
+
+  return status;
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlsm_w4_remote_resp
+**
+** Description      data link connection is waiting for connection confirm from
+**                  peer
+**
+** Returns          tLLCP_STATUS
+**
+*******************************************************************************/
+static tLLCP_STATUS llcp_dlsm_w4_remote_resp(tLLCP_DLCB* p_dlcb,
+                                             tLLCP_DLC_EVENT event,
+                                             void* p_data) {
+  tLLCP_STATUS status = LLCP_STATUS_SUCCESS;
+  tLLCP_SAP_CBACK_DATA data;
+  tLLCP_CONNECTION_PARAMS* p_params;
+
+  switch (event) {
+    case LLCP_DLC_EVENT_PEER_CONNECT_CFM:
+
+      /* peer device accepted data link connection */
+      nfc_stop_quick_timer(&p_dlcb->timer);
+
+      p_params = (tLLCP_CONNECTION_PARAMS*)p_data;
+
+      /* data link MIU must be up to link MIU */
+      if (p_params->miu > llcp_cb.lcb.peer_miu) {
+        LOG(WARNING) << StringPrintf(
+            "Peer sent data link MIU bigger than "
+            "peer's link MIU");
+        p_params->miu = llcp_cb.lcb.peer_miu;
+      }
+
+      p_dlcb->remote_miu = p_params->miu;
+      p_dlcb->remote_rw = p_params->rw;
+
+      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+          "Remote MIU:%d, RW:%d", p_dlcb->remote_miu, p_dlcb->remote_rw);
+
+      p_dlcb->state = LLCP_DLC_STATE_CONNECTED;
+      llcp_util_adjust_dl_rx_congestion();
+
+      data.connect_resp.event = LLCP_SAP_EVT_CONNECT_RESP;
+      data.connect_resp.remote_sap = p_dlcb->remote_sap;
+      data.connect_resp.local_sap = p_dlcb->local_sap;
+      data.connect_resp.miu = p_params->miu;
+      data.connect_resp.rw = p_params->rw;
+
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+
+      if (llcp_cb.overall_rx_congested) {
+        p_dlcb->flags |= LLCP_DATA_LINK_FLAG_PENDING_RR_RNR;
+      }
+      break;
+
+    case LLCP_DLC_EVENT_PEER_DISCONNECT_RESP:
+    case LLCP_DLC_EVENT_TIMEOUT:
+
+      /* peer device rejected connection or didn't respond */
+      data.disconnect_resp.event = LLCP_SAP_EVT_DISCONNECT_RESP;
+      data.disconnect_resp.local_sap = p_dlcb->local_sap;
+      data.disconnect_resp.remote_sap = p_dlcb->remote_sap;
+      data.disconnect_resp.reason = *((uint8_t*)p_data);
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+
+      /* stop timer, flush any pending data in queue and deallocate control
+       * block */
+      llcp_util_deallocate_data_link(p_dlcb);
+
+      llcp_util_adjust_dl_rx_congestion();
+      break;
+
+    case LLCP_DLC_EVENT_FRAME_ERROR:
+    case LLCP_DLC_EVENT_LINK_ERROR:
+
+      /* received bad frame or link is deactivated */
+      data.disconnect_ind.event = LLCP_SAP_EVT_DISCONNECT_IND;
+      data.disconnect_ind.local_sap = p_dlcb->local_sap;
+      data.disconnect_ind.remote_sap = p_dlcb->remote_sap;
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+
+      llcp_util_deallocate_data_link(p_dlcb);
+      llcp_util_adjust_dl_rx_congestion();
+      break;
+
+    default:
+      LOG(ERROR) << StringPrintf("Unexpected event");
+      status = LLCP_STATUS_FAIL;
+      break;
+  }
+
+  return status;
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlsm_w4_local_resp
+**
+** Description      data link connection is waiting for connection confirm from
+**                  application
+**
+** Returns          tLLCP_STATUS
+**
+*******************************************************************************/
+static tLLCP_STATUS llcp_dlsm_w4_local_resp(tLLCP_DLCB* p_dlcb,
+                                            tLLCP_DLC_EVENT event,
+                                            void* p_data) {
+  tLLCP_STATUS status = LLCP_STATUS_SUCCESS;
+  tLLCP_CONNECTION_PARAMS* p_params;
+  tLLCP_SAP_CBACK_DATA data;
+  uint8_t reason;
+
+  switch (event) {
+    case LLCP_DLC_EVENT_API_CONNECT_CFM:
+
+      /* upper layer accepted data link connection */
+      nfc_stop_quick_timer(&p_dlcb->timer);
+
+      p_params = (tLLCP_CONNECTION_PARAMS*)p_data;
+
+      p_dlcb->local_miu = p_params->miu;
+      p_dlcb->local_rw = p_params->rw;
+
+      p_dlcb->state = LLCP_DLC_STATE_CONNECTED;
+
+      if (llcp_cb.overall_rx_congested) {
+        p_dlcb->flags |= LLCP_DATA_LINK_FLAG_PENDING_RR_RNR;
+      }
+
+      status = llcp_util_send_cc(p_dlcb, p_params);
+
+      if (status == LLCP_STATUS_SUCCESS) {
+        llcp_util_adjust_dl_rx_congestion();
+      } else {
+        data.disconnect_ind.event = LLCP_SAP_EVT_DISCONNECT_IND;
+        data.disconnect_ind.local_sap = p_dlcb->local_sap;
+        data.disconnect_ind.remote_sap = p_dlcb->remote_sap;
+        (*p_dlcb->p_app_cb->p_app_cback)(&data);
+
+        llcp_util_deallocate_data_link(p_dlcb);
+      }
+      break;
+
+    case LLCP_DLC_EVENT_API_CONNECT_REJECT:
+    case LLCP_DLC_EVENT_TIMEOUT:
+
+      if (event == LLCP_DLC_EVENT_TIMEOUT)
+        reason = LLCP_SAP_DM_REASON_TEMP_REJECT_THIS;
+      else
+        reason = *((uint8_t*)p_data);
+
+      /* upper layer rejected connection or didn't respond */
+      llcp_util_send_dm(p_dlcb->remote_sap, p_dlcb->local_sap, reason);
+
+      /* stop timer, flush any pending data in queue and deallocate control
+       * block */
+      llcp_util_deallocate_data_link(p_dlcb);
+      llcp_util_adjust_dl_rx_congestion();
+      break;
+
+    case LLCP_DLC_EVENT_FRAME_ERROR:
+    case LLCP_DLC_EVENT_LINK_ERROR:
+
+      /* received bad frame or link is deactivated */
+      data.disconnect_ind.event = LLCP_SAP_EVT_DISCONNECT_IND;
+      data.disconnect_ind.local_sap = p_dlcb->local_sap;
+      data.disconnect_ind.remote_sap = p_dlcb->remote_sap;
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+
+      llcp_util_deallocate_data_link(p_dlcb);
+      llcp_util_adjust_dl_rx_congestion();
+      break;
+
+    default:
+      LOG(ERROR) << StringPrintf("Unexpected event");
+      status = LLCP_STATUS_FAIL;
+      break;
+  }
+
+  return status;
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlsm_connected
+**
+** Description      data link connection is connected
+**
+** Returns          tLLCP_STATUS
+**
+*******************************************************************************/
+static tLLCP_STATUS llcp_dlsm_connected(tLLCP_DLCB* p_dlcb,
+                                        tLLCP_DLC_EVENT event, void* p_data) {
+  bool flush;
+  tLLCP_STATUS status = LLCP_STATUS_SUCCESS;
+  tLLCP_SAP_CBACK_DATA data;
+
+  switch (event) {
+    case LLCP_DLC_EVENT_API_DISCONNECT_REQ:
+
+      /* upper layer requests to disconnect */
+      flush = *(bool*)(p_data);
+
+      /*
+      ** if upper layer asks to discard any pending data
+      ** or there is no pending data/ack to send and it is not waiting for ack
+      */
+      if ((flush) || ((p_dlcb->i_xmit_q.count == 0) &&
+                      (p_dlcb->next_rx_seq == p_dlcb->sent_ack_seq) &&
+                      (p_dlcb->next_tx_seq == p_dlcb->rcvd_ack_seq))) {
+        /* wait for disconnect response */
+        p_dlcb->state = LLCP_DLC_STATE_W4_REMOTE_DM;
+
+        llcp_util_send_disc(p_dlcb->remote_sap, p_dlcb->local_sap);
+
+        nfc_start_quick_timer(&p_dlcb->timer, NFC_TTYPE_LLCP_DATA_LINK,
+                              (uint32_t)(llcp_cb.lcb.data_link_timeout *
+                                         QUICK_TIMER_TICKS_PER_SEC) /
+                                  1000);
+      } else {
+        /* set flag to send DISC when tx queue is empty */
+        p_dlcb->flags |= LLCP_DATA_LINK_FLAG_PENDING_DISC;
+      }
+      break;
+
+    case LLCP_DLC_EVENT_PEER_DISCONNECT_IND:
+
+      /* peer device requests to disconnect */
+
+      /* send disconnect response and notify upper layer */
+      llcp_util_send_dm(p_dlcb->remote_sap, p_dlcb->local_sap,
+                        LLCP_SAP_DM_REASON_RESP_DISC);
+
+      data.disconnect_ind.event = LLCP_SAP_EVT_DISCONNECT_IND;
+      data.disconnect_ind.local_sap = p_dlcb->local_sap;
+      data.disconnect_ind.remote_sap = p_dlcb->remote_sap;
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+
+      llcp_util_deallocate_data_link(p_dlcb);
+      llcp_util_adjust_dl_rx_congestion();
+      break;
+
+    case LLCP_DLC_EVENT_API_DATA_REQ:
+
+      /* upper layer requests to send data */
+
+      /* if peer device can receive data */
+      if (p_dlcb->remote_rw) {
+        /* enqueue data and check if data can be sent */
+        GKI_enqueue(&p_dlcb->i_xmit_q, p_data);
+        llcp_cb.total_tx_i_pdu++;
+
+        llcp_link_check_send_data();
+
+        if ((p_dlcb->is_tx_congested) || (llcp_cb.overall_tx_congested) ||
+            (p_dlcb->remote_busy) ||
+            (p_dlcb->i_xmit_q.count >=
+             p_dlcb->remote_rw)) /*if enough data to send next round */
+        {
+          DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+              "Data link (SSAP:DSAP=0x%X:0x%X) "
+              "congested: xmit_q.count=%d",
+              p_dlcb->local_sap, p_dlcb->remote_sap, p_dlcb->i_xmit_q.count);
+
+          /* set congested here so overall congestion check routine will not
+           * report event again */
+          p_dlcb->is_tx_congested = true;
+          status = LLCP_STATUS_CONGESTED;
+        }
+      } else {
+        LOG(ERROR) << StringPrintf("Remote RW is zero: discard data");
+        /* buffer will be freed when returned to API function */
+        status = LLCP_STATUS_FAIL;
+      }
+      break;
+
+    case LLCP_DLC_EVENT_PEER_DATA_IND:
+      /* peer device sends data so notify upper layer to read data from data
+       * link connection */
+
+      data.data_ind.event = LLCP_SAP_EVT_DATA_IND;
+      data.data_ind.local_sap = p_dlcb->local_sap;
+      data.data_ind.link_type = LLCP_LINK_TYPE_DATA_LINK_CONNECTION;
+      data.data_ind.remote_sap = p_dlcb->remote_sap;
+
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+      break;
+
+    case LLCP_DLC_EVENT_FRAME_ERROR:
+    case LLCP_DLC_EVENT_LINK_ERROR:
+
+      /* received bad frame or link is deactivated */
+      data.disconnect_ind.event = LLCP_SAP_EVT_DISCONNECT_IND;
+      data.disconnect_ind.local_sap = p_dlcb->local_sap;
+      data.disconnect_ind.remote_sap = p_dlcb->remote_sap;
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+
+      llcp_util_deallocate_data_link(p_dlcb);
+      llcp_util_adjust_dl_rx_congestion();
+      break;
+
+    default:
+      LOG(ERROR) << StringPrintf("Unexpected event");
+      status = LLCP_STATUS_FAIL;
+      break;
+  }
+
+  return status;
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlsm_w4_remote_dm
+**
+** Description      data link connection is waiting for disconnection confirm
+**                  from peer
+**
+** Returns          tLLCP_STATUS
+**
+*******************************************************************************/
+static tLLCP_STATUS llcp_dlsm_w4_remote_dm(tLLCP_DLCB* p_dlcb,
+                                           tLLCP_DLC_EVENT event) {
+  tLLCP_STATUS status = LLCP_STATUS_SUCCESS;
+  tLLCP_SAP_CBACK_DATA data;
+
+  switch (event) {
+    case LLCP_DLC_EVENT_PEER_DISCONNECT_RESP:
+    case LLCP_DLC_EVENT_TIMEOUT:
+
+      /* peer device sends disconnect response or didn't responde */
+      data.disconnect_resp.event = LLCP_SAP_EVT_DISCONNECT_RESP;
+      data.disconnect_resp.local_sap = p_dlcb->local_sap;
+      data.disconnect_resp.remote_sap = p_dlcb->remote_sap;
+      data.disconnect_resp.reason = LLCP_SAP_DM_REASON_RESP_DISC;
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+
+      llcp_util_deallocate_data_link(p_dlcb);
+      llcp_util_adjust_dl_rx_congestion();
+      break;
+
+    case LLCP_DLC_EVENT_FRAME_ERROR:
+    case LLCP_DLC_EVENT_LINK_ERROR:
+
+      /* received bad frame or link is deactivated */
+      data.disconnect_ind.event = LLCP_SAP_EVT_DISCONNECT_IND;
+      data.disconnect_ind.local_sap = p_dlcb->local_sap;
+      data.disconnect_ind.remote_sap = p_dlcb->remote_sap;
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+
+      llcp_util_deallocate_data_link(p_dlcb);
+      llcp_util_adjust_dl_rx_congestion();
+      break;
+
+    case LLCP_DLC_EVENT_PEER_DATA_IND:
+      break;
+
+    case LLCP_DLC_EVENT_PEER_DISCONNECT_IND:
+      /* it's race condition, send disconnect response and wait for DM */
+      llcp_util_send_dm(p_dlcb->remote_sap, p_dlcb->local_sap,
+                        LLCP_SAP_DM_REASON_RESP_DISC);
+      break;
+
+    default:
+      LOG(ERROR) << StringPrintf("Unexpected event");
+      status = LLCP_STATUS_FAIL;
+      break;
+  }
+
+  return status;
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_find_dlcb_by_local_sap
+**
+** Description      Find tLLCP_DLCB by local SAP and remote SAP
+**                  if remote_sap is LLCP_INVALID_SAP, it will return a DLCB
+**                  which is waiting for CC from peer.
+**
+** Returns          tLLCP_DLCB *
+**
+*******************************************************************************/
+tLLCP_DLCB* llcp_dlc_find_dlcb_by_sap(uint8_t local_sap, uint8_t remote_sap) {
+  int i;
+
+  for (i = 0; i < LLCP_MAX_DATA_LINK; i++) {
+    if ((llcp_cb.dlcb[i].state != LLCP_DLC_STATE_IDLE) &&
+        (llcp_cb.dlcb[i].local_sap == local_sap)) {
+      if ((remote_sap == LLCP_INVALID_SAP) &&
+          (llcp_cb.dlcb[i].state == LLCP_DLC_STATE_W4_REMOTE_RESP)) {
+        /* Remote SAP has not been finalized because we are watiing for CC */
+        return (&llcp_cb.dlcb[i]);
+      } else if (llcp_cb.dlcb[i].remote_sap == remote_sap) {
+        return (&llcp_cb.dlcb[i]);
+      }
+    }
+  }
+  return NULL;
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_flush_q
+**
+** Description      Free buffers in tx and rx queue in data link
+**
+** Returns          void
+**
+*******************************************************************************/
+void llcp_dlc_flush_q(tLLCP_DLCB* p_dlcb) {
+  if (p_dlcb) {
+    DLOG_IF(INFO, nfc_debug_enabled)
+        << StringPrintf("local SAP:0x%02X", p_dlcb->local_sap);
+
+    /* Release any held buffers */
+    while (p_dlcb->i_xmit_q.p_first) {
+      GKI_freebuf(GKI_dequeue(&p_dlcb->i_xmit_q));
+      llcp_cb.total_tx_i_pdu--;
+    }
+
+    /* discard any received I PDU on data link  including in AGF */
+    LLCP_FlushDataLinkRxData(p_dlcb->local_sap, p_dlcb->remote_sap);
+  } else {
+    LOG(ERROR) << StringPrintf("p_dlcb is NULL");
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_proc_connect_pdu
+**
+** Description      Process CONNECT PDU
+**
+** Returns          void
+**
+*******************************************************************************/
+static void llcp_dlc_proc_connect_pdu(uint8_t dsap, uint8_t ssap,
+                                      uint16_t length, uint8_t* p_data) {
+  tLLCP_DLCB* p_dlcb;
+  tLLCP_STATUS status;
+  tLLCP_APP_CB* p_app_cb;
+
+  tLLCP_CONNECTION_PARAMS params;
+
+  DLOG_IF(INFO, nfc_debug_enabled) << __func__;
+
+  p_app_cb = llcp_util_get_app_cb(dsap);
+
+  if ((p_app_cb == NULL) || (p_app_cb->p_app_cback == NULL) ||
+      ((p_app_cb->link_type & LLCP_LINK_TYPE_DATA_LINK_CONNECTION) == 0)) {
+    LOG(ERROR) << StringPrintf("Unregistered SAP:0x%x", dsap);
+    llcp_util_send_dm(ssap, dsap, LLCP_SAP_DM_REASON_NO_SERVICE);
+    return;
+  }
+
+  /* parse CONNECT PDU and get connection parameters */
+  if (llcp_util_parse_connect(p_data, length, &params) != LLCP_STATUS_SUCCESS) {
+    LOG(ERROR) << StringPrintf("Bad format CONNECT");
+    /* fix to pass TC_CTO_TAR_BI_02_x (x=5) test case
+     * As per the LLCP test specification v1.2.00 by receiving erroneous SNL PDU
+     * i'e with improper length and service name "urn:nfc:sn:dta-co-echo-in",
+     * the IUT should not send any PDU except SYMM PDU */
+
+    if (appl_dta_mode_flag == 1 &&
+        p_data[1] == strlen((const char*)&p_data[2])) {
+      DLOG_IF(INFO, nfc_debug_enabled)
+          << StringPrintf("%s: Strings are not equal", __func__);
+      llcp_util_send_dm(ssap, dsap, LLCP_SAP_DM_REASON_NO_SERVICE);
+    } else {
+      llcp_util_send_dm(ssap, dsap, LLCP_SAP_DM_REASON_NO_SERVICE);
+    }
+    return;
+  }
+
+  /* if this is connection by service name */
+  if (dsap == LLCP_SAP_SDP) {
+    /* find registered SAP with service name */
+    if (strlen(params.sn))
+      dsap = llcp_sdp_get_sap_by_name(params.sn, (uint8_t)strlen(params.sn));
+    else {
+      /* if SN type is included without SN */
+      if (params.sn[1] == LLCP_SN_TYPE) {
+        llcp_util_send_dm(ssap, LLCP_SAP_SDP, LLCP_SAP_DM_REASON_NO_SERVICE);
+      } else {
+        /* SDP doesn't accept connection */
+        llcp_util_send_dm(ssap, LLCP_SAP_SDP,
+                          LLCP_SAP_DM_REASON_PERM_REJECT_THIS);
+      }
+      return;
+    }
+
+    if (dsap == LLCP_SAP_SDP) {
+      LOG(ERROR) << StringPrintf("SDP doesn't accept connection");
+
+      llcp_util_send_dm(ssap, LLCP_SAP_SDP,
+                        LLCP_SAP_DM_REASON_PERM_REJECT_THIS);
+      return;
+    } else if (dsap == 0) {
+      LOG(ERROR) << StringPrintf("Unregistered Service:%s", params.sn);
+
+      llcp_util_send_dm(ssap, LLCP_SAP_SDP, LLCP_SAP_DM_REASON_NO_SERVICE);
+      return;
+    } else {
+      /* check if this application can support connection-oriented transport */
+      p_app_cb = llcp_util_get_app_cb(dsap);
+
+      if ((p_app_cb == NULL) || (p_app_cb->p_app_cback == NULL) ||
+          ((p_app_cb->link_type & LLCP_LINK_TYPE_DATA_LINK_CONNECTION) == 0)) {
+        LOG(ERROR) << StringPrintf(
+            "SAP(0x%x) doesn't support "
+            "connection-oriented",
+            dsap);
+        llcp_util_send_dm(ssap, dsap, LLCP_SAP_DM_REASON_NO_SERVICE);
+        return;
+      }
+    }
+  }
+
+  /* check if any data link */
+  p_dlcb = llcp_dlc_find_dlcb_by_sap(dsap, ssap);
+  if (p_dlcb) {
+    LOG(ERROR) << StringPrintf("Data link is aleady established");
+    llcp_util_send_dm(ssap, dsap, LLCP_SAP_DM_REASON_TEMP_REJECT_THIS);
+  } else {
+    /* allocate data link connection control block and notify upper layer
+     * through state machine */
+    p_dlcb = llcp_util_allocate_data_link(dsap, ssap);
+
+    if (p_dlcb) {
+      status =
+          llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_PEER_CONNECT_IND, &params);
+      if (status != LLCP_STATUS_SUCCESS) {
+        LOG(ERROR) << StringPrintf("Error in state machine");
+        llcp_util_deallocate_data_link(p_dlcb);
+      }
+    } else {
+      LOG(ERROR) << StringPrintf("Out of resource");
+      llcp_util_send_dm(ssap, dsap, LLCP_SAP_DM_REASON_TEMP_REJECT_ANY);
+    }
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_proc_disc_pdu
+**
+** Description      Process DISC PDU
+**
+** Returns          void
+**
+*******************************************************************************/
+static void llcp_dlc_proc_disc_pdu(uint8_t dsap, uint8_t ssap,
+                                   uint16_t length) {
+  tLLCP_DLCB* p_dlcb;
+
+  DLOG_IF(INFO, nfc_debug_enabled) << __func__;
+
+  p_dlcb = llcp_dlc_find_dlcb_by_sap(dsap, ssap);
+  if (p_dlcb) {
+    if (length > 0) {
+      LOG(ERROR) << StringPrintf(
+          "Received extra data (%d bytes) in DISC "
+          "PDU",
+          length);
+
+      llcp_util_send_frmr(p_dlcb,
+                          LLCP_FRMR_W_ERROR_FLAG | LLCP_FRMR_I_ERROR_FLAG,
+                          LLCP_PDU_DISC_TYPE, 0);
+      llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_FRAME_ERROR, NULL);
+    } else {
+      llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_PEER_DISCONNECT_IND, NULL);
+    }
+  } else {
+    LOG(ERROR) << StringPrintf("No data link for SAP (0x%x,0x%x)", dsap, ssap);
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_proc_cc_pdu
+**
+** Description      Process CC PDU
+**
+** Returns          void
+**
+*******************************************************************************/
+static void llcp_dlc_proc_cc_pdu(uint8_t dsap, uint8_t ssap, uint16_t length,
+                                 uint8_t* p_data) {
+  tLLCP_DLCB* p_dlcb;
+  tLLCP_CONNECTION_PARAMS params;
+  tLLCP_STATUS status;
+
+  DLOG_IF(INFO, nfc_debug_enabled) << __func__;
+
+  /* find a DLCB waiting for CC on this local SAP */
+  p_dlcb = llcp_dlc_find_dlcb_by_sap(dsap, LLCP_INVALID_SAP);
+  if (p_dlcb) {
+    /* The CC may contain a SSAP that is different from the DSAP in the CONNECT
+     */
+    p_dlcb->remote_sap = ssap;
+
+    if (llcp_util_parse_cc(p_data, length, &(params.miu), &(params.rw)) ==
+        LLCP_STATUS_SUCCESS) {
+      status =
+          llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_PEER_CONNECT_CFM, &params);
+      if (status != LLCP_STATUS_SUCCESS) {
+        LOG(ERROR) << StringPrintf("Error in state machine");
+        llcp_util_deallocate_data_link(p_dlcb);
+      }
+    } else {
+      llcp_util_send_frmr(p_dlcb,
+                          LLCP_FRMR_W_ERROR_FLAG | LLCP_FRMR_I_ERROR_FLAG,
+                          LLCP_PDU_DISC_TYPE, 0);
+      llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_FRAME_ERROR, NULL);
+    }
+  } else {
+    LOG(ERROR) << StringPrintf("No data link for SAP (0x%x,0x%x)", dsap, ssap);
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_proc_dm_pdu
+**
+** Description      Process DM PDU
+**
+** Returns          void
+**
+*******************************************************************************/
+static void llcp_dlc_proc_dm_pdu(uint8_t dsap, uint8_t ssap, uint16_t length,
+                                 uint8_t* p_data) {
+  tLLCP_DLCB* p_dlcb;
+
+  DLOG_IF(INFO, nfc_debug_enabled) << __func__;
+
+  if (length != LLCP_PDU_DM_SIZE - LLCP_PDU_HEADER_SIZE) {
+    LOG(ERROR) << StringPrintf("Received invalid DM PDU");
+  } else {
+    if (*p_data == LLCP_SAP_DM_REASON_RESP_DISC) {
+      /* local device initiated disconnecting */
+      p_dlcb = llcp_dlc_find_dlcb_by_sap(dsap, ssap);
+    } else {
+      /* peer device rejected connection with any reason */
+      /* find a DLCB waiting for CC on this local SAP    */
+      p_dlcb = llcp_dlc_find_dlcb_by_sap(dsap, LLCP_INVALID_SAP);
+    }
+
+    if (p_dlcb) {
+      llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_PEER_DISCONNECT_RESP,
+                        p_data); /* passing reason */
+    } else {
+      LOG(ERROR) << StringPrintf("No data link for SAP (0x%x,0x%x)", dsap,
+                                 ssap);
+    }
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_proc_i_pdu
+**
+** Description      Process I PDU
+**
+** Returns          void
+**
+*******************************************************************************/
+void llcp_dlc_proc_i_pdu(uint8_t dsap, uint8_t ssap, uint16_t i_pdu_length,
+                         uint8_t* p_i_pdu, NFC_HDR* p_msg) {
+  uint8_t *p, *p_dst, send_seq, rcv_seq, error_flags;
+  uint16_t info_len, available_bytes;
+  tLLCP_DLCB* p_dlcb;
+  bool appended;
+  NFC_HDR* p_last_buf;
+
+  DLOG_IF(INFO, nfc_debug_enabled) << __func__;
+
+  p_dlcb = llcp_dlc_find_dlcb_by_sap(dsap, ssap);
+
+  if ((p_dlcb) && (p_dlcb->state == LLCP_DLC_STATE_CONNECTED)) {
+    error_flags = 0;
+
+    if (p_msg) {
+      i_pdu_length = p_msg->len;
+      p_i_pdu = (uint8_t*)(p_msg + 1) + p_msg->offset;
+    }
+
+    info_len = i_pdu_length - LLCP_PDU_HEADER_SIZE - LLCP_SEQUENCE_SIZE;
+
+    if (info_len > p_dlcb->local_miu) {
+      LOG(ERROR) << StringPrintf(
+          "exceeding local MIU (%d bytes): got %d "
+          "bytes SDU",
+          p_dlcb->local_miu, info_len);
+
+      error_flags |= LLCP_FRMR_I_ERROR_FLAG;
+    }
+
+    /* get sequence numbers */
+    p = p_i_pdu + LLCP_PDU_HEADER_SIZE;
+
+    send_seq = LLCP_GET_NS(*p);
+    rcv_seq = LLCP_GET_NR(*p);
+
+    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+        "LLCP RX I PDU - N(S,R):(%d,%d) V(S,SA,R,RA):(%d,%d,%d,%d)", send_seq,
+        rcv_seq, p_dlcb->next_tx_seq, p_dlcb->rcvd_ack_seq, p_dlcb->next_rx_seq,
+        p_dlcb->sent_ack_seq);
+
+    /* if send sequence number, N(S) is not expected one, V(R) */
+    if (p_dlcb->next_rx_seq != send_seq) {
+      LOG(ERROR) << StringPrintf("Bad N(S) got:%d, expected:%d", send_seq,
+                                 p_dlcb->next_rx_seq);
+
+      error_flags |= LLCP_FRMR_S_ERROR_FLAG;
+    } else {
+      /* if peer device sends more than our receiving window size */
+      if ((uint8_t)(send_seq - p_dlcb->sent_ack_seq) % LLCP_SEQ_MODULO >=
+          p_dlcb->local_rw) {
+        LOG(ERROR) << StringPrintf("Bad N(S):%d >= V(RA):%d + RW(L):%d",
+                                   send_seq, p_dlcb->sent_ack_seq,
+                                   p_dlcb->local_rw);
+
+        error_flags |= LLCP_FRMR_S_ERROR_FLAG;
+      }
+    }
+
+    /* check N(R) is in valid range; V(SA) <= N(R) <= V(S) */
+    if ((uint8_t)(rcv_seq - p_dlcb->rcvd_ack_seq) % LLCP_SEQ_MODULO +
+            (uint8_t)(p_dlcb->next_tx_seq - rcv_seq) % LLCP_SEQ_MODULO !=
+        (uint8_t)(p_dlcb->next_tx_seq - p_dlcb->rcvd_ack_seq) %
+            LLCP_SEQ_MODULO) {
+      error_flags |= LLCP_FRMR_R_ERROR_FLAG;
+      LOG(ERROR) << StringPrintf("Bad N(R):%d valid range [V(SA):%d, V(S):%d]",
+                                 rcv_seq, p_dlcb->rcvd_ack_seq,
+                                 p_dlcb->next_tx_seq);
+    }
+
+    /* if any error is found */
+    if (error_flags) {
+      llcp_util_send_frmr(p_dlcb, error_flags, LLCP_PDU_I_TYPE, *p);
+      llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_FRAME_ERROR, NULL);
+    } else {
+      /* update local sequence variables */
+      p_dlcb->next_rx_seq = (p_dlcb->next_rx_seq + 1) % LLCP_SEQ_MODULO;
+      p_dlcb->rcvd_ack_seq = rcv_seq;
+
+      appended = false;
+
+      /* get last buffer in rx queue */
+      p_last_buf = (NFC_HDR*)GKI_getlast(&p_dlcb->i_rx_q);
+
+      if (p_last_buf) {
+        /* get max length to append at the end of buffer */
+        available_bytes = GKI_get_buf_size(p_last_buf) - NFC_HDR_SIZE -
+                          p_last_buf->offset - p_last_buf->len;
+
+        /* if new UI PDU with length can be attached at the end of buffer */
+        if (available_bytes >= LLCP_PDU_AGF_LEN_SIZE + info_len) {
+          p_dst =
+              (uint8_t*)(p_last_buf + 1) + p_last_buf->offset + p_last_buf->len;
+
+          /* add length of information in I PDU */
+          UINT16_TO_BE_STREAM(p_dst, info_len);
+
+          /* copy information of I PDU */
+          p = p_i_pdu + LLCP_PDU_HEADER_SIZE + LLCP_SEQUENCE_SIZE;
+
+          memcpy(p_dst, p, info_len);
+
+          p_last_buf->len += LLCP_PDU_AGF_LEN_SIZE + info_len;
+
+          if (p_msg) {
+            GKI_freebuf(p_msg);
+            p_msg = NULL;
+          }
+
+          appended = true;
+        }
+      }
+
+      /* if it is not available to append */
+      if (!appended) {
+        /* if it's not from AGF PDU */
+        if (p_msg) {
+          /* add length of information in front of information */
+          p = p_i_pdu + LLCP_PDU_HEADER_SIZE + LLCP_SEQUENCE_SIZE -
+              LLCP_PDU_AGF_LEN_SIZE;
+          UINT16_TO_BE_STREAM(p, info_len);
+
+          p_msg->offset +=
+              LLCP_PDU_HEADER_SIZE + LLCP_SEQUENCE_SIZE - LLCP_PDU_AGF_LEN_SIZE;
+          p_msg->len -=
+              LLCP_PDU_HEADER_SIZE + LLCP_SEQUENCE_SIZE - LLCP_PDU_AGF_LEN_SIZE;
+          p_msg->layer_specific = 0;
+        } else {
+          p_msg = (NFC_HDR*)GKI_getpoolbuf(LLCP_POOL_ID);
+
+          if (p_msg) {
+            p_dst = (uint8_t*)(p_msg + 1);
+
+            /* add length of information in front of information */
+            UINT16_TO_BE_STREAM(p_dst, info_len);
+
+            p = p_i_pdu + LLCP_PDU_HEADER_SIZE + LLCP_SEQUENCE_SIZE;
+            memcpy(p_dst, p, info_len);
+
+            p_msg->offset = 0;
+            p_msg->len = LLCP_PDU_AGF_LEN_SIZE + info_len;
+            p_msg->layer_specific = 0;
+          } else {
+            LOG(ERROR) << StringPrintf("out of buffer");
+          }
+        }
+
+        /* insert I PDU in rx queue */
+        if (p_msg) {
+          GKI_enqueue(&p_dlcb->i_rx_q, p_msg);
+          p_msg = NULL;
+          llcp_cb.total_rx_i_pdu++;
+
+          llcp_util_check_rx_congested_status();
+        }
+      }
+
+      p_dlcb->num_rx_i_pdu++;
+
+      if ((!p_dlcb->local_busy) && (p_dlcb->num_rx_i_pdu == 1)) {
+        /* notify rx data is available so upper layer reads data until queue is
+         * empty */
+        llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_PEER_DATA_IND, NULL);
+      }
+
+      if ((!p_dlcb->is_rx_congested) &&
+          (p_dlcb->num_rx_i_pdu >= p_dlcb->rx_congest_threshold)) {
+        DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+            "congested num_rx_i_pdu=%d, "
+            "rx_congest_threshold=%d",
+            p_dlcb->num_rx_i_pdu, p_dlcb->rx_congest_threshold);
+
+        /* send RNR */
+        p_dlcb->is_rx_congested = true;
+        p_dlcb->flags |= LLCP_DATA_LINK_FLAG_PENDING_RR_RNR;
+      }
+    }
+  } else {
+    LOG(ERROR) << StringPrintf("No data link for SAP (0x%x,0x%x)", dsap, ssap);
+    llcp_util_send_dm(ssap, dsap, LLCP_SAP_DM_REASON_NO_ACTIVE_CONNECTION);
+  }
+
+  if (p_msg) {
+    GKI_freebuf(p_msg);
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_proc_rr_rnr_pdu
+**
+** Description      Process RR or RNR PDU
+**
+** Returns          void
+**
+*******************************************************************************/
+static void llcp_dlc_proc_rr_rnr_pdu(uint8_t dsap, uint8_t ptype, uint8_t ssap,
+                                     uint16_t length, uint8_t* p_data) {
+  uint8_t rcv_seq, error_flags;
+  tLLCP_DLCB* p_dlcb;
+  bool flush = true;
+  tLLCP_SAP_CBACK_DATA cback_data;
+  bool old_remote_busy;
+
+  DLOG_IF(INFO, nfc_debug_enabled) << __func__;
+
+  p_dlcb = llcp_dlc_find_dlcb_by_sap(dsap, ssap);
+  if (p_dlcb != NULL) {
+    error_flags = 0;
+
+    rcv_seq = LLCP_GET_NR(*p_data);
+
+    if (length != LLCP_PDU_RR_SIZE - LLCP_PDU_HEADER_SIZE) {
+      error_flags |= LLCP_FRMR_W_ERROR_FLAG | LLCP_FRMR_I_ERROR_FLAG;
+    }
+
+    /* check N(R) is in valid range; V(SA) <= N(R) <= V(S) */
+    if ((uint8_t)(rcv_seq - p_dlcb->rcvd_ack_seq) % LLCP_SEQ_MODULO +
+            (uint8_t)(p_dlcb->next_tx_seq - rcv_seq) % LLCP_SEQ_MODULO !=
+        (uint8_t)(p_dlcb->next_tx_seq - p_dlcb->rcvd_ack_seq) %
+            LLCP_SEQ_MODULO) {
+      error_flags |= LLCP_FRMR_R_ERROR_FLAG;
+      LOG(ERROR) << StringPrintf(
+          "Bad N(R):%d valid range [V(SA):%d, "
+          "V(S):%d]",
+          rcv_seq, p_dlcb->rcvd_ack_seq, p_dlcb->next_tx_seq);
+    }
+
+    if (error_flags) {
+      llcp_util_send_frmr(p_dlcb, error_flags, ptype, *p_data);
+      llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_FRAME_ERROR, NULL);
+    } else {
+      p_dlcb->rcvd_ack_seq = rcv_seq;
+
+      DLOG_IF(INFO, nfc_debug_enabled)
+          << StringPrintf("LLCP RX - N(S,R):(NA,%d) V(S,SA,R,RA):(%d,%d,%d,%d)",
+                          rcv_seq, p_dlcb->next_tx_seq, p_dlcb->rcvd_ack_seq,
+                          p_dlcb->next_rx_seq, p_dlcb->sent_ack_seq);
+      old_remote_busy = p_dlcb->remote_busy;
+      if (ptype == LLCP_PDU_RNR_TYPE) {
+        p_dlcb->remote_busy = true;
+        /* if upper layer hasn't get congestion started notification */
+        if ((!old_remote_busy) && (!p_dlcb->is_tx_congested)) {
+          LOG(WARNING) << StringPrintf(
+              "Data link (SSAP:DSAP=0x%X:0x%X) "
+              "congestion start: i_xmit_q.count=%d",
+              p_dlcb->local_sap, p_dlcb->remote_sap, p_dlcb->i_xmit_q.count);
+
+          cback_data.congest.event = LLCP_SAP_EVT_CONGEST;
+          cback_data.congest.local_sap = p_dlcb->local_sap;
+          cback_data.congest.remote_sap = p_dlcb->remote_sap;
+          cback_data.congest.is_congested = true;
+          cback_data.congest.link_type = LLCP_LINK_TYPE_DATA_LINK_CONNECTION;
+
+          (*p_dlcb->p_app_cb->p_app_cback)(&cback_data);
+        }
+      } else {
+        p_dlcb->remote_busy = false;
+        /* if upper layer hasn't get congestion ended notification and data link
+         * is not congested */
+        if ((old_remote_busy) && (!p_dlcb->is_tx_congested)) {
+          LOG(WARNING) << StringPrintf(
+              "Data link (SSAP:DSAP=0x%X:0x%X) "
+              "congestion end: i_xmit_q.count=%d",
+              p_dlcb->local_sap, p_dlcb->remote_sap, p_dlcb->i_xmit_q.count);
+
+          cback_data.congest.event = LLCP_SAP_EVT_CONGEST;
+          cback_data.congest.local_sap = p_dlcb->local_sap;
+          cback_data.congest.remote_sap = p_dlcb->remote_sap;
+          cback_data.congest.is_congested = false;
+          cback_data.congest.link_type = LLCP_LINK_TYPE_DATA_LINK_CONNECTION;
+
+          (*p_dlcb->p_app_cb->p_app_cback)(&cback_data);
+        }
+      }
+
+      /* check flag to send DISC when tx queue is empty */
+      if (p_dlcb->flags & LLCP_DATA_LINK_FLAG_PENDING_DISC) {
+        /* if no pending data and all PDU is acked */
+        if ((p_dlcb->i_xmit_q.count == 0) &&
+            (p_dlcb->next_rx_seq == p_dlcb->sent_ack_seq) &&
+            (p_dlcb->next_tx_seq == p_dlcb->rcvd_ack_seq)) {
+          p_dlcb->flags &= ~LLCP_DATA_LINK_FLAG_PENDING_DISC;
+          llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_API_DISCONNECT_REQ, &flush);
+        }
+      }
+    }
+  } else {
+    LOG(ERROR) << StringPrintf("No data link for SAP (0x%x,0x%x)", dsap, ssap);
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_proc_rx_pdu
+**
+** Description      Process PDU for data link
+**
+** Returns          void
+**
+*******************************************************************************/
+void llcp_dlc_proc_rx_pdu(uint8_t dsap, uint8_t ptype, uint8_t ssap,
+                          uint16_t length, uint8_t* p_data) {
+  tLLCP_DLCB* p_dlcb;
+
+  DLOG_IF(INFO, nfc_debug_enabled)
+      << StringPrintf("DSAP:0x%x, PTYPE:0x%x, SSAP:0x%x", dsap, ptype, ssap);
+
+  if (dsap == LLCP_SAP_LM) {
+    LOG(ERROR) << StringPrintf("Invalid SAP:0x%x for PTYPE:0x%x", dsap, ptype);
+    return;
+  }
+
+  switch (ptype) {
+    case LLCP_PDU_CONNECT_TYPE:
+      llcp_dlc_proc_connect_pdu(dsap, ssap, length, p_data);
+      break;
+
+    case LLCP_PDU_DISC_TYPE:
+      llcp_dlc_proc_disc_pdu(dsap, ssap, length);
+      break;
+
+    case LLCP_PDU_CC_TYPE:
+      llcp_dlc_proc_cc_pdu(dsap, ssap, length, p_data);
+      break;
+
+    case LLCP_PDU_DM_TYPE:
+      llcp_dlc_proc_dm_pdu(dsap, ssap, length, p_data);
+      break;
+
+    case LLCP_PDU_FRMR_TYPE:
+      p_dlcb = llcp_dlc_find_dlcb_by_sap(dsap, ssap);
+      if (p_dlcb) {
+        llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_FRAME_ERROR, NULL);
+      }
+      break;
+
+    case LLCP_PDU_RR_TYPE:
+    case LLCP_PDU_RNR_TYPE:
+      llcp_dlc_proc_rr_rnr_pdu(dsap, ptype, ssap, length, p_data);
+      break;
+
+    default:
+      LOG(ERROR) << StringPrintf("Unexpected PDU type (0x%x)", ptype);
+
+      p_dlcb = llcp_dlc_find_dlcb_by_sap(dsap, ssap);
+      if (p_dlcb) {
+        llcp_util_send_frmr(p_dlcb, LLCP_FRMR_W_ERROR_FLAG, ptype, 0);
+        llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_FRAME_ERROR, NULL);
+      }
+      break;
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_check_to_send_rr_rnr
+**
+** Description      Send RR or RNR if necessary
+**
+** Returns          void
+**
+*******************************************************************************/
+void llcp_dlc_check_to_send_rr_rnr(void) {
+  uint8_t idx;
+  bool flush = true;
+
+  DLOG_IF(INFO, nfc_debug_enabled) << __func__;
+
+  /*
+  ** DLC doesn't send RR PDU for each received I PDU because multiple I PDUs
+  ** can be aggregated in a received AGF PDU. In this case, this is post
+  ** processing of AGF PDU to send single RR or RNR after processing all I
+  ** PDUs in received AGF if there was no I-PDU to carry N(R).
+  **
+  ** Send RR or RNR if any change of local busy condition or rx congestion
+  ** status, or V(RA) is not V(R).
+  */
+  for (idx = 0; idx < LLCP_MAX_DATA_LINK; idx++) {
+    if (llcp_cb.dlcb[idx].state == LLCP_DLC_STATE_CONNECTED) {
+      llcp_util_send_rr_rnr(&(llcp_cb.dlcb[idx]));
+
+      /* check flag to send DISC when tx queue is empty */
+      if (llcp_cb.dlcb[idx].flags & LLCP_DATA_LINK_FLAG_PENDING_DISC) {
+        /* if no pending data and all PDU is acked */
+        if ((llcp_cb.dlcb[idx].i_xmit_q.count == 0) &&
+            (llcp_cb.dlcb[idx].next_rx_seq == llcp_cb.dlcb[idx].sent_ack_seq) &&
+            (llcp_cb.dlcb[idx].next_tx_seq == llcp_cb.dlcb[idx].rcvd_ack_seq)) {
+          llcp_cb.dlcb[idx].flags &= ~LLCP_DATA_LINK_FLAG_PENDING_DISC;
+          llcp_dlsm_execute(&(llcp_cb.dlcb[idx]),
+                            LLCP_DLC_EVENT_API_DISCONNECT_REQ, &flush);
+        }
+      }
+    }
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_is_rw_open
+**
+** Description      check if receive window is open in remote
+**
+** Returns          TRUE if remote can receive more data
+**
+*******************************************************************************/
+bool llcp_dlc_is_rw_open(tLLCP_DLCB* p_dlcb) {
+  if ((uint8_t)(p_dlcb->next_tx_seq - p_dlcb->rcvd_ack_seq) % LLCP_SEQ_MODULO <
+      p_dlcb->remote_rw) {
+    return true;
+  } else {
+    DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+        "Flow Off, V(S):%d, V(SA):%d, RW(R):%d", p_dlcb->next_tx_seq,
+        p_dlcb->rcvd_ack_seq, p_dlcb->remote_rw);
+    return false;
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_get_next_pdu
+**
+** Description      Get a PDU from tx queue of data link
+**
+** Returns          NFC_HDR*
+**
+*******************************************************************************/
+NFC_HDR* llcp_dlc_get_next_pdu(tLLCP_DLCB* p_dlcb) {
+  NFC_HDR* p_msg = NULL;
+  bool flush = true;
+  tLLCP_SAP_CBACK_DATA data;
+
+  uint8_t send_seq = p_dlcb->next_tx_seq;
+
+  /* if there is data to send and remote device can receive it */
+  if ((p_dlcb->i_xmit_q.count) && (!p_dlcb->remote_busy) &&
+      (llcp_dlc_is_rw_open(p_dlcb))) {
+    p_msg = (NFC_HDR*)GKI_dequeue(&p_dlcb->i_xmit_q);
+    llcp_cb.total_tx_i_pdu--;
+
+    if (p_msg->offset >= LLCP_MIN_OFFSET) {
+      /* add LLCP header, DSAP, PTYPE, SSAP, N(S), N(R) and update sent_ack_seq,
+       * V(RA) */
+      llcp_util_build_info_pdu(p_dlcb, p_msg);
+
+      p_dlcb->next_tx_seq = (p_dlcb->next_tx_seq + 1) % LLCP_SEQ_MODULO;
+
+      DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
+          "LLCP TX - N(S,R):(%d,%d) V(S,SA,R,RA):(%d,%d,%d,%d)", send_seq,
+          p_dlcb->next_rx_seq, p_dlcb->next_tx_seq, p_dlcb->rcvd_ack_seq,
+          p_dlcb->next_rx_seq, p_dlcb->sent_ack_seq);
+    } else {
+      LOG(ERROR) << StringPrintf("offset (%d) must be %d at least",
+                                 p_msg->offset, LLCP_MIN_OFFSET);
+      GKI_freebuf(p_msg);
+      p_msg = NULL;
+    }
+  }
+
+  /* if tx queue is empty and all PDU is acknowledged */
+  if ((p_dlcb->i_xmit_q.count == 0) &&
+      (p_dlcb->next_rx_seq == p_dlcb->sent_ack_seq) &&
+      (p_dlcb->next_tx_seq == p_dlcb->rcvd_ack_seq)) {
+    /* check flag to send DISC */
+    if (p_dlcb->flags & LLCP_DATA_LINK_FLAG_PENDING_DISC) {
+      p_dlcb->flags &= ~LLCP_DATA_LINK_FLAG_PENDING_DISC;
+      llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_API_DISCONNECT_REQ, &flush);
+    }
+
+    /* check flag to notify upper layer */
+    if (p_dlcb->flags & LLCP_DATA_LINK_FLAG_NOTIFY_TX_DONE) {
+      p_dlcb->flags &= ~LLCP_DATA_LINK_FLAG_NOTIFY_TX_DONE;
+
+      data.tx_complete.event = LLCP_SAP_EVT_TX_COMPLETE;
+      data.tx_complete.local_sap = p_dlcb->local_sap;
+      data.tx_complete.remote_sap = p_dlcb->remote_sap;
+
+      (*p_dlcb->p_app_cb->p_app_cback)(&data);
+    }
+  }
+
+  return p_msg;
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlc_get_next_pdu_length
+**
+** Description      return length of PDU which is top in tx queue of data link
+**
+** Returns          length of PDU
+**
+*******************************************************************************/
+uint16_t llcp_dlc_get_next_pdu_length(tLLCP_DLCB* p_dlcb) {
+  NFC_HDR* p_msg;
+
+  /* if there is data to send and remote device can receive it */
+  if ((p_dlcb->i_xmit_q.count) && (!p_dlcb->remote_busy) &&
+      (llcp_dlc_is_rw_open(p_dlcb))) {
+    p_msg = (NFC_HDR*)p_dlcb->i_xmit_q.p_first;
+
+    return (p_msg->len + LLCP_PDU_HEADER_SIZE + LLCP_SEQUENCE_SIZE);
+  }
+  return 0;
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlsm_get_state_name
+**
+** Description      This function returns the state name.
+**
+** Returns          pointer to the name
+**
+*******************************************************************************/
+static std::string llcp_dlsm_get_state_name(tLLCP_DLC_STATE state) {
+  switch (state) {
+    case LLCP_DLC_STATE_IDLE:
+      return "IDLE";
+    case LLCP_DLC_STATE_W4_REMOTE_RESP:
+      return "W4_REMOTE_RESP";
+    case LLCP_DLC_STATE_W4_LOCAL_RESP:
+      return "W4_LOCAL_RESP";
+    case LLCP_DLC_STATE_CONNECTED:
+      return "CONNECTED";
+    case LLCP_DLC_STATE_W4_REMOTE_DM:
+      return "W4_REMOTE_DM";
+    default:
+      return "???? UNKNOWN STATE";
+  }
+}
+
+/*******************************************************************************
+**
+** Function         llcp_dlsm_get_event_name
+**
+** Description      This function returns the event name.
+**
+** Returns          pointer to the name
+**
+*******************************************************************************/
+static std::string llcp_dlsm_get_event_name(tLLCP_DLC_EVENT event) {
+  switch (event) {
+    case LLCP_DLC_EVENT_API_CONNECT_REQ:
+      return "API_CONNECT_REQ";
+    case LLCP_DLC_EVENT_API_CONNECT_CFM:
+      return "API_CONNECT_CFM";
+    case LLCP_DLC_EVENT_API_CONNECT_REJECT:
+      return "API_CONNECT_REJECT";
+    case LLCP_DLC_EVENT_PEER_CONNECT_IND:
+      return "PEER_CONNECT_IND";
+    case LLCP_DLC_EVENT_PEER_CONNECT_CFM:
+      return "PEER_CONNECT_CFM";
+    case LLCP_DLC_EVENT_API_DATA_REQ:
+      return "API_DATA_REQ";
+    case LLCP_DLC_EVENT_PEER_DATA_IND:
+      return "PEER_DATA_IND";
+    case LLCP_DLC_EVENT_API_DISCONNECT_REQ:
+      return "API_DISCONNECT_REQ";
+    case LLCP_DLC_EVENT_PEER_DISCONNECT_IND:
+      return "PEER_DISCONNECT_IND";
+    case LLCP_DLC_EVENT_PEER_DISCONNECT_RESP:
+      return "PEER_DISCONNECT_RESP";
+    case LLCP_DLC_EVENT_FRAME_ERROR:
+      return "FRAME_ERROR";
+    case LLCP_DLC_EVENT_LINK_ERROR:
+      return "LINK_ERROR";
+    case LLCP_DLC_EVENT_TIMEOUT:
+      return "TIMEOUT";
+    default:
+      return "???? UNKNOWN EVENT";
+  }
+}