/****************************************************************************** * * Copyright (C) 2010-2012 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 API code * ******************************************************************************/ #include #include "gki.h" #include "nfc_target.h" #include "bt_types.h" #include "llcp_api.h" #include "llcp_int.h" #include "llcp_defs.h" #if (LLCP_TEST_INCLUDED == TRUE) /* this is for LLCP testing */ tLLCP_TEST_PARAMS llcp_test_params = { LLCP_VERSION_VALUE, 0, /* not override */ }; /******************************************************************************* ** ** Function LLCP_SetTestParams ** ** Description Set test parameters for LLCP ** ** ** Returns void ** *******************************************************************************/ void LLCP_SetTestParams (UINT8 version, UINT16 wks) { LLCP_TRACE_API2 ("LLCP_SetTestParams () version:0x%02X, wks:0x%04X", version, wks); if (version != 0xFF) llcp_test_params.version = version; if (wks != 0xFFFF) llcp_test_params.wks = wks; } #endif /******************************************************************************* ** ** Function LLCP_RegisterDtaCback ** ** Description Register callback function for LLCP DTA testing ** ** ** Returns void ** *******************************************************************************/ void LLCP_RegisterDtaCback (tLLCP_DTA_CBACK *p_dta_cback) { LLCP_TRACE_API0 ("LLCP_RegisterDtaCback ()"); llcp_cb.p_dta_cback = p_dta_cback; } /******************************************************************************* ** ** Function LLCP_SetConfig ** ** Description Set configuration parameters for LLCP ** - Local Link MIU ** - Option parameter ** - Response Waiting Time Index ** - Local Link Timeout ** - Inactivity Timeout as initiator role ** - Inactivity Timeout as target role ** - Delay SYMM response ** - Data link connection timeout ** - Delay timeout to send first PDU as initiator ** ** Returns void ** *******************************************************************************/ void LLCP_SetConfig (UINT16 link_miu, UINT8 opt, UINT8 wt, UINT16 link_timeout, UINT16 inact_timeout_init, UINT16 inact_timeout_target, UINT16 symm_delay, UINT16 data_link_timeout, UINT16 delay_first_pdu_timeout) { LLCP_TRACE_API4 ("LLCP_SetConfig () link_miu:%d, opt:0x%02X, wt:%d, link_timeout:%d", link_miu, opt, wt, link_timeout); LLCP_TRACE_API4 (" inact_timeout (init:%d,target:%d), symm_delay:%d, data_link_timeout:%d", inact_timeout_init, inact_timeout_target, symm_delay, data_link_timeout); LLCP_TRACE_API1 (" delay_first_pdu_timeout:%d", delay_first_pdu_timeout); if (link_miu < LLCP_DEFAULT_MIU) { LLCP_TRACE_ERROR1 ("LLCP_SetConfig (): link_miu shall not be smaller than LLCP_DEFAULT_MIU (%d)", LLCP_DEFAULT_MIU); link_miu = LLCP_DEFAULT_MIU; } else if (link_miu > LLCP_MAX_MIU) { LLCP_TRACE_ERROR1 ("LLCP_SetConfig (): link_miu shall not be bigger than LLCP_MAX_MIU (%d)", LLCP_MAX_MIU); link_miu = LLCP_MAX_MIU; } /* if Link MIU is bigger than GKI buffer */ if (link_miu > LLCP_MIU) { LLCP_TRACE_ERROR1 ("LLCP_SetConfig (): link_miu shall not be bigger than LLCP_MIU (%d)", LLCP_MIU); llcp_cb.lcb.local_link_miu = LLCP_MIU; } else llcp_cb.lcb.local_link_miu = link_miu; llcp_cb.lcb.local_opt = opt; llcp_cb.lcb.local_wt = wt; if (link_timeout < LLCP_LTO_UNIT) { LLCP_TRACE_ERROR1 ("LLCP_SetConfig (): link_timeout shall not be smaller than LLCP_LTO_UNIT (%d ms)", LLCP_LTO_UNIT); llcp_cb.lcb.local_lto = LLCP_DEFAULT_LTO_IN_MS; } else if (link_timeout > LLCP_MAX_LTO_IN_MS) { LLCP_TRACE_ERROR1 ("LLCP_SetConfig (): link_timeout shall not be bigger than LLCP_MAX_LTO_IN_MS (%d ms)", LLCP_MAX_LTO_IN_MS); llcp_cb.lcb.local_lto = LLCP_MAX_LTO_IN_MS; } else llcp_cb.lcb.local_lto = link_timeout; llcp_cb.lcb.inact_timeout_init = inact_timeout_init; llcp_cb.lcb.inact_timeout_target = inact_timeout_target; llcp_cb.lcb.symm_delay = symm_delay; llcp_cb.lcb.data_link_timeout = data_link_timeout; llcp_cb.lcb.delay_first_pdu_timeout = delay_first_pdu_timeout; } /******************************************************************************* ** ** Function LLCP_GetConfig ** ** Description Get configuration parameters for LLCP ** - Local Link MIU ** - Option parameter ** - Response Waiting Time Index ** - Local Link Timeout ** - Inactivity Timeout as initiator role ** - Inactivity Timeout as target role ** - Delay SYMM response ** - Data link connection timeout ** - Delay timeout to send first PDU as initiator ** ** Returns void ** *******************************************************************************/ void LLCP_GetConfig (UINT16 *p_link_miu, UINT8 *p_opt, UINT8 *p_wt, UINT16 *p_link_timeout, UINT16 *p_inact_timeout_init, UINT16 *p_inact_timeout_target, UINT16 *p_symm_delay, UINT16 *p_data_link_timeout, UINT16 *p_delay_first_pdu_timeout) { *p_link_miu = llcp_cb.lcb.local_link_miu; *p_opt = llcp_cb.lcb.local_opt; *p_wt = llcp_cb.lcb.local_wt; *p_link_timeout = llcp_cb.lcb.local_lto; *p_inact_timeout_init = llcp_cb.lcb.inact_timeout_init; *p_inact_timeout_target = llcp_cb.lcb.inact_timeout_target; *p_symm_delay = llcp_cb.lcb.symm_delay; *p_data_link_timeout = llcp_cb.lcb.data_link_timeout; *p_delay_first_pdu_timeout = llcp_cb.lcb.delay_first_pdu_timeout; LLCP_TRACE_API4 ("LLCP_GetConfig () link_miu:%d, opt:0x%02X, wt:%d, link_timeout:%d", *p_link_miu, *p_opt, *p_wt, *p_link_timeout); LLCP_TRACE_API4 (" inact_timeout (init:%d, target:%d), symm_delay:%d, data_link_timeout:%d", *p_inact_timeout_init, *p_inact_timeout_target, *p_symm_delay, *p_data_link_timeout); LLCP_TRACE_API1 (" delay_first_pdu_timeout:%d", *p_delay_first_pdu_timeout); } /******************************************************************************* ** ** Function LLCP_GetDiscoveryConfig ** ** Description Returns discovery config for ISO 18092 MAC link activation ** This function is called to get general bytes for NFC_PMID_ATR_REQ_GEN_BYTES ** or NFC_PMID_ATR_RES_GEN_BYTES before starting discovery. ** ** wt:Waiting time 0 - 8, only for listen ** p_gen_bytes: pointer to store LLCP magic number and paramters ** p_gen_bytes_len: length of buffer for gen bytes as input ** (NOTE:it must be bigger than LLCP_MIN_GEN_BYTES) ** actual gen bytes size as output ** ** Restrictions on the use of ISO 18092 ** 1. The DID features shall not be used. ** 2. the NAD features shall not be used. ** 3. Frame waiting time extentions (WTX) shall not be used. ** ** Returns None ** *******************************************************************************/ void LLCP_GetDiscoveryConfig (UINT8 *p_wt, UINT8 *p_gen_bytes, UINT8 *p_gen_bytes_len) { UINT8 *p = p_gen_bytes; LLCP_TRACE_API0 ("LLCP_GetDiscoveryConfig ()"); if (*p_gen_bytes_len < LLCP_MIN_GEN_BYTES) { LLCP_TRACE_ERROR1 ("LLCP_GetDiscoveryConfig (): GenBytes length shall not be smaller than LLCP_MIN_GEN_BYTES (%d)", LLCP_MIN_GEN_BYTES); *p_gen_bytes_len = 0; return; } *p_wt = llcp_cb.lcb.local_wt; UINT8_TO_BE_STREAM (p, LLCP_MAGIC_NUMBER_BYTE0); UINT8_TO_BE_STREAM (p, LLCP_MAGIC_NUMBER_BYTE1); UINT8_TO_BE_STREAM (p, LLCP_MAGIC_NUMBER_BYTE2); #if (LLCP_TEST_INCLUDED == TRUE) /* this is for LLCP testing */ UINT8_TO_BE_STREAM (p, LLCP_VERSION_TYPE); UINT8_TO_BE_STREAM (p, LLCP_VERSION_LEN); UINT8_TO_BE_STREAM (p, llcp_test_params.version); UINT8_TO_BE_STREAM (p, LLCP_MIUX_TYPE); UINT8_TO_BE_STREAM (p, LLCP_MIUX_LEN); UINT16_TO_BE_STREAM (p, (llcp_cb.lcb.local_link_miu - LLCP_DEFAULT_MIU)); UINT8_TO_BE_STREAM (p, LLCP_WKS_TYPE); UINT8_TO_BE_STREAM (p, LLCP_WKS_LEN); if (llcp_test_params.wks == 0) /* not override */ { UINT16_TO_BE_STREAM (p, llcp_cb.lcb.wks); } else { UINT16_TO_BE_STREAM (p, llcp_test_params.wks); } #else UINT8_TO_BE_STREAM (p, LLCP_VERSION_TYPE); UINT8_TO_BE_STREAM (p, LLCP_VERSION_LEN); UINT8_TO_BE_STREAM (p, LLCP_VERSION_VALUE); UINT8_TO_BE_STREAM (p, LLCP_MIUX_TYPE); UINT8_TO_BE_STREAM (p, LLCP_MIUX_LEN); UINT16_TO_BE_STREAM (p, (llcp_cb.lcb.local_link_miu - LLCP_DEFAULT_MIU)); UINT8_TO_BE_STREAM (p, LLCP_WKS_TYPE); UINT8_TO_BE_STREAM (p, LLCP_WKS_LEN); UINT16_TO_BE_STREAM (p, llcp_cb.lcb.wks); #endif UINT8_TO_BE_STREAM (p, LLCP_LTO_TYPE); UINT8_TO_BE_STREAM (p, LLCP_LTO_LEN); UINT8_TO_BE_STREAM (p, (llcp_cb.lcb.local_lto/LLCP_LTO_UNIT)); UINT8_TO_BE_STREAM (p, LLCP_OPT_TYPE); UINT8_TO_BE_STREAM (p, LLCP_OPT_LEN); UINT8_TO_BE_STREAM (p, llcp_cb.lcb.local_opt); *p_gen_bytes_len = (UINT8) (p - p_gen_bytes); } /******************************************************************************* ** ** Function LLCP_ActivateLink ** ** Description This function will activate LLCP link with LR, WT and Gen Bytes ** in activation NTF from NFCC. ** ** LLCP_LINK_ACTIVATION_COMPLETE_EVT will be returned through ** callback function if successful. ** Otherwise, LLCP_LINK_ACTIVATION_FAILED_EVT will be returned. ** ** Returns LLCP_STATUS_SUCCESS if success ** *******************************************************************************/ tLLCP_STATUS LLCP_ActivateLink (tLLCP_ACTIVATE_CONFIG config, tLLCP_LINK_CBACK *p_link_cback) { LLCP_TRACE_API1 ("LLCP_ActivateLink () link_state = %d", llcp_cb.lcb.link_state); if ( (llcp_cb.lcb.link_state == LLCP_LINK_STATE_DEACTIVATED) &&(p_link_cback) ) { llcp_cb.lcb.p_link_cback = p_link_cback; return (llcp_link_activate (&config)); } else return LLCP_STATUS_FAIL; } /******************************************************************************* ** ** Function LLCP_DeactivateLink ** ** Description Deactivate LLCP link ** ** LLCP_LINK_DEACTIVATED_EVT will be returned through callback ** when LLCP link is deactivated. Then NFC link may be deactivated. ** ** Returns LLCP_STATUS_SUCCESS if success ** *******************************************************************************/ tLLCP_STATUS LLCP_DeactivateLink (void) { LLCP_TRACE_API1 ("LLCP_DeactivateLink () link_state = %d", llcp_cb.lcb.link_state); if (llcp_cb.lcb.link_state != LLCP_LINK_STATE_DEACTIVATED) { llcp_link_deactivate (LLCP_LINK_LOCAL_INITIATED); return LLCP_STATUS_SUCCESS; } else return LLCP_STATUS_FAIL; } /******************************************************************************* ** ** Function LLCP_RegisterServer ** ** Description Register server and callback function ** ** reg_sap : Well-Known SAP except LM and SDP (0x02 - 0x0F) ** Advertized by SDP (0x10 - 0x1F) ** LLCP_INVALID_SAP, LLCP will allocate between 0x10 and 0x1F ** link_type : LLCP_LINK_TYPE_LOGICAL_DATA_LINK ** and/or LLCP_LINK_TYPE_DATA_LINK_CONNECTION ** p_service_name : Null-terminated string up to LLCP_MAX_SN_LEN ** ** Returns SAP between 0x02 and 0x1F, if success ** LLCP_INVALID_SAP, otherwise ** *******************************************************************************/ UINT8 LLCP_RegisterServer (UINT8 reg_sap, UINT8 link_type, char *p_service_name, tLLCP_APP_CBACK *p_app_cback) { UINT8 sap; UINT16 length; tLLCP_APP_CB *p_app_cb; LLCP_TRACE_API3 ("LLCP_RegisterServer (): SAP:0x%x, link_type:0x%x, ServiceName:<%s>", reg_sap, link_type, ((p_service_name == NULL) ? "" : p_service_name)); if (!p_app_cback) { LLCP_TRACE_ERROR0 ("LLCP_RegisterServer (): Callback must be provided"); return LLCP_INVALID_SAP; } else if ( ((link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK) == 0x00) &&((link_type & LLCP_LINK_TYPE_DATA_LINK_CONNECTION) == 0x00) ) { LLCP_TRACE_ERROR1 ("LLCP_RegisterServer (): link type (0x%x) must be specified", link_type); return LLCP_INVALID_SAP; } if (reg_sap == LLCP_INVALID_SAP) { /* allocate a SAP between 0x10 and 0x1F */ for (sap = 0; sap <= LLCP_MAX_SERVER; sap++) { if (llcp_cb.server_cb[sap].p_app_cback == NULL) { p_app_cb = &llcp_cb.server_cb[sap]; reg_sap = LLCP_LOWER_BOUND_SDP_SAP + sap; break; } } if (reg_sap == LLCP_INVALID_SAP) { LLCP_TRACE_ERROR0 ("LLCP_RegisterServer (): out of resource"); return LLCP_INVALID_SAP; } } else if (reg_sap == LLCP_SAP_LM) { LLCP_TRACE_ERROR1 ("LLCP_RegisterServer (): SAP (0x%x) is for link manager", reg_sap); return LLCP_INVALID_SAP; } else if (reg_sap <= LLCP_UPPER_BOUND_WK_SAP) { if (reg_sap >= LLCP_MAX_WKS) { LLCP_TRACE_ERROR1 ("LLCP_RegisterServer (): out of resource for SAP (0x%x)", reg_sap); return LLCP_INVALID_SAP; } else if (llcp_cb.wks_cb[reg_sap].p_app_cback) { LLCP_TRACE_ERROR1 ("LLCP_RegisterServer (): SAP (0x%x) is already registered", reg_sap); return LLCP_INVALID_SAP; } else { p_app_cb = &llcp_cb.wks_cb[reg_sap]; } } else if (reg_sap <= LLCP_UPPER_BOUND_SDP_SAP) { if (reg_sap - LLCP_LOWER_BOUND_SDP_SAP >= LLCP_MAX_SERVER) { LLCP_TRACE_ERROR1 ("LLCP_RegisterServer (): out of resource for SAP (0x%x)", reg_sap); return LLCP_INVALID_SAP; } else if (llcp_cb.server_cb[reg_sap - LLCP_LOWER_BOUND_SDP_SAP].p_app_cback) { LLCP_TRACE_ERROR1 ("LLCP_RegisterServer (): SAP (0x%x) is already registered", reg_sap); return LLCP_INVALID_SAP; } else { p_app_cb = &llcp_cb.server_cb[reg_sap - LLCP_LOWER_BOUND_SDP_SAP]; } } else if (reg_sap >= LLCP_LOWER_BOUND_LOCAL_SAP) { LLCP_TRACE_ERROR2 ("LLCP_RegisterServer (): SAP (0x%x) must be less than 0x%x", reg_sap, LLCP_LOWER_BOUND_LOCAL_SAP); return LLCP_INVALID_SAP; } memset (p_app_cb, 0x00, sizeof (tLLCP_APP_CB)); if (p_service_name) { length = (UINT8) strlen (p_service_name); if (length > LLCP_MAX_SN_LEN) { LLCP_TRACE_ERROR1 ("LLCP_RegisterServer (): Service Name (%d bytes) is too long", length); return LLCP_INVALID_SAP; } p_app_cb->p_service_name = (UINT8 *) GKI_getbuf ((UINT16) (length + 1)); if (p_app_cb->p_service_name == NULL) { LLCP_TRACE_ERROR0 ("LLCP_RegisterServer (): Out of resource"); return LLCP_INVALID_SAP; } BCM_STRNCPY_S ((char *) p_app_cb->p_service_name, length + 1, (char *) p_service_name, length + 1); p_app_cb->p_service_name[length] = 0; } else p_app_cb->p_service_name = NULL; p_app_cb->p_app_cback = p_app_cback; p_app_cb->link_type = link_type; if (reg_sap <= LLCP_UPPER_BOUND_WK_SAP) { llcp_cb.lcb.wks |= (1 << reg_sap); } LLCP_TRACE_DEBUG1 ("LLCP_RegisterServer (): Registered SAP = 0x%02X", reg_sap); if (link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK) { llcp_cb.num_logical_data_link++; llcp_util_adjust_ll_congestion (); } return reg_sap; } /******************************************************************************* ** ** Function LLCP_RegisterClient ** ** Description Register client and callback function ** ** link_type : LLCP_LINK_TYPE_LOGICAL_DATA_LINK ** and/or LLCP_LINK_TYPE_DATA_LINK_CONNECTION ** ** Returns SAP between 0x20 and 0x3F, if success ** LLCP_INVALID_SAP, otherwise ** *******************************************************************************/ UINT8 LLCP_RegisterClient (UINT8 link_type, tLLCP_APP_CBACK *p_app_cback) { UINT8 reg_sap = LLCP_INVALID_SAP; UINT8 sap; tLLCP_APP_CB *p_app_cb; LLCP_TRACE_API1 ("LLCP_RegisterClient (): link_type = 0x%x", link_type); if (!p_app_cback) { LLCP_TRACE_ERROR0 ("LLCP_RegisterClient (): Callback must be provided"); return LLCP_INVALID_SAP; } else if ( ((link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK) == 0x00) &&((link_type & LLCP_LINK_TYPE_DATA_LINK_CONNECTION) == 0x00) ) { LLCP_TRACE_ERROR1 ("LLCP_RegisterClient (): link type (0x%x) must be specified", link_type); return LLCP_INVALID_SAP; } /* allocate a SAP between 0x20 and 0x3F */ for (sap = 0; sap <= LLCP_MAX_CLIENT; sap++) { if (llcp_cb.client_cb[sap].p_app_cback == NULL) { p_app_cb = &llcp_cb.client_cb[sap]; memset (p_app_cb, 0x00, sizeof (tLLCP_APP_CB)); reg_sap = LLCP_LOWER_BOUND_LOCAL_SAP + sap; break; } } if (reg_sap == LLCP_INVALID_SAP) { LLCP_TRACE_ERROR0 ("LLCP_RegisterClient (): out of resource"); return LLCP_INVALID_SAP; } p_app_cb->p_app_cback = p_app_cback; p_app_cb->p_service_name = NULL; p_app_cb->link_type = link_type; LLCP_TRACE_DEBUG1 ("LLCP_RegisterClient (): Registered SAP = 0x%02X", reg_sap); if (link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK) { llcp_cb.num_logical_data_link++; llcp_util_adjust_ll_congestion (); } return reg_sap; } /******************************************************************************* ** ** Function LLCP_Deregister ** ** Description Deregister server or client ** ** ** Returns LLCP_STATUS_SUCCESS if success ** *******************************************************************************/ tLLCP_STATUS LLCP_Deregister (UINT8 local_sap) { UINT8 idx; tLLCP_APP_CB *p_app_cb; LLCP_TRACE_API1 ("LLCP_Deregister () SAP:0x%x", local_sap); p_app_cb = llcp_util_get_app_cb (local_sap); if ((!p_app_cb) || (p_app_cb->p_app_cback == NULL)) { LLCP_TRACE_ERROR1 ("LLCP_Deregister (): SAP (0x%x) is not registered", local_sap); return LLCP_STATUS_FAIL; } if (p_app_cb->p_service_name) GKI_freebuf (p_app_cb->p_service_name); /* update WKS bit map */ if (local_sap <= LLCP_UPPER_BOUND_WK_SAP) { llcp_cb.lcb.wks &= ~ (1 << local_sap); } /* discard any received UI PDU on this SAP */ LLCP_FlushLogicalLinkRxData (local_sap); llcp_cb.total_rx_ui_pdu = 0; /* deallocate any data link connection on this SAP */ for (idx = 0; idx < LLCP_MAX_DATA_LINK; idx++) { if ( (llcp_cb.dlcb[idx].state != LLCP_DLC_STATE_IDLE) &&(llcp_cb.dlcb[idx].local_sap == local_sap) ) { llcp_util_deallocate_data_link (&llcp_cb.dlcb[idx]); } } p_app_cb->p_app_cback = NULL; /* discard any pending tx UI PDU from this SAP */ while (p_app_cb->ui_xmit_q.p_first) { GKI_freebuf (GKI_dequeue (&p_app_cb->ui_xmit_q)); llcp_cb.total_tx_ui_pdu--; } if (p_app_cb->link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK) { llcp_cb.num_logical_data_link--; llcp_util_adjust_ll_congestion (); } /* check rx congestion status */ llcp_util_check_rx_congested_status (); return LLCP_STATUS_SUCCESS; } /******************************************************************************* ** ** Function LLCP_IsLogicalLinkCongested ** ** Description Check if logical link is congested ** ** ** Returns TRUE if congested ** *******************************************************************************/ BOOLEAN LLCP_IsLogicalLinkCongested (UINT8 local_sap, UINT8 num_pending_ui_pdu, UINT8 total_pending_ui_pdu, UINT8 total_pending_i_pdu) { tLLCP_APP_CB *p_app_cb; LLCP_TRACE_API4 ("LLCP_IsLogicalLinkCongested () Local SAP:0x%x, pending = (%d, %d, %d)", local_sap, num_pending_ui_pdu, total_pending_ui_pdu, total_pending_i_pdu); p_app_cb = llcp_util_get_app_cb (local_sap); if ( (llcp_cb.lcb.link_state != LLCP_LINK_STATE_ACTIVATED) ||(p_app_cb == NULL) ||(p_app_cb->p_app_cback == NULL) ||((p_app_cb->link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK) == 0) ||(p_app_cb->is_ui_tx_congested) ) { return (TRUE); } else if ( (num_pending_ui_pdu + p_app_cb->ui_xmit_q.count >= llcp_cb.ll_tx_congest_start) ||(total_pending_ui_pdu + llcp_cb.total_tx_ui_pdu >= llcp_cb.max_num_ll_tx_buff) ||(total_pending_ui_pdu + total_pending_i_pdu + llcp_cb.total_tx_ui_pdu + llcp_cb.total_tx_i_pdu >= llcp_cb.max_num_tx_buff) ) { /* set flag so LLCP can notify uncongested status later */ p_app_cb->is_ui_tx_congested = TRUE; return (TRUE); } return (FALSE); } /******************************************************************************* ** ** Function LLCP_SendUI ** ** Description Send connnectionless data to DSAP ** ** ** Returns LLCP_STATUS_SUCCESS if success ** LLCP_STATUS_CONGESTED if logical link is congested ** LLCP_STATUS_FAIL, otherwise ** *******************************************************************************/ tLLCP_STATUS LLCP_SendUI (UINT8 ssap, UINT8 dsap, BT_HDR *p_buf) { tLLCP_STATUS status = LLCP_STATUS_FAIL; tLLCP_APP_CB *p_app_cb; LLCP_TRACE_API2 ("LLCP_SendUI () SSAP=0x%x, DSAP=0x%x", ssap, dsap); p_app_cb = llcp_util_get_app_cb (ssap); if ( (p_app_cb == NULL) ||(p_app_cb->p_app_cback == NULL) ) { LLCP_TRACE_ERROR1 ("LLCP_SendUI (): SSAP (0x%x) is not registered", ssap); } else if ((p_app_cb->link_type & LLCP_LINK_TYPE_LOGICAL_DATA_LINK) == 0) { LLCP_TRACE_ERROR1 ("LLCP_SendUI (): Logical link on SSAP (0x%x) is not enabled", ssap); } else if (llcp_cb.lcb.link_state != LLCP_LINK_STATE_ACTIVATED) { LLCP_TRACE_ERROR0 ("LLCP_SendUI (): LLCP link is not activated"); } else if ( (llcp_cb.lcb.peer_opt == LLCP_LSC_UNKNOWN) ||(llcp_cb.lcb.peer_opt & LLCP_LSC_1) ) { if (p_buf->len <= llcp_cb.lcb.peer_miu) { if (p_buf->offset >= LLCP_MIN_OFFSET) { status = llcp_util_send_ui (ssap, dsap, p_app_cb, p_buf); } else { LLCP_TRACE_ERROR2 ("LLCP_SendUI (): offset (%d) must be %d at least", p_buf->offset, LLCP_MIN_OFFSET ); } } else { LLCP_TRACE_ERROR0 ("LLCP_SendUI (): Data length shall not be bigger than peer's link MIU"); } } else { LLCP_TRACE_ERROR0 ("LLCP_SendUI (): Peer doesn't support connectionless link"); } if (status == LLCP_STATUS_FAIL) { GKI_freebuf (p_buf); } return status; } /******************************************************************************* ** ** Function LLCP_ReadLogicalLinkData ** ** Description Read information of UI PDU for local SAP ** ** - Remote SAP who sent UI PDU is returned. ** - Information of UI PDU up to max_data_len is copied into p_data. ** - Information of next UI PDU is not concatenated. ** - Recommended max_data_len is link MIU of local device ** ** Returns TRUE if more information of UI PDU or more UI PDU in queue ** *******************************************************************************/ BOOLEAN LLCP_ReadLogicalLinkData (UINT8 local_sap, UINT32 max_data_len, UINT8 *p_remote_sap, UINT32 *p_data_len, UINT8 *p_data) { tLLCP_APP_CB *p_app_cb; BT_HDR *p_buf; UINT8 *p_ui_pdu; UINT16 pdu_hdr, ui_pdu_length; LLCP_TRACE_API1 ("LLCP_ReadLogicalLinkData () Local SAP:0x%x", local_sap); *p_data_len = 0; p_app_cb = llcp_util_get_app_cb (local_sap); /* if application is registered */ if ((p_app_cb) && (p_app_cb->p_app_cback)) { /* if any UI PDU in rx queue */ if (p_app_cb->ui_rx_q.p_first) { p_buf = (BT_HDR *) p_app_cb->ui_rx_q.p_first; p_ui_pdu = (UINT8*) (p_buf + 1) + p_buf->offset; /* get length of UI PDU */ BE_STREAM_TO_UINT16 (ui_pdu_length, p_ui_pdu); /* get remote SAP from LLCP header */ BE_STREAM_TO_UINT16 (pdu_hdr, p_ui_pdu); *p_remote_sap = LLCP_GET_SSAP (pdu_hdr); /* layer_specific has the offset to read within UI PDU */ p_ui_pdu += p_buf->layer_specific; /* copy data up to max_data_len */ if (max_data_len >= (UINT32) (ui_pdu_length - LLCP_PDU_HEADER_SIZE - p_buf->layer_specific)) { /* copy information without LLCP header */ *p_data_len = (UINT32) (ui_pdu_length - LLCP_PDU_HEADER_SIZE - p_buf->layer_specific); /* move to next UI PDU if any */ p_buf->layer_specific = 0; /* reset offset to read from the first byte of next UI PDU */ p_buf->offset += LLCP_PDU_AGF_LEN_SIZE + ui_pdu_length; p_buf->len -= LLCP_PDU_AGF_LEN_SIZE + ui_pdu_length; } else { *p_data_len = max_data_len; /* update offset to read from remaining UI PDU next time */ p_buf->layer_specific += max_data_len; } memcpy (p_data, p_ui_pdu, *p_data_len); /* if read all of UI PDU */ if (p_buf->len == 0) { GKI_dequeue (&p_app_cb->ui_rx_q); GKI_freebuf (p_buf); /* decrease number of received UI PDU in in all of ui_rx_q and check rx congestion status */ llcp_cb.total_rx_ui_pdu--; llcp_util_check_rx_congested_status (); } } /* if there is more UI PDU in rx queue */ if (p_app_cb->ui_rx_q.p_first) { return (TRUE); } else { return (FALSE); } } else { LLCP_TRACE_ERROR1 ("LLCP_ReadLogicalLinkData (): Unregistered SAP:0x%x", local_sap); return (FALSE); } } /******************************************************************************* ** ** Function LLCP_FlushLogicalLinkRxData ** ** Description Discard received data in logical data link of local SAP ** ** ** Returns length of data flushed ** *******************************************************************************/ UINT32 LLCP_FlushLogicalLinkRxData (UINT8 local_sap) { BT_HDR *p_buf; UINT32 flushed_length = 0; tLLCP_APP_CB *p_app_cb; UINT8 *p_ui_pdu; UINT16 ui_pdu_length; LLCP_TRACE_API1 ("LLCP_FlushLogicalLinkRxData () Local SAP:0x%x", local_sap); p_app_cb = llcp_util_get_app_cb (local_sap); /* if application is registered */ if ((p_app_cb) && (p_app_cb->p_app_cback)) { /* if any UI PDU in rx queue */ while (p_app_cb->ui_rx_q.p_first) { p_buf = (BT_HDR *) p_app_cb->ui_rx_q.p_first; p_ui_pdu = (UINT8*) (p_buf + 1) + p_buf->offset; /* get length of UI PDU */ BE_STREAM_TO_UINT16 (ui_pdu_length, p_ui_pdu); flushed_length += (UINT32) (ui_pdu_length - LLCP_PDU_HEADER_SIZE - p_buf->layer_specific); /* move to next UI PDU if any */ p_buf->layer_specific = 0; /* offset */ p_buf->offset += LLCP_PDU_AGF_LEN_SIZE + ui_pdu_length; p_buf->len -= LLCP_PDU_AGF_LEN_SIZE + ui_pdu_length; /* if read all of UI PDU */ if (p_buf->len == 0) { GKI_dequeue (&p_app_cb->ui_rx_q); GKI_freebuf (p_buf); llcp_cb.total_rx_ui_pdu--; } } /* number of received UI PDU is decreased so check rx congestion status */ llcp_util_check_rx_congested_status (); } else { LLCP_TRACE_ERROR1 ("LLCP_FlushLogicalLinkRxData (): Unregistered SAP:0x%x", local_sap); } return (flushed_length); } /******************************************************************************* ** ** Function LLCP_ConnectReq ** ** Description Create data link connection between registered SAP and DSAP ** in peer LLCP, ** ** ** Returns LLCP_STATUS_SUCCESS if success ** LLCP_STATUS_FAIL, otherwise ** *******************************************************************************/ tLLCP_STATUS LLCP_ConnectReq (UINT8 reg_sap, UINT8 dsap, tLLCP_CONNECTION_PARAMS *p_params) { tLLCP_DLCB *p_dlcb; tLLCP_STATUS status; tLLCP_APP_CB *p_app_cb; LLCP_TRACE_API2 ("LLCP_ConnectReq () reg_sap=0x%x, DSAP=0x%x", reg_sap, dsap); if ( (llcp_cb.lcb.peer_opt != LLCP_LSC_UNKNOWN) &&((llcp_cb.lcb.peer_opt & LLCP_LSC_2) == 0) ) { LLCP_TRACE_ERROR0 ("LLCP_ConnectReq (): Peer doesn't support connection-oriented link"); return LLCP_STATUS_FAIL; } p_app_cb = llcp_util_get_app_cb (reg_sap); /* if application is registered */ if ( (p_app_cb == NULL) ||(p_app_cb->p_app_cback == NULL) ) { LLCP_TRACE_ERROR1 ("LLCP_ConnectReq (): SSAP (0x%x) is not registered", reg_sap); return LLCP_STATUS_FAIL; } if (dsap == LLCP_SAP_LM) { LLCP_TRACE_ERROR1 ("LLCP_ConnectReq (): DSAP (0x%x) must not be link manager SAP", dsap); return LLCP_STATUS_FAIL; } if (dsap == LLCP_SAP_SDP) { if (strlen (p_params->sn) > LLCP_MAX_SN_LEN) { LLCP_TRACE_ERROR1 ("LLCP_ConnectReq (): Service Name (%d bytes) is too long", strlen (p_params->sn)); return LLCP_STATUS_FAIL; } } if ((p_params) && (p_params->miu > llcp_cb.lcb.local_link_miu)) { LLCP_TRACE_ERROR0 ("LLCP_ConnectReq (): Data link MIU shall not be bigger than local link MIU"); return LLCP_STATUS_FAIL; } /* check if any pending connection request on this reg_sap */ p_dlcb = llcp_dlc_find_dlcb_by_sap (reg_sap, LLCP_INVALID_SAP); if (p_dlcb) { /* ** Accepting LLCP may change SAP in CC, so we cannot find right data link connection ** if there is multiple pending connection request on the same local SAP. */ LLCP_TRACE_ERROR0 ("LLCP_ConnectReq (): There is pending connect request on this reg_sap"); return LLCP_STATUS_FAIL; } p_dlcb = llcp_util_allocate_data_link (reg_sap, dsap); if (p_dlcb) { status = llcp_dlsm_execute (p_dlcb, LLCP_DLC_EVENT_API_CONNECT_REQ, p_params); if (status != LLCP_STATUS_SUCCESS) { LLCP_TRACE_ERROR0 ("LLCP_ConnectReq (): Error in state machine"); llcp_util_deallocate_data_link (p_dlcb); return LLCP_STATUS_FAIL; } } else { return LLCP_STATUS_FAIL; } return LLCP_STATUS_SUCCESS; } /******************************************************************************* ** ** Function LLCP_ConnectCfm ** ** Description Accept connection request from peer LLCP ** ** ** Returns LLCP_STATUS_SUCCESS if success ** LLCP_STATUS_FAIL, otherwise ** *******************************************************************************/ tLLCP_STATUS LLCP_ConnectCfm (UINT8 local_sap, UINT8 remote_sap, tLLCP_CONNECTION_PARAMS *p_params) { tLLCP_STATUS status; tLLCP_DLCB *p_dlcb; LLCP_TRACE_API2 ("LLCP_ConnectCfm () Local SAP:0x%x, Remote SAP:0x%x)", local_sap, remote_sap); if ((p_params) && (p_params->miu > llcp_cb.lcb.local_link_miu)) { LLCP_TRACE_ERROR0 ("LLCP_ConnectCfm (): Data link MIU shall not be bigger than local link MIU"); return LLCP_STATUS_FAIL; } p_dlcb = llcp_dlc_find_dlcb_by_sap (local_sap, remote_sap); if (p_dlcb) { status = llcp_dlsm_execute (p_dlcb, LLCP_DLC_EVENT_API_CONNECT_CFM, p_params); } else { LLCP_TRACE_ERROR0 ("LLCP_ConnectCfm (): No data link"); status = LLCP_STATUS_FAIL; } return status; } /******************************************************************************* ** ** Function LLCP_ConnectReject ** ** Description Reject connection request from peer LLCP ** ** reason : LLCP_SAP_DM_REASON_APP_REJECTED ** LLCP_SAP_DM_REASON_PERM_REJECT_THIS ** LLCP_SAP_DM_REASON_PERM_REJECT_ANY ** LLCP_SAP_DM_REASON_TEMP_REJECT_THIS ** LLCP_SAP_DM_REASON_TEMP_REJECT_ANY ** ** Returns LLCP_STATUS_SUCCESS if success ** LLCP_STATUS_FAIL, otherwise ** *******************************************************************************/ tLLCP_STATUS LLCP_ConnectReject (UINT8 local_sap, UINT8 remote_sap, UINT8 reason) { tLLCP_STATUS status; tLLCP_DLCB *p_dlcb; LLCP_TRACE_API3 ("LLCP_ConnectReject () Local SAP:0x%x, Remote SAP:0x%x, reason:0x%x", local_sap, remote_sap, reason); p_dlcb = llcp_dlc_find_dlcb_by_sap (local_sap, remote_sap); if (p_dlcb) { status = llcp_dlsm_execute (p_dlcb, LLCP_DLC_EVENT_API_CONNECT_REJECT, &reason); llcp_util_deallocate_data_link (p_dlcb); } else { LLCP_TRACE_ERROR0 ("LLCP_ConnectReject (): No data link"); status = LLCP_STATUS_FAIL; } return status; } /******************************************************************************* ** ** Function LLCP_IsDataLinkCongested ** ** Description Check if data link connection is congested ** ** ** Returns TRUE if congested ** *******************************************************************************/ BOOLEAN LLCP_IsDataLinkCongested (UINT8 local_sap, UINT8 remote_sap, UINT8 num_pending_i_pdu, UINT8 total_pending_ui_pdu, UINT8 total_pending_i_pdu) { tLLCP_DLCB *p_dlcb; LLCP_TRACE_API5 ("LLCP_IsDataLinkCongested () Local SAP:0x%x, Remote SAP:0x%x, pending = (%d, %d, %d)", local_sap, remote_sap, num_pending_i_pdu, total_pending_ui_pdu, total_pending_i_pdu); p_dlcb = llcp_dlc_find_dlcb_by_sap (local_sap, remote_sap); if (p_dlcb) { if ( (p_dlcb->is_tx_congested) ||(p_dlcb->remote_busy) ) { return (TRUE); } else if ( (num_pending_i_pdu + p_dlcb->i_xmit_q.count >= p_dlcb->remote_rw) ||(total_pending_ui_pdu + total_pending_i_pdu + llcp_cb.total_tx_ui_pdu + llcp_cb.total_tx_i_pdu >= llcp_cb.max_num_tx_buff) ) { /* set flag so LLCP can notify uncongested status later */ p_dlcb->is_tx_congested = TRUE; return (TRUE); } return (FALSE); } return (TRUE); } /******************************************************************************* ** ** Function LLCP_SendData ** ** Description Send connection-oriented data ** ** ** Returns LLCP_STATUS_SUCCESS if success ** LLCP_STATUS_CONGESTED if data link is congested ** *******************************************************************************/ tLLCP_STATUS LLCP_SendData (UINT8 local_sap, UINT8 remote_sap, BT_HDR *p_buf) { tLLCP_STATUS status = LLCP_STATUS_FAIL; tLLCP_DLCB *p_dlcb; LLCP_TRACE_API2 ("LLCP_SendData () Local SAP:0x%x, Remote SAP:0x%x", local_sap, remote_sap); p_dlcb = llcp_dlc_find_dlcb_by_sap (local_sap, remote_sap); if (p_dlcb) { if (p_dlcb->remote_miu >= p_buf->len) { if (p_buf->offset >= LLCP_MIN_OFFSET) { status = llcp_dlsm_execute (p_dlcb, LLCP_DLC_EVENT_API_DATA_REQ, p_buf); } else { LLCP_TRACE_ERROR2 ("LLCP_SendData (): offset (%d) must be %d at least", p_buf->offset, LLCP_MIN_OFFSET ); } } else { LLCP_TRACE_ERROR2 ("LLCP_SendData (): Information (%d bytes) cannot be more than peer MIU (%d bytes)", p_buf->len, p_dlcb->remote_miu); } } else { LLCP_TRACE_ERROR0 ("LLCP_SendData (): No data link"); } if (status == LLCP_STATUS_FAIL) { GKI_freebuf (p_buf); } return status; } /******************************************************************************* ** ** Function LLCP_ReadDataLinkData ** ** Description Read information of I PDU for data link connection ** ** - Information of I PDU up to max_data_len is copied into p_data. ** - Information of next I PDU is not concatenated. ** - Recommended max_data_len is data link connection MIU of local ** end point ** ** Returns TRUE if more data in queue ** *******************************************************************************/ BOOLEAN LLCP_ReadDataLinkData (UINT8 local_sap, UINT8 remote_sap, UINT32 max_data_len, UINT32 *p_data_len, UINT8 *p_data) { tLLCP_DLCB *p_dlcb; BT_HDR *p_buf; UINT8 *p_i_pdu; UINT16 i_pdu_length; LLCP_TRACE_API2 ("LLCP_ReadDataLinkData () Local SAP:0x%x, Remote SAP:0x%x", local_sap, remote_sap); p_dlcb = llcp_dlc_find_dlcb_by_sap (local_sap, remote_sap); *p_data_len = 0; if (p_dlcb) { /* if any I PDU in rx queue */ if (p_dlcb->i_rx_q.p_first) { p_buf = (BT_HDR *) p_dlcb->i_rx_q.p_first; p_i_pdu = (UINT8*) (p_buf + 1) + p_buf->offset; /* get length of I PDU */ BE_STREAM_TO_UINT16 (i_pdu_length, p_i_pdu); /* layer_specific has the offset to read within I PDU */ p_i_pdu += p_buf->layer_specific; /* copy data up to max_data_len */ if (max_data_len >= (UINT32) (i_pdu_length - p_buf->layer_specific)) { /* copy information */ *p_data_len = (UINT32) (i_pdu_length - p_buf->layer_specific); /* move to next I PDU if any */ p_buf->layer_specific = 0; /* reset offset to read from the first byte of next I PDU */ p_buf->offset += LLCP_PDU_AGF_LEN_SIZE + i_pdu_length; p_buf->len -= LLCP_PDU_AGF_LEN_SIZE + i_pdu_length; } else { *p_data_len = max_data_len; /* update offset to read from remaining I PDU next time */ p_buf->layer_specific += max_data_len; } memcpy (p_data, p_i_pdu, *p_data_len); if (p_buf->layer_specific == 0) { p_dlcb->num_rx_i_pdu--; } /* if read all of I PDU */ if (p_buf->len == 0) { GKI_dequeue (&p_dlcb->i_rx_q); GKI_freebuf (p_buf); /* decrease number of received I PDU in in all of ui_rx_q and check rx congestion status */ llcp_cb.total_rx_i_pdu--; llcp_util_check_rx_congested_status (); } } /* if getting out of rx congestion */ if ( (!p_dlcb->local_busy) &&(p_dlcb->is_rx_congested) &&(p_dlcb->num_rx_i_pdu <= p_dlcb->rx_congest_threshold / 2) ) { /* send RR */ p_dlcb->is_rx_congested = FALSE; p_dlcb->flags |= LLCP_DATA_LINK_FLAG_PENDING_RR_RNR; } /* if there is more I PDU in rx queue */ if (p_dlcb->i_rx_q.p_first) { return (TRUE); } else { return (FALSE); } } else { LLCP_TRACE_ERROR0 ("LLCP_ReadDataLinkData (): No data link connection"); return (FALSE); } } /******************************************************************************* ** ** Function LLCP_FlushDataLinkRxData ** ** Description Discard received data in data link connection ** ** ** Returns length of rx data flushed ** *******************************************************************************/ UINT32 LLCP_FlushDataLinkRxData (UINT8 local_sap, UINT8 remote_sap) { tLLCP_DLCB *p_dlcb; BT_HDR *p_buf; UINT32 flushed_length = 0; UINT8 *p_i_pdu; UINT16 i_pdu_length; LLCP_TRACE_API2 ("LLCP_FlushDataLinkRxData () Local SAP:0x%x, Remote SAP:0x%x", local_sap, remote_sap); p_dlcb = llcp_dlc_find_dlcb_by_sap (local_sap, remote_sap); if (p_dlcb) { /* if any I PDU in rx queue */ while (p_dlcb->i_rx_q.p_first) { p_buf = (BT_HDR *) p_dlcb->i_rx_q.p_first; p_i_pdu = (UINT8*) (p_buf + 1) + p_buf->offset; /* get length of I PDU */ BE_STREAM_TO_UINT16 (i_pdu_length, p_i_pdu); flushed_length += (UINT32) (i_pdu_length - p_buf->layer_specific); /* move to next I PDU if any */ p_buf->layer_specific = 0; /* offset */ p_buf->offset += LLCP_PDU_AGF_LEN_SIZE + i_pdu_length; p_buf->len -= LLCP_PDU_AGF_LEN_SIZE + i_pdu_length; /* if read all of I PDU */ if (p_buf->len == 0) { GKI_dequeue (&p_dlcb->i_rx_q); GKI_freebuf (p_buf); llcp_cb.total_rx_i_pdu--; } } p_dlcb->num_rx_i_pdu = 0; /* if getting out of rx congestion */ if ( (!p_dlcb->local_busy) &&(p_dlcb->is_rx_congested) ) { /* send RR */ p_dlcb->is_rx_congested = FALSE; p_dlcb->flags |= LLCP_DATA_LINK_FLAG_PENDING_RR_RNR; } /* number of received I PDU is decreased so check rx congestion status */ llcp_util_check_rx_congested_status (); } else { LLCP_TRACE_ERROR0 ("LLCP_FlushDataLinkRxData (): No data link connection"); } return (flushed_length); } /******************************************************************************* ** ** Function LLCP_DisconnectReq ** ** Description Disconnect data link ** discard any pending data if flush is set to TRUE ** ** Returns LLCP_STATUS_SUCCESS if success ** *******************************************************************************/ tLLCP_STATUS LLCP_DisconnectReq (UINT8 local_sap, UINT8 remote_sap, BOOLEAN flush) { tLLCP_STATUS status; tLLCP_DLCB *p_dlcb; LLCP_TRACE_API3 ("LLCP_DisconnectReq () Local SAP:0x%x, Remote SAP:0x%x, flush=%d", local_sap, remote_sap, flush); p_dlcb = llcp_dlc_find_dlcb_by_sap (local_sap, remote_sap); if (p_dlcb) { status = llcp_dlsm_execute (p_dlcb, LLCP_DLC_EVENT_API_DISCONNECT_REQ, &flush); } else { LLCP_TRACE_ERROR0 ("LLCP_DisconnectReq (): No data link"); status = LLCP_STATUS_FAIL; } return status; } /******************************************************************************* ** ** Function LLCP_SetTxCompleteNtf ** ** Description This function is called to get LLCP_SERVICE_TX_COMPLETE ** when Tx queue is empty and all PDU is acked. ** This is one time event, so upper layer shall call this function ** again to get next LLCP_SERVICE_TX_COMPLETE. ** ** Returns LLCP_STATUS_SUCCESS if success ** *******************************************************************************/ tLLCP_STATUS LLCP_SetTxCompleteNtf (UINT8 local_sap, UINT8 remote_sap) { tLLCP_STATUS status; tLLCP_DLCB *p_dlcb; LLCP_TRACE_API2 ("LLCP_SetTxCompleteNtf () Local SAP:0x%x, Remote SAP:0x%x", local_sap, remote_sap); p_dlcb = llcp_dlc_find_dlcb_by_sap (local_sap, remote_sap); if (p_dlcb) { /* set flag to notify upper later when tx complete */ p_dlcb->flags |= LLCP_DATA_LINK_FLAG_NOTIFY_TX_DONE; status = LLCP_STATUS_SUCCESS; } else { LLCP_TRACE_ERROR0 ("LLCP_SetTxCompleteNtf (): No data link"); status = LLCP_STATUS_FAIL; } return status; } /******************************************************************************* ** ** Function LLCP_SetLocalBusyStatus ** ** Description Set local busy status ** ** ** Returns LLCP_STATUS_SUCCESS if success ** *******************************************************************************/ tLLCP_STATUS LLCP_SetLocalBusyStatus (UINT8 local_sap, UINT8 remote_sap, BOOLEAN is_busy) { tLLCP_STATUS status; tLLCP_DLCB *p_dlcb; LLCP_TRACE_API2 ("LLCP_SetLocalBusyStatus () Local SAP:0x%x, is_busy=%d", local_sap, is_busy); p_dlcb = llcp_dlc_find_dlcb_by_sap (local_sap, remote_sap); if (p_dlcb) { if (p_dlcb->local_busy != is_busy) { p_dlcb->local_busy = is_busy; /* send RR or RNR with valid sequence */ p_dlcb->flags |= LLCP_DATA_LINK_FLAG_PENDING_RR_RNR; if (is_busy == FALSE) { if (p_dlcb->i_rx_q.count) { llcp_dlsm_execute (p_dlcb, LLCP_DLC_EVENT_PEER_DATA_IND, NULL); } } } status = LLCP_STATUS_SUCCESS; } else { LLCP_TRACE_ERROR0 ("LLCP_SetLocalBusyStatus (): No data link"); status = LLCP_STATUS_FAIL; } return status; } /******************************************************************************* ** ** Function LLCP_GetRemoteWKS ** ** Description Return well-known service bitmap of connected device ** ** ** Returns WKS bitmap if success ** *******************************************************************************/ UINT16 LLCP_GetRemoteWKS (void) { LLCP_TRACE_API1 ("LLCP_GetRemoteWKS () WKS:0x%04x", (llcp_cb.lcb.link_state == LLCP_LINK_STATE_ACTIVATED) ? llcp_cb.lcb.peer_wks :0); if (llcp_cb.lcb.link_state == LLCP_LINK_STATE_ACTIVATED) return (llcp_cb.lcb.peer_wks); else return (0); } /******************************************************************************* ** ** Function LLCP_GetRemoteLSC ** ** Description Return link service class of connected device ** ** ** Returns link service class ** *******************************************************************************/ UINT8 LLCP_GetRemoteLSC (void) { LLCP_TRACE_API1 ("LLCP_GetRemoteLSC () LSC:0x%x", (llcp_cb.lcb.link_state == LLCP_LINK_STATE_ACTIVATED) ? llcp_cb.lcb.peer_opt & (LLCP_LSC_1 | LLCP_LSC_2) :0); if (llcp_cb.lcb.link_state == LLCP_LINK_STATE_ACTIVATED) return (llcp_cb.lcb.peer_opt & (LLCP_LSC_1 | LLCP_LSC_2)); else return (LLCP_LSC_UNKNOWN); } /******************************************************************************* ** ** Function LLCP_GetLinkMIU ** ** Description Return local and remote link MIU ** ** ** Returns None ** *******************************************************************************/ LLCP_API void LLCP_GetLinkMIU (UINT16 *p_local_link_miu, UINT16 *p_remote_link_miu) { LLCP_TRACE_API0 ("LLCP_GetLinkMIU ()"); if (llcp_cb.lcb.link_state == LLCP_LINK_STATE_ACTIVATED) { *p_local_link_miu = llcp_cb.lcb.local_link_miu; *p_remote_link_miu = llcp_cb.lcb.effective_miu; } else { *p_local_link_miu = 0; *p_remote_link_miu = 0; } LLCP_TRACE_DEBUG2 ("LLCP_GetLinkMIU (): local_link_miu = %d, remote_link_miu = %d", *p_local_link_miu, *p_remote_link_miu); } /******************************************************************************* ** ** Function LLCP_DiscoverService ** ** Description Return SAP of service name in connected device through callback ** ** ** Returns LLCP_STATUS_SUCCESS if success ** *******************************************************************************/ tLLCP_STATUS LLCP_DiscoverService (char *p_name, tLLCP_SDP_CBACK *p_cback, UINT8 *p_tid) { tLLCP_STATUS status; UINT8 i; LLCP_TRACE_API1 ("LLCP_DiscoverService () Service Name:%s", p_name); if (llcp_cb.lcb.link_state != LLCP_LINK_STATE_ACTIVATED) { LLCP_TRACE_ERROR0 ("LLCP_DiscoverService (): Link is not activated"); return LLCP_STATUS_FAIL; } if (!p_cback) { LLCP_TRACE_ERROR0 ("LLCP_DiscoverService (): Callback must be provided."); return LLCP_STATUS_FAIL; } /* if peer version is less than V1.1 then SNL is not supported */ if ((llcp_cb.lcb.agreed_major_version == 0x01) && (llcp_cb.lcb.agreed_minor_version < 0x01)) { LLCP_TRACE_ERROR0 ("LLCP_DiscoverService (): Peer doesn't support SNL"); return LLCP_STATUS_FAIL; } for (i = 0; i < LLCP_MAX_SDP_TRANSAC; i++) { if (!llcp_cb.sdp_cb.transac[i].p_cback) { llcp_cb.sdp_cb.transac[i].tid = llcp_cb.sdp_cb.next_tid; llcp_cb.sdp_cb.next_tid++; llcp_cb.sdp_cb.transac[i].p_cback = p_cback; status = llcp_sdp_send_sdreq (llcp_cb.sdp_cb.transac[i].tid, p_name); if (status == LLCP_STATUS_FAIL) { llcp_cb.sdp_cb.transac[i].p_cback = NULL; } *p_tid = llcp_cb.sdp_cb.transac[i].tid; return (status); } } LLCP_TRACE_ERROR0 ("LLCP_DiscoverService (): Out of resource"); return LLCP_STATUS_FAIL; }