| /****************************************************************************** |
| * |
| * 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 Link Management |
| * |
| ******************************************************************************/ |
| |
| #include <string.h> |
| #include "bt_types.h" |
| #include "gki.h" |
| #include "llcp_defs.h" |
| #include "llcp_int.h" |
| #include "nfa_dm_int.h" |
| #include "nfc_int.h" |
| #include "trace_api.h" |
| |
| const uint16_t llcp_link_rwt |
| [15] = /* RWT = (302us)*2**WT; 302us = 256*16/fc; fc = 13.56MHz */ |
| { |
| 1, /* WT=0, 302us */ |
| 1, /* WT=1, 604us */ |
| 2, /* WT=2, 1208us */ |
| 3, /* WT=3, 2.4ms */ |
| 5, /* WT=4, 4.8ms */ |
| 10, /* WT=5, 9.7ms */ |
| 20, /* WT=6, 19.3ms */ |
| 39, /* WT=7, 38.7ms */ |
| 78, /* WT=8, 77.3ms */ |
| 155, /* WT=9, 154.6ms */ |
| 310, /* WT=10, 309.2ms */ |
| 619, /* WT=11, 618.5ms */ |
| 1237, /* WT=12, 1237.0ms */ |
| 2474, /* WT=13, 2474.0ms */ |
| 4948, /* WT=14, 4948.0ms */ |
| }; |
| |
| static bool llcp_link_parse_gen_bytes(uint8_t gen_bytes_len, |
| uint8_t* p_gen_bytes); |
| static bool llcp_link_version_agreement(void); |
| |
| static void llcp_link_send_SYMM(void); |
| static void llcp_link_update_status(bool is_activated); |
| static void llcp_link_check_congestion(void); |
| static void llcp_link_check_uncongested(void); |
| static void llcp_link_proc_ui_pdu(uint8_t local_sap, uint8_t remote_sap, |
| uint16_t ui_pdu_length, uint8_t* p_ui_pdu, |
| NFC_HDR* p_msg); |
| static void llcp_link_proc_agf_pdu(NFC_HDR* p_msg); |
| static void llcp_link_proc_rx_pdu(uint8_t dsap, uint8_t ptype, uint8_t ssap, |
| NFC_HDR* p_msg); |
| static void llcp_link_proc_rx_data(NFC_HDR* p_msg); |
| |
| static NFC_HDR* llcp_link_get_next_pdu(bool length_only, |
| uint16_t* p_next_pdu_length); |
| static NFC_HDR* llcp_link_build_next_pdu(NFC_HDR* p_agf); |
| static void llcp_link_send_to_lower(NFC_HDR* p_msg); |
| |
| #if (LLCP_TEST_INCLUDED == TRUE) /* this is for LLCP testing */ |
| extern tLLCP_TEST_PARAMS llcp_test_params; |
| #endif |
| |
| extern unsigned char appl_dta_mode_flag; |
| |
| /* debug functions type */ |
| static std::string llcp_pdu_type(uint8_t ptype); |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_start_inactivity_timer |
| ** |
| ** Description This function start LLCP link inactivity timer. |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_start_inactivity_timer(void) { |
| if ((llcp_cb.lcb.inact_timer.in_use == false) && |
| (llcp_cb.lcb.inact_timeout > 0)) { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "Start inactivity_timer: %d ms", llcp_cb.lcb.inact_timeout); |
| |
| nfc_start_quick_timer(&llcp_cb.lcb.inact_timer, NFC_TTYPE_LLCP_LINK_INACT, |
| ((uint32_t)llcp_cb.lcb.inact_timeout) * |
| QUICK_TIMER_TICKS_PER_SEC / 1000); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_stop_inactivity_timer |
| ** |
| ** Description This function stop LLCP link inactivity timer. |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_stop_inactivity_timer(void) { |
| if (llcp_cb.lcb.inact_timer.in_use) { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("Stop inactivity_timer"); |
| |
| nfc_stop_quick_timer(&llcp_cb.lcb.inact_timer); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_start_link_timer |
| ** |
| ** Description This function starts LLCP link timer (LTO or delay response) |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_start_link_timer(void) { |
| if (llcp_cb.lcb.symm_state == LLCP_LINK_SYMM_LOCAL_XMIT_NEXT) { |
| /* wait for application layer sending data */ |
| nfc_start_quick_timer( |
| &llcp_cb.lcb.timer, NFC_TTYPE_LLCP_LINK_MANAGER, |
| (((uint32_t)llcp_cb.lcb.symm_delay) * QUICK_TIMER_TICKS_PER_SEC) / |
| 1000); |
| } else { |
| /* wait for data to receive from remote */ |
| nfc_start_quick_timer( |
| &llcp_cb.lcb.timer, NFC_TTYPE_LLCP_LINK_MANAGER, |
| ((uint32_t)llcp_cb.lcb.peer_lto) * QUICK_TIMER_TICKS_PER_SEC / 1000); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_stop_link_timer |
| ** |
| ** Description This function stop LLCP link timer (LTO or delay response). |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_stop_link_timer(void) { |
| nfc_stop_quick_timer(&llcp_cb.lcb.timer); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_activate |
| ** |
| ** Description Activate LLCP link |
| ** |
| ** Returns tLLCP_STATUS |
| ** |
| *******************************************************************************/ |
| tLLCP_STATUS llcp_link_activate(tLLCP_ACTIVATE_CONFIG* p_config) { |
| DLOG_IF(INFO, nfc_debug_enabled) << __func__; |
| |
| /* At this point, MAC link activation procedure has been successfully |
| * completed */ |
| |
| /* The Length Reduction values LRi and LRt MUST be 11b. (254bytes) */ |
| if (p_config->max_payload_size != LLCP_NCI_MAX_PAYL_SIZE) { |
| LOG(WARNING) << StringPrintf("max payload size (%d) must be %d bytes", |
| p_config->max_payload_size, |
| LLCP_NCI_MAX_PAYL_SIZE); |
| } |
| |
| /* Processing the parametes that have been received with the MAC link |
| * activation */ |
| if (llcp_link_parse_gen_bytes(p_config->gen_bytes_len, |
| p_config->p_gen_bytes) == false) { |
| LOG(ERROR) << StringPrintf("Failed to parse general bytes"); |
| /* For LLCP DTA test, In case of bad magic bytes normal p2p communication is |
| * expected,but in case of wrong magic bytes in ATR_REQ, LLC layer will be |
| * disconnected but P2P connection is expected to be in connected state |
| * and non LLC PDU is expected. |
| * As per NFC forum expectation below changes is to send PDU after |
| * disconnect of LLCP PDU. |
| * This is fix for TC_MAC_TAR_BI_01 LLCP test case */ |
| |
| if (appl_dta_mode_flag == 1 && p_config->is_initiator == FALSE) { |
| NFC_HDR* p_msg = (NFC_HDR*)GKI_getpoolbuf(LLCP_POOL_ID); |
| |
| if (p_msg) { |
| /*LLCP test scenario requires non LLC PDU to be sent in case of wrong |
| magic bytes. So sending NFC-DEP pdu with size 1 (0x00)*/ |
| p_msg->len = 1; |
| p_msg->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| |
| NFC_SendData(NFC_RF_CONN_ID, p_msg); |
| } |
| } |
| |
| (*llcp_cb.lcb.p_link_cback)(LLCP_LINK_ACTIVATION_FAILED_EVT, |
| LLCP_LINK_BAD_GEN_BYTES); |
| |
| if (p_config->is_initiator == false) { |
| /* repond to any incoming PDU with invalid LLCP PDU */ |
| llcp_cb.lcb.link_state = LLCP_LINK_STATE_ACTIVATION_FAILED; |
| NFC_SetStaticRfCback(llcp_link_connection_cback); |
| } |
| return LLCP_STATUS_FAIL; |
| } |
| |
| /* |
| ** For the Target device, the scaled value of RWT MUST be less than or equal |
| ** to the scaled value of the LLC Link Timeout (LTO). |
| */ |
| if ((p_config->is_initiator) && |
| (llcp_link_rwt[p_config->waiting_time] > llcp_cb.lcb.peer_lto)) { |
| LOG(WARNING) << StringPrintf( |
| "WT (%d, %dms) must be less than or equal to " |
| "LTO (%dms)", |
| p_config->waiting_time, llcp_link_rwt[p_config->waiting_time], |
| llcp_cb.lcb.peer_lto); |
| } |
| /* For DTA mode Peer LTO Should not include TX RX Delay, Just llcp deactivate |
| * after Peer LTO time */ |
| if (!appl_dta_mode_flag) { |
| /* extend LTO as much as internally required processing time and propagation |
| * delays */ |
| llcp_cb.lcb.peer_lto += LLCP_INTERNAL_TX_DELAY + LLCP_INTERNAL_RX_DELAY; |
| } |
| /* LLCP version number agreement */ |
| if (llcp_link_version_agreement() == false) { |
| LOG(ERROR) << StringPrintf("Failed to agree version"); |
| (*llcp_cb.lcb.p_link_cback)(LLCP_LINK_ACTIVATION_FAILED_EVT, |
| LLCP_LINK_VERSION_FAILED); |
| |
| if (p_config->is_initiator == false) { |
| /* repond to any incoming PDU with invalid LLCP PDU */ |
| llcp_cb.lcb.link_state = LLCP_LINK_STATE_ACTIVATION_FAILED; |
| NFC_SetStaticRfCback(llcp_link_connection_cback); |
| } |
| return LLCP_STATUS_FAIL; |
| } |
| |
| llcp_cb.lcb.received_first_packet = false; |
| llcp_cb.lcb.is_initiator = p_config->is_initiator; |
| |
| /* reset internal flags */ |
| llcp_cb.lcb.flags = 0x00; |
| |
| /* set tx MIU to MIN (MIU of local LLCP, MIU of peer LLCP) */ |
| |
| if (llcp_cb.lcb.local_link_miu >= llcp_cb.lcb.peer_miu) |
| llcp_cb.lcb.effective_miu = llcp_cb.lcb.peer_miu; |
| else |
| llcp_cb.lcb.effective_miu = llcp_cb.lcb.local_link_miu; |
| |
| /* |
| ** When entering the normal operation phase, LLCP shall initialize the |
| ** symmetry procedure. |
| */ |
| if (llcp_cb.lcb.is_initiator) { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("Connected as Initiator"); |
| |
| llcp_cb.lcb.inact_timeout = llcp_cb.lcb.inact_timeout_init; |
| llcp_cb.lcb.symm_state = LLCP_LINK_SYMM_LOCAL_XMIT_NEXT; |
| |
| if (llcp_cb.lcb.delay_first_pdu_timeout > 0) { |
| /* give a chance to upper layer to send PDU if need */ |
| nfc_start_quick_timer(&llcp_cb.lcb.timer, NFC_TTYPE_LLCP_DELAY_FIRST_PDU, |
| (((uint32_t)llcp_cb.lcb.delay_first_pdu_timeout) * |
| QUICK_TIMER_TICKS_PER_SEC) / |
| 1000); |
| } else { |
| llcp_link_send_SYMM(); |
| } |
| } else { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("Connected as Target"); |
| llcp_cb.lcb.inact_timeout = llcp_cb.lcb.inact_timeout_target; |
| llcp_cb.lcb.symm_state = LLCP_LINK_SYMM_REMOTE_XMIT_NEXT; |
| |
| /* wait for data to receive from remote */ |
| llcp_link_start_link_timer(); |
| } |
| |
| /* |
| ** Set state to LLCP_LINK_STATE_ACTIVATED and notify activation before set |
| ** data callback because LLCP PDU could be in NCI queue. |
| */ |
| llcp_cb.lcb.link_state = LLCP_LINK_STATE_ACTIVATED; |
| |
| /* LLCP Link Activation completed */ |
| (*llcp_cb.lcb.p_link_cback)(LLCP_LINK_ACTIVATION_COMPLETE_EVT, |
| LLCP_LINK_SUCCESS); |
| |
| /* Update link status to service layer */ |
| llcp_link_update_status(true); |
| |
| NFC_SetStaticRfCback(llcp_link_connection_cback); |
| |
| return (LLCP_STATUS_SUCCESS); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_deactivate_cleanup |
| ** |
| ** Description Clean up for link deactivation |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_deactivate_cleanup(uint8_t reason) { |
| /* report SDP failure for any pending request */ |
| llcp_sdp_proc_deactivation(); |
| |
| /* Update link status to service layer */ |
| llcp_link_update_status(false); |
| |
| /* We had sent out DISC */ |
| llcp_cb.lcb.link_state = LLCP_LINK_STATE_DEACTIVATED; |
| |
| llcp_link_stop_link_timer(); |
| |
| /* stop inactivity timer */ |
| llcp_link_stop_inactivity_timer(); |
| |
| /* Let upper layer deactivate local link */ |
| (*llcp_cb.lcb.p_link_cback)(LLCP_LINK_DEACTIVATED_EVT, reason); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_process_link_timeout |
| ** |
| ** Description Process timeout events for LTO, SYMM and deactivating |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void llcp_link_process_link_timeout(void) { |
| if (llcp_cb.lcb.link_state == LLCP_LINK_STATE_ACTIVATED) { |
| if ((llcp_cb.lcb.symm_delay > 0) && |
| (llcp_cb.lcb.symm_state == LLCP_LINK_SYMM_LOCAL_XMIT_NEXT)) { |
| /* upper layer doesn't have anything to send */ |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "LEVT_TIMEOUT in state of " |
| "LLCP_LINK_SYMM_LOCAL_XMIT_NEXT"); |
| llcp_link_send_SYMM(); |
| |
| /* wait for data to receive from remote */ |
| llcp_link_start_link_timer(); |
| |
| /* start inactivity timer */ |
| if (llcp_cb.num_data_link_connection == 0) { |
| llcp_link_start_inactivity_timer(); |
| } |
| } else { |
| LOG(ERROR) << StringPrintf( |
| "LEVT_TIMEOUT in state of " |
| "LLCP_LINK_SYMM_REMOTE_XMIT_NEXT"); |
| llcp_link_deactivate(LLCP_LINK_TIMEOUT); |
| } |
| } else if (llcp_cb.lcb.link_state == LLCP_LINK_STATE_DEACTIVATING) { |
| llcp_deactivate_cleanup(llcp_cb.lcb.link_deact_reason); |
| |
| NFC_SetStaticRfCback(NULL); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_deactivate |
| ** |
| ** Description Deactivate LLCP link |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void llcp_link_deactivate(uint8_t reason) { |
| uint8_t local_sap, idx; |
| tLLCP_DLCB* p_dlcb; |
| tLLCP_APP_CB* p_app_cb; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf("reason = 0x%x", reason); |
| |
| /* Release any held buffers in signaling PDU queue */ |
| while (llcp_cb.lcb.sig_xmit_q.p_first) |
| GKI_freebuf(GKI_dequeue(&llcp_cb.lcb.sig_xmit_q)); |
| |
| /* Release any held buffers in UI PDU queue */ |
| for (local_sap = LLCP_SAP_SDP + 1; local_sap < LLCP_NUM_SAPS; local_sap++) { |
| p_app_cb = llcp_util_get_app_cb(local_sap); |
| |
| if ((p_app_cb) && (p_app_cb->p_app_cback)) { |
| while (p_app_cb->ui_xmit_q.p_first) |
| GKI_freebuf(GKI_dequeue(&p_app_cb->ui_xmit_q)); |
| |
| p_app_cb->is_ui_tx_congested = false; |
| |
| while (p_app_cb->ui_rx_q.p_first) |
| GKI_freebuf(GKI_dequeue(&p_app_cb->ui_rx_q)); |
| } |
| } |
| |
| llcp_cb.total_tx_ui_pdu = 0; |
| llcp_cb.total_rx_ui_pdu = 0; |
| |
| /* Notify all of data link */ |
| for (idx = 0; idx < LLCP_MAX_DATA_LINK; idx++) { |
| if (llcp_cb.dlcb[idx].state != LLCP_DLC_STATE_IDLE) { |
| p_dlcb = &(llcp_cb.dlcb[idx]); |
| |
| llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_LINK_ERROR, NULL); |
| } |
| } |
| llcp_cb.total_tx_i_pdu = 0; |
| llcp_cb.total_rx_i_pdu = 0; |
| |
| llcp_cb.overall_tx_congested = false; |
| llcp_cb.overall_rx_congested = false; |
| |
| /* As per the LLCP test specification v1.2.00 for test case TC_LLC_TAR_BV_04 |
| * the receiving LLC shall commence sending an LLC PDU to the remote |
| * LLC. So, after IUT receives DISC PDU from LT(remote device), IUT shall |
| * send DISC PDU to LT. appl_dta_mode_flag condition is added to fulfil |
| * above requirement. Only in CR8, the IUT shall acknoweledge with SYMM for |
| * DISC PDU. For other CRx, send DISC PDU. |
| */ |
| if ((reason == LLCP_LINK_FRAME_ERROR) || |
| (reason == LLCP_LINK_LOCAL_INITIATED) || |
| (appl_dta_mode_flag && reason == LLCP_LINK_REMOTE_INITIATED && |
| llcp_cb.lcb.is_initiator == false && |
| (nfa_dm_cb.eDtaMode & 0xF0) != NFA_DTA_CR8)) { |
| /* get rid of the data pending in NFC tx queue, so DISC PDU can be sent ASAP |
| */ |
| NFC_FlushData(NFC_RF_CONN_ID); |
| |
| llcp_util_send_disc(LLCP_SAP_LM, LLCP_SAP_LM); |
| |
| /* Wait until DISC is sent to peer */ |
| DLOG_IF(INFO, nfc_debug_enabled) |
| << StringPrintf("Wait until DISC is sent to peer"); |
| |
| llcp_cb.lcb.link_state = LLCP_LINK_STATE_DEACTIVATING; |
| |
| if (llcp_cb.lcb.sig_xmit_q.count == 0) { |
| /* if DISC is sent to NFCC, wait for short period for NFCC to send it to |
| * peer */ |
| nfc_start_quick_timer(&llcp_cb.lcb.timer, NFC_TTYPE_LLCP_LINK_MANAGER, |
| ((uint32_t)50) * QUICK_TIMER_TICKS_PER_SEC / 1000); |
| } |
| |
| llcp_cb.lcb.link_deact_reason = reason; |
| return; |
| } else if ((reason == LLCP_LINK_REMOTE_INITIATED) && |
| (!llcp_cb.lcb.is_initiator)) { |
| /* if received DISC to deactivate LLCP link as target role, send SYMM PDU */ |
| llcp_link_send_SYMM(); |
| } else /* for link timeout and interface error */ |
| { |
| /* if got RF link loss receiving no LLC PDU from peer */ |
| if ((reason == LLCP_LINK_RF_LINK_LOSS_ERR) && |
| (!(llcp_cb.lcb.flags & LLCP_LINK_FLAGS_RX_ANY_LLC_PDU))) { |
| reason = LLCP_LINK_RF_LINK_LOSS_NO_RX_LLC; |
| } |
| |
| NFC_FlushData(NFC_RF_CONN_ID); |
| } |
| |
| llcp_deactivate_cleanup(reason); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_parse_gen_bytes |
| ** |
| ** Description Check LLCP magic number and get parameters in general bytes |
| ** |
| ** Returns TRUE if success |
| ** |
| *******************************************************************************/ |
| static bool llcp_link_parse_gen_bytes(uint8_t gen_bytes_len, |
| uint8_t* p_gen_bytes) { |
| uint8_t* p = p_gen_bytes + LLCP_MAGIC_NUMBER_LEN; |
| uint8_t length = gen_bytes_len - LLCP_MAGIC_NUMBER_LEN; |
| |
| if ((gen_bytes_len >= LLCP_MAGIC_NUMBER_LEN) && |
| (*(p_gen_bytes) == LLCP_MAGIC_NUMBER_BYTE0) && |
| (*(p_gen_bytes + 1) == LLCP_MAGIC_NUMBER_BYTE1) && |
| (*(p_gen_bytes + 2) == LLCP_MAGIC_NUMBER_BYTE2)) { |
| /* in case peer didn't include these */ |
| llcp_cb.lcb.peer_miu = LLCP_DEFAULT_MIU; |
| llcp_cb.lcb.peer_lto = LLCP_DEFAULT_LTO_IN_MS; |
| |
| return (llcp_util_parse_link_params(length, p)); |
| } else /* if this is not LLCP */ |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_version_agreement |
| ** |
| ** Description LLCP version number agreement |
| ** |
| ** Returns TRUE if success |
| ** |
| *******************************************************************************/ |
| static bool llcp_link_version_agreement(void) { |
| uint8_t peer_major_version, peer_minor_version; |
| |
| peer_major_version = LLCP_GET_MAJOR_VERSION(llcp_cb.lcb.peer_version); |
| peer_minor_version = LLCP_GET_MINOR_VERSION(llcp_cb.lcb.peer_version); |
| |
| if (peer_major_version < LLCP_MIN_MAJOR_VERSION) { |
| LOG(ERROR) << StringPrintf( |
| "unsupported peer version number. Peer " |
| "Major Version:%d", |
| peer_major_version); |
| return false; |
| } else { |
| if (peer_major_version == LLCP_VERSION_MAJOR) { |
| llcp_cb.lcb.agreed_major_version = LLCP_VERSION_MAJOR; |
| if (peer_minor_version >= LLCP_VERSION_MINOR) { |
| llcp_cb.lcb.agreed_minor_version = LLCP_VERSION_MINOR; |
| } else { |
| llcp_cb.lcb.agreed_minor_version = peer_minor_version; |
| } |
| } else if (peer_major_version < LLCP_VERSION_MAJOR) { |
| /* so far we can support backward compatibility */ |
| llcp_cb.lcb.agreed_major_version = peer_major_version; |
| llcp_cb.lcb.agreed_minor_version = peer_minor_version; |
| } else { |
| /* let peer (higher major version) decide it */ |
| llcp_cb.lcb.agreed_major_version = LLCP_VERSION_MAJOR; |
| llcp_cb.lcb.agreed_minor_version = LLCP_VERSION_MINOR; |
| } |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "local version:%d.%d, remote version:%d.%d, agreed version:%d.%d", |
| LLCP_VERSION_MAJOR, LLCP_VERSION_MINOR, peer_major_version, |
| peer_minor_version, llcp_cb.lcb.agreed_major_version, |
| llcp_cb.lcb.agreed_minor_version); |
| |
| return true; |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_update_status |
| ** |
| ** Description Notify all of service layer client link status change |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_update_status(bool is_activated) { |
| tLLCP_SAP_CBACK_DATA data; |
| tLLCP_APP_CB* p_app_cb; |
| uint8_t sap; |
| |
| data.link_status.event = LLCP_SAP_EVT_LINK_STATUS; |
| data.link_status.is_activated = is_activated; |
| data.link_status.is_initiator = llcp_cb.lcb.is_initiator; |
| |
| /* notify all SAP so they can create connection while link is activated */ |
| for (sap = LLCP_SAP_SDP + 1; sap < LLCP_NUM_SAPS; sap++) { |
| p_app_cb = llcp_util_get_app_cb(sap); |
| |
| if ((p_app_cb) && (p_app_cb->p_app_cback)) { |
| data.link_status.local_sap = sap; |
| p_app_cb->p_app_cback(&data); |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_check_congestion |
| ** |
| ** Description Check overall congestion status |
| ** Notify to all of upper layer if congested |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_check_congestion(void) { |
| tLLCP_SAP_CBACK_DATA data; |
| tLLCP_APP_CB* p_app_cb; |
| uint8_t sap, idx; |
| |
| if (llcp_cb.overall_tx_congested) { |
| /* already congested so no need to check again */ |
| return; |
| } |
| |
| if (llcp_cb.total_tx_ui_pdu + llcp_cb.total_tx_i_pdu >= |
| llcp_cb.max_num_tx_buff) { |
| /* overall buffer usage is high */ |
| llcp_cb.overall_tx_congested = true; |
| |
| LOG(WARNING) << StringPrintf( |
| "overall tx congestion start: total_tx_ui_pdu=%d, total_tx_i_pdu=%d", |
| llcp_cb.total_tx_ui_pdu, llcp_cb.total_tx_i_pdu); |
| |
| data.congest.event = LLCP_SAP_EVT_CONGEST; |
| data.congest.is_congested = true; |
| |
| /* notify logical data link congestion status */ |
| data.congest.remote_sap = LLCP_INVALID_SAP; |
| data.congest.link_type = LLCP_LINK_TYPE_LOGICAL_DATA_LINK; |
| |
| for (sap = LLCP_SAP_SDP + 1; sap < LLCP_NUM_SAPS; sap++) { |
| p_app_cb = llcp_util_get_app_cb(sap); |
| |
| if ((p_app_cb) && (p_app_cb->p_app_cback) && |
| (p_app_cb->link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK)) { |
| /* if already congested then no need to notify again */ |
| if (!p_app_cb->is_ui_tx_congested) { |
| p_app_cb->is_ui_tx_congested = true; |
| |
| LOG(WARNING) << StringPrintf( |
| "Logical link (SAP=0x%X) congestion start: count=%d", sap, |
| p_app_cb->ui_xmit_q.count); |
| |
| data.congest.local_sap = sap; |
| p_app_cb->p_app_cback(&data); |
| } |
| } |
| } |
| |
| /* notify data link connection congestion status */ |
| data.congest.link_type = LLCP_LINK_TYPE_DATA_LINK_CONNECTION; |
| |
| for (idx = 0; idx < LLCP_MAX_DATA_LINK; idx++) { |
| if ((llcp_cb.dlcb[idx].state == LLCP_DLC_STATE_CONNECTED) && |
| (llcp_cb.dlcb[idx].remote_busy == false) && |
| (llcp_cb.dlcb[idx].is_tx_congested == false)) { |
| llcp_cb.dlcb[idx].is_tx_congested = true; |
| |
| LOG(WARNING) << StringPrintf( |
| "Data link (SSAP:DSAP=0x%X:0x%X) congestion start: count=%d", |
| llcp_cb.dlcb[idx].local_sap, llcp_cb.dlcb[idx].remote_sap, |
| llcp_cb.dlcb[idx].i_xmit_q.count); |
| |
| data.congest.local_sap = llcp_cb.dlcb[idx].local_sap; |
| data.congest.remote_sap = llcp_cb.dlcb[idx].remote_sap; |
| |
| (*llcp_cb.dlcb[idx].p_app_cb->p_app_cback)(&data); |
| } |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_check_uncongested |
| ** |
| ** Description Check overall congestion status, logical data link and |
| ** data link connection congestion status |
| ** Notify to each upper layer if uncongested |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_check_uncongested(void) { |
| tLLCP_SAP_CBACK_DATA data; |
| tLLCP_APP_CB* p_app_cb; |
| uint8_t xx, sap, idx; |
| |
| if (llcp_cb.overall_tx_congested) { |
| if (llcp_cb.total_tx_ui_pdu + llcp_cb.total_tx_i_pdu <= |
| llcp_cb.max_num_tx_buff / 2) { |
| /* overall congestion is cleared */ |
| llcp_cb.overall_tx_congested = false; |
| |
| LOG(WARNING) << StringPrintf( |
| "overall tx congestion end: total_tx_ui_pdu=%d, total_tx_i_pdu=%d", |
| llcp_cb.total_tx_ui_pdu, llcp_cb.total_tx_i_pdu); |
| } else { |
| /* wait until more data packets are sent out */ |
| return; |
| } |
| } |
| |
| data.congest.event = LLCP_SAP_EVT_CONGEST; |
| data.congest.is_congested = false; |
| |
| /* if total number of UI PDU is below threshold */ |
| if (llcp_cb.total_tx_ui_pdu < llcp_cb.max_num_ll_tx_buff) { |
| /* check and notify logical data link congestion status */ |
| data.congest.remote_sap = LLCP_INVALID_SAP; |
| data.congest.link_type = LLCP_LINK_TYPE_LOGICAL_DATA_LINK; |
| |
| /* |
| ** start point of uncongested status notification is in round robin |
| ** so each logical data link has equal chance of transmitting. |
| */ |
| sap = llcp_cb.ll_tx_uncongest_ntf_start_sap; |
| |
| for (xx = LLCP_SAP_SDP + 1; xx < LLCP_NUM_SAPS; xx++) { |
| /* no logical data link on LM and SDP */ |
| if (sap > LLCP_SAP_SDP) { |
| p_app_cb = llcp_util_get_app_cb(sap); |
| |
| if ((p_app_cb) && (p_app_cb->p_app_cback) && |
| (p_app_cb->link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK) && |
| (p_app_cb->is_ui_tx_congested) && |
| (p_app_cb->ui_xmit_q.count <= llcp_cb.ll_tx_congest_end)) { |
| /* if it was congested but now tx queue count is below threshold */ |
| p_app_cb->is_ui_tx_congested = false; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "Logical link (SAP=0x%X) congestion end: count=%d", sap, |
| p_app_cb->ui_xmit_q.count); |
| |
| data.congest.local_sap = sap; |
| p_app_cb->p_app_cback(&data); |
| } |
| } |
| |
| sap = (sap + 1) % LLCP_NUM_SAPS; |
| } |
| |
| /* move start point for next logical data link */ |
| for (xx = 0; xx < LLCP_NUM_SAPS; xx++) { |
| sap = (llcp_cb.ll_tx_uncongest_ntf_start_sap + 1) % LLCP_NUM_SAPS; |
| |
| if (sap > LLCP_SAP_SDP) { |
| p_app_cb = llcp_util_get_app_cb(sap); |
| |
| if ((p_app_cb) && (p_app_cb->p_app_cback) && |
| (p_app_cb->link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK)) { |
| llcp_cb.ll_tx_uncongest_ntf_start_sap = sap; |
| break; |
| } |
| } |
| } |
| } |
| |
| /* notify data link connection congestion status */ |
| data.congest.link_type = LLCP_LINK_TYPE_DATA_LINK_CONNECTION; |
| |
| /* |
| ** start point of uncongested status notification is in round robin |
| ** so each data link connection has equal chance of transmitting. |
| */ |
| idx = llcp_cb.dl_tx_uncongest_ntf_start_idx; |
| |
| for (xx = 0; xx < LLCP_MAX_DATA_LINK; xx++) { |
| /* if it was congested but now tx queue is below threshold (receiving |
| * window) */ |
| if ((llcp_cb.dlcb[idx].state == LLCP_DLC_STATE_CONNECTED) && |
| (llcp_cb.dlcb[idx].is_tx_congested) && |
| (llcp_cb.dlcb[idx].i_xmit_q.count <= llcp_cb.dlcb[idx].remote_rw / 2)) { |
| llcp_cb.dlcb[idx].is_tx_congested = false; |
| |
| if (llcp_cb.dlcb[idx].remote_busy == false) { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "Data link (SSAP:DSAP=0x%X:0x%X) congestion end: count=%d", |
| llcp_cb.dlcb[idx].local_sap, llcp_cb.dlcb[idx].remote_sap, |
| llcp_cb.dlcb[idx].i_xmit_q.count); |
| |
| data.congest.local_sap = llcp_cb.dlcb[idx].local_sap; |
| data.congest.remote_sap = llcp_cb.dlcb[idx].remote_sap; |
| |
| (*llcp_cb.dlcb[idx].p_app_cb->p_app_cback)(&data); |
| } |
| } |
| idx = (idx + 1) % LLCP_MAX_DATA_LINK; |
| } |
| |
| /* move start point for next data link connection */ |
| for (xx = 0; xx < LLCP_MAX_DATA_LINK; xx++) { |
| idx = (llcp_cb.dl_tx_uncongest_ntf_start_idx + 1) % LLCP_MAX_DATA_LINK; |
| if (llcp_cb.dlcb[idx].state == LLCP_DLC_STATE_CONNECTED) { |
| llcp_cb.dl_tx_uncongest_ntf_start_idx = idx; |
| break; |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_send_SYMM |
| ** |
| ** Description Send SYMM PDU |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_send_SYMM(void) { |
| NFC_HDR* p_msg; |
| uint8_t* p; |
| |
| p_msg = (NFC_HDR*)GKI_getpoolbuf(LLCP_POOL_ID); |
| |
| if (p_msg) { |
| p_msg->len = LLCP_PDU_SYMM_SIZE; |
| p_msg->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| |
| p = (uint8_t*)(p_msg + 1) + p_msg->offset; |
| UINT16_TO_BE_STREAM( |
| p, LLCP_GET_PDU_HEADER(LLCP_SAP_LM, LLCP_PDU_SYMM_TYPE, LLCP_SAP_LM)); |
| |
| llcp_link_send_to_lower(p_msg); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_send_invalid_pdu |
| ** |
| ** Description Send invalid LLC PDU in LLCP_LINK_STATE_ACTIVATION_FAILED |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_send_invalid_pdu(void) { |
| NFC_HDR* p_msg; |
| uint8_t* p; |
| |
| p_msg = (NFC_HDR*)GKI_getpoolbuf(LLCP_POOL_ID); |
| |
| if (p_msg) { |
| /* send one byte of 0x00 as invalid LLC PDU */ |
| p_msg->len = 1; |
| p_msg->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| |
| p = (uint8_t*)(p_msg + 1) + p_msg->offset; |
| *p = 0x00; |
| |
| NFC_SendData(NFC_RF_CONN_ID, p_msg); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_check_send_data |
| ** |
| ** Description Send PDU to peer |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void llcp_link_check_send_data(void) { |
| NFC_HDR* p_pdu; |
| |
| /* don't re-enter while processing to prevent out of sequence */ |
| if (llcp_cb.lcb.is_sending_data) |
| return; |
| else |
| llcp_cb.lcb.is_sending_data = true; |
| |
| /* |
| ** check overall congestion due to high usage of buffer pool |
| ** if congested then notify all of upper layers not to send any more data |
| */ |
| llcp_link_check_congestion(); |
| |
| if (llcp_cb.lcb.symm_state == LLCP_LINK_SYMM_LOCAL_XMIT_NEXT || |
| (appl_dta_mode_flag && |
| llcp_cb.lcb.link_state == LLCP_LINK_STATE_DEACTIVATING)) { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "in state of " |
| "LLCP_LINK_SYMM_LOCAL_XMIT_NEXT"); |
| |
| p_pdu = llcp_link_build_next_pdu(NULL); |
| |
| /* |
| ** For data link connection, |
| ** V(RA) was updated and N(R) was set to V(RA), if I PDU was added in |
| ** this transmission. If there was no I PDU to carry V(RA) and V(RA) is |
| ** not V(R) and it's not congested, then RR PDU will be sent. |
| ** If there was no I PDU to carry V(RA) and V(RA) is not V(R) and it's |
| ** congested, then RNR PDU will be sent. |
| ** If local busy state has been changed then RR or RNR PDU may be sent. |
| */ |
| llcp_dlc_check_to_send_rr_rnr(); |
| |
| /* add RR/RNR PDU to be sent if any */ |
| p_pdu = llcp_link_build_next_pdu(p_pdu); |
| |
| if (p_pdu != NULL) { |
| llcp_link_send_to_lower(p_pdu); |
| |
| /* stop inactivity timer */ |
| llcp_link_stop_inactivity_timer(); |
| |
| /* check congestion status after sending out some data */ |
| llcp_link_check_uncongested(); |
| } else { |
| /* There is no data to send, so send SYMM */ |
| if (llcp_cb.lcb.link_state == LLCP_LINK_STATE_ACTIVATED) { |
| if (llcp_cb.lcb.symm_delay > 0) { |
| /* wait for application layer sending data */ |
| llcp_link_start_link_timer(); |
| llcp_cb.lcb.is_sending_data = false; |
| return; |
| } else { |
| llcp_link_send_SYMM(); |
| |
| /* start inactivity timer */ |
| if (llcp_cb.num_data_link_connection == 0) { |
| llcp_link_start_inactivity_timer(); |
| } |
| } |
| } else { |
| llcp_cb.lcb.is_sending_data = false; |
| return; |
| } |
| } |
| |
| if (llcp_cb.lcb.link_state == LLCP_LINK_STATE_DEACTIVATING) { |
| /* wait for short period for NFCC to send DISC */ |
| nfc_start_quick_timer(&llcp_cb.lcb.timer, NFC_TTYPE_LLCP_LINK_MANAGER, |
| ((uint32_t)50) * QUICK_TIMER_TICKS_PER_SEC / 1000); |
| } else { |
| /* wait for data to receive from remote */ |
| llcp_link_start_link_timer(); |
| } |
| } |
| |
| llcp_cb.lcb.is_sending_data = false; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_proc_ui_pdu |
| ** |
| ** Description Process UI PDU from peer device |
| ** |
| ** Returns None |
| ** |
| *******************************************************************************/ |
| static void llcp_link_proc_ui_pdu(uint8_t local_sap, uint8_t remote_sap, |
| uint16_t ui_pdu_length, uint8_t* p_ui_pdu, |
| NFC_HDR* p_msg) { |
| bool appended; |
| NFC_HDR* p_last_buf; |
| uint16_t available_bytes; |
| uint8_t* p_dst; |
| tLLCP_APP_CB* p_app_cb; |
| tLLCP_SAP_CBACK_DATA data; |
| tLLCP_DLCB* p_dlcb; |
| |
| p_app_cb = llcp_util_get_app_cb(local_sap); |
| /*if UI PDU sent to SAP with data link connection*/ |
| p_dlcb = llcp_dlc_find_dlcb_by_sap(local_sap, remote_sap); |
| if (p_dlcb) { |
| llcp_util_send_frmr(p_dlcb, LLCP_FRMR_W_ERROR_FLAG, LLCP_PDU_UI_TYPE, 0); |
| llcp_dlsm_execute(p_dlcb, LLCP_DLC_EVENT_FRAME_ERROR, NULL); |
| if (p_msg) { |
| GKI_freebuf(p_msg); |
| } |
| return; |
| } |
| |
| /* if application is registered and expecting UI PDU on logical data link */ |
| if ((p_app_cb) && (p_app_cb->p_app_cback) && |
| (p_app_cb->link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK)) { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "Local SAP:0x%x, Remote SAP:0x%x", local_sap, remote_sap); |
| |
| /* if this is not from AGF PDU */ |
| if (p_msg) { |
| ui_pdu_length = p_msg->len; /* including LLCP header */ |
| p_ui_pdu = (uint8_t*)(p_msg + 1) + p_msg->offset; |
| } |
| |
| appended = false; |
| |
| /* get last buffer in rx queue */ |
| p_last_buf = (NFC_HDR*)GKI_getlast(&p_app_cb->ui_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 + ui_pdu_length) { |
| p_dst = |
| (uint8_t*)(p_last_buf + 1) + p_last_buf->offset + p_last_buf->len; |
| |
| /* add length of UI PDU */ |
| UINT16_TO_BE_STREAM(p_dst, ui_pdu_length); |
| |
| /* copy UI PDU with LLCP header */ |
| memcpy(p_dst, p_ui_pdu, ui_pdu_length); |
| |
| p_last_buf->len += LLCP_PDU_AGF_LEN_SIZE + ui_pdu_length; |
| |
| if (p_msg) GKI_freebuf(p_msg); |
| |
| appended = true; |
| } |
| } |
| |
| /* if it is not available to append */ |
| if (!appended) { |
| /* if it's not from AGF PDU */ |
| if (p_msg) { |
| /* add length of PDU in front of UI PDU (reuse room for NCI header) */ |
| p_ui_pdu -= LLCP_PDU_AGF_LEN_SIZE; |
| UINT16_TO_BE_STREAM(p_ui_pdu, ui_pdu_length); |
| |
| p_msg->offset -= LLCP_PDU_AGF_LEN_SIZE; |
| p_msg->len += 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 PDU in front of UI PDU */ |
| UINT16_TO_BE_STREAM(p_dst, ui_pdu_length); |
| |
| memcpy(p_dst, p_ui_pdu, ui_pdu_length); |
| |
| p_msg->offset = 0; |
| p_msg->len = LLCP_PDU_AGF_LEN_SIZE + ui_pdu_length; |
| p_msg->layer_specific = 0; |
| } else { |
| LOG(ERROR) << StringPrintf("out of buffer"); |
| } |
| } |
| |
| /* insert UI PDU in rx queue */ |
| if (p_msg) { |
| GKI_enqueue(&p_app_cb->ui_rx_q, p_msg); |
| llcp_cb.total_rx_ui_pdu++; |
| } |
| } |
| |
| if (p_app_cb->ui_rx_q.count > llcp_cb.ll_rx_congest_start) { |
| LOG(WARNING) << StringPrintf( |
| "SAP:0x%x, rx link is congested (%d), " |
| "discard oldest UI PDU", |
| local_sap, p_app_cb->ui_rx_q.count); |
| |
| GKI_freebuf(GKI_dequeue(&p_app_cb->ui_rx_q)); |
| llcp_cb.total_rx_ui_pdu--; |
| } |
| |
| if ((p_app_cb->ui_rx_q.count == 1) && (appended == false)) { |
| data.data_ind.event = LLCP_SAP_EVT_DATA_IND; |
| data.data_ind.local_sap = local_sap; |
| data.data_ind.remote_sap = remote_sap; |
| data.data_ind.link_type = LLCP_LINK_TYPE_LOGICAL_DATA_LINK; |
| (*p_app_cb->p_app_cback)(&data); |
| } |
| } else { |
| LOG(ERROR) << StringPrintf("Unregistered SAP:0x%x", local_sap); |
| |
| if (p_msg) { |
| GKI_freebuf(p_msg); |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_proc_agf_pdu |
| ** |
| ** Description Process AGF PDU from peer device |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_proc_agf_pdu(NFC_HDR* p_agf) { |
| uint16_t agf_length; |
| uint8_t *p, *p_info, *p_pdu_length; |
| uint16_t pdu_hdr, pdu_length; |
| uint8_t dsap, ptype, ssap; |
| |
| p_agf->len -= LLCP_PDU_HEADER_SIZE; |
| p_agf->offset += LLCP_PDU_HEADER_SIZE; |
| |
| /* |
| ** check integrity of AGF PDU and get number of PDUs in AGF PDU |
| */ |
| agf_length = p_agf->len; |
| p = (uint8_t*)(p_agf + 1) + p_agf->offset; |
| |
| while (agf_length > 0) { |
| if (agf_length > LLCP_PDU_AGF_LEN_SIZE) { |
| BE_STREAM_TO_UINT16(pdu_length, p); |
| agf_length -= LLCP_PDU_AGF_LEN_SIZE; |
| } else { |
| break; |
| } |
| |
| if (pdu_length <= agf_length) { |
| p += pdu_length; |
| agf_length -= pdu_length; |
| } else { |
| break; |
| } |
| } |
| |
| if (agf_length != 0) { |
| LOG(ERROR) << StringPrintf("Received invalid AGF PDU"); |
| GKI_freebuf(p_agf); |
| return; |
| } |
| |
| /* |
| ** Process PDUs in AGF |
| */ |
| agf_length = p_agf->len; |
| p = (uint8_t*)(p_agf + 1) + p_agf->offset; |
| |
| while (agf_length > 0) { |
| /* get length of PDU */ |
| p_pdu_length = p; |
| BE_STREAM_TO_UINT16(pdu_length, p); |
| agf_length -= LLCP_PDU_AGF_LEN_SIZE; |
| |
| /* get DSAP/PTYPE/SSAP */ |
| p_info = p; |
| BE_STREAM_TO_UINT16(pdu_hdr, p_info); |
| |
| dsap = LLCP_GET_DSAP(pdu_hdr); |
| ptype = (uint8_t)(LLCP_GET_PTYPE(pdu_hdr)); |
| ssap = LLCP_GET_SSAP(pdu_hdr); |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "Rx DSAP:0x%x, PTYPE:%s (0x%x), SSAP:0x%x " |
| "in AGF", |
| dsap, llcp_pdu_type(ptype).c_str(), ptype, ssap); |
| |
| if ((ptype == LLCP_PDU_DISC_TYPE) && (dsap == LLCP_SAP_LM) && |
| (ssap == LLCP_SAP_LM)) { |
| GKI_freebuf(p_agf); |
| llcp_link_deactivate(LLCP_LINK_REMOTE_INITIATED); |
| return; |
| } else if (ptype == LLCP_PDU_SYMM_TYPE) { |
| LOG(ERROR) << StringPrintf("SYMM PDU exchange shall not be in AGF"); |
| } else if (ptype == LLCP_PDU_PAX_TYPE) { |
| LOG(ERROR) << StringPrintf("PAX PDU exchange shall not be used"); |
| } else if (ptype == LLCP_PDU_SNL_TYPE) { |
| llcp_sdp_proc_snl((uint16_t)(pdu_length - LLCP_PDU_HEADER_SIZE), p_info); |
| } else if ((ptype == LLCP_PDU_UI_TYPE) && |
| (pdu_length > LLCP_PDU_HEADER_SIZE)) { |
| llcp_link_proc_ui_pdu(dsap, ssap, pdu_length, p, NULL); |
| } else if (ptype == LLCP_PDU_I_TYPE) { |
| llcp_dlc_proc_i_pdu(dsap, ssap, pdu_length, p, NULL); |
| } else /* let data link connection handle PDU */ |
| { |
| llcp_dlc_proc_rx_pdu(dsap, ptype, ssap, |
| (uint16_t)(pdu_length - LLCP_PDU_HEADER_SIZE), |
| p_info); |
| } |
| |
| p += pdu_length; |
| agf_length -= pdu_length; |
| } |
| |
| GKI_freebuf(p_agf); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_proc_rx_pdu |
| ** |
| ** Description Process received PDU from peer device |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_proc_rx_pdu(uint8_t dsap, uint8_t ptype, uint8_t ssap, |
| NFC_HDR* p_msg) { |
| bool free_buffer = true; |
| uint8_t* p_data; |
| |
| switch (ptype) { |
| case LLCP_PDU_PAX_TYPE: |
| LOG(ERROR) << StringPrintf("; PAX PDU exchange shall not be used"); |
| break; |
| |
| case LLCP_PDU_DISC_TYPE: |
| if ((dsap == LLCP_SAP_LM) && (ssap == LLCP_SAP_LM)) { |
| llcp_link_deactivate(LLCP_LINK_REMOTE_INITIATED); |
| } else { |
| p_data = (uint8_t*)(p_msg + 1) + p_msg->offset + LLCP_PDU_HEADER_SIZE; |
| llcp_dlc_proc_rx_pdu(dsap, ptype, ssap, |
| (uint16_t)(p_msg->len - LLCP_PDU_HEADER_SIZE), |
| p_data); |
| } |
| break; |
| |
| case LLCP_PDU_SNL_TYPE: |
| p_data = (uint8_t*)(p_msg + 1) + p_msg->offset + LLCP_PDU_HEADER_SIZE; |
| llcp_sdp_proc_snl((uint16_t)(p_msg->len - LLCP_PDU_HEADER_SIZE), p_data); |
| break; |
| |
| case LLCP_PDU_AGF_TYPE: |
| llcp_link_proc_agf_pdu(p_msg); |
| free_buffer = false; |
| break; |
| |
| case LLCP_PDU_UI_TYPE: |
| llcp_link_proc_ui_pdu(dsap, ssap, 0, NULL, p_msg); |
| free_buffer = false; |
| break; |
| |
| case LLCP_PDU_I_TYPE: |
| llcp_dlc_proc_i_pdu(dsap, ssap, 0, NULL, p_msg); |
| free_buffer = false; |
| break; |
| |
| default: |
| p_data = (uint8_t*)(p_msg + 1) + p_msg->offset + LLCP_PDU_HEADER_SIZE; |
| llcp_dlc_proc_rx_pdu(dsap, ptype, ssap, |
| (uint16_t)(p_msg->len - LLCP_PDU_HEADER_SIZE), |
| p_data); |
| break; |
| } |
| |
| if (free_buffer) GKI_freebuf(p_msg); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_proc_rx_data |
| ** |
| ** Description Process received data from NFCC and maintain symmetry state |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_proc_rx_data(NFC_HDR* p_msg) { |
| uint8_t* p; |
| uint16_t pdu_hdr, info_length = 0; |
| uint8_t dsap, ptype, ssap; |
| bool free_buffer = true; |
| bool frame_error = false; |
| |
| if (llcp_cb.lcb.symm_state == LLCP_LINK_SYMM_REMOTE_XMIT_NEXT) { |
| llcp_link_stop_link_timer(); |
| |
| if (llcp_cb.lcb.received_first_packet == false) { |
| llcp_cb.lcb.received_first_packet = true; |
| (*llcp_cb.lcb.p_link_cback)(LLCP_LINK_FIRST_PACKET_RECEIVED_EVT, |
| LLCP_LINK_SUCCESS); |
| } |
| if ((llcp_cb.lcb.link_state == LLCP_LINK_STATE_DEACTIVATING) && |
| (llcp_cb.lcb.sig_xmit_q.count == 0)) { |
| /* this indicates that DISC PDU had been sent out to peer */ |
| /* initiator may wait for SYMM PDU */ |
| if (appl_dta_mode_flag == 0x01) |
| llcp_util_send_disc(LLCP_SAP_LM, LLCP_SAP_LM); |
| else |
| llcp_link_process_link_timeout(); |
| } else { |
| if (p_msg->len < LLCP_PDU_HEADER_SIZE) { |
| LOG(ERROR) << StringPrintf("Received too small PDU: got %d bytes", |
| p_msg->len); |
| frame_error = true; |
| } else { |
| p = (uint8_t*)(p_msg + 1) + p_msg->offset; |
| BE_STREAM_TO_UINT16(pdu_hdr, p); |
| |
| dsap = LLCP_GET_DSAP(pdu_hdr); |
| ptype = (uint8_t)(LLCP_GET_PTYPE(pdu_hdr)); |
| ssap = LLCP_GET_SSAP(pdu_hdr); |
| |
| /* get length of information per PDU type */ |
| if ((ptype == LLCP_PDU_I_TYPE) || (ptype == LLCP_PDU_RR_TYPE) || |
| (ptype == LLCP_PDU_RNR_TYPE)) { |
| if (p_msg->len >= LLCP_PDU_HEADER_SIZE + LLCP_SEQUENCE_SIZE) { |
| info_length = |
| p_msg->len - LLCP_PDU_HEADER_SIZE - LLCP_SEQUENCE_SIZE; |
| } else { |
| LOG(ERROR) << StringPrintf( |
| "Received I/RR/RNR PDU without sequence"); |
| frame_error = true; |
| } |
| } else { |
| info_length = p_msg->len - LLCP_PDU_HEADER_SIZE; |
| } |
| |
| /* check if length of information is bigger than link MIU */ |
| if ((!frame_error) && (info_length > llcp_cb.lcb.local_link_miu)) { |
| LOG(ERROR) << StringPrintf( |
| "Received exceeding MIU (%d): got %d bytes SDU", |
| llcp_cb.lcb.local_link_miu, info_length); |
| |
| frame_error = true; |
| } else { |
| DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf( |
| "DSAP:0x%x, PTYPE:%s (0x%x), " |
| "SSAP:0x%x", |
| dsap, llcp_pdu_type(ptype).c_str(), ptype, ssap); |
| |
| if (ptype == LLCP_PDU_SYMM_TYPE) { |
| if (info_length > 0) { |
| LOG(ERROR) << StringPrintf( |
| "Received extra data (%d bytes) in SYMM PDU", info_length); |
| frame_error = true; |
| } |
| } else { |
| /* received other than SYMM */ |
| llcp_link_stop_inactivity_timer(); |
| |
| llcp_link_proc_rx_pdu(dsap, ptype, ssap, p_msg); |
| free_buffer = false; |
| } |
| } |
| } |
| |
| llcp_cb.lcb.symm_state = LLCP_LINK_SYMM_LOCAL_XMIT_NEXT; |
| |
| /* check if any pending packet */ |
| llcp_link_check_send_data(); |
| } |
| } else { |
| LOG(ERROR) << StringPrintf("Received PDU in state of SYMM_MUST_XMIT_NEXT"); |
| } |
| |
| if (free_buffer) GKI_freebuf(p_msg); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_get_next_pdu |
| ** |
| ** Description Get next PDU from link manager or data links w/wo dequeue |
| ** |
| ** Returns pointer of a PDU to send if length_only is FALSE |
| ** NULL otherwise |
| ** |
| *******************************************************************************/ |
| static NFC_HDR* llcp_link_get_next_pdu(bool length_only, |
| uint16_t* p_next_pdu_length) { |
| NFC_HDR* p_msg; |
| int count, xx; |
| tLLCP_APP_CB* p_app_cb; |
| |
| /* processing signalling PDU first */ |
| if (llcp_cb.lcb.sig_xmit_q.p_first) { |
| if (length_only) { |
| p_msg = (NFC_HDR*)llcp_cb.lcb.sig_xmit_q.p_first; |
| *p_next_pdu_length = p_msg->len; |
| return NULL; |
| } else |
| p_msg = (NFC_HDR*)GKI_dequeue(&llcp_cb.lcb.sig_xmit_q); |
| |
| return p_msg; |
| } else { |
| /* transmitting logical data link and data link connection equaly */ |
| for (xx = 0; xx < 2; xx++) { |
| if (!llcp_cb.lcb.ll_served) { |
| /* Get one from logical link connection */ |
| for (count = 0; count < LLCP_NUM_SAPS; count++) { |
| /* round robin schedule without priority */ |
| p_app_cb = llcp_util_get_app_cb(llcp_cb.lcb.ll_idx); |
| |
| if ((p_app_cb) && (p_app_cb->p_app_cback) && |
| (p_app_cb->ui_xmit_q.count)) { |
| if (length_only) { |
| /* don't alternate next data link to return the same length of PDU |
| */ |
| p_msg = (NFC_HDR*)p_app_cb->ui_xmit_q.p_first; |
| *p_next_pdu_length = p_msg->len; |
| return NULL; |
| } else { |
| /* check data link connection first in next time */ |
| llcp_cb.lcb.ll_served = !llcp_cb.lcb.ll_served; |
| |
| p_msg = (NFC_HDR*)GKI_dequeue(&p_app_cb->ui_xmit_q); |
| llcp_cb.total_tx_ui_pdu--; |
| |
| /* this logical link has been served, so start from next logical |
| * link next time */ |
| llcp_cb.lcb.ll_idx = (llcp_cb.lcb.ll_idx + 1) % LLCP_NUM_SAPS; |
| |
| return p_msg; |
| } |
| } else { |
| /* check next logical link connection */ |
| llcp_cb.lcb.ll_idx = (llcp_cb.lcb.ll_idx + 1) % LLCP_NUM_SAPS; |
| } |
| } |
| |
| /* no data, so check data link connection if not checked yet */ |
| llcp_cb.lcb.ll_served = !llcp_cb.lcb.ll_served; |
| } else { |
| /* Get one from data link connection */ |
| for (count = 0; count < LLCP_MAX_DATA_LINK; count++) { |
| /* round robin schedule without priority */ |
| if (llcp_cb.dlcb[llcp_cb.lcb.dl_idx].state != LLCP_DLC_STATE_IDLE) { |
| if (length_only) { |
| *p_next_pdu_length = llcp_dlc_get_next_pdu_length( |
| &llcp_cb.dlcb[llcp_cb.lcb.dl_idx]); |
| |
| if (*p_next_pdu_length > 0) { |
| /* don't change data link connection to return the same length |
| * of PDU */ |
| return NULL; |
| } else { |
| /* no data, so check next data link connection */ |
| llcp_cb.lcb.dl_idx = |
| (llcp_cb.lcb.dl_idx + 1) % LLCP_MAX_DATA_LINK; |
| } |
| } else { |
| p_msg = llcp_dlc_get_next_pdu(&llcp_cb.dlcb[llcp_cb.lcb.dl_idx]); |
| |
| /* this data link has been served, so start from next data link |
| * next time */ |
| llcp_cb.lcb.dl_idx = |
| (llcp_cb.lcb.dl_idx + 1) % LLCP_MAX_DATA_LINK; |
| |
| if (p_msg) { |
| /* serve logical data link next time */ |
| llcp_cb.lcb.ll_served = !llcp_cb.lcb.ll_served; |
| return p_msg; |
| } |
| } |
| } else { |
| /* check next data link connection */ |
| llcp_cb.lcb.dl_idx = (llcp_cb.lcb.dl_idx + 1) % LLCP_MAX_DATA_LINK; |
| } |
| } |
| |
| /* if all of data link connection doesn't have data to send */ |
| if (count >= LLCP_MAX_DATA_LINK) { |
| llcp_cb.lcb.ll_served = !llcp_cb.lcb.ll_served; |
| } |
| } |
| } |
| } |
| |
| /* nothing to send */ |
| *p_next_pdu_length = 0; |
| return NULL; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_build_next_pdu |
| ** |
| ** Description Build a PDU from Link Manager and Data Link |
| ** Perform aggregation procedure if necessary |
| ** |
| ** Returns NFC_HDR* if sent any PDU |
| ** |
| *******************************************************************************/ |
| static NFC_HDR* llcp_link_build_next_pdu(NFC_HDR* p_pdu) { |
| NFC_HDR *p_agf = NULL, *p_msg = NULL, *p_next_pdu; |
| uint8_t *p, ptype; |
| uint16_t next_pdu_length, pdu_hdr; |
| |
| DLOG_IF(INFO, nfc_debug_enabled) << __func__; |
| |
| /* add any pending SNL PDU into sig_xmit_q for transmitting */ |
| llcp_sdp_check_send_snl(); |
| |
| if (p_pdu) { |
| /* get PDU type */ |
| p = (uint8_t*)(p_pdu + 1) + p_pdu->offset; |
| BE_STREAM_TO_UINT16(pdu_hdr, p); |
| |
| ptype = (uint8_t)(LLCP_GET_PTYPE(pdu_hdr)); |
| |
| if (ptype == LLCP_PDU_AGF_TYPE) { |
| /* add more PDU into this AGF PDU */ |
| p_agf = p_pdu; |
| } else { |
| p_msg = p_pdu; |
| } |
| } else { |
| /* Get a PDU from link manager or data links */ |
| p_msg = llcp_link_get_next_pdu(false, &next_pdu_length); |
| |
| if (!p_msg) { |
| return NULL; |
| } |
| } |
| |
| /* Get length of next PDU from link manager or data links without dequeue */ |
| llcp_link_get_next_pdu(true, &next_pdu_length); |
| while (next_pdu_length > 0) { |
| /* if it's first visit */ |
| if (!p_agf) { |
| /* if next PDU fits into MIU, allocate AGF PDU and copy the first PDU */ |
| if (2 + p_msg->len + 2 + next_pdu_length <= llcp_cb.lcb.effective_miu) { |
| p_agf = (NFC_HDR*)GKI_getpoolbuf(LLCP_POOL_ID); |
| if (p_agf) { |
| p_agf->offset = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE; |
| |
| p = (uint8_t*)(p_agf + 1) + p_agf->offset; |
| |
| UINT16_TO_BE_STREAM( |
| p, |
| LLCP_GET_PDU_HEADER(LLCP_SAP_LM, LLCP_PDU_AGF_TYPE, LLCP_SAP_LM)); |
| UINT16_TO_BE_STREAM(p, p_msg->len); |
| memcpy(p, (uint8_t*)(p_msg + 1) + p_msg->offset, p_msg->len); |
| |
| p_agf->len = LLCP_PDU_HEADER_SIZE + 2 + p_msg->len; |
| |
| GKI_freebuf(p_msg); |
| p_msg = p_agf; |
| } else { |
| LOG(ERROR) << StringPrintf("Out of buffer"); |
| return p_msg; |
| } |
| } else { |
| break; |
| } |
| } |
| |
| /* if next PDU fits into MIU, copy the next PDU into AGF */ |
| if (p_agf->len - LLCP_PDU_HEADER_SIZE + 2 + next_pdu_length <= |
| llcp_cb.lcb.effective_miu) { |
| /* Get a next PDU from link manager or data links */ |
| p_next_pdu = llcp_link_get_next_pdu(false, &next_pdu_length); |
| |
| p = (uint8_t*)(p_agf + 1) + p_agf->offset + p_agf->len; |
| |
| UINT16_TO_BE_STREAM(p, p_next_pdu->len); |
| memcpy(p, (uint8_t*)(p_next_pdu + 1) + p_next_pdu->offset, |
| p_next_pdu->len); |
| |
| p_agf->len += 2 + p_next_pdu->len; |
| |
| GKI_freebuf(p_next_pdu); |
| |
| /* Get next PDU length from link manager or data links without dequeue */ |
| llcp_link_get_next_pdu(true, &next_pdu_length); |
| } else { |
| break; |
| } |
| } |
| |
| if (p_agf) |
| return p_agf; |
| else |
| return p_msg; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_send_to_lower |
| ** |
| ** Description Send PDU to lower layer |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static void llcp_link_send_to_lower(NFC_HDR* p_pdu) { |
| DispLLCP(p_pdu, false); |
| |
| llcp_cb.lcb.symm_state = LLCP_LINK_SYMM_REMOTE_XMIT_NEXT; |
| |
| NFC_SendData(NFC_RF_CONN_ID, p_pdu); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_link_connection_cback |
| ** |
| ** Description processing incoming data |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| void llcp_link_connection_cback(__attribute__((unused)) uint8_t conn_id, |
| tNFC_CONN_EVT event, tNFC_CONN* p_data) { |
| if (event == NFC_DATA_CEVT) { |
| DispLLCP((NFC_HDR*)p_data->data.p_data, true); |
| if (llcp_cb.lcb.link_state == LLCP_LINK_STATE_DEACTIVATED) { |
| /* respoding SYMM while LLCP is deactivated but RF link is not deactivated |
| * yet */ |
| llcp_link_send_SYMM(); |
| GKI_freebuf((NFC_HDR*)p_data->data.p_data); |
| } else if (llcp_cb.lcb.link_state == LLCP_LINK_STATE_ACTIVATION_FAILED) { |
| /* respoding with invalid LLC PDU until initiator deactivates RF link |
| *after LLCP activation was failed, |
| ** so that initiator knows LLCP link activation was failed. |
| */ |
| llcp_link_send_invalid_pdu(); |
| GKI_freebuf((NFC_HDR*)p_data->data.p_data); |
| } else { |
| llcp_cb.lcb.flags |= LLCP_LINK_FLAGS_RX_ANY_LLC_PDU; |
| llcp_link_proc_rx_data((NFC_HDR*)p_data->data.p_data); |
| } |
| } else if (event == NFC_ERROR_CEVT) { |
| /* RF interface specific status code */ |
| llcp_link_deactivate(*(uint8_t*)p_data); |
| } else if (event == NFC_DEACTIVATE_CEVT) { |
| if ((llcp_cb.lcb.link_state == LLCP_LINK_STATE_DEACTIVATING) && |
| (!llcp_cb.lcb.is_initiator)) { |
| /* peer initiates NFC link deactivation before timeout */ |
| llcp_link_stop_link_timer(); |
| llcp_link_process_link_timeout(); |
| } else if (llcp_cb.lcb.link_state == LLCP_LINK_STATE_ACTIVATION_FAILED) { |
| /* do not notify to upper layer because activation failure was already |
| * notified */ |
| NFC_FlushData(NFC_RF_CONN_ID); |
| llcp_cb.lcb.link_state = LLCP_LINK_STATE_DEACTIVATED; |
| } else if (llcp_cb.lcb.link_state != LLCP_LINK_STATE_DEACTIVATED) { |
| llcp_link_deactivate(LLCP_LINK_RF_LINK_LOSS_ERR); |
| } |
| |
| NFC_SetStaticRfCback(NULL); |
| } else if (event == NFC_DATA_START_CEVT) { |
| if (llcp_cb.lcb.symm_state == LLCP_LINK_SYMM_REMOTE_XMIT_NEXT) { |
| /* LLCP shall stop LTO timer when receiving the first bit of LLC PDU */ |
| llcp_link_stop_link_timer(); |
| } |
| } |
| |
| /* LLCP ignores the following events |
| |
| NFC_CONN_CREATE_CEVT |
| NFC_CONN_CLOSE_CEVT |
| */ |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function llcp_pdu_type |
| ** |
| ** Description |
| ** |
| ** Returns string of PDU type |
| ** |
| *******************************************************************************/ |
| static std::string llcp_pdu_type(uint8_t ptype) { |
| switch (ptype) { |
| case LLCP_PDU_SYMM_TYPE: |
| return "SYMM"; |
| case LLCP_PDU_PAX_TYPE: |
| return "PAX"; |
| case LLCP_PDU_AGF_TYPE: |
| return "AGF"; |
| case LLCP_PDU_UI_TYPE: |
| return "UI"; |
| case LLCP_PDU_CONNECT_TYPE: |
| return "CONNECT"; |
| case LLCP_PDU_DISC_TYPE: |
| return "DISC"; |
| case LLCP_PDU_CC_TYPE: |
| return "CC"; |
| case LLCP_PDU_DM_TYPE: |
| return "DM"; |
| case LLCP_PDU_FRMR_TYPE: |
| return "FRMR"; |
| case LLCP_PDU_SNL_TYPE: |
| return "SNL"; |
| case LLCP_PDU_I_TYPE: |
| return "I"; |
| case LLCP_PDU_RR_TYPE: |
| return "RR"; |
| case LLCP_PDU_RNR_TYPE: |
| return "RNR"; |
| default: |
| return "RESERVED"; |
| } |
| } |