summaryrefslogtreecommitdiff
path: root/lib/bt/host/bluedroid/stack/l2cap
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-03-28 14:32:49 +1100
committerjacqueline <me@jacqueline.id.au>2024-03-28 14:32:49 +1100
commitee29c25b29eaa4fac4e897442634b69ecc8d8125 (patch)
tree8c5f1a140463f20f104316fa3492984e191154e9 /lib/bt/host/bluedroid/stack/l2cap
parent239e6d89507a24c849385f4bfa93ac4ad58e5de5 (diff)
downloadtangara-fw-ee29c25b29eaa4fac4e897442634b69ecc8d8125.tar.gz
Fork ESP-IDF's bluetooth component
i want better sbc encoding, and no cla will stop me
Diffstat (limited to 'lib/bt/host/bluedroid/stack/l2cap')
-rw-r--r--lib/bt/host/bluedroid/stack/l2cap/include/l2c_int.h824
-rw-r--r--lib/bt/host/bluedroid/stack/l2cap/l2c_api.c2456
-rw-r--r--lib/bt/host/bluedroid/stack/l2cap/l2c_ble.c1707
-rw-r--r--lib/bt/host/bluedroid/stack/l2cap/l2c_csm.c1263
-rw-r--r--lib/bt/host/bluedroid/stack/l2cap/l2c_fcr.c2229
-rw-r--r--lib/bt/host/bluedroid/stack/l2cap/l2c_link.c1509
-rw-r--r--lib/bt/host/bluedroid/stack/l2cap/l2c_main.c1065
-rw-r--r--lib/bt/host/bluedroid/stack/l2cap/l2c_ucd.c1079
-rw-r--r--lib/bt/host/bluedroid/stack/l2cap/l2c_utils.c3731
-rw-r--r--lib/bt/host/bluedroid/stack/l2cap/l2cap_client.c462
10 files changed, 16325 insertions, 0 deletions
diff --git a/lib/bt/host/bluedroid/stack/l2cap/include/l2c_int.h b/lib/bt/host/bluedroid/stack/l2cap/include/l2c_int.h
new file mode 100644
index 00000000..045fb10d
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/l2cap/include/l2c_int.h
@@ -0,0 +1,824 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-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 L2CAP internal definitions
+ *
+ ******************************************************************************/
+#ifndef L2C_INT_H
+#define L2C_INT_H
+
+#include <stdbool.h>
+#include "stack/btm_api.h"
+#include "stack/l2c_api.h"
+#include "stack/l2cdefs.h"
+#include "osi/list.h"
+#include "osi/fixed_queue.h"
+
+#define L2CAP_MIN_MTU 48 /* Minimum acceptable MTU is 48 bytes */
+
+/* LE credit based L2CAP connection parameters */
+#define L2CAP_LE_MIN_MTU 23
+#define L2CAP_LE_MIN_MPS 23
+#define L2CAP_LE_MAX_MPS 65533
+#define L2CAP_LE_MIN_CREDIT 0
+#define L2CAP_LE_MAX_CREDIT 65535
+#define L2CAP_LE_DEFAULT_MTU 512
+#define L2CAP_LE_DEFAULT_MPS 23
+#define L2CAP_LE_DEFAULT_CREDIT 1
+
+
+/* Timeouts. Since L2CAP works off a 1-second list, all are in seconds.
+*/
+#define L2CAP_LINK_ROLE_SWITCH_TOUT 10 /* 10 seconds */
+#define L2CAP_LINK_CONNECT_TOUT 60 /* 30 seconds */
+#define L2CAP_LINK_CONNECT_TOUT_EXT 120 /* 120 seconds */
+#define L2CAP_ECHO_RSP_TOUT 30 /* 30 seconds */
+#define L2CAP_LINK_FLOW_CONTROL_TOUT 2 /* 2 seconds */
+#define L2CAP_LINK_DISCONNECT_TOUT 45 /* 45 seconds */
+
+#ifndef L2CAP_CHNL_CONNECT_TOUT /* BTIF needs to override for internal project needs */
+#define L2CAP_CHNL_CONNECT_TOUT 60 /* 60 seconds */
+#endif
+
+#define L2CAP_CHNL_CONNECT_TOUT_EXT 120 /* 120 seconds */
+#define L2CAP_CHNL_CFG_TIMEOUT 30 /* 30 seconds */
+#define L2CAP_CHNL_DISCONNECT_TOUT 10 /* 10 seconds */
+#define L2CAP_DELAY_CHECK_SM4 2 /* 2 seconds */
+#define L2CAP_WAIT_INFO_RSP_TOUT 3 /* 3 seconds */
+#define L2CAP_WAIT_UNPARK_TOUT 2 /* 2 seconds */
+#define L2CAP_LINK_INFO_RESP_TOUT 2 /* 2 seconds */
+#define L2CAP_UPDATE_CONN_PARAM_TOUT 6 /* 6 seconds */
+#define L2CAP_BLE_LINK_CONNECT_TOUT BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT // configed in menuconfig
+#define L2CAP_BLE_CONN_PARAM_UPD_TOUT 30 /* 30 seconds */
+
+/* quick timer uses millisecond unit */
+#define L2CAP_DEFAULT_RETRANS_TOUT 2000 /* 2000 milliseconds */
+#define L2CAP_DEFAULT_MONITOR_TOUT 12000 /* 12000 milliseconds */
+#define L2CAP_FCR_ACK_TOUT 200 /* 200 milliseconds */
+
+#define L2CAP_CACHE_ATT_ACL_NUM 10
+
+/* Define the possible L2CAP channel states. The names of
+** the states may seem a bit strange, but they are taken from
+** the Bluetooth specification.
+*/
+typedef enum {
+ CST_CLOSED, /* Channel is in clodes state */
+ CST_ORIG_W4_SEC_COMP, /* Originator waits security clearence */
+ CST_TERM_W4_SEC_COMP, /* Acceptor waits security clearence */
+ CST_W4_L2CAP_CONNECT_RSP, /* Waiting for peer conenct response */
+ CST_W4_L2CA_CONNECT_RSP, /* Waiting for upper layer connect rsp */
+ CST_CONFIG, /* Negotiating configuration */
+ CST_OPEN, /* Data transfer state */
+ CST_W4_L2CAP_DISCONNECT_RSP, /* Waiting for peer disconnect rsp */
+ CST_W4_L2CA_DISCONNECT_RSP /* Waiting for upper layer disc rsp */
+} tL2C_CHNL_STATE;
+
+/* Define the possible L2CAP link states
+*/
+typedef enum {
+ LST_DISCONNECTED,
+ LST_CONNECT_HOLDING,
+ LST_CONNECTING_WAIT_SWITCH,
+ LST_CONNECTING,
+ LST_CONNECTED,
+ LST_DISCONNECTING
+} tL2C_LINK_STATE;
+
+
+
+/* Define input events to the L2CAP link and channel state machines. The names
+** of the events may seem a bit strange, but they are taken from
+** the Bluetooth specification.
+*/
+#define L2CEVT_LP_CONNECT_CFM 0 /* Lower layer connect confirm */
+#define L2CEVT_LP_CONNECT_CFM_NEG 1 /* Lower layer connect confirm (failed) */
+#define L2CEVT_LP_CONNECT_IND 2 /* Lower layer connect indication */
+#define L2CEVT_LP_DISCONNECT_IND 3 /* Lower layer disconnect indication */
+#define L2CEVT_LP_QOS_CFM 4 /* Lower layer QOS confirmation */
+#define L2CEVT_LP_QOS_CFM_NEG 5 /* Lower layer QOS confirmation (failed)*/
+#define L2CEVT_LP_QOS_VIOLATION_IND 6 /* Lower layer QOS violation indication */
+
+#define L2CEVT_SEC_COMP 7 /* Security cleared successfully */
+#define L2CEVT_SEC_COMP_NEG 8 /* Security procedure failed */
+
+#define L2CEVT_L2CAP_CONNECT_REQ 10 /* Peer connection request */
+#define L2CEVT_L2CAP_CONNECT_RSP 11 /* Peer connection response */
+#define L2CEVT_L2CAP_CONNECT_RSP_PND 12 /* Peer connection response pending */
+#define L2CEVT_L2CAP_CONNECT_RSP_NEG 13 /* Peer connection response (failed) */
+#define L2CEVT_L2CAP_CONFIG_REQ 14 /* Peer configuration request */
+#define L2CEVT_L2CAP_CONFIG_RSP 15 /* Peer configuration response */
+#define L2CEVT_L2CAP_CONFIG_RSP_NEG 16 /* Peer configuration response (failed) */
+#define L2CEVT_L2CAP_DISCONNECT_REQ 17 /* Peer disconnect request */
+#define L2CEVT_L2CAP_DISCONNECT_RSP 18 /* Peer disconnect response */
+#define L2CEVT_L2CAP_INFO_RSP 19 /* Peer information response */
+#define L2CEVT_L2CAP_DATA 20 /* Peer data */
+
+#define L2CEVT_L2CA_CONNECT_REQ 21 /* Upper layer connect request */
+#define L2CEVT_L2CA_CONNECT_RSP 22 /* Upper layer connect response */
+#define L2CEVT_L2CA_CONNECT_RSP_NEG 23 /* Upper layer connect response (failed)*/
+#define L2CEVT_L2CA_CONFIG_REQ 24 /* Upper layer config request */
+#define L2CEVT_L2CA_CONFIG_RSP 25 /* Upper layer config response */
+#define L2CEVT_L2CA_CONFIG_RSP_NEG 26 /* Upper layer config response (failed) */
+#define L2CEVT_L2CA_DISCONNECT_REQ 27 /* Upper layer disconnect request */
+#define L2CEVT_L2CA_DISCONNECT_RSP 28 /* Upper layer disconnect response */
+#define L2CEVT_L2CA_DATA_READ 29 /* Upper layer data read */
+#define L2CEVT_L2CA_DATA_WRITE 30 /* Upper layer data write */
+#define L2CEVT_L2CA_FLUSH_REQ 31 /* Upper layer flush */
+
+#define L2CEVT_TIMEOUT 32 /* Timeout */
+#define L2CEVT_SEC_RE_SEND_CMD 33 /* btm_sec has enough info to proceed */
+
+#define L2CEVT_ACK_TIMEOUT 34 /* RR delay timeout */
+
+
+/* Bitmask to skip over Broadcom feature reserved (ID) to avoid sending two
+ successive ID values, '0' id only or both */
+#define L2CAP_ADJ_BRCM_ID 0x1
+#define L2CAP_ADJ_ZERO_ID 0x2
+#define L2CAP_ADJ_ID 0x3
+
+/* Return values for l2cu_process_peer_cfg_req() */
+#define L2CAP_PEER_CFG_UNACCEPTABLE 0
+#define L2CAP_PEER_CFG_OK 1
+#define L2CAP_PEER_CFG_DISCONNECT 2
+
+/* eL2CAP option constants */
+#define L2CAP_MIN_RETRANS_TOUT 2000 /* Min retransmission timeout if no flush timeout or PBF */
+#define L2CAP_MIN_MONITOR_TOUT 12000 /* Min monitor timeout if no flush timeout or PBF */
+
+#define L2CAP_MAX_FCR_CFG_TRIES 2 /* Config attempts before disconnecting */
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+typedef uint8_t tL2C_BLE_FIXED_CHNLS_MASK;
+
+typedef struct {
+ UINT8 next_tx_seq; /* Next sequence number to be Tx'ed */
+ UINT8 last_rx_ack; /* Last sequence number ack'ed by the peer */
+ UINT8 next_seq_expected; /* Next peer sequence number expected */
+ UINT8 last_ack_sent; /* Last peer sequence number ack'ed */
+ UINT8 num_tries; /* Number of retries to send a packet */
+ UINT8 max_held_acks; /* Max acks we can hold before sending */
+
+ BOOLEAN remote_busy; /* TRUE if peer has flowed us off */
+ BOOLEAN local_busy; /* TRUE if we have flowed off the peer */
+
+ BOOLEAN rej_sent; /* Reject was sent */
+ BOOLEAN srej_sent; /* Selective Reject was sent */
+ BOOLEAN wait_ack; /* Transmitter is waiting ack (poll sent) */
+ BOOLEAN rej_after_srej; /* Send a REJ when SREJ clears */
+
+ BOOLEAN send_f_rsp; /* We need to send an F-bit response */
+
+ UINT16 rx_sdu_len; /* Length of the SDU being received */
+ BT_HDR *p_rx_sdu; /* Buffer holding the SDU being received */
+ fixed_queue_t *waiting_for_ack_q; /* Buffers sent and waiting for peer to ack */
+ fixed_queue_t *srej_rcv_hold_q; /* Buffers rcvd but held pending SREJ rsp */
+ fixed_queue_t *retrans_q; /* Buffers being retransmitted */
+
+ TIMER_LIST_ENT ack_timer; /* Timer delaying RR */
+ TIMER_LIST_ENT mon_retrans_timer; /* Timer Monitor or Retransmission */
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ UINT32 connect_tick_count; /* Time channel was established */
+ UINT32 ertm_pkt_counts[2]; /* Packets sent and received */
+ UINT32 ertm_byte_counts[2]; /* Bytes sent and received */
+ UINT32 s_frames_sent[4]; /* S-frames sent (RR, REJ, RNR, SREJ) */
+ UINT32 s_frames_rcvd[4]; /* S-frames rcvd (RR, REJ, RNR, SREJ) */
+ UINT32 xmit_window_closed; /* # of times the xmit window was closed */
+ UINT32 controller_idle; /* # of times less than 2 packets in controller */
+ /* when the xmit window was closed */
+ UINT32 pkts_retransmitted; /* # of packets that were retransmitted */
+ UINT32 retrans_touts; /* # of retransmission timouts */
+ UINT32 xmit_ack_touts; /* # of xmit ack timouts */
+
+#define L2CAP_ERTM_STATS_NUM_AVG 10
+#define L2CAP_ERTM_STATS_AVG_NUM_SAMPLES 100
+ UINT32 ack_delay_avg_count;
+ UINT32 ack_delay_avg_index;
+ UINT32 throughput_start;
+ UINT32 throughput[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_delay_avg[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_delay_min[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_delay_max[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_q_count_avg[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_q_count_min[L2CAP_ERTM_STATS_NUM_AVG];
+ UINT32 ack_q_count_max[L2CAP_ERTM_STATS_NUM_AVG];
+#endif
+} tL2C_FCRB;
+
+
+/* Define a registration control block. Every application (e.g. RFCOMM, SDP,
+** TCS etc) that registers with L2CAP is assigned one of these.
+*/
+#if (L2CAP_UCD_INCLUDED == TRUE)
+#define L2C_UCD_RCB_ID 0x00
+#define L2C_UCD_STATE_UNUSED 0x00
+#define L2C_UCD_STATE_W4_DATA 0x01
+#define L2C_UCD_STATE_W4_RECEPTION 0x02
+#define L2C_UCD_STATE_W4_MTU 0x04
+
+typedef struct {
+ UINT8 state;
+ tL2CAP_UCD_CB_INFO cb_info;
+} tL2C_UCD_REG;
+#endif
+
+typedef struct {
+ BOOLEAN in_use;
+ UINT16 psm;
+ UINT16 real_psm; /* This may be a dummy RCB for an o/b connection but */
+ /* this is the real PSM that we need to connect to */
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ tL2C_UCD_REG ucd;
+#endif
+
+ tL2CAP_APPL_INFO api;
+} tL2C_RCB;
+
+typedef void (tL2CAP_SEC_CBACK) (BD_ADDR bd_addr, tBT_TRANSPORT transport,
+ void *p_ref_data, tBTM_STATUS result);
+
+typedef struct
+{
+ UINT16 psm;
+ tBT_TRANSPORT transport;
+ BOOLEAN is_originator;
+ tL2CAP_SEC_CBACK *p_callback;
+ void *p_ref_data;
+}tL2CAP_SEC_DATA;
+
+#ifndef L2CAP_CBB_DEFAULT_DATA_RATE_BUFF_QUOTA
+#define L2CAP_CBB_DEFAULT_DATA_RATE_BUFF_QUOTA 10
+#endif
+/* Define a channel control block (CCB). There may be many channel control blocks
+** between the same two Bluetooth devices (i.e. on the same link).
+** Each CCB has unique local and remote CIDs. All channel control blocks on
+** the same physical link and are chained together.
+*/
+typedef struct t_l2c_ccb {
+ BOOLEAN in_use; /* TRUE when in use, FALSE when not */
+ tL2C_CHNL_STATE chnl_state; /* Channel state */
+ tL2CAP_LE_CFG_INFO local_conn_cfg; /* Our config for ble conn oriented channel */
+ tL2CAP_LE_CFG_INFO peer_conn_cfg; /* Peer device config ble conn oriented channel */
+
+ struct t_l2c_ccb *p_next_ccb; /* Next CCB in the chain */
+ struct t_l2c_ccb *p_prev_ccb; /* Previous CCB in the chain */
+ struct t_l2c_linkcb *p_lcb; /* Link this CCB is assigned to */
+
+ UINT16 local_cid; /* Local CID */
+ UINT16 remote_cid; /* Remote CID */
+
+ TIMER_LIST_ENT timer_entry; /* CCB Timer List Entry */
+
+ tL2C_RCB *p_rcb; /* Registration CB for this Channel */
+ bool should_free_rcb; /* True if RCB was allocated on the heap */
+
+#define IB_CFG_DONE 0x01
+#define OB_CFG_DONE 0x02
+#define RECONFIG_FLAG 0x04 /* True after initial configuration */
+#define CFG_DONE_MASK (IB_CFG_DONE | OB_CFG_DONE)
+
+ UINT8 config_done; /* Configuration flag word */
+ UINT8 local_id; /* Transaction ID for local trans */
+ UINT8 remote_id; /* Transaction ID for local */
+
+#define CCB_FLAG_NO_RETRY 0x01 /* no more retry */
+#define CCB_FLAG_SENT_PENDING 0x02 /* already sent pending response */
+ UINT8 flags;
+
+ tL2CAP_CFG_INFO our_cfg; /* Our saved configuration options */
+ tL2CAP_CH_CFG_BITS peer_cfg_bits; /* Store what peer wants to configure */
+ tL2CAP_CFG_INFO peer_cfg; /* Peer's saved configuration options */
+
+ fixed_queue_t *xmit_hold_q; /* Transmit data hold queue */
+ BOOLEAN cong_sent; /* Set when congested status sent */
+ UINT16 buff_quota; /* Buffer quota before sending congestion */
+
+ tL2CAP_CHNL_PRIORITY ccb_priority; /* Channel priority */
+ tL2CAP_CHNL_DATA_RATE tx_data_rate; /* Channel Tx data rate */
+ tL2CAP_CHNL_DATA_RATE rx_data_rate; /* Channel Rx data rate */
+
+ /* Fields used for eL2CAP */
+ tL2CAP_ERTM_INFO ertm_info;
+ tL2C_FCRB fcrb;
+ UINT16 tx_mps; /* TX MPS adjusted based on current controller */
+ UINT16 max_rx_mtu;
+ UINT8 fcr_cfg_tries; /* Max number of negotiation attempts */
+ BOOLEAN peer_cfg_already_rejected; /* If mode rejected once, set to TRUE */
+ BOOLEAN out_cfg_fcr_present; /* TRUE if cfg response shoulkd include fcr options */
+
+#define L2CAP_CFG_FCS_OUR 0x01 /* Our desired config FCS option */
+#define L2CAP_CFG_FCS_PEER 0x02 /* Peer's desired config FCS option */
+#define L2CAP_BYPASS_FCS (L2CAP_CFG_FCS_OUR | L2CAP_CFG_FCS_PEER)
+ UINT8 bypass_fcs;
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ BOOLEAN is_flushable; /* TRUE if channel is flushable */
+#endif
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0) || (L2CAP_UCD_INCLUDED == TRUE)
+ UINT16 fixed_chnl_idle_tout; /* Idle timeout to use for the fixed channel */
+#endif
+ UINT16 tx_data_len;
+} tL2C_CCB;
+
+/***********************************************************************
+** Define a queue of linked CCBs.
+*/
+typedef struct {
+ tL2C_CCB *p_first_ccb; /* The first channel in this queue */
+ tL2C_CCB *p_last_ccb; /* The last channel in this queue */
+} tL2C_CCB_Q;
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+
+/* Round-Robin service for the same priority channels */
+#define L2CAP_NUM_CHNL_PRIORITY 3 /* Total number of priority group (high, medium, low)*/
+#define L2CAP_CHNL_PRIORITY_WEIGHT 5 /* weight per priority for burst transmission quota */
+#define L2CAP_GET_PRIORITY_QUOTA(pri) ((L2CAP_NUM_CHNL_PRIORITY - (pri)) * L2CAP_CHNL_PRIORITY_WEIGHT)
+
+/* CCBs within the same LCB are served in round robin with priority */
+/* It will make sure that low priority channel (for example, HF signaling on RFCOMM) */
+/* can be sent to headset even if higher priority channel (for example, AV media channel) */
+/* is congested. */
+
+typedef struct {
+ tL2C_CCB *p_serve_ccb; /* current serving ccb within priority group */
+ tL2C_CCB *p_first_ccb; /* first ccb of priority group */
+ UINT8 num_ccb; /* number of channels in priority group */
+ UINT8 quota; /* burst transmission quota */
+} tL2C_RR_SERV;
+
+#endif /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */
+
+
+/* Define a link control block. There is one link control block between
+** this device and any other device (i.e. BD ADDR).
+*/
+typedef struct t_l2c_linkcb {
+ BOOLEAN in_use; /* TRUE when in use, FALSE when not */
+ tL2C_LINK_STATE link_state;
+ BOOLEAN is_aux; /* This variable used for BLE 5.0 or higher version when do auxiliary connection */
+ TIMER_LIST_ENT timer_entry; /* Timer list entry for timeout evt */
+ UINT16 handle; /* The handle used with LM */
+
+ tL2C_CCB_Q ccb_queue; /* Queue of CCBs on this LCB */
+
+ tL2C_CCB *p_pending_ccb; /* ccb of waiting channel during link disconnect */
+ TIMER_LIST_ENT info_timer_entry; /* Timer entry for info resp timeout evt */
+ TIMER_LIST_ENT upda_con_timer; /* Timer entry for update connection parametr */
+ BD_ADDR remote_bd_addr; /* The BD address of the remote */
+
+ UINT8 link_role; /* Master or slave */
+ UINT8 id;
+ UINT8 cur_echo_id; /* Current id value for echo request */
+ tL2CA_ECHO_RSP_CB *p_echo_rsp_cb; /* Echo response callback */
+ UINT16 idle_timeout; /* Idle timeout */
+ BOOLEAN is_bonding; /* True - link active only for bonding */
+
+ UINT16 link_flush_tout; /* Flush timeout used */
+
+ UINT16 link_xmit_quota; /* Num outstanding pkts allowed */
+ UINT16 sent_not_acked; /* Num packets sent but not acked */
+
+ BOOLEAN partial_segment_being_sent; /* Set TRUE when a partial segment */
+ /* is being sent. */
+ BOOLEAN w4_info_rsp; /* TRUE when info request is active */
+ UINT8 info_rx_bits; /* set 1 if received info type */
+ UINT32 peer_ext_fea; /* Peer's extended features mask */
+ list_t *link_xmit_data_q; /* Link transmit data buffer queue */
+
+ UINT8 peer_chnl_mask[L2CAP_FIXED_CHNL_ARRAY_SIZE];
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ UINT16 ucd_mtu; /* peer MTU on UCD */
+ fixed_queue_t *ucd_out_sec_pending_q; /* Security pending outgoing UCD packet */
+ fixed_queue_t *ucd_in_sec_pending_q; /* Security pending incoming UCD packet */
+#endif
+
+ BT_HDR *p_hcit_rcv_acl; /* Current HCIT ACL buf being rcvd */
+ UINT16 idle_timeout_sv; /* Save current Idle timeout */
+ UINT8 acl_priority; /* L2C_PRIORITY_NORMAL or L2C_PRIORITY_HIGH */
+ tL2CA_NOCP_CB *p_nocp_cb; /* Num Cmpl pkts callback */
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ tL2C_CCB *p_fixed_ccbs[L2CAP_NUM_FIXED_CHNLS];
+ UINT16 disc_reason;
+#endif
+
+ tBT_TRANSPORT transport;
+#if (BLE_INCLUDED == TRUE)
+ tBLE_ADDR_TYPE open_addr_type; /* be set by open API */
+ tBLE_ADDR_TYPE ble_addr_type;
+ UINT16 tx_data_len; /* tx data length used in data length extension */
+ fixed_queue_t *le_sec_pending_q; /* LE coc channels waiting for security check completion */
+ UINT8 sec_act;
+#define L2C_BLE_CONN_UPDATE_DISABLE 0x1 /* disable update connection parameters */
+#define L2C_BLE_NEW_CONN_PARAM 0x2 /* new connection parameter to be set */
+#define L2C_BLE_UPDATE_PENDING 0x4 /* waiting for connection update finished */
+#define L2C_BLE_NOT_DEFAULT_PARAM 0x8 /* not using default connection parameters */
+#define L2C_BLE_UPDATE_PARAM_FULL 0x10 /* update connection parameters full, can not update */
+ UINT8 conn_update_mask;
+ /* cache connection parameters that wait to update */
+ UINT16 waiting_update_conn_min_interval;
+ UINT16 waiting_update_conn_max_interval;
+ UINT16 waiting_update_conn_latency;
+ UINT16 waiting_update_conn_timeout;
+ /* cache parameters that is being updated */
+ UINT16 updating_conn_min_interval;
+ UINT16 updating_conn_max_interval;
+ bool updating_param_flag;
+ /* current connection parameters that current connection is using */
+ UINT16 current_used_conn_interval;
+ UINT16 current_used_conn_latency;
+ UINT16 current_used_conn_timeout;
+ /* connection parameters update order:
+ waiting_update_conn_xx -> updating_conn_xx -> current_used_conn_xx
+ */
+ /* create connection retry count*/
+ UINT8 retry_create_con;
+ UINT32 start_time_s;
+#endif
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ /* each priority group is limited burst transmission */
+ /* round robin service for the same priority channels */
+ tL2C_RR_SERV rr_serv[L2CAP_NUM_CHNL_PRIORITY];
+ UINT8 rr_pri; /* current serving priority group */
+#endif
+
+} tL2C_LCB;
+
+
+/* Define the L2CAP control structure
+*/
+typedef struct {
+ UINT8 l2cap_trace_level;
+ UINT16 controller_xmit_window; /* Total ACL window for all links */
+
+ UINT16 round_robin_quota; /* Round-robin link quota */
+ UINT16 round_robin_unacked; /* Round-robin unacked */
+ BOOLEAN check_round_robin; /* Do a round robin check */
+
+ BOOLEAN is_cong_cback_context;
+
+ list_t *p_lcb_pool; /* Link Control Block pool */
+ list_t *p_ccb_pool; /* Channel Control Block pool */
+ tL2C_RCB rcb_pool[MAX_L2CAP_CLIENTS]; /* Registration info pool */
+
+
+ UINT8 desire_role; /* desire to be master/slave when accepting a connection */
+ BOOLEAN disallow_switch; /* FALSE, to allow switch at create conn */
+ UINT16 num_lm_acl_bufs; /* # of ACL buffers on controller */
+ UINT16 idle_timeout; /* Idle timeout */
+
+ list_t *rcv_pending_q; /* Recv pending queue */
+ TIMER_LIST_ENT rcv_hold_tle; /* Timer list entry for rcv hold */
+
+ tL2C_LCB *p_cur_hcit_lcb; /* Current HCI Transport buffer */
+ UINT16 num_links_active; /* Number of links active */
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ UINT16 non_flushable_pbf; /* L2CAP_PKT_START_NON_FLUSHABLE if controller supports */
+ /* Otherwise, L2CAP_PKT_START */
+ BOOLEAN is_flush_active; /* TRUE if an HCI_Enhanced_Flush has been sent */
+#endif
+
+#if L2CAP_CONFORMANCE_TESTING == TRUE
+ UINT32 test_info_resp; /* Conformance testing needs a dynamic response */
+#endif
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ tL2CAP_FIXED_CHNL_REG fixed_reg[L2CAP_NUM_FIXED_CHNLS]; /* Reg info for fixed channels */
+#endif
+
+#if (BLE_INCLUDED == TRUE)
+ UINT16 num_ble_links_active; /* Number of LE links active */
+ BOOLEAN is_ble_connecting;
+ BD_ADDR ble_connecting_bda;
+ UINT16 controller_le_xmit_window; /* Total ACL window for all links */
+ tL2C_BLE_FIXED_CHNLS_MASK l2c_ble_fixed_chnls_mask; // LE fixed channels mask
+ UINT16 num_lm_ble_bufs; /* # of ACL buffers on controller */
+ UINT16 ble_round_robin_quota; /* Round-robin link quota */
+ UINT16 ble_round_robin_unacked; /* Round-robin unacked */
+ BOOLEAN ble_check_round_robin; /* Do a round robin check */
+ tL2C_RCB ble_rcb_pool[BLE_MAX_L2CAP_CLIENTS]; /* Registration info pool */
+#endif
+
+ tL2CA_ECHO_DATA_CB *p_echo_data_cb; /* Echo data callback */
+
+#if (defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) && (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE))
+ UINT16 high_pri_min_xmit_quota; /* Minimum number of ACL credit for high priority link */
+#endif /* (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE) */
+
+ UINT16 dyn_psm;
+} tL2C_CB;
+
+
+
+/* Define a structure that contains the information about a connection.
+** This structure is used to pass between functions, and not all the
+** fields will always be filled in.
+*/
+typedef struct {
+ BD_ADDR bd_addr; /* Remote BD address */
+ UINT8 status; /* Connection status */
+ UINT16 psm; /* PSM of the connection */
+ UINT16 l2cap_result; /* L2CAP result */
+ UINT16 l2cap_status; /* L2CAP status */
+ UINT16 remote_cid; /* Remote CID */
+} tL2C_CONN_INFO;
+
+
+typedef void (tL2C_FCR_MGMT_EVT_HDLR) (UINT8, tL2C_CCB *);
+
+/* The offset in a buffer that L2CAP will use when building commands.
+*/
+#define L2CAP_SEND_CMD_OFFSET 0
+
+
+/* Number of ACL buffers to use for high priority channel
+*/
+#if (!defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) || (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == FALSE))
+#define L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A (L2CAP_HIGH_PRI_MIN_XMIT_QUOTA)
+#else
+#define L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A (l2cb.high_pri_min_xmit_quota)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* L2CAP global data
+************************************
+*/
+#if (!defined L2C_DYNAMIC_MEMORY) || (L2C_DYNAMIC_MEMORY == FALSE)
+extern tL2C_CB l2cb;
+#else
+extern tL2C_CB *l2c_cb_ptr;
+#define l2cb (*l2c_cb_ptr)
+#endif
+
+
+/* Functions provided by l2c_main.c
+************************************
+*/
+void l2c_init(void);
+void l2c_free(void);
+
+extern void l2c_process_timeout (TIMER_LIST_ENT *p_tle);
+extern UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flag);
+extern void l2c_rcv_acl_data (BT_HDR *p_msg);
+extern void l2c_process_held_packets (BOOLEAN timed_out);
+
+/* Functions provided by l2c_utils.c
+************************************
+*/
+extern tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding, tBT_TRANSPORT transport);
+extern BOOLEAN l2cu_start_post_bond_timer (UINT16 handle);
+extern void l2cu_release_lcb (tL2C_LCB *p_lcb);
+extern tL2C_LCB *l2cu_find_lcb_by_bd_addr (BD_ADDR p_bd_addr, tBT_TRANSPORT transport);
+extern tL2C_LCB *l2cu_find_lcb_by_handle (UINT16 handle);
+extern uint8_t l2cu_plcb_active_count(void);
+extern void l2cu_update_lcb_4_bonding (BD_ADDR p_bd_addr, BOOLEAN is_bonding);
+
+extern UINT8 l2cu_get_conn_role (tL2C_LCB *p_this_lcb);
+extern BOOLEAN l2cu_set_acl_priority (BD_ADDR bd_addr, UINT8 priority, BOOLEAN reset_after_rs);
+
+extern void l2cu_enqueue_ccb (tL2C_CCB *p_ccb);
+extern void l2cu_dequeue_ccb (tL2C_CCB *p_ccb);
+extern void l2cu_change_pri_ccb (tL2C_CCB *p_ccb, tL2CAP_CHNL_PRIORITY priority);
+
+extern tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid);
+extern void l2cu_release_ccb (tL2C_CCB *p_ccb);
+extern tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid);
+extern tL2C_CCB *l2cu_find_free_ccb(void);
+extern tL2C_CCB *l2cu_find_ccb_by_remote_cid (tL2C_LCB *p_lcb, UINT16 remote_cid);
+extern void l2cu_adj_id (tL2C_LCB *p_lcb, UINT8 adj_mask);
+extern BOOLEAN l2c_is_cmd_rejected (UINT8 cmd_code, UINT8 id, tL2C_LCB *p_lcb);
+
+extern void l2cu_send_peer_cmd_reject (tL2C_LCB *p_lcb, UINT16 reason,
+ UINT8 rem_id, UINT16 p1, UINT16 p2);
+extern void l2cu_send_peer_connect_req (tL2C_CCB *p_ccb);
+extern void l2cu_send_peer_connect_rsp (tL2C_CCB *p_ccb, UINT16 result, UINT16 status);
+extern void l2cu_send_peer_config_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2cu_send_peer_config_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2cu_send_peer_config_rej (tL2C_CCB *p_ccb, UINT8 *p_data, UINT16 data_len, UINT16 rej_len);
+extern void l2cu_send_peer_disc_req (tL2C_CCB *p_ccb);
+extern void l2cu_send_peer_disc_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 local_cid, UINT16 remote_cid);
+extern void l2cu_send_peer_echo_req (tL2C_LCB *p_lcb, UINT8 *p_data, UINT16 data_len);
+extern void l2cu_send_peer_echo_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len);
+extern void l2cu_send_peer_info_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT16 info_type);
+extern void l2cu_reject_connection (tL2C_LCB *p_lcb, UINT16 remote_cid, UINT8 rem_id, UINT16 result);
+extern void l2cu_send_peer_info_req (tL2C_LCB *p_lcb, UINT16 info_type);
+extern void l2cu_set_acl_hci_header (BT_HDR *p_buf, tL2C_CCB *p_ccb);
+extern void l2cu_check_channel_congestion (tL2C_CCB *p_ccb);
+extern void l2cu_disconnect_chnl (tL2C_CCB *p_ccb);
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+extern void l2cu_set_non_flushable_pbf(BOOLEAN);
+#endif
+
+#if (BLE_INCLUDED == TRUE)
+extern void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout);
+extern void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id);
+#endif
+
+extern BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr);
+extern void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb);
+extern void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb);
+
+/* Functions provided by l2c_ucd.c
+************************************
+*/
+#if (L2CAP_UCD_INCLUDED == TRUE)
+void l2c_ucd_delete_sec_pending_q(tL2C_LCB *p_lcb);
+void l2c_ucd_enqueue_pending_out_sec_q(tL2C_CCB *p_ccb, void *p_data);
+BOOLEAN l2c_ucd_check_pending_info_req(tL2C_CCB *p_ccb);
+BOOLEAN l2c_ucd_check_pending_out_sec_q(tL2C_CCB *p_ccb);
+void l2c_ucd_send_pending_out_sec_q(tL2C_CCB *p_ccb);
+void l2c_ucd_discard_pending_out_sec_q(tL2C_CCB *p_ccb);
+BOOLEAN l2c_ucd_check_pending_in_sec_q(tL2C_CCB *p_ccb);
+void l2c_ucd_send_pending_in_sec_q(tL2C_CCB *p_ccb);
+void l2c_ucd_discard_pending_in_sec_q(tL2C_CCB *p_ccb);
+BOOLEAN l2c_ucd_check_rx_pkts(tL2C_LCB *p_lcb, BT_HDR *p_msg);
+BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+#endif
+
+#if (BLE_INCLUDED == TRUE)
+extern void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int, UINT16 latency, UINT16 timeout);
+extern void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id);
+extern void l2cu_reject_ble_connection (tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result);
+extern void l2cu_send_peer_ble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result);
+extern void l2cu_send_peer_ble_credit_based_conn_req (tL2C_CCB *p_ccb);
+extern void l2cu_send_peer_ble_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value);
+extern void l2cu_send_peer_ble_credit_based_disconn_req(tL2C_CCB *p_ccb);
+
+#endif
+
+extern BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr);
+extern void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb);
+extern void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb);
+
+
+/* Functions provided for Broadcom Aware
+****************************************
+*/
+extern BOOLEAN l2cu_check_feature_req (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len);
+extern void l2cu_check_feature_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len);
+extern void l2cu_send_feature_req (tL2C_CCB *p_ccb);
+
+extern tL2C_RCB *l2cu_allocate_rcb (UINT16 psm);
+extern tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm);
+extern void l2cu_release_rcb (tL2C_RCB *p_rcb);
+extern tL2C_RCB *l2cu_allocate_ble_rcb (UINT16 psm);
+extern tL2C_RCB *l2cu_find_ble_rcb_by_psm (UINT16 psm);
+
+#if (L2CAP_COC_INCLUDED == TRUE)
+extern UINT8 l2cu_process_peer_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2cu_process_peer_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2cu_process_our_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2cu_process_our_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+#endif // (L2CAP_COC_INCLUDED == TRUE)
+
+extern void l2cu_device_reset (void);
+extern tL2C_LCB *l2cu_find_lcb_by_state (tL2C_LINK_STATE state);
+extern BOOLEAN l2cu_lcb_disconnecting (void);
+
+extern BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb, tBT_TRANSPORT transport);
+extern BOOLEAN l2cu_create_conn_after_switch (tL2C_LCB *p_lcb);
+extern BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb);
+extern void l2cu_resubmit_pending_sec_req (BD_ADDR p_bda);
+extern void l2cu_initialize_amp_ccb (tL2C_LCB *p_lcb);
+extern void l2cu_adjust_out_mps (tL2C_CCB *p_ccb);
+
+/* Functions provided by l2c_link.c
+************************************
+*/
+extern BOOLEAN l2c_link_hci_conn_req (BD_ADDR bd_addr);
+extern BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda);
+extern BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason);
+extern BOOLEAN l2c_link_hci_qos_violation (UINT16 handle);
+extern void l2c_link_timeout (tL2C_LCB *p_lcb);
+extern void l2c_info_timeout (tL2C_LCB *p_lcb);
+extern void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf);
+extern void l2c_link_adjust_allocation (void);
+extern void l2c_link_process_num_completed_pkts (UINT8 *p);
+extern void l2c_link_process_num_completed_blocks (UINT8 controller_id, UINT8 *p, UINT16 evt_len);
+extern void l2c_link_processs_num_bufs (UINT16 num_lm_acl_bufs);
+extern UINT8 l2c_link_pkts_rcvd (UINT16 *num_pkts, UINT16 *handles);
+extern void l2c_link_role_changed (BD_ADDR bd_addr, UINT8 new_role, UINT8 hci_status);
+extern void l2c_link_sec_comp (BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data, UINT8 status);
+extern void l2c_link_segments_xmitted (BT_HDR *p_msg);
+extern void l2c_pin_code_request (BD_ADDR bd_addr);
+extern void l2c_link_adjust_chnl_allocation (void);
+
+#if (BLE_INCLUDED == TRUE)
+extern void l2c_link_processs_ble_num_bufs (UINT16 num_lm_acl_bufs);
+#endif
+
+#if L2CAP_WAKE_PARKED_LINK == TRUE
+extern BOOLEAN l2c_link_check_power_mode ( tL2C_LCB *p_lcb );
+#define L2C_LINK_CHECK_POWER_MODE(x) l2c_link_check_power_mode ((x))
+#else // L2CAP_WAKE_PARKED_LINK
+#define L2C_LINK_CHECK_POWER_MODE(x) (FALSE)
+#endif // L2CAP_WAKE_PARKED_LINK
+
+#if L2CAP_CONFORMANCE_TESTING == TRUE
+/* Used only for conformance testing */
+extern void l2cu_set_info_rsp_mask (UINT32 mask);
+#endif
+
+/* Functions provided by l2c_csm.c
+************************************
+*/
+#if (L2CAP_COC_INCLUDED == TRUE)
+extern void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+#endif
+extern void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf);
+
+/* Functions provided by l2c_fcr.c
+************************************
+*/
+extern void l2c_fcr_cleanup (tL2C_CCB *p_ccb);
+extern void l2c_fcr_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf);
+extern void l2c_fcr_proc_tout (tL2C_CCB *p_ccb);
+extern void l2c_fcr_proc_ack_tout (tL2C_CCB *p_ccb);
+extern void l2c_fcr_send_S_frame (tL2C_CCB *p_ccb, UINT16 function_code, UINT16 pf_bit);
+extern BT_HDR *l2c_fcr_clone_buf (BT_HDR *p_buf, UINT16 new_offset, UINT16 no_of_bytes);
+extern BOOLEAN l2c_fcr_is_flow_controlled (tL2C_CCB *p_ccb);
+extern BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length);
+extern void l2c_fcr_start_timer (tL2C_CCB *p_ccb);
+
+/* Configuration negotiation */
+extern UINT8 l2c_fcr_chk_chan_modes (tL2C_CCB *p_ccb);
+extern BOOLEAN l2c_fcr_adj_our_req_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2c_fcr_adj_our_rsp_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_peer_cfg);
+extern BOOLEAN l2c_fcr_renegotiate_chan(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern UINT8 l2c_fcr_process_peer_cfg_req(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg);
+extern void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb);
+extern void l2c_fcr_stop_timer (tL2C_CCB *p_ccb);
+extern void l2c_fcr_free_timer (tL2C_CCB *p_ccb);
+/* Functions provided by l2c_ble.c
+************************************
+*/
+#if (BLE_INCLUDED == TRUE)
+extern BOOLEAN l2cble_create_conn (tL2C_LCB *p_lcb);
+extern void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len);
+extern void l2cble_conn_comp (UINT16 handle, UINT8 role, BD_ADDR bda, tBLE_ADDR_TYPE type,
+ UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout);
+extern BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb);
+extern void l2cble_notify_le_connection (BD_ADDR bda);
+extern void l2c_ble_link_adjust_allocation (void);
+extern void l2cble_process_conn_update_evt (UINT16 handle, UINT8 status, UINT16 conn_interval,
+ UINT16 conn_latency, UINT16 conn_timeout);
+extern void l2cble_get_conn_param_format_err_from_contoller(UINT8 status, UINT16 handle);
+
+extern void l2cble_credit_based_conn_req (tL2C_CCB *p_ccb);
+extern void l2cble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result);
+extern void l2cble_send_peer_disc_req(tL2C_CCB *p_ccb);
+extern void l2cble_send_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value);
+extern BOOLEAN l2ble_sec_access_req(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, tL2CAP_SEC_CBACK *p_callback, void *p_ref_data);
+
+
+#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE)
+extern void l2cble_process_rc_param_request_evt(UINT16 handle, UINT16 int_min, UINT16 int_max,
+ UINT16 latency, UINT16 timeout);
+#endif
+
+extern void l2cble_update_data_length(tL2C_LCB *p_lcb);
+extern void l2cble_set_fixed_channel_tx_data_length(BD_ADDR remote_bda, UINT16 fix_cid,
+ UINT16 tx_mtu);
+extern void l2c_send_update_conn_params_cb(tL2C_LCB *p_lcb, UINT8 status);
+extern void l2cble_process_data_length_change_event(UINT16 handle, UINT16 tx_data_len,
+ UINT16 rx_data_len);
+extern UINT32 CalConnectParamTimeout(tL2C_LCB *p_lcb);
+
+#endif
+extern void l2cu_process_fixed_disc_cback (tL2C_LCB *p_lcb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_api.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_api.c
new file mode 100644
index 00000000..fa01790d
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_api.c
@@ -0,0 +1,2456 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-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 L2CAP API code
+ *
+ ******************************************************************************/
+
+//#define LOG_TAG "bt_l2cap"
+
+//#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "common/bt_trace.h"
+#include "stack/bt_types.h"
+#include "stack/hcidefs.h"
+#include "stack/hcimsgs.h"
+#include "stack/l2cdefs.h"
+#include "l2c_int.h"
+#include "stack/btu.h"
+#include "stack/btm_api.h"
+#include "osi/allocator.h"
+#include "gatt_int.h"
+#if (CLASSIC_BT_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function L2CA_Register
+**
+** Description Other layers call this function to register for L2CAP
+** services.
+**
+** Returns PSM to use or zero if error. Typically, the PSM returned
+** is the same as was passed in, but for an outgoing-only
+** connection to a dynamic PSM, a "virtual" PSM is returned
+** and should be used in the calls to L2CA_ConnectReq(),
+** L2CA_ErtmConnectReq() and L2CA_Deregister()
+**
+*******************************************************************************/
+UINT16 L2CA_Register (UINT16 psm, tL2CAP_APPL_INFO *p_cb_info)
+{
+ tL2C_RCB *p_rcb;
+ UINT16 vpsm = psm;
+
+
+ /* Verify that the required callback info has been filled in
+ ** Note: Connection callbacks are required but not checked
+ ** for here because it is possible to be only a client
+ ** or only a server.
+ */
+ if ((!p_cb_info->pL2CA_ConfigCfm_Cb)
+ || (!p_cb_info->pL2CA_ConfigInd_Cb)
+ || (!p_cb_info->pL2CA_DataInd_Cb)
+ || (!p_cb_info->pL2CA_DisconnectInd_Cb)) {
+ L2CAP_TRACE_ERROR ("L2CAP - no cb registering PSM: 0x%04x", psm);
+ return (0);
+ }
+
+ /* Verify PSM is valid */
+ if (L2C_INVALID_PSM(psm)) {
+ L2CAP_TRACE_ERROR ("L2CAP - invalid PSM value, PSM: 0x%04x", psm);
+ return (0);
+ }
+
+ /* Check if this is a registration for an outgoing-only connection to */
+ /* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */
+ if ( (psm >= 0x1001) && (p_cb_info->pL2CA_ConnectInd_Cb == NULL) ) {
+ for (vpsm = 0x1002; vpsm < 0x8000; vpsm += 2) {
+ if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL) {
+ break;
+ }
+ }
+
+ //L2CAP_TRACE_API ("L2CA_Register - Real PSM: 0x%04x Virtual PSM: 0x%04x", psm, vpsm);
+ }
+
+ /* If registration block already there, just overwrite it */
+ if ((p_rcb = l2cu_find_rcb_by_psm (vpsm)) == NULL) {
+ if ((p_rcb = l2cu_allocate_rcb (vpsm)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no RCB available, PSM: 0x%04x vPSM: 0x%04x", psm, vpsm);
+ return (0);
+ }
+ }
+
+ p_rcb->api = *p_cb_info;
+ p_rcb->real_psm = psm;
+
+ return (vpsm);
+}
+
+
+
+/*******************************************************************************
+**
+** Function L2CA_Deregister
+**
+** Description Other layers call this function to de-register for L2CAP
+** services.
+**
+** Returns void
+**
+*******************************************************************************/
+void L2CA_Deregister (UINT16 psm)
+{
+ tL2C_RCB *p_rcb;
+ tL2C_CCB *p_ccb;
+ tL2C_LCB *p_lcb;
+ list_node_t *p_node = NULL;
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) != NULL) {
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (p_lcb->in_use) {
+ if (((p_ccb = p_lcb->ccb_queue.p_first_ccb) == NULL)
+ || (p_lcb->link_state == LST_DISCONNECTING)) {
+ continue;
+ }
+
+ if ((p_ccb->in_use) &&
+ ((p_ccb->chnl_state == CST_W4_L2CAP_DISCONNECT_RSP) ||
+ (p_ccb->chnl_state == CST_W4_L2CA_DISCONNECT_RSP))) {
+ continue;
+ }
+
+ if (p_ccb->p_rcb == p_rcb) {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
+ }
+ }
+ }
+ l2cu_release_rcb (p_rcb);
+ } else {
+ L2CAP_TRACE_WARNING ("L2CAP - PSM: 0x%04x not found for deregistration", psm);
+ }
+}
+
+/*******************************************************************************
+**
+** Function L2CA_AllocatePSM
+**
+** Description Other layers call this function to find an unused PSM for L2CAP
+** services.
+**
+** Returns PSM to use.
+**
+*******************************************************************************/
+UINT16 L2CA_AllocatePSM(void)
+{
+ BOOLEAN done = FALSE;
+ UINT16 psm = l2cb.dyn_psm;
+
+ while (!done) {
+ psm += 2;
+ if (psm > 0xfeff) {
+ psm = 0x1001;
+ } else if (psm & 0x0100) {
+ /* the upper byte must be even */
+ psm += 0x0100;
+ }
+
+ /* if psm is in range of reserved BRCM Aware features */
+ if ((BRCM_RESERVED_PSM_START <= psm) && (psm <= BRCM_RESERVED_PSM_END)) {
+ continue;
+ }
+
+ /* make sure the newlly allocated psm is not used right now */
+ if ((l2cu_find_rcb_by_psm (psm)) == NULL) {
+ done = TRUE;
+ }
+ }
+ l2cb.dyn_psm = psm;
+
+ return (psm);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_ConnectReq
+**
+** Description Higher layers call this function to create an L2CAP connection.
+** Note that the connection is not established at this time, but
+** connection establishment gets started. The callback function
+** will be invoked when connection establishes or fails.
+**
+** Returns the CID of the connection, or 0 if it failed to start
+**
+*******************************************************************************/
+UINT16 L2CA_ConnectReq (UINT16 psm, BD_ADDR p_bd_addr)
+{
+ return L2CA_ErtmConnectReq (psm, p_bd_addr, NULL);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_ErtmConnectReq
+**
+** Description Higher layers call this function to create an L2CAP connection.
+** Note that the connection is not established at this time, but
+** connection establishment gets started. The callback function
+** will be invoked when connection establishes or fails.
+**
+** Parameters: PSM: L2CAP PSM for the connection
+** BD address of the peer
+** Enhaced retransmission mode configurations
+
+** Returns the CID of the connection, or 0 if it failed to start
+**
+*******************************************************************************/
+UINT16 L2CA_ErtmConnectReq (UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_ERTM_INFO *p_ertm_info)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+
+ //counter_add("l2cap.conn.req", 1);
+ L2CAP_TRACE_API ("L2CA_ErtmConnectReq() PSM: 0x%04x BDA: %08x%04x p_ertm_info: %p allowed:0x%x preferred:%d", psm,
+ (p_bd_addr[0] << 24) + (p_bd_addr[1] << 16) + (p_bd_addr[2] << 8) + p_bd_addr[3],
+ (p_bd_addr[4] << 8) + p_bd_addr[5], p_ertm_info,
+ (p_ertm_info) ? p_ertm_info->allowed_modes : 0,
+ (p_ertm_info) ? p_ertm_info->preferred_mode : 0);
+
+ /* Fail if we have not established communications with the controller */
+ if (!BTM_IsDeviceUp()) {
+ L2CAP_TRACE_WARNING ("L2CAP connect req - BTU not ready");
+ return (0);
+ }
+ /* Fail if the PSM is not registered */
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_conn_req, PSM: 0x%04x", psm);
+ return (0);
+ }
+
+ /* First, see if we already have a link to the remote */
+ /* assume all ERTM l2cap connection is going over BR/EDR for now */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) {
+ /* No link. Get an LCB and start link establishment */
+ if ( ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE, BT_TRANSPORT_BR_EDR)) == NULL)
+ /* currently use BR/EDR for ERTM mode l2cap connection */
+ || (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == FALSE) ) {
+ L2CAP_TRACE_WARNING ("L2CAP - conn not started for PSM: 0x%04x p_lcb: %p", psm, p_lcb);
+ return (0);
+ }
+ }
+
+ /* Allocate a channel control block */
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_conn_req, PSM: 0x%04x", psm);
+ return (0);
+ }
+
+ /* Save registration info */
+ p_ccb->p_rcb = p_rcb;
+
+ if (p_ertm_info) {
+ p_ccb->ertm_info = *p_ertm_info;
+
+ /* Replace default indicators with the actual default pool */
+ if (p_ccb->ertm_info.fcr_rx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) {
+ p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_FCR_RX_BUF_SIZE;
+ }
+
+ if (p_ccb->ertm_info.fcr_tx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) {
+ p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_FCR_TX_BUF_SIZE;
+ }
+
+ if (p_ccb->ertm_info.user_rx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) {
+ p_ccb->ertm_info.user_rx_buf_size = L2CAP_USER_RX_BUF_SIZE;
+ }
+
+ if (p_ccb->ertm_info.user_tx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) {
+ p_ccb->ertm_info.user_tx_buf_size = L2CAP_USER_TX_BUF_SIZE;
+ }
+
+ p_ccb->max_rx_mtu = p_ertm_info->user_rx_buf_size -
+ (L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET + L2CAP_FCS_LEN);
+ }
+
+
+
+ /* If link is up, start the L2CAP connection */
+ if (p_lcb->link_state == LST_CONNECTED) {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL);
+ }
+
+ /* If link is disconnecting, save link info to retry after disconnect
+ * Possible Race condition when a reconnect occurs
+ * on the channel during a disconnect of link. This
+ * ccb will be automatically retried after link disconnect
+ * arrives
+ */
+ else if (p_lcb->link_state == LST_DISCONNECTING) {
+ L2CAP_TRACE_DEBUG ("L2CAP API - link disconnecting: RETRY LATER");
+
+ /* Save ccb so it can be started after disconnect is finished */
+ p_lcb->p_pending_ccb = p_ccb;
+ }
+
+ L2CAP_TRACE_API ("L2CAP - L2CA_conn_req(psm: 0x%04x) returned CID: 0x%04x", psm, p_ccb->local_cid);
+
+ /* Return the local CID as our handle */
+ return (p_ccb->local_cid);
+}
+
+bool L2CA_SetConnectionCallbacks(uint16_t local_cid, const tL2CAP_APPL_INFO *callbacks)
+{
+ assert(callbacks != NULL);
+ assert(callbacks->pL2CA_ConnectInd_Cb == NULL);
+ assert(callbacks->pL2CA_ConnectCfm_Cb != NULL);
+ assert(callbacks->pL2CA_ConfigInd_Cb != NULL);
+ assert(callbacks->pL2CA_ConfigCfm_Cb != NULL);
+ assert(callbacks->pL2CA_DisconnectInd_Cb != NULL);
+ assert(callbacks->pL2CA_DisconnectCfm_Cb != NULL);
+ assert(callbacks->pL2CA_CongestionStatus_Cb != NULL);
+ assert(callbacks->pL2CA_DataInd_Cb != NULL);
+ assert(callbacks->pL2CA_TxComplete_Cb != NULL);
+
+ tL2C_CCB *channel_control_block = l2cu_find_ccb_by_cid(NULL, local_cid);
+ if (!channel_control_block) {
+ L2CAP_TRACE_ERROR("%s no channel control block found for L2CAP LCID=0x%04x.", __func__, local_cid);
+ return false;
+ }
+
+ // We're making a connection-specific registration control block so we check if
+ // we already have a private one allocated to us on the heap. If not, we make a
+ // new allocation, mark it as heap-allocated, and inherit the fields from the old
+ // control block.
+ tL2C_RCB *registration_control_block = channel_control_block->p_rcb;
+ if (!channel_control_block->should_free_rcb) {
+ registration_control_block = (tL2C_RCB *)osi_calloc(sizeof(tL2C_RCB));
+ if (!registration_control_block) {
+ L2CAP_TRACE_ERROR("%s unable to allocate registration control block.", __func__);
+ return false;
+ }
+
+ *registration_control_block = *channel_control_block->p_rcb;
+ channel_control_block->p_rcb = registration_control_block;
+ channel_control_block->should_free_rcb = true;
+ }
+
+ registration_control_block->api = *callbacks;
+ return true;
+}
+
+/*******************************************************************************
+**
+** Function L2CA_ConnectRsp
+**
+** Description Higher layers call this function to accept an incoming
+** L2CAP connection, for which they had gotten an connect
+** indication callback.
+**
+** Returns TRUE for success, FALSE for failure
+**
+*******************************************************************************/
+BOOLEAN L2CA_ConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid,
+ UINT16 result, UINT16 status)
+{
+ return L2CA_ErtmConnectRsp (p_bd_addr, id, lcid, result, status, NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_ErtmConnectRsp
+**
+** Description Higher layers call this function to accept an incoming
+** L2CAP connection, for which they had gotten an connect
+** indication callback.
+**
+** Returns TRUE for success, FALSE for failure
+**
+*******************************************************************************/
+BOOLEAN L2CA_ErtmConnectRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result,
+ UINT16 status, tL2CAP_ERTM_INFO *p_ertm_info)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+
+ //counter_add("l2cap.conn.rsp", 1);
+ L2CAP_TRACE_API ("L2CA_ErtmConnectRsp() CID: 0x%04x Result: %d Status: %d BDA: %08x%04x p_ertm_info:%p",
+ lcid, result, status,
+ (p_bd_addr[0] << 24) + (p_bd_addr[1] << 16) + (p_bd_addr[2] << 8) + p_bd_addr[3],
+ (p_bd_addr[4] << 8) + p_bd_addr[5], p_ertm_info);
+
+ /* First, find the link control block */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) {
+ /* No link. Get an LCB and start link establishment */
+ L2CAP_TRACE_WARNING ("L2CAP - no LCB for L2CA_conn_rsp");
+ return (FALSE);
+ }
+ /* Now, find the channel control block */
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_conn_rsp");
+ return (FALSE);
+ }
+
+ /* The IDs must match */
+ if (p_ccb->remote_id != id) {
+ L2CAP_TRACE_WARNING ("L2CAP - bad id in L2CA_conn_rsp. Exp: %d Got: %d", p_ccb->remote_id, id);
+ return (FALSE);
+ }
+
+ if (p_ertm_info) {
+ p_ccb->ertm_info = *p_ertm_info;
+
+ /* Replace default indicators with the actual default pool */
+ if (p_ccb->ertm_info.fcr_rx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) {
+ p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_FCR_RX_BUF_SIZE;
+ }
+
+ if (p_ccb->ertm_info.fcr_tx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) {
+ p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_FCR_TX_BUF_SIZE;
+ }
+
+ if (p_ccb->ertm_info.user_rx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) {
+ p_ccb->ertm_info.user_rx_buf_size = L2CAP_USER_RX_BUF_SIZE;
+ }
+
+ if (p_ccb->ertm_info.user_tx_buf_size == L2CAP_INVALID_ERM_BUF_SIZE) {
+ p_ccb->ertm_info.user_tx_buf_size = L2CAP_USER_TX_BUF_SIZE;
+ }
+
+ p_ccb->max_rx_mtu = p_ertm_info->user_rx_buf_size -
+ (L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET + L2CAP_FCS_LEN);
+ }
+
+ if (result == L2CAP_CONN_OK) {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, NULL);
+ } else {
+ tL2C_CONN_INFO conn_info;
+
+ conn_info.l2cap_result = result;
+ conn_info.l2cap_status = status;
+
+ if (result == L2CAP_CONN_PENDING) {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, &conn_info);
+ } else {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP_NEG, &conn_info);
+ }
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_ConfigReq
+**
+** Description Higher layers call this function to send configuration.
+**
+** Note: The FCR options of p_cfg are not used.
+**
+** Returns TRUE if configuration sent, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_ConfigReq (UINT16 cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tL2C_CCB *p_ccb;
+
+ //counter_add("l2cap.cfg.req", 1);
+ L2CAP_TRACE_API ("L2CA_ConfigReq() CID 0x%04x: fcr_present:%d (mode %d) mtu_present:%d (%d)",
+ cid, p_cfg->fcr_present, p_cfg->fcr.mode, p_cfg->mtu_present, p_cfg->mtu);
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_cfg_req, CID: %d", cid);
+ return (FALSE);
+ }
+
+ /* We need to have at least one mode type common with the peer */
+ if (!l2c_fcr_adj_our_req_options(p_ccb, p_cfg)) {
+ return (FALSE);
+ }
+
+ /* Don't adjust FCR options if not used */
+ if ((!p_cfg->fcr_present) || (p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE)) {
+ /* FCR and FCS options are not used in basic mode */
+ p_cfg->fcs_present = FALSE;
+ p_cfg->ext_flow_spec_present = FALSE;
+
+ if ( (p_cfg->mtu_present) && (p_cfg->mtu > L2CAP_MTU_SIZE) ) {
+ L2CAP_TRACE_WARNING ("L2CAP - adjust MTU: %u too large", p_cfg->mtu);
+ p_cfg->mtu = L2CAP_MTU_SIZE;
+ }
+ }
+
+ /* Save the adjusted configuration in case it needs to be used for renegotiation */
+ p_ccb->our_cfg = *p_cfg;
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_REQ, p_cfg);
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_ConfigRsp
+**
+** Description Higher layers call this function to send a configuration
+** response.
+**
+** Returns TRUE if configuration response sent, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_ConfigRsp (UINT16 cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ tL2C_CCB *p_ccb;
+
+ //counter_add("l2cap.cfg.rsp", 1);
+ L2CAP_TRACE_API ("L2CA_ConfigRsp() CID: 0x%04x Result: %d MTU present:%d Flush TO:%d FCR:%d FCS:%d",
+ cid, p_cfg->result, p_cfg->mtu_present, p_cfg->flush_to_present, p_cfg->fcr_present, p_cfg->fcs_present);
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_cfg_rsp, CID: %d", cid);
+ return (FALSE);
+ }
+
+ if ( (p_cfg->result == L2CAP_CFG_OK) || (p_cfg->result == L2CAP_CFG_PENDING) ) {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_RSP, p_cfg);
+ } else {
+ p_cfg->fcr_present = FALSE; /* FCR options already negotiated before this point */
+
+ /* Clear out any cached options that are being returned as an error (excluding FCR) */
+ if (p_cfg->mtu_present) {
+ p_ccb->peer_cfg.mtu_present = FALSE;
+ }
+ if (p_cfg->flush_to_present) {
+ p_ccb->peer_cfg.flush_to_present = FALSE;
+ }
+ if (p_cfg->qos_present) {
+ p_ccb->peer_cfg.qos_present = FALSE;
+ }
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONFIG_RSP_NEG, p_cfg);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_DisconnectReq
+**
+** Description Higher layers call this function to disconnect a channel.
+**
+** Returns TRUE if disconnect sent, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_DisconnectReq (UINT16 cid)
+{
+ tL2C_CCB *p_ccb;
+
+ //counter_add("l2cap.disconn.req", 1);
+ L2CAP_TRACE_API ("L2CA_DisconnectReq() CID: 0x%04x", cid);
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_disc_req, CID: %d", cid);
+ return (FALSE);
+ }
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_DisconnectRsp
+**
+** Description Higher layers call this function to acknowledge the
+** disconnection of a channel.
+**
+** Returns void
+**
+*******************************************************************************/
+BOOLEAN L2CA_DisconnectRsp (UINT16 cid)
+{
+ tL2C_CCB *p_ccb;
+
+ //counter_add("l2cap.disconn.rsp", 1);
+ L2CAP_TRACE_API ("L2CA_DisconnectRsp() CID: 0x%04x", cid);
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_disc_rsp, CID: %d", cid);
+ return (FALSE);
+ }
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_DISCONNECT_RSP, NULL);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_Ping
+**
+** Description Higher layers call this function to send an echo request.
+**
+** Returns TRUE if echo request sent, else FALSE.
+**
+*******************************************************************************/
+BOOLEAN L2CA_Ping (BD_ADDR p_bd_addr, tL2CA_ECHO_RSP_CB *p_callback)
+{
+ tL2C_LCB *p_lcb;
+
+ L2CAP_TRACE_API ("L2CA_Ping() BDA: %02x-%02x-%02x-%02x-%02x-%02x",
+ p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]);
+
+ /* Fail if we have not established communications with the controller */
+ if (!BTM_IsDeviceUp()) {
+ return (FALSE);
+ }
+
+ /* First, see if we already have a link to the remote */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) {
+ /* No link. Get an LCB and start link establishment */
+ if ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE, BT_TRANSPORT_BR_EDR)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no LCB for L2CA_ping");
+ return (FALSE);
+ }
+ if (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == FALSE) {
+ return (FALSE);
+ }
+
+ p_lcb->p_echo_rsp_cb = p_callback;
+
+ return (TRUE);
+ }
+
+ /* We only allow 1 ping outstanding at a time */
+ if (p_lcb->p_echo_rsp_cb != NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - rejected second L2CA_ping");
+ return (FALSE);
+ }
+
+ /* Have a link control block. If link is disconnecting, tell user to retry later */
+ if (p_lcb->link_state == LST_DISCONNECTING) {
+ L2CAP_TRACE_WARNING ("L2CAP - L2CA_ping rejected - link disconnecting");
+ return (FALSE);
+ }
+
+ /* Save address of callback */
+ p_lcb->p_echo_rsp_cb = p_callback;
+
+ if (p_lcb->link_state == LST_CONNECTED) {
+ l2cu_adj_id(p_lcb, L2CAP_ADJ_BRCM_ID); /* Make sure not using Broadcom ID */
+ l2cu_send_peer_echo_req (p_lcb, NULL, 0);
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_ECHO_RSP_TOUT);
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_Echo
+**
+** Description Higher layers call this function to send an echo request
+** with application-specific data.
+**
+** Returns TRUE if echo request sent, else FALSE.
+**
+*******************************************************************************/
+BOOLEAN L2CA_Echo (BD_ADDR p_bd_addr, BT_HDR *p_data, tL2CA_ECHO_DATA_CB *p_callback)
+{
+ tL2C_LCB *p_lcb;
+ UINT8 *pp;
+
+ L2CAP_TRACE_API ("L2CA_Echo() BDA: %08X%04X",
+ ((p_bd_addr[0] << 24) + (p_bd_addr[1] << 16) + (p_bd_addr[2] << 8) + (p_bd_addr[3])),
+ ((p_bd_addr[4] << 8) + (p_bd_addr[5])));
+
+ /* Fail if we have not established communications with the controller */
+ if (!BTM_IsDeviceUp()) {
+ return (FALSE);
+ }
+
+ if ((memcmp(BT_BD_ANY, p_bd_addr, BD_ADDR_LEN) == 0) && (p_data == NULL)) {
+ /* Only register callback without sending message. */
+ l2cb.p_echo_data_cb = p_callback;
+ return TRUE;
+ }
+
+ /* We assume the upper layer will call this function only when the link is established. */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) {
+ L2CAP_TRACE_ERROR ("L2CA_Echo ERROR : link not established");
+ return FALSE;
+ }
+
+ if (p_lcb->link_state != LST_CONNECTED) {
+ L2CAP_TRACE_ERROR ("L2CA_Echo ERROR : link is not connected");
+ return FALSE;
+ }
+
+ /* Save address of callback */
+ l2cb.p_echo_data_cb = p_callback;
+
+ /* Set the pointer to the beginning of the data */
+ pp = (UINT8 *)(p_data + 1) + p_data->offset;
+ l2cu_adj_id(p_lcb, L2CAP_ADJ_BRCM_ID); /* Make sure not using Broadcom ID */
+ l2cu_send_peer_echo_req (p_lcb, pp, p_data->len);
+
+ return (TRUE);
+
+}
+
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+
+bool L2CA_GetIdentifiers(uint16_t lcid, uint16_t *rcid, uint16_t *handle)
+{
+ tL2C_CCB *control_block = l2cu_find_ccb_by_cid(NULL, lcid);
+ if (!control_block) {
+ return false;
+ }
+
+ if (rcid) {
+ *rcid = control_block->remote_cid;
+ }
+ if (handle) {
+ *handle = control_block->p_lcb->handle;
+ }
+
+ return true;
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetIdleTimeout
+**
+** Description Higher layers call this function to set the idle timeout for
+** a connection, or for all future connections. The "idle timeout"
+** is the amount of time that a connection can remain up with
+** no L2CAP channels on it. A timeout of zero means that the
+** connection will be torn down immediately when the last channel
+** is removed. A timeout of 0xFFFF means no timeout. Values are
+** in seconds.
+**
+** Returns TRUE if command succeeded, FALSE if failed
+**
+** NOTE This timeout takes effect after at least 1 channel has been
+** established and removed. L2CAP maintains its own timer from
+** whan a connection is established till the first channel is
+** set up.
+*******************************************************************************/
+BOOLEAN L2CA_SetIdleTimeout (UINT16 cid, UINT16 timeout, BOOLEAN is_global)
+{
+ tL2C_CCB *p_ccb;
+ tL2C_LCB *p_lcb;
+
+ if (is_global) {
+ l2cb.idle_timeout = timeout;
+ } else {
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_SetIdleTimeout, CID: %d", cid);
+ return (FALSE);
+ }
+
+ p_lcb = p_ccb->p_lcb;
+
+ if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) {
+ p_lcb->idle_timeout = timeout;
+ } else {
+ return (FALSE);
+ }
+ }
+
+ return (TRUE);
+}
+
+
+
+/*******************************************************************************
+**
+** Function L2CA_SetIdleTimeoutByBdAddr
+**
+** Description Higher layers call this function to set the idle timeout for
+** a connection. The "idle timeout" is the amount of time that
+** a connection can remain up with no L2CAP channels on it.
+** A timeout of zero means that the connection will be torn
+** down immediately when the last channel is removed.
+** A timeout of 0xFFFF means no timeout. Values are in seconds.
+** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY,
+** then the idle timeouts for all active l2cap links will be
+** changed.
+**
+** Returns TRUE if command succeeded, FALSE if failed
+**
+** NOTE This timeout applies to all logical channels active on the
+** ACL link.
+*******************************************************************************/
+BOOLEAN L2CA_SetIdleTimeoutByBdAddr(BD_ADDR bd_addr, UINT16 timeout, tBT_TRANSPORT transport)
+{
+ tL2C_LCB *p_lcb;
+ list_node_t *p_node = NULL;
+
+ if (memcmp (BT_BD_ANY, bd_addr, BD_ADDR_LEN)) {
+ p_lcb = l2cu_find_lcb_by_bd_addr( bd_addr, transport);
+ if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) {
+ p_lcb->idle_timeout = timeout;
+
+ if (!p_lcb->ccb_queue.p_first_ccb) {
+ l2cu_no_dynamic_ccbs (p_lcb);
+ }
+ } else {
+ return FALSE;
+ }
+ } else {
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) {
+ p_lcb->idle_timeout = timeout;
+
+ if (!p_lcb->ccb_queue.p_first_ccb) {
+ l2cu_no_dynamic_ccbs (p_lcb);
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+/*******************************************************************************
+**
+** Function L2CA_SetTraceLevel
+**
+** Description This function sets the trace level for L2CAP. If called with
+** a value of 0xFF, it simply reads the current trace level.
+**
+** Returns the new (current) trace level
+**
+*******************************************************************************/
+UINT8 L2CA_SetTraceLevel (UINT8 new_level)
+{
+ if (new_level != 0xFF) {
+ l2cb.l2cap_trace_level = new_level;
+ }
+
+ return (l2cb.l2cap_trace_level);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_SetDesireRole
+**
+** Description This function sets the desire role for L2CAP.
+** If the new role is L2CAP_ROLE_ALLOW_SWITCH, allow switch on
+** HciCreateConnection.
+** If the new role is L2CAP_ROLE_DISALLOW_SWITCH, do not allow switch on
+** HciCreateConnection.
+**
+** If the new role is a valid role (HCI_ROLE_MASTER or HCI_ROLE_SLAVE),
+** the desire role is set to the new value. Otherwise, it is not changed.
+**
+** Returns the new (current) role
+**
+*******************************************************************************/
+UINT8 L2CA_SetDesireRole (UINT8 new_role)
+{
+ L2CAP_TRACE_API ("L2CA_SetDesireRole() new:x%x, disallow_switch:%d",
+ new_role, l2cb.disallow_switch);
+
+ if (L2CAP_ROLE_CHECK_SWITCH != (L2CAP_ROLE_CHECK_SWITCH & new_role)) {
+ /* do not process the allow_switch when both bits are set */
+ if (new_role & L2CAP_ROLE_ALLOW_SWITCH) {
+ l2cb.disallow_switch = FALSE;
+ }
+ if (new_role & L2CAP_ROLE_DISALLOW_SWITCH) {
+ l2cb.disallow_switch = TRUE;
+ }
+ }
+
+ if (new_role == HCI_ROLE_MASTER || new_role == HCI_ROLE_SLAVE) {
+ l2cb.desire_role = new_role;
+ }
+
+ return (l2cb.desire_role);
+}
+
+#if (CLASSIC_BT_INCLUDED == TRUE)
+
+/*******************************************************************************
+**
+** Function L2CA_LocalLoopbackReq
+**
+** Description This function sets up a CID for local loopback
+**
+** Returns CID of 0 if none.
+**
+*******************************************************************************/
+UINT16 L2CA_LocalLoopbackReq (UINT16 psm, UINT16 handle, BD_ADDR p_bd_addr)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_API ("L2CA_LocalLoopbackReq() PSM: %d Handle: 0x%04x", psm, handle);
+
+ /* Fail if we have not established communications with the controller */
+ if (!BTM_IsDeviceUp()) {
+ L2CAP_TRACE_WARNING ("L2CAP loop req - BTU not ready");
+ return (0);
+ }
+
+ /* Fail if the PSM is not registered */
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_conn_req, PSM: %d", psm);
+ return (0);
+ }
+
+ if ((p_lcb = l2cu_allocate_lcb (p_bd_addr, FALSE, BT_TRANSPORT_BR_EDR)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no LCB for L2CA_conn_req");
+ return (0);
+ }
+
+ p_lcb->link_state = LST_CONNECTED;
+ p_lcb->handle = handle;
+
+ /* Allocate a channel control block */
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_conn_req");
+ return (0);
+ }
+
+ /* Save registration info */
+ p_ccb->p_rcb = p_rcb;
+ p_ccb->chnl_state = CST_OPEN;
+ p_ccb->remote_cid = p_ccb->local_cid;
+ p_ccb->config_done = CFG_DONE_MASK;
+
+ /* Return the local CID as our handle */
+ return (p_ccb->local_cid);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetAclPriority
+**
+** Description Sets the transmission priority for a channel.
+** (For initial implementation only two values are valid.
+** L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH).
+**
+** Returns TRUE if a valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_SetAclPriority (BD_ADDR bd_addr, UINT8 priority)
+{
+ L2CAP_TRACE_API ("L2CA_SetAclPriority() bdaddr: %02x%02x%02x%02x%04x, priority:%d",
+ bd_addr[0], bd_addr[1], bd_addr[2],
+ bd_addr[3], (bd_addr[4] << 8) + bd_addr[5], priority);
+
+ return (l2cu_set_acl_priority(bd_addr, priority, FALSE));
+}
+
+/*******************************************************************************
+**
+** Function L2CA_FlowControl
+**
+** Description Higher layers call this function to flow control a channel.
+**
+** data_enabled - TRUE data flows, FALSE data is stopped
+**
+** Returns TRUE if valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_FlowControl (UINT16 cid, BOOLEAN data_enabled)
+{
+ tL2C_CCB *p_ccb;
+ BOOLEAN on_off = !data_enabled;
+
+ L2CAP_TRACE_API ("L2CA_FlowControl(%d) CID: 0x%04x", on_off, cid);
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_FlowControl, CID: 0x%04x data_enabled: %d", cid, data_enabled);
+ return (FALSE);
+ }
+
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) {
+ L2CAP_TRACE_EVENT ("L2CA_FlowControl() invalid mode:%d", p_ccb->peer_cfg.fcr.mode);
+ return (FALSE);
+ }
+ if (p_ccb->fcrb.local_busy != on_off) {
+ p_ccb->fcrb.local_busy = on_off;
+
+ if ( (p_ccb->chnl_state == CST_OPEN) && (!p_ccb->fcrb.wait_ack) ) {
+ if (on_off) {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0);
+ } else {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT);
+ }
+ }
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SendTestSFrame
+**
+** Description Higher layers call this function to send a test S-frame.
+**
+** Returns TRUE if valid Channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_SendTestSFrame (UINT16 cid, UINT8 sup_type, UINT8 back_track)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API ("L2CA_SendTestSFrame() CID: 0x%04x Type: 0x%02x back_track: %u", cid, sup_type, back_track);
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_SendTestSFrame, CID: %d", cid);
+ return (FALSE);
+ }
+
+ if ( (p_ccb->chnl_state != CST_OPEN) || (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) ) {
+ return (FALSE);
+ }
+
+ p_ccb->fcrb.next_seq_expected -= back_track;
+
+ l2c_fcr_send_S_frame (p_ccb, (UINT16)(sup_type & 3), (UINT16)(sup_type & (L2CAP_FCR_P_BIT | L2CAP_FCR_F_BIT)));
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_SetTxPriority
+**
+** Description Sets the transmission priority for a channel.
+**
+** Returns TRUE if a valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_SetTxPriority (UINT16 cid, tL2CAP_CHNL_PRIORITY priority)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API ("L2CA_SetTxPriority() CID: 0x%04x, priority:%d", cid, priority);
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_SetTxPriority, CID: %d", cid);
+ return (FALSE);
+ }
+
+ /* it will update the order of CCB in LCB by priority and update round robin service variables */
+ l2cu_change_pri_ccb (p_ccb, priority);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetChnlDataRate
+**
+** Description Sets the tx/rx data rate for a channel.
+**
+** Returns TRUE if a valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_SetChnlDataRate (UINT16 cid, tL2CAP_CHNL_DATA_RATE tx, tL2CAP_CHNL_DATA_RATE rx)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API ("L2CA_SetChnlDataRate() CID: 0x%04x, tx:%d, rx:%d", cid, tx, rx);
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_SetChnlDataRate, CID: %d", cid);
+ return (FALSE);
+ }
+
+ p_ccb->tx_data_rate = tx;
+ p_ccb->rx_data_rate = rx;
+
+ /* Adjust channel buffer allocation */
+ l2c_link_adjust_chnl_allocation ();
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SetFlushTimeout
+**
+** Description This function set the automatic flush time out in Baseband
+** for ACL-U packets.
+** BdAddr : the remote BD address of ACL link. If it is BT_DB_ANY
+** then the flush time out will be applied to all ACL link.
+** FlushTimeout: flush time out in ms
+** 0x0000 : No automatic flush
+** L2CAP_NO_RETRANSMISSION : No retransmission
+** 0x0002 - 0xFFFE : flush time out, if (flush_tout*8)+3/5)
+** <= HCI_MAX_AUTO_FLUSH_TOUT (in 625us slot).
+** Otherwise, return FALSE.
+** L2CAP_NO_AUTOMATIC_FLUSH : No automatic flush
+**
+** Returns TRUE if command succeeded, FALSE if failed
+**
+** NOTE This flush timeout applies to all logical channels active on the
+** ACL link.
+*******************************************************************************/
+BOOLEAN L2CA_SetFlushTimeout (BD_ADDR bd_addr, UINT16 flush_tout)
+{
+ tL2C_LCB *p_lcb;
+ UINT16 hci_flush_to;
+ UINT32 temp;
+
+ /* no automatic flush (infinite timeout) */
+ if (flush_tout == 0x0000) {
+ hci_flush_to = flush_tout;
+ flush_tout = L2CAP_NO_AUTOMATIC_FLUSH;
+ }
+ /* no retransmission */
+ else if (flush_tout == L2CAP_NO_RETRANSMISSION) {
+ /* not mandatory range for controller */
+ /* Packet is flushed before getting any ACK/NACK */
+ /* To do this, flush timeout should be 1 baseband slot */
+ hci_flush_to = flush_tout;
+ }
+ /* no automatic flush (infinite timeout) */
+ else if (flush_tout == L2CAP_NO_AUTOMATIC_FLUSH) {
+ hci_flush_to = 0x0000;
+ } else {
+ /* convert L2CAP flush_to to 0.625 ms units, with round */
+ temp = (((UINT32)flush_tout * 8) + 3) / 5;
+
+ /* if L2CAP flush_to within range of HCI, set HCI flush timeout */
+ if (temp > HCI_MAX_AUTO_FLUSH_TOUT) {
+ L2CAP_TRACE_WARNING("WARNING L2CA_SetFlushTimeout timeout(0x%x) is out of range", flush_tout);
+ return FALSE;
+ } else {
+ hci_flush_to = (UINT16)temp;
+ }
+ }
+
+ if (memcmp (BT_BD_ANY, bd_addr, BD_ADDR_LEN)) {
+ p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_BR_EDR);
+
+ if ((p_lcb) && (p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) {
+ if (p_lcb->link_flush_tout != flush_tout) {
+ p_lcb->link_flush_tout = flush_tout;
+
+ L2CAP_TRACE_API ("L2CA_SetFlushTimeout 0x%04x ms for bd_addr [...;%02x%02x%02x]",
+ flush_tout, bd_addr[3], bd_addr[4], bd_addr[5]);
+
+ if (!btsnd_hcic_write_auto_flush_tout (p_lcb->handle, hci_flush_to)) {
+ return (FALSE);
+ }
+ }
+ } else {
+ L2CAP_TRACE_WARNING ("WARNING L2CA_SetFlushTimeout No lcb for bd_addr [...;%02x%02x%02x]",
+ bd_addr[3], bd_addr[4], bd_addr[5]);
+ return (FALSE);
+ }
+ } else {
+ list_node_t *p_node = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTED)) {
+ if (p_lcb->link_flush_tout != flush_tout) {
+ p_lcb->link_flush_tout = flush_tout;
+
+ L2CAP_TRACE_API ("L2CA_SetFlushTimeout 0x%04x ms for bd_addr [...;%02x%02x%02x]",
+ flush_tout, p_lcb->remote_bd_addr[3],
+ p_lcb->remote_bd_addr[4], p_lcb->remote_bd_addr[5]);
+
+ if (!btsnd_hcic_write_auto_flush_tout(p_lcb->handle, hci_flush_to)) {
+ return (FALSE);
+ }
+ }
+ }
+ }
+ }
+
+ return (TRUE);
+}
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+
+/*******************************************************************************
+**
+** Function L2CA_GetPeerFeatures
+**
+** Description Get a peers features and fixed channel map
+**
+** Parameters: BD address of the peer
+** Pointers to features and channel mask storage area
+**
+** Return value: TRUE if peer is connected
+**
+*******************************************************************************/
+BOOLEAN L2CA_GetPeerFeatures (BD_ADDR bd_addr, UINT32 *p_ext_feat, UINT8 *p_chnl_mask)
+{
+ tL2C_LCB *p_lcb;
+
+ /* We must already have a link to the remote */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CA_GetPeerFeatures() No BDA: %08x%04x",
+ (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3],
+ (bd_addr[4] << 8) + bd_addr[5]);
+ return (FALSE);
+ }
+
+ L2CAP_TRACE_API ("L2CA_GetPeerFeatures() BDA: %08x%04x ExtFea: 0x%08x Chnl_Mask[0]: 0x%02x",
+ (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3],
+ (bd_addr[4] << 8) + bd_addr[5], p_lcb->peer_ext_fea, p_lcb->peer_chnl_mask[0]);
+
+ *p_ext_feat = p_lcb->peer_ext_fea;
+
+ memcpy (p_chnl_mask, p_lcb->peer_chnl_mask, L2CAP_FIXED_CHNL_ARRAY_SIZE);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_GetBDAddrbyHandle
+**
+** Description Get BD address for the given HCI handle
+**
+** Parameters: HCI handle
+** BD address of the peer
+**
+** Return value: TRUE if found lcb for the given handle, FALSE otherwise
+**
+*******************************************************************************/
+BOOLEAN L2CA_GetBDAddrbyHandle (UINT16 handle, BD_ADDR bd_addr)
+{
+ tL2C_LCB *p_lcb = NULL;
+ BOOLEAN found_dev = FALSE;
+
+ p_lcb = l2cu_find_lcb_by_handle (handle);
+ if (p_lcb) {
+ found_dev = TRUE;
+ memcpy (bd_addr, p_lcb->remote_bd_addr, BD_ADDR_LEN);
+ }
+
+ return found_dev;
+}
+
+#if (CLASSIC_BT_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function L2CA_GetChnlFcrMode
+**
+** Description Get the channel FCR mode
+**
+** Parameters: Local CID
+**
+** Return value: Channel mode
+**
+*******************************************************************************/
+UINT8 L2CA_GetChnlFcrMode (UINT16 lcid)
+{
+ tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid (NULL, lcid);
+
+ if (p_ccb) {
+ L2CAP_TRACE_API ("L2CA_GetChnlFcrMode() returns mode %d", p_ccb->peer_cfg.fcr.mode);
+ return (p_ccb->peer_cfg.fcr.mode);
+ }
+
+ L2CAP_TRACE_API ("L2CA_GetChnlFcrMode() returns mode L2CAP_FCR_BASIC_MODE");
+ return (L2CAP_FCR_BASIC_MODE);
+}
+
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+#if (BLE_L2CAP_COC_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function L2CA_RegisterLECoc
+**
+** Description Other layers call this function to register for L2CAP
+** Connection Oriented Channel.
+**
+** Returns PSM to use or zero if error. Typically, the PSM returned
+** is the same as was passed in, but for an outgoing-only
+** connection to a dynamic PSM, a "virtual" PSM is returned
+** and should be used in the calls to L2CA_ConnectLECocReq()
+** and L2CA_DeregisterLECoc()
+**
+*******************************************************************************/
+UINT16 L2CA_RegisterLECoc(UINT16 psm, tL2CAP_APPL_INFO *p_cb_info)
+{
+ L2CAP_TRACE_API("%s called for LE PSM: 0x%04x", __func__, psm);
+
+ /* Verify that the required callback info has been filled in
+ ** Note: Connection callbacks are required but not checked
+ ** for here because it is possible to be only a client
+ ** or only a server.
+ */
+ if ((!p_cb_info->pL2CA_DataInd_Cb)
+ || (!p_cb_info->pL2CA_DisconnectInd_Cb))
+ {
+ L2CAP_TRACE_ERROR("%s No cb registering BLE PSM: 0x%04x", __func__, psm);
+ return 0;
+ }
+
+ /* Verify PSM is valid */
+ if (!L2C_IS_VALID_LE_PSM(psm))
+ {
+ L2CAP_TRACE_ERROR("%s Invalid BLE PSM value, PSM: 0x%04x", __func__, psm);
+ return 0;
+ }
+
+ tL2C_RCB *p_rcb;
+ UINT16 vpsm = psm;
+
+ /* Check if this is a registration for an outgoing-only connection to */
+ /* a dynamic PSM. If so, allocate a "virtual" PSM for the app to use. */
+ if ((psm >= 0x0080) && (p_cb_info->pL2CA_ConnectInd_Cb == NULL))
+ {
+ for (vpsm = 0x0080; vpsm < 0x0100; vpsm++)
+ {
+ p_rcb = l2cu_find_ble_rcb_by_psm(vpsm);
+ if (p_rcb == NULL) {
+ break;
+ }
+ }
+
+ L2CAP_TRACE_API("%s Real PSM: 0x%04x Virtual PSM: 0x%04x", __func__, psm, vpsm);
+ }
+
+ /* If registration block already there, just overwrite it */
+ p_rcb = l2cu_find_ble_rcb_by_psm(vpsm);
+ if (p_rcb == NULL)
+ {
+ p_rcb = l2cu_allocate_ble_rcb(vpsm);
+ if (p_rcb == NULL)
+ {
+ L2CAP_TRACE_WARNING("%s No BLE RCB available, PSM: 0x%04x vPSM: 0x%04x",
+ __func__, psm, vpsm);
+ return 0;
+ }
+ }
+
+ p_rcb->api = *p_cb_info;
+ p_rcb->real_psm = psm;
+
+ return vpsm;
+}
+
+/*******************************************************************************
+**
+** Function L2CA_DeregisterLECoc
+**
+** Description Other layers call this function to de-register for L2CAP
+** Connection Oriented Channel.
+**
+** Returns void
+**
+*******************************************************************************/
+void L2CA_DeregisterLECoc(UINT16 psm)
+{
+ L2CAP_TRACE_API("%s called for PSM: 0x%04x", __func__, psm);
+
+ tL2C_RCB *p_rcb = l2cu_find_ble_rcb_by_psm(psm);
+ if (p_rcb == NULL)
+ {
+ L2CAP_TRACE_WARNING("%s PSM: 0x%04x not found for deregistration", __func__, psm);
+ return;
+ }
+
+ tL2C_LCB *p_lcb = NULL;
+ list_node_t *p_node = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (!p_lcb->in_use || p_lcb->transport != BT_TRANSPORT_LE) {
+ continue;
+ }
+
+ tL2C_CCB *p_ccb = p_lcb->ccb_queue.p_first_ccb;
+ if ((p_ccb == NULL) || (p_lcb->link_state == LST_DISCONNECTING)) {
+ continue;
+ }
+
+ if (p_ccb->in_use &&
+ (p_ccb->chnl_state == CST_W4_L2CAP_DISCONNECT_RSP ||
+ p_ccb->chnl_state == CST_W4_L2CA_DISCONNECT_RSP)) {
+ continue;
+ }
+
+ if (p_ccb->p_rcb == p_rcb) {
+ l2c_csm_execute(p_ccb, L2CEVT_L2CA_DISCONNECT_REQ, NULL);
+ }
+ }
+
+ l2cu_release_rcb (p_rcb);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_ConnectLECocReq
+**
+** Description Higher layers call this function to create an L2CAP connection.
+** Note that the connection is not established at this time, but
+** connection establishment gets started. The callback function
+** will be invoked when connection establishes or fails.
+**
+** Parameters: PSM: L2CAP PSM for the connection
+** BD address of the peer
+** Local Coc configurations
+
+** Returns the CID of the connection, or 0 if it failed to start
+**
+*******************************************************************************/
+UINT16 L2CA_ConnectLECocReq(UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_LE_CFG_INFO *p_cfg)
+{
+ L2CAP_TRACE_API("%s PSM: 0x%04x BDA: %02x:%02x:%02x:%02x:%02x:%02x", __func__, psm,
+ p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]);
+
+ /* Fail if we have not established communications with the controller */
+ if (!BTM_IsDeviceUp())
+ {
+ L2CAP_TRACE_WARNING("%s BTU not ready", __func__);
+ return 0;
+ }
+
+ /* Fail if the PSM is not registered */
+ tL2C_RCB *p_rcb = l2cu_find_ble_rcb_by_psm(psm);
+ if (p_rcb == NULL)
+ {
+ L2CAP_TRACE_WARNING("%s No BLE RCB, PSM: 0x%04x", __func__, psm);
+ return 0;
+ }
+
+ /* First, see if we already have a le link to the remote */
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_LE);
+ if (p_lcb == NULL)
+ {
+ /* No link. Get an LCB and start link establishment */
+ p_lcb = l2cu_allocate_lcb(p_bd_addr, FALSE, BT_TRANSPORT_LE);
+ if ((p_lcb == NULL)
+ /* currently use BR/EDR for ERTM mode l2cap connection */
+ || (l2cu_create_conn(p_lcb, BT_TRANSPORT_LE) == FALSE) )
+ {
+ L2CAP_TRACE_WARNING("%s conn not started for PSM: 0x%04x p_lcb: 0x%p",
+ __func__, psm, p_lcb);
+ return 0;
+ }
+ }
+
+ /* Allocate a channel control block */
+ tL2C_CCB *p_ccb = l2cu_allocate_ccb(p_lcb, 0);
+ if (p_ccb == NULL)
+ {
+ L2CAP_TRACE_WARNING("%s no CCB, PSM: 0x%04x", __func__, psm);
+ return 0;
+ }
+
+ /* Save registration info */
+ p_ccb->p_rcb = p_rcb;
+
+ /* Save the configuration */
+ if (p_cfg) {
+ memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO));
+ }
+
+ /* If link is up, start the L2CAP connection */
+ if (p_lcb->link_state == LST_CONNECTED)
+ {
+ if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE)
+ {
+ L2CAP_TRACE_DEBUG("%s LE Link is up", __func__);
+ l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL);
+ }
+ }
+
+ /* If link is disconnecting, save link info to retry after disconnect
+ * Possible Race condition when a reconnect occurs
+ * on the channel during a disconnect of link. This
+ * ccb will be automatically retried after link disconnect
+ * arrives
+ */
+ else if (p_lcb->link_state == LST_DISCONNECTING)
+ {
+ L2CAP_TRACE_DEBUG("%s link disconnecting: RETRY LATER", __func__);
+
+ /* Save ccb so it can be started after disconnect is finished */
+ p_lcb->p_pending_ccb = p_ccb;
+ }
+
+ L2CAP_TRACE_API("%s(psm: 0x%04x) returned CID: 0x%04x", __func__, psm, p_ccb->local_cid);
+
+ /* Return the local CID as our handle */
+ return p_ccb->local_cid;
+}
+
+/*******************************************************************************
+**
+** Function L2CA_ConnectLECocRsp
+**
+** Description Higher layers call this function to accept an incoming
+** L2CAP COC connection, for which they had gotten an connect
+** indication callback.
+**
+** Returns TRUE for success, FALSE for failure
+**
+*******************************************************************************/
+BOOLEAN L2CA_ConnectLECocRsp (BD_ADDR p_bd_addr, UINT8 id, UINT16 lcid, UINT16 result,
+ UINT16 status, tL2CAP_LE_CFG_INFO *p_cfg)
+{
+ L2CAP_TRACE_API("%s CID: 0x%04x Result: %d Status: %d BDA: %02x:%02x:%02x:%02x:%02x:%02x",
+ __func__, lcid, result, status,
+ p_bd_addr[0], p_bd_addr[1], p_bd_addr[2], p_bd_addr[3], p_bd_addr[4], p_bd_addr[5]);
+
+
+ /* First, find the link control block */
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_LE);
+ if (p_lcb == NULL)
+ {
+ /* No link. Get an LCB and start link establishment */
+ L2CAP_TRACE_WARNING("%s no LCB", __func__);
+ return FALSE;
+ }
+
+ /* Now, find the channel control block */
+ tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid);
+ if (p_ccb == NULL)
+ {
+ L2CAP_TRACE_WARNING("%s no CCB", __func__);
+ return FALSE;
+ }
+
+ /* The IDs must match */
+ if (p_ccb->remote_id != id)
+ {
+ L2CAP_TRACE_WARNING("%s bad id. Expected: %d Got: %d", __func__, p_ccb->remote_id, id);
+ return FALSE;
+ }
+
+ if (p_cfg) {
+ memcpy(&p_ccb->local_conn_cfg, p_cfg, sizeof(tL2CAP_LE_CFG_INFO));
+ }
+
+ if (result == L2CAP_CONN_OK)
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_RSP, NULL);
+ else
+ {
+ tL2C_CONN_INFO conn_info;
+ memcpy(conn_info.bd_addr, p_bd_addr, BD_ADDR_LEN);
+ conn_info.l2cap_result = result;
+ conn_info.l2cap_status = status;
+ l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_RSP_NEG, &conn_info);
+ }
+
+ return TRUE;
+}
+
+/*******************************************************************************
+**
+** Function L2CA_GetPeerLECocConfig
+**
+** Description Get a peers configuration for LE Connection Oriented Channel.
+**
+** Parameters: local channel id
+** Pointers to peers configuration storage area
+**
+** Return value: TRUE if peer is connected
+**
+*******************************************************************************/
+BOOLEAN L2CA_GetPeerLECocConfig (UINT16 lcid, tL2CAP_LE_CFG_INFO* peer_cfg)
+{
+ L2CAP_TRACE_API ("%s CID: 0x%04x", __func__, lcid);
+
+ tL2C_CCB *p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
+ if (p_ccb == NULL)
+ {
+ L2CAP_TRACE_ERROR("%s No CCB for CID:0x%04x", __func__, lcid);
+ return FALSE;
+ }
+
+ if (peer_cfg != NULL) {
+ memcpy(peer_cfg, &p_ccb->peer_conn_cfg, sizeof(tL2CAP_LE_CFG_INFO));
+ }
+
+ return TRUE;
+}
+#endif // (BLE_L2CAP_COC_INCLUDED == TRUE)
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+/*******************************************************************************
+**
+** Function L2CA_RegisterFixedChannel
+**
+** Description Register a fixed channel.
+**
+** Parameters: Fixed Channel #
+** Channel Callbacks and config
+**
+** Return value: -
+**
+*******************************************************************************/
+BOOLEAN L2CA_RegisterFixedChannel (UINT16 fixed_cid, tL2CAP_FIXED_CHNL_REG *p_freg)
+{
+ L2CAP_TRACE_DEBUG ("L2CA_RegisterFixedChannel() CID: 0x%04x, %p", fixed_cid,p_freg);
+ if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL) ) {
+ L2CAP_TRACE_ERROR ("L2CA_RegisterFixedChannel() Invalid CID: 0x%04x", fixed_cid);
+
+ return (FALSE);
+ }
+
+ l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = *p_freg;
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_ConnectFixedChnl
+**
+** Description Connect an fixed signalling channel to a remote device.
+**
+** Parameters: Fixed CID
+** BD Address of remote
+** BD Address type
+**
+** Return value: TRUE if connection started
+**
+*******************************************************************************/
+BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda, tBLE_ADDR_TYPE bd_addr_type, BOOLEAN is_aux)
+{
+ tL2C_LCB *p_lcb;
+ tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
+
+ L2CAP_TRACE_API ("%s() CID: 0x%04x BDA: %08x%04x", __func__, fixed_cid,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]);
+
+ // Check CID is valid and registered
+ if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL)
+ || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) ) {
+ L2CAP_TRACE_ERROR ("%s() Invalid CID: 0x%04x", __func__, fixed_cid);
+ return (FALSE);
+ }
+
+ // Fail if BT is not yet up
+ if (!BTM_IsDeviceUp()) {
+ L2CAP_TRACE_WARNING ("%s(0x%04x) - BTU not ready", __func__, fixed_cid);
+ return (FALSE);
+ }
+
+#if BLE_INCLUDED == TRUE
+ if (fixed_cid >= L2CAP_ATT_CID && fixed_cid <= L2CAP_SMP_CID) {
+ transport = BT_TRANSPORT_LE;
+ }
+#endif
+
+ tL2C_BLE_FIXED_CHNLS_MASK peer_channel_mask;
+
+ // If we already have a link to the remote, check if it supports that CID
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, transport)) != NULL) {
+ // Fixed channels are mandatory on LE transports so ignore the received
+ // channel mask and use the locally cached LE channel mask.
+
+#if BLE_INCLUDED == TRUE
+ if (transport == BT_TRANSPORT_LE) {
+ peer_channel_mask = l2cb.l2c_ble_fixed_chnls_mask;
+ } else
+#endif
+ {
+ peer_channel_mask = p_lcb->peer_chnl_mask[0];
+ }
+
+ // Check for supported channel
+ if (!(peer_channel_mask & (1 << fixed_cid))) {
+ L2CAP_TRACE_EVENT ("%s() CID:0x%04x BDA: %08x%04x not supported", __func__,
+ fixed_cid, (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5]);
+ return FALSE;
+ }
+
+ // Get a CCB and link the lcb to it
+ if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid,
+ &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) {
+ L2CAP_TRACE_WARNING ("%s(0x%04x) - LCB but no CCB", __func__, fixed_cid);
+ return FALSE;
+ }
+
+ // racing with disconnecting, queue the connection request
+ if (p_lcb->link_state == LST_DISCONNECTING) {
+ L2CAP_TRACE_DEBUG ("%s() - link disconnecting: RETRY LATER", __func__);
+ /* Save ccb so it can be started after disconnect is finished */
+ p_lcb->p_pending_ccb = p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL];
+ return TRUE;
+ }
+
+ (*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)
+ (fixed_cid, p_lcb->remote_bd_addr, TRUE, 0, transport);
+
+ return TRUE;
+ }
+
+ // No link. Get an LCB and start link establishment
+ if ((p_lcb = l2cu_allocate_lcb (rem_bda, FALSE, transport)) == NULL) {
+ L2CAP_TRACE_WARNING ("%s(0x%04x) - no LCB", __func__, fixed_cid);
+ return FALSE;
+ }
+
+ // Get a CCB and link the lcb to it
+ if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid,
+ &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) {
+ p_lcb->disc_reason = L2CAP_CONN_NO_RESOURCES;
+ L2CAP_TRACE_WARNING ("%s(0x%04x) - no CCB", __func__, fixed_cid);
+ l2cu_release_lcb (p_lcb);
+ return FALSE;
+ }
+#if (BLE_INCLUDED == TRUE)
+ p_lcb->is_aux = is_aux;
+ p_lcb->open_addr_type = bd_addr_type;
+#endif
+ if (!l2cu_create_conn(p_lcb, transport)) {
+ L2CAP_TRACE_WARNING ("%s() - create_conn failed", __func__);
+ l2cu_release_lcb (p_lcb);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*******************************************************************************
+**
+** Function L2CA_SendFixedChnlData
+**
+** Description Write data on a fixed channel.
+**
+** Parameters: Fixed CID
+** BD Address of remote
+** Pointer to buffer of type BT_HDR
+**
+** Return value L2CAP_DW_SUCCESS, if data accepted
+** L2CAP_DW_FAILED, if error
+**
+*******************************************************************************/
+UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf)
+{
+ tL2C_LCB *p_lcb;
+ tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
+
+ L2CAP_TRACE_API ("L2CA_SendFixedChnlData() CID: 0x%04x BDA: %08x%04x", fixed_cid,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]);
+
+#if BLE_INCLUDED == TRUE
+ if (fixed_cid >= L2CAP_ATT_CID && fixed_cid <= L2CAP_SMP_CID) {
+ transport = BT_TRANSPORT_LE;
+ }
+#endif
+
+ // Check CID is valid and registered
+ if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL)
+ || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) ) {
+ L2CAP_TRACE_ERROR ("L2CA_SendFixedChnlData() Invalid CID: 0x%04x", fixed_cid);
+ osi_free (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ // Fail if BT is not yet up
+ if (!BTM_IsDeviceUp()) {
+ L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData(0x%04x) - BTU not ready", fixed_cid);
+ osi_free (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ // We need to have a link up
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, transport)) == NULL ||
+ /* if link is disconnecting, also report data sending failure */
+ p_lcb->link_state == LST_DISCONNECTING) {
+ L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData(0x%04x) - no LCB", fixed_cid);
+ osi_free (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ tL2C_BLE_FIXED_CHNLS_MASK peer_channel_mask;
+
+ // Select peer channels mask to use depending on transport
+#if BLE_INCLUDED == TRUE
+ if (transport == BT_TRANSPORT_LE) {
+ peer_channel_mask = l2cb.l2c_ble_fixed_chnls_mask;
+ } else
+#endif
+ {
+ peer_channel_mask = p_lcb->peer_chnl_mask[0];
+ }
+
+ if ((peer_channel_mask & (1 << fixed_cid)) == 0) {
+ L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData() - peer does not support fixed chnl: 0x%04x", fixed_cid);
+ osi_free (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ p_buf->event = 0;
+ p_buf->layer_specific = L2CAP_FLUSHABLE_CH_BASED;
+
+ if (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) {
+ if (!l2cu_initialize_fixed_ccb (p_lcb, fixed_cid, &l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) {
+ L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData() - no CCB for chnl: 0x%4x", fixed_cid);
+ osi_free (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+ }
+
+ // If already congested, do not accept any more packets
+ if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent && fixed_cid != L2CAP_SMP_CID) {
+ L2CAP_TRACE_DEBUG ("L2CAP - CID: 0x%04x cannot send, already congested\
+ xmit_hold_q.count: %u buff_quota: %u", fixed_cid,
+ fixed_queue_length(p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->xmit_hold_q),
+ p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->buff_quota);
+ osi_free(p_buf);
+ return (L2CAP_DW_CONGESTED);
+ }
+
+ l2c_enqueue_peer_data (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL], p_buf);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, NULL);
+
+ // If there is no dynamic CCB on the link, restart the idle timer each time something is sent
+ if (p_lcb->in_use && p_lcb->link_state == LST_CONNECTED && !p_lcb->ccb_queue.p_first_ccb) {
+ l2cu_no_dynamic_ccbs (p_lcb);
+ }
+
+ if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent) {
+ return (L2CAP_DW_CONGESTED);
+ }
+
+ return (L2CAP_DW_SUCCESS);
+}
+
+BOOLEAN L2CA_CheckIsCongest(UINT16 fixed_cid, BD_ADDR addr)
+{
+ tL2C_LCB *p_lcb;
+ p_lcb = l2cu_find_lcb_by_bd_addr(addr, BT_TRANSPORT_LE);
+
+ if (p_lcb != NULL && p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] != NULL) {
+ return p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent;
+ }
+
+ return TRUE;
+}
+
+#if (BLE_INCLUDED == TRUE)
+UINT16 L2CA_GetFreePktBufferNum_LE(void)
+{
+ return l2cb.controller_le_xmit_window;
+}
+UINT16 L2CA_GetCurFreePktBufferNum_LE(UINT16 conn_id)
+{
+ uint16_t num = 0;
+ tl2c_buff_param_t param;
+ param.conn_id = conn_id;
+ param.get_num = &num;
+ l2ble_update_att_acl_pkt_num(L2CA_GET_ATT_NUM, &param);
+ return num;
+}
+#endif
+
+/*******************************************************************************
+**
+** Function L2CA_RemoveFixedChnl
+**
+** Description Remove a fixed channel to a remote device.
+**
+** Parameters: Fixed CID
+** BD Address of remote
+** Idle timeout to use (or 0xFFFF if don't care)
+**
+** Return value: TRUE if channel removed
+**
+*******************************************************************************/
+BOOLEAN L2CA_RemoveFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
+
+ /* Check CID is valid and registered */
+ if ( (fixed_cid < L2CAP_FIRST_FIXED_CHNL) || (fixed_cid > L2CAP_LAST_FIXED_CHNL)
+ || (l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb == NULL) ) {
+ L2CAP_TRACE_ERROR ("L2CA_RemoveFixedChnl() Invalid CID: 0x%04x", fixed_cid);
+ return (FALSE);
+ }
+
+#if BLE_INCLUDED == TRUE
+ if (fixed_cid >= L2CAP_ATT_CID && fixed_cid <= L2CAP_SMP_CID) {
+ transport = BT_TRANSPORT_LE;
+ }
+#endif
+
+ /* Is a fixed channel connected to the remote BDA ?*/
+ p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, transport);
+
+ if ( ((p_lcb) == NULL) || (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) ) {
+ L2CAP_TRACE_DEBUG ("L2CA_RemoveFixedChnl() CID: 0x%04x BDA: %08x%04x not connected", fixed_cid,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]);
+ return (FALSE);
+ }
+
+ L2CAP_TRACE_API ("L2CA_RemoveFixedChnl() CID: 0x%04x BDA: %08x%04x", fixed_cid,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]);
+
+ /* Release the CCB, starting an inactivity timeout on the LCB if no other CCBs exist */
+ p_ccb = p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL];
+
+ p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = NULL;
+ p_lcb->disc_reason = HCI_ERR_CONN_CAUSE_LOCAL_HOST;
+
+#if BLE_INCLUDED == TRUE
+ // Retain the link for a few more seconds after SMP pairing is done, since the Android
+ // platform always does service discovery after pairing is complete. This will avoid
+ // the link down (pairing is complete) and an immediate re-connection for service
+ // discovery.
+ // Some devices do not do auto advertising when link is dropped, thus fail the second
+ // connection and service discovery.
+ if ((fixed_cid == L2CAP_ATT_CID ) && !p_lcb->ccb_queue.p_first_ccb) {
+ p_lcb->idle_timeout = 0;
+ }
+#endif
+
+ l2cu_release_ccb (p_ccb);
+
+ return (TRUE);
+}
+
+#if BLE_INCLUDED == TRUE
+BOOLEAN L2CA_BleDisconnect (BD_ADDR rem_bda)
+{
+ tL2C_LCB *p_lcb;
+ tGATT_TCB *p_tcb;
+
+ p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_LE);
+ if (p_lcb == NULL) {
+ return FALSE;
+ }
+
+ if (p_lcb->link_state != LST_CONNECTED) {
+ return FALSE;
+ }
+
+ p_lcb->disc_reason = HCI_ERR_CONN_CAUSE_LOCAL_HOST;
+ p_lcb->link_state = LST_DISCONNECTING;
+ btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER);
+
+ p_tcb = gatt_find_tcb_by_addr(rem_bda, BT_TRANSPORT_LE);
+ if (p_tcb == NULL) {
+ return FALSE;
+ }
+
+ gatt_set_ch_state(p_tcb, GATT_CH_CLOSING);
+
+ return TRUE;
+}
+#endif
+
+/*******************************************************************************
+**
+** Function L2CA_SetFixedChannelTout
+**
+** Description Higher layers call this function to set the idle timeout for
+** a fixed channel. The "idle timeout" is the amount of time that
+** a connection can remain up with no L2CAP channels on it.
+** A timeout of zero means that the connection will be torn
+** down immediately when the last channel is removed.
+** A timeout of 0xFFFF means no timeout. Values are in seconds.
+** A bd_addr is the remote BD address. If bd_addr = BT_BD_ANY,
+** then the idle timeouts for all active l2cap links will be
+** changed.
+**
+** Returns TRUE if command succeeded, FALSE if failed
+**
+*******************************************************************************/
+BOOLEAN L2CA_SetFixedChannelTout (BD_ADDR rem_bda, UINT16 fixed_cid, UINT16 idle_tout)
+{
+ tL2C_LCB *p_lcb;
+ tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
+
+#if BLE_INCLUDED == TRUE
+ if (fixed_cid >= L2CAP_ATT_CID && fixed_cid <= L2CAP_SMP_CID) {
+ transport = BT_TRANSPORT_LE;
+ }
+#endif
+ if (fixed_cid<L2CAP_FIRST_FIXED_CHNL) {
+ return (FALSE);
+ }
+
+ /* Is a fixed channel connected to the remote BDA ?*/
+ p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, transport);
+ if ( ((p_lcb) == NULL) || (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) ) {
+ L2CAP_TRACE_WARNING ("L2CA_SetFixedChannelTout() CID: 0x%04x BDA: %08x%04x not connected", fixed_cid,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]);
+ return (FALSE);
+ }
+
+ p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->fixed_chnl_idle_tout = idle_tout;
+
+ if (p_lcb->in_use && p_lcb->link_state == LST_CONNECTED && !p_lcb->ccb_queue.p_first_ccb) {
+ /* If there are no dynamic CCBs, (re)start the idle timer in case we changed it */
+ l2cu_no_dynamic_ccbs (p_lcb);
+ }
+
+ return TRUE;
+}
+
+#endif /* #if (L2CAP_NUM_FIXED_CHNLS > 0) */
+
+#if (CLASSIC_BT_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function L2CA_GetCurrentConfig
+**
+** Description This function returns configurations of L2CAP channel
+** pp_our_cfg : pointer of our saved configuration options
+** p_our_cfg_bits : valid config in bitmap
+** pp_peer_cfg: pointer of peer's saved configuration options
+** p_peer_cfg_bits : valid config in bitmap
+**
+** Returns TRUE if successful
+**
+*******************************************************************************/
+BOOLEAN L2CA_GetCurrentConfig (UINT16 lcid,
+ tL2CAP_CFG_INFO **pp_our_cfg, tL2CAP_CH_CFG_BITS *p_our_cfg_bits,
+ tL2CAP_CFG_INFO **pp_peer_cfg, tL2CAP_CH_CFG_BITS *p_peer_cfg_bits)
+{
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API ("L2CA_GetCurrentConfig() CID: 0x%04x", lcid);
+
+ p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
+
+ if (p_ccb) {
+ *pp_our_cfg = &(p_ccb->our_cfg);
+
+ /* convert valid config items into bitmap */
+ *p_our_cfg_bits = 0;
+ if (p_ccb->our_cfg.mtu_present) {
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_MTU;
+ }
+ if (p_ccb->our_cfg.qos_present) {
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_QOS;
+ }
+ if (p_ccb->our_cfg.flush_to_present) {
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FLUSH_TO;
+ }
+ if (p_ccb->our_cfg.fcr_present) {
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCR;
+ }
+ if (p_ccb->our_cfg.fcs_present) {
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_FCS;
+ }
+ if (p_ccb->our_cfg.ext_flow_spec_present) {
+ *p_our_cfg_bits |= L2CAP_CH_CFG_MASK_EXT_FLOW_SPEC;
+ }
+
+ *pp_peer_cfg = &(p_ccb->peer_cfg);
+ *p_peer_cfg_bits = p_ccb->peer_cfg_bits;
+
+ return TRUE;
+ } else {
+ L2CAP_TRACE_ERROR ("No CCB for CID:0x%04x", lcid);
+ return FALSE;
+ }
+}
+
+/*******************************************************************************
+**
+** Function L2CA_RegForNoCPEvt
+**
+** Description Register callback for Number of Completed Packets event.
+**
+** Input Param p_cb - callback for Number of completed packets event
+** p_bda - BT address of remote device
+**
+** Returns TRUE if registered OK, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_RegForNoCPEvt(tL2CA_NOCP_CB *p_cb, BD_ADDR p_bda)
+{
+ tL2C_LCB *p_lcb;
+
+ /* Find the link that is associated with this remote bdaddr */
+ p_lcb = l2cu_find_lcb_by_bd_addr (p_bda, BT_TRANSPORT_BR_EDR);
+
+ /* If no link for this handle, nothing to do. */
+ if (!p_lcb) {
+ return FALSE;
+ }
+
+ p_lcb->p_nocp_cb = p_cb;
+
+ return TRUE;
+}
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+/*******************************************************************************
+**
+** Function L2CA_DataWrite
+**
+** Description Higher layers call this function to write data.
+**
+** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE
+** L2CAP_DW_CONGESTED, if data accepted and the channel is congested
+** L2CAP_DW_FAILED, if error
+**
+*******************************************************************************/
+#if (CLASSIC_BT_INCLUDED == TRUE)
+UINT8 L2CA_DataWrite (UINT16 cid, BT_HDR *p_data)
+{
+ L2CAP_TRACE_API ("L2CA_DataWrite() CID: 0x%04x Len: %d", cid, p_data->len);
+ return l2c_data_write (cid, p_data, L2CAP_FLUSHABLE_CH_BASED);
+}
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+/*******************************************************************************
+**
+** Function l2cap_bqb_write_data
+**
+** Description Call L2CA_DataWrite and write I-Frame data for BQB test.
+**
+** Returns None
+**
+*******************************************************************************/
+#if (BT_CLASSIC_BQB_INCLUDED == TRUE)
+void l2cap_bqb_write_data(UINT16 cid)
+{
+ BT_HDR *p_buf;
+ uint8_t *p;
+
+ if ((p_buf = (BT_HDR *)osi_malloc(SDP_DATA_BUF_SIZE)) != NULL) {
+ p_buf->len = 30;
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+ for(int i = 0 ; i < 10; i++) {
+ UINT8_TO_BE_STREAM(p, 0)
+ }
+ L2CA_DataWrite(cid, p_buf);
+ }
+}
+#endif /* BT_CLASSIC_BQB_INCLUDED */
+
+/*******************************************************************************
+**
+** Function L2CA_SetChnlFlushability
+**
+** Description Higher layers call this function to set a channels
+** flushability flags
+**
+** Returns TRUE if CID found, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_SetChnlFlushability (UINT16 cid, BOOLEAN is_flushable)
+{
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+
+ tL2C_CCB *p_ccb;
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_SetChnlFlushability, CID: %d", cid);
+ return (FALSE);
+ }
+
+ p_ccb->is_flushable = is_flushable;
+
+ L2CAP_TRACE_API ("L2CA_SetChnlFlushability() CID: 0x%04x is_flushable: %d", cid, is_flushable);
+
+#endif
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_DataWriteEx
+**
+** Description Higher layers call this function to write data with extended
+** flags.
+** flags : L2CAP_FLUSHABLE_CH_BASED
+** L2CAP_FLUSHABLE_PKT
+** L2CAP_NON_FLUSHABLE_PKT
+**
+** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE
+** L2CAP_DW_CONGESTED, if data accepted and the channel is congested
+** L2CAP_DW_FAILED, if error
+**
+*******************************************************************************/
+#if (CLASSIC_BT_INCLUDED == TRUE)
+UINT8 L2CA_DataWriteEx (UINT16 cid, BT_HDR *p_data, UINT16 flags)
+{
+ L2CAP_TRACE_API ("L2CA_DataWriteEx() CID: 0x%04x Len: %d Flags:0x%04X",
+ cid, p_data->len, flags);
+ return l2c_data_write (cid, p_data, flags);
+}
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+/*******************************************************************************
+**
+** Function L2CA_FlushChannel
+**
+** Description This function flushes none, some or all buffers queued up
+** for xmission for a particular CID. If called with
+** L2CAP_FLUSH_CHANS_GET (0), it simply returns the number
+** of buffers queued for that CID L2CAP_FLUSH_CHANS_ALL (0xffff)
+** flushes all buffers. All other values specifies the maximum
+** buffers to flush.
+**
+** Returns Number of buffers left queued for that CID
+**
+*******************************************************************************/
+UINT16 L2CA_FlushChannel (UINT16 lcid, UINT16 num_to_flush)
+{
+ tL2C_CCB *p_ccb;
+ tL2C_LCB *p_lcb;
+ UINT16 num_left = 0,
+ num_flushed1 = 0,
+ num_flushed2 = 0;
+
+ p_ccb = l2cu_find_ccb_by_cid(NULL, lcid);
+
+ if ( !p_ccb || ((p_lcb = p_ccb->p_lcb) == NULL) ) {
+ L2CAP_TRACE_WARNING ("L2CA_FlushChannel() abnormally returning 0 CID: 0x%04x", lcid);
+ return (0);
+ }
+
+ if (num_to_flush != L2CAP_FLUSH_CHANS_GET) {
+ L2CAP_TRACE_API ("L2CA_FlushChannel (FLUSH) CID: 0x%04x NumToFlush: %d QC: %u pFirst: %p",
+ lcid, num_to_flush,
+ fixed_queue_length(p_ccb->xmit_hold_q),
+ fixed_queue_try_peek_first(p_ccb->xmit_hold_q));
+ } else {
+ L2CAP_TRACE_API ("L2CA_FlushChannel (QUERY) CID: 0x%04x", lcid);
+ }
+
+ /* Cannot flush eRTM buffers once they have a sequence number */
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) {
+#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE
+ if (num_to_flush != L2CAP_FLUSH_CHANS_GET) {
+ /* If the controller supports enhanced flush, flush the data queued at the controller */
+ if ( (HCI_NON_FLUSHABLE_PB_SUPPORTED(BTM_ReadLocalFeatures ()))
+ && (BTM_GetNumScoLinks() == 0) ) {
+ if ( l2cb.is_flush_active == FALSE ) {
+ l2cb.is_flush_active = TRUE;
+
+ /* The only packet type defined - 0 - Automatically-Flushable Only */
+ btsnd_hcic_enhanced_flush (p_lcb->handle, 0);
+ }
+ }
+ }
+#endif
+
+ // Iterate though list and flush the amount requested from
+ // the transmit data queue that satisfy the layer and event conditions.
+ for (const list_node_t *node = list_begin(p_lcb->link_xmit_data_q);
+ (num_to_flush > 0) && node != list_end(p_lcb->link_xmit_data_q);) {
+ BT_HDR *p_buf = (BT_HDR *)list_node(node);
+ node = list_next(node);
+ if ((p_buf->layer_specific == 0) && (p_buf->event == lcid)) {
+ num_to_flush--;
+ num_flushed1++;
+
+ list_remove(p_lcb->link_xmit_data_q, p_buf);
+ osi_free(p_buf);
+ }
+ }
+ }
+
+ /* If needed, flush buffers in the CCB xmit hold queue */
+ while ( (num_to_flush != 0) && (!fixed_queue_is_empty(p_ccb->xmit_hold_q))) {
+ BT_HDR *p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0);
+ if (p_buf) {
+ osi_free (p_buf);
+ }
+ num_to_flush--;
+ num_flushed2++;
+ }
+
+ /* If app needs to track all packets, call him */
+ if ( (p_ccb->p_rcb) && (p_ccb->p_rcb->api.pL2CA_TxComplete_Cb) && (num_flushed2) ) {
+ (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, num_flushed2);
+ }
+
+ /* Now count how many are left */
+ for (const list_node_t *node = list_begin(p_lcb->link_xmit_data_q);
+ node != list_end(p_lcb->link_xmit_data_q);
+ node = list_next(node)) {
+
+ BT_HDR *p_buf = (BT_HDR *)list_node(node);
+ if (p_buf->event == lcid) {
+ num_left++;
+ }
+ }
+
+ /* Add in the number in the CCB xmit queue */
+ num_left += fixed_queue_length(p_ccb->xmit_hold_q);
+
+ /* Return the local number of buffers left for the CID */
+ L2CAP_TRACE_DEBUG ("L2CA_FlushChannel() flushed: %u + %u, num_left: %u", num_flushed1, num_flushed2, num_left);
+
+ /* If we were congested, and now we are not, tell the app */
+ l2cu_check_channel_congestion (p_ccb);
+
+ return (num_left);
+}
+
+/******************************************************************************
+**
+** Function update_acl_pkt_num
+**
+** Description Update the number of att acl packets to be sent in xmit_hold_q.
+**
+** Returns None
+**
+*******************************************************************************/
+#if BLE_INCLUDED == TRUE
+void l2ble_update_att_acl_pkt_num(UINT8 type, tl2c_buff_param_t *param)
+{
+ static SemaphoreHandle_t buff_semaphore = NULL ;
+ static INT16 btc_buf;
+ static INT16 btu_buf;
+
+ if(buff_semaphore == NULL && type != L2CA_BUFF_INI){
+ L2CAP_TRACE_ERROR("%s buff_semaphore not init", __func__);
+ return;
+ }
+ switch (type)
+ {
+ case L2CA_ADD_BTC_NUM:{
+ xSemaphoreTake(buff_semaphore, portMAX_DELAY);
+ btc_buf ++;
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+ case L2CA_DECREASE_BTC_NUM:{
+ xSemaphoreTake(buff_semaphore, portMAX_DELAY);
+ btc_buf --;
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+ case L2CA_ADD_BTU_NUM:{
+ xSemaphoreTake(buff_semaphore, portMAX_DELAY);
+ btu_buf ++;
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+ case L2CA_DECREASE_BTU_NUM:{
+ xSemaphoreTake(buff_semaphore, portMAX_DELAY);
+ btu_buf --;
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+ case L2CA_GET_ATT_NUM:{
+ xSemaphoreTake(buff_semaphore, portMAX_DELAY);
+ INT16 att_acl_pkt_num = 0;
+ INT16 att_max_num = 0;
+ *(param->get_num) = 0;
+ UINT8 tcb_idx = param->conn_id;
+ tGATT_TCB * p_tcb = gatt_get_tcb_by_idx(tcb_idx);
+ if (p_tcb == NULL){
+ L2CAP_TRACE_ERROR("%s not found p_tcb", __func__);
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+
+ if (!gatt_check_connection_state_by_tcb(p_tcb)) {
+ L2CAP_TRACE_ERROR("connection not established\n");
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+
+ tL2C_LCB * p_lcb = l2cu_find_lcb_by_bd_addr (p_tcb->peer_bda, BT_TRANSPORT_LE);
+ if (p_lcb == NULL){
+ L2CAP_TRACE_ERROR("%s not found p_lcb", __func__);
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+
+ tL2C_CCB *p_ccb = p_lcb->p_fixed_ccbs[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL];
+ if(p_ccb == NULL) {
+ L2CAP_TRACE_ERROR("%s not found p_ccb", __func__);
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+
+ fixed_queue_t * queue = p_ccb->xmit_hold_q;
+ att_max_num = MIN(p_lcb->link_xmit_quota, L2CAP_CACHE_ATT_ACL_NUM);
+ if (queue == NULL){
+ L2CAP_TRACE_ERROR("%s not found queue", __func__);
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+ att_acl_pkt_num = fixed_queue_length(queue);
+ if(att_acl_pkt_num < att_max_num){
+ if(btc_buf + btu_buf < att_max_num - att_acl_pkt_num){
+ *(param->get_num) = att_max_num - att_acl_pkt_num - (btc_buf + btu_buf);
+ }
+ }
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+ case L2CA_BUFF_INI:{
+ btc_buf = 0;
+ btu_buf = 0;
+ buff_semaphore = xSemaphoreCreateBinary();
+ if (buff_semaphore == NULL) {
+ L2CAP_TRACE_ERROR("%s NO MEMORY", __func__);
+ break;
+ }
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+ case L2CA_BUFF_DEINIT:{
+ xSemaphoreTake(buff_semaphore, portMAX_DELAY);
+ btc_buf = 0;
+ btu_buf = 0;
+ xSemaphoreGive(buff_semaphore);
+ vSemaphoreDelete(buff_semaphore);
+ buff_semaphore = NULL;
+ break;
+ }
+ case L2CA_BUFF_FREE:{
+ xSemaphoreTake(buff_semaphore, portMAX_DELAY);
+ // Do nothing
+ xSemaphoreGive(buff_semaphore);
+ break;
+ }
+ default:
+ break;
+ }
+}
+#endif
diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_ble.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_ble.c
new file mode 100644
index 00000000..4e6c8534
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_ble.c
@@ -0,0 +1,1707 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-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 functions relating to BLE management.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "common/bt_target.h"
+//#include "bt_utils.h"
+#include "stack/l2cdefs.h"
+#include "l2c_int.h"
+#include "stack/btu.h"
+#include "btm_int.h"
+#include "stack/hcimsgs.h"
+#include "device/controller.h"
+
+#if (BLE_INCLUDED == TRUE)
+
+#if (BLE_50_FEATURE_SUPPORT == TRUE)
+#define EXT_CONN_INT_DEF_1M MAX(((MAX_ACL_CONNECTIONS + 1) * 4), 12)
+#define EXT_CONN_INT_DEF_2M MAX(((MAX_ACL_CONNECTIONS + 1) * 2), 12)
+#define EXT_CONN_INT_DEF_CODED (320) // 306-> 362Kbps
+
+const static tHCI_ExtConnParams ext_conn_params_1m_phy = {
+ .scan_interval = 0x40,
+ .scan_window = 0x40,
+ .conn_interval_min = EXT_CONN_INT_DEF_1M,
+ .conn_interval_max = EXT_CONN_INT_DEF_1M,
+ .conn_latency = 0,
+ .sup_timeout = 600,
+ .min_ce_len = 0,
+ .max_ce_len = 0,
+};
+const static tHCI_ExtConnParams ext_conn_params_2m_phy = {
+ .scan_interval = 0x40,
+ .scan_window = 0x40,
+ .conn_interval_min = EXT_CONN_INT_DEF_2M,
+ .conn_interval_max = EXT_CONN_INT_DEF_2M,
+ .conn_latency = 0,
+ .sup_timeout = 600,
+ .min_ce_len = 0,
+ .max_ce_len = 0,
+};
+const static tHCI_ExtConnParams ext_conn_params_coded_phy = {
+ .scan_interval = 0x40,
+ .scan_window = 0x40,
+ .conn_interval_min = EXT_CONN_INT_DEF_CODED,
+ .conn_interval_max = EXT_CONN_INT_DEF_CODED,
+ .conn_latency = 0,
+ .sup_timeout = 600,
+ .min_ce_len = 0,
+ .max_ce_len = 0,
+};
+#define BLE_PHY_NO_PREF 0
+#define BLE_PHY_PREF_MASK ((1 << 2) | (1 << 1) | (1 << 0))
+
+#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
+
+static BOOLEAN l2cble_start_conn_update (tL2C_LCB *p_lcb);
+extern int64_t esp_system_get_time(void);
+
+/*******************************************************************************
+**
+** Function L2CA_CancelBleConnectReq
+**
+** Description Cancel a pending connection attempt to a BLE device.
+**
+** Parameters: BD Address of remote
+**
+** Return value: TRUE if connection was cancelled
+**
+*******************************************************************************/
+BOOLEAN L2CA_CancelBleConnectReq (BD_ADDR rem_bda)
+{
+ tL2C_LCB *p_lcb;
+
+ /* There can be only one BLE connection request outstanding at a time */
+ if (btm_ble_get_conn_st() == BLE_CONN_IDLE) {
+ L2CAP_TRACE_WARNING ("L2CA_CancelBleConnectReq - no connection pending");
+ return (FALSE);
+ }
+
+ if (memcmp (rem_bda, l2cb.ble_connecting_bda, BD_ADDR_LEN)) {
+ L2CAP_TRACE_WARNING ("L2CA_CancelBleConnectReq - different BDA Connecting: %08x%04x Cancel: %08x%04x",
+ (l2cb.ble_connecting_bda[0] << 24) + (l2cb.ble_connecting_bda[1] << 16) + (l2cb.ble_connecting_bda[2] << 8) + l2cb.ble_connecting_bda[3],
+ (l2cb.ble_connecting_bda[4] << 8) + l2cb.ble_connecting_bda[5],
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3], (rem_bda[4] << 8) + rem_bda[5]);
+
+ return (FALSE);
+ }
+
+ if (btsnd_hcic_ble_create_conn_cancel()) {
+ p_lcb = l2cu_find_lcb_by_bd_addr(rem_bda, BT_TRANSPORT_LE);
+ /* Do not remove lcb if an LE link is already up as a peripheral */
+ if (p_lcb != NULL &&
+ !(p_lcb->link_role == HCI_ROLE_SLAVE && BTM_ACL_IS_CONNECTED(rem_bda))) {
+ p_lcb->disc_reason = L2CAP_CONN_CANCEL;
+ l2cu_release_lcb (p_lcb);
+ }
+ /* update state to be cancel, wait for connection cancel complete */
+ btm_ble_set_conn_st (BLE_CONN_CANCEL);
+
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UpdateBleConnParams
+**
+** Description Update BLE connection parameters.
+**
+** Parameters: BD Address of remote
+**
+** Return value: TRUE if update started
+**
+*******************************************************************************/
+BOOLEAN L2CA_UpdateBleConnParams (BD_ADDR rem_bda, UINT16 min_int, UINT16 max_int,
+ UINT16 latency, UINT16 timeout)
+{
+ tL2C_LCB *p_lcb;
+ tACL_CONN *p_acl_cb = btm_bda_to_acl(rem_bda, BT_TRANSPORT_LE);
+ UINT8 status = HCI_SUCCESS;
+ BOOLEAN need_cb = false;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_LE);
+
+ /* If we don't have one, create one and accept the connection. */
+ if (!p_lcb || !p_acl_cb) {
+ L2CAP_TRACE_WARNING ("L2CA_UpdateBleConnParams - unknown BD_ADDR %08x%04x",
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5]);
+ return (FALSE);
+ }
+
+ if (p_lcb->transport != BT_TRANSPORT_LE) {
+ L2CAP_TRACE_WARNING ("L2CA_UpdateBleConnParams - BD_ADDR %08x%04x not LE",
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5]);
+ return (FALSE);
+ }
+
+ /* Check whether the request conn params is already set */
+ if ((max_int == p_lcb->current_used_conn_interval) && (latency == p_lcb->current_used_conn_latency) &&
+ (timeout == p_lcb->current_used_conn_timeout)) {
+ status = HCI_SUCCESS;
+ need_cb = true;
+ L2CAP_TRACE_WARNING("%s connection parameter already set", __func__);
+ }
+
+ if (p_lcb->conn_update_mask & L2C_BLE_UPDATE_PARAM_FULL){
+ status = HCI_ERR_ILLEGAL_COMMAND;
+ need_cb = true;
+ L2CAP_TRACE_ERROR("There are two connection parameter requests that are being updated, please try later ");
+ }
+
+ if ((need_cb == TRUE) && (conn_param_update_cb.update_conn_param_cb != NULL)) {
+ tBTM_LE_UPDATE_CONN_PRAMS update_param;
+ update_param.max_conn_int = max_int;
+ update_param.min_conn_int = min_int;
+ update_param.conn_int = p_lcb->current_used_conn_interval;
+ update_param.slave_latency = p_lcb->current_used_conn_latency;
+ update_param.supervision_tout = p_lcb->current_used_conn_timeout;
+ (conn_param_update_cb.update_conn_param_cb)(status, p_lcb->remote_bd_addr, &update_param);
+ return (status == HCI_SUCCESS);
+ }
+
+ p_lcb->waiting_update_conn_min_interval = min_int;
+ p_lcb->waiting_update_conn_max_interval = max_int;
+ p_lcb->waiting_update_conn_latency = latency;
+ p_lcb->waiting_update_conn_timeout = timeout;
+
+ p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM;
+
+ if(l2cble_start_conn_update(p_lcb) == TRUE) {
+ UINT32 time = CalConnectParamTimeout(p_lcb);
+ btu_start_timer(&p_lcb->upda_con_timer, BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS, time);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_EnableUpdateBleConnParams
+**
+** Description Enable or disable update based on the request from the peer
+**
+** Parameters: BD Address of remote
+**
+** Return value: TRUE if update started
+**
+*******************************************************************************/
+BOOLEAN L2CA_EnableUpdateBleConnParams (BD_ADDR rem_bda, BOOLEAN enable)
+{
+ tL2C_LCB *p_lcb;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_LE);
+
+ if (!p_lcb) {
+ L2CAP_TRACE_WARNING ("L2CA_EnableUpdateBleConnParams - unknown BD_ADDR %08x%04x",
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5]);
+ return (FALSE);
+ }
+
+ L2CAP_TRACE_API ("%s - BD_ADDR %08x%04x enable %d current upd state 0x%02x", __FUNCTION__,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5], enable, p_lcb->conn_update_mask);
+
+ if (p_lcb->transport != BT_TRANSPORT_LE) {
+ L2CAP_TRACE_WARNING ("%s - BD_ADDR %08x%04x not LE (link role %d)", __FUNCTION__,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5], p_lcb->link_role);
+ return (FALSE);
+ }
+
+ if (p_lcb->current_used_conn_interval <= BTM_BLE_CONN_INT_MAX_DEF && (p_lcb->conn_update_mask & L2C_BLE_CONN_UPDATE_DISABLE) == 0){
+ return (FALSE);
+ }
+ bool is_disable = (p_lcb->conn_update_mask & L2C_BLE_CONN_UPDATE_DISABLE);
+ if(l2cu_plcb_active_count() >1 && !(enable && is_disable)) {
+ return FALSE;
+ }
+
+ if (enable) {
+ p_lcb->conn_update_mask &= ~L2C_BLE_CONN_UPDATE_DISABLE;
+ } else {
+ p_lcb->conn_update_mask |= L2C_BLE_CONN_UPDATE_DISABLE;
+ }
+
+ if (l2cble_start_conn_update(p_lcb) == TRUE) {
+ UINT32 time = CalConnectParamTimeout(p_lcb);
+ btu_start_timer(&p_lcb->upda_con_timer, BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS, time);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function L2CA_GetBleConnRole
+**
+** Description This function returns the connection role.
+**
+** Returns link role.
+**
+*******************************************************************************/
+UINT8 L2CA_GetBleConnRole (BD_ADDR bd_addr)
+{
+ UINT8 role = HCI_ROLE_UNKNOWN;
+
+ tL2C_LCB *p_lcb;
+
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_LE)) != NULL) {
+ role = p_lcb->link_role;
+ }
+
+ return role;
+}
+
+/*******************************************************************************
+**
+** Function l2cble_notify_le_connection
+**
+** Description This function notifiy the l2cap connection to the app layer
+**
+** Returns none
+**
+*******************************************************************************/
+void l2cble_notify_le_connection (BD_ADDR bda)
+{
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (bda, BT_TRANSPORT_LE);
+ tACL_CONN *p_acl = btm_bda_to_acl(bda, BT_TRANSPORT_LE) ;
+
+ if (p_lcb != NULL && p_acl != NULL && p_lcb->link_state != LST_CONNECTED) {
+
+ if(p_acl->link_role == HCI_ROLE_SLAVE) {
+ //clear p_cb->state, controller will stop adv when ble connected.
+ tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var;
+ if(p_cb) {
+ p_cb->adv_mode = BTM_BLE_ADV_DISABLE;
+ p_cb->state &= ~BTM_BLE_ADVERTISING;
+ }
+ }
+ /* update link status */
+ btm_establish_continue(p_acl);
+ /* update l2cap link status and send callback */
+ p_lcb->link_state = LST_CONNECTED;
+ l2cu_process_fixed_chnl_resp (p_lcb);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cble_scanner_conn_comp
+**
+** Description This function is called when an HCI Connection Complete
+** event is received while we are a scanner (so we are master).
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_scanner_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type,
+ UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout)
+{
+ tL2C_LCB *p_lcb;
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bda);
+
+ L2CAP_TRACE_DEBUG ("l2cble_scanner_conn_comp: HANDLE=%d addr_type=%d conn_interval=%d slave_latency=%d supervision_tout=%d",
+ handle, type, conn_interval, conn_latency, conn_timeout);
+
+ l2cb.is_ble_connecting = FALSE;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (bda, BT_TRANSPORT_LE);
+
+ /* If we don't have one, create one. this is auto connection complete. */
+ if (!p_lcb) {
+ p_lcb = l2cu_allocate_lcb (bda, FALSE, BT_TRANSPORT_LE);
+ if (!p_lcb) {
+#if (SMP_INCLUDED == TRUE)
+ btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION);
+ L2CAP_TRACE_ERROR ("l2cble_scanner_conn_comp - failed to allocate LCB");
+#endif ///SMP_INCLUDED == TRUE
+ return;
+ } else {
+ if (!l2cu_initialize_fixed_ccb (p_lcb, L2CAP_ATT_CID, &l2cb.fixed_reg[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) {
+#if (SMP_INCLUDED == TRUE)
+ btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION);
+ L2CAP_TRACE_WARNING ("l2cble_scanner_conn_comp - LCB but no CCB");
+#endif ///SMP_INCLUDED == TRUE
+ return ;
+ }
+ }
+ } else if (p_lcb->link_state != LST_CONNECTING) {
+ L2CAP_TRACE_ERROR ("L2CAP got BLE scanner conn_comp in bad state: %d", p_lcb->link_state);
+ return;
+ }
+ btu_stop_timer(&p_lcb->timer_entry);
+
+ /* Save the handle */
+ p_lcb->handle = handle;
+
+ /* Connected OK. Change state to connected, we were scanning so we are master */
+ p_lcb->link_role = HCI_ROLE_MASTER;
+ p_lcb->transport = BT_TRANSPORT_LE;
+
+ /* update link parameter, set slave link as non-spec default upon link up */
+ p_lcb->waiting_update_conn_min_interval = p_lcb->waiting_update_conn_max_interval = p_lcb->current_used_conn_interval = conn_interval;
+ p_lcb->waiting_update_conn_timeout = p_lcb->current_used_conn_timeout = conn_timeout;
+ p_lcb->waiting_update_conn_latency = p_lcb->current_used_conn_latency = conn_latency;
+ p_lcb->conn_update_mask = L2C_BLE_NOT_DEFAULT_PARAM;
+ p_lcb->updating_param_flag = false;
+
+ /* If there are any preferred connection parameters, set them now */
+ if ( (p_dev_rec->conn_params.min_conn_int >= BTM_BLE_CONN_INT_MIN ) &&
+ (p_dev_rec->conn_params.min_conn_int <= BTM_BLE_CONN_INT_MAX ) &&
+ (p_dev_rec->conn_params.max_conn_int >= BTM_BLE_CONN_INT_MIN ) &&
+ (p_dev_rec->conn_params.max_conn_int <= BTM_BLE_CONN_INT_MAX ) &&
+ (p_dev_rec->conn_params.slave_latency <= BTM_BLE_CONN_LATENCY_MAX ) &&
+ (p_dev_rec->conn_params.supervision_tout >= BTM_BLE_CONN_SUP_TOUT_MIN) &&
+ (p_dev_rec->conn_params.supervision_tout <= BTM_BLE_CONN_SUP_TOUT_MAX) &&
+ ((conn_interval < p_dev_rec->conn_params.min_conn_int &&
+ p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ||
+ (conn_interval > p_dev_rec->conn_params.max_conn_int) ||
+ (conn_latency > p_dev_rec->conn_params.slave_latency) ||
+ (conn_timeout > p_dev_rec->conn_params.supervision_tout))) {
+ L2CAP_TRACE_ERROR ("upd_ll_conn_params: HANDLE=%d min_conn_int=%d max_conn_int=%d slave_latency=%d supervision_tout=%d",
+ handle, p_dev_rec->conn_params.min_conn_int, p_dev_rec->conn_params.max_conn_int,
+ p_dev_rec->conn_params.slave_latency, p_dev_rec->conn_params.supervision_tout);
+
+ p_lcb->waiting_update_conn_min_interval = p_dev_rec->conn_params.min_conn_int;
+ p_lcb->waiting_update_conn_max_interval = p_dev_rec->conn_params.max_conn_int;
+ p_lcb->waiting_update_conn_timeout = p_dev_rec->conn_params.supervision_tout;
+ p_lcb->waiting_update_conn_latency = p_dev_rec->conn_params.slave_latency;
+
+ btsnd_hcic_ble_upd_ll_conn_params (handle,
+ p_dev_rec->conn_params.min_conn_int,
+ p_dev_rec->conn_params.max_conn_int,
+ p_dev_rec->conn_params.slave_latency,
+ p_dev_rec->conn_params.supervision_tout,
+ BLE_CE_LEN_MIN, BLE_CE_LEN_MIN);
+ }
+
+ /* Tell BTM Acl management about the link */
+ btm_acl_created (bda, NULL, p_dev_rec->sec_bd_name, handle, p_lcb->link_role, BT_TRANSPORT_LE);
+
+ p_lcb->peer_chnl_mask[0] = L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT;
+
+ btm_ble_set_conn_st(BLE_CONN_IDLE);
+
+#if BLE_PRIVACY_SPT == TRUE
+ btm_ble_disable_resolving_list(BTM_BLE_RL_INIT, TRUE);
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function l2cble_advertiser_conn_comp
+**
+** Description This function is called when an HCI Connection Complete
+** event is received while we are an advertiser (so we are slave).
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_advertiser_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type,
+ UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout)
+{
+ tL2C_LCB *p_lcb;
+ tBTM_SEC_DEV_REC *p_dev_rec;
+ UNUSED(type);
+ UNUSED(conn_interval);
+ UNUSED(conn_latency);
+ UNUSED(conn_timeout);
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (bda, BT_TRANSPORT_LE);
+
+ /* If we don't have one, create one and accept the connection. */
+ if (!p_lcb) {
+ p_lcb = l2cu_allocate_lcb (bda, FALSE, BT_TRANSPORT_LE);
+ if (!p_lcb) {
+#if (SMP_INCLUDED == TRUE)
+ btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION);
+#endif ///SMP_INCLUDED == TRUE
+ L2CAP_TRACE_ERROR ("l2cble_advertiser_conn_comp - failed to allocate LCB");
+ return;
+ } else {
+ if (!l2cu_initialize_fixed_ccb (p_lcb, L2CAP_ATT_CID, &l2cb.fixed_reg[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) {
+#if (SMP_INCLUDED == TRUE)
+ btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION);
+#endif ///SMP_INCLUDED == TRUE
+ L2CAP_TRACE_WARNING ("l2cble_scanner_conn_comp - LCB but no CCB");
+ return ;
+ }
+ }
+ }
+
+ /* Save the handle */
+ p_lcb->handle = handle;
+
+ /* Connected OK. Change state to connected, we were advertising, so we are slave */
+ p_lcb->link_role = HCI_ROLE_SLAVE;
+ p_lcb->transport = BT_TRANSPORT_LE;
+
+ /* update link parameter, set slave link as non-spec default upon link up */
+ p_lcb->waiting_update_conn_min_interval = p_lcb->waiting_update_conn_max_interval = p_lcb->current_used_conn_interval = conn_interval;
+ p_lcb->waiting_update_conn_timeout = p_lcb->current_used_conn_timeout = conn_timeout;
+ p_lcb->waiting_update_conn_latency = p_lcb->current_used_conn_latency = conn_latency;
+ p_lcb->conn_update_mask = L2C_BLE_NOT_DEFAULT_PARAM;
+ p_lcb->updating_param_flag = false;
+
+ /* Tell BTM Acl management about the link */
+ p_dev_rec = btm_find_or_alloc_dev (bda);
+
+ btm_acl_created (bda, NULL, p_dev_rec->sec_bd_name, handle, p_lcb->link_role, BT_TRANSPORT_LE);
+
+#if BLE_PRIVACY_SPT == TRUE
+ btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE);
+#endif
+
+ p_lcb->peer_chnl_mask[0] = L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT;
+
+ if (!HCI_LE_SLAVE_INIT_FEAT_EXC_SUPPORTED(controller_get_interface()->get_features_ble()->as_array)) {
+ p_lcb->link_state = LST_CONNECTED;
+ l2cu_process_fixed_chnl_resp (p_lcb);
+ }
+
+ /* when adv and initiating are both active, cancel the direct connection */
+ if (l2cb.is_ble_connecting && memcmp(bda, l2cb.ble_connecting_bda, BD_ADDR_LEN) == 0) {
+ L2CA_CancelBleConnectReq(bda);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cble_conn_comp
+**
+** Description This function is called when an HCI Connection Complete
+** event is received.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_conn_comp(UINT16 handle, UINT8 role, BD_ADDR bda, tBLE_ADDR_TYPE type,
+ UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout)
+{
+ btm_ble_update_link_topology_mask(role, TRUE);
+
+ if (role == HCI_ROLE_MASTER) {
+ l2cble_scanner_conn_comp(handle, bda, type, conn_interval, conn_latency, conn_timeout);
+ } else {
+ l2cble_advertiser_conn_comp(handle, bda, type, conn_interval, conn_latency, conn_timeout);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cble_start_conn_update
+**
+** Description start BLE connection parameter update process based on status
+**
+** Parameters: lcb : l2cap link control block
+**
+** Return value: true if successfully sending the request to peer device, else false.
+**
+*******************************************************************************/
+static BOOLEAN l2cble_start_conn_update (tL2C_LCB *p_lcb)
+{
+ UINT16 min_conn_int, max_conn_int, slave_latency, supervision_tout;
+#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) && (BLE_SLAVE_UPD_CONN_PARAMS == TRUE)
+ tACL_CONN *p_acl_cb = btm_bda_to_acl(p_lcb->remote_bd_addr, BT_TRANSPORT_LE);
+#endif /* defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) && (BLE_SLAVE_UPD_CONN_PARAMS == TRUE */
+
+ if (p_lcb->conn_update_mask & L2C_BLE_UPDATE_PENDING) {
+ L2CAP_TRACE_WARNING("%s, the last connection update command still pending.", __func__);
+ p_lcb->conn_update_mask |= L2C_BLE_UPDATE_PARAM_FULL;
+ return FALSE;
+ }
+
+ if (p_lcb->conn_update_mask & L2C_BLE_CONN_UPDATE_DISABLE) {
+ /* application requests to disable parameters update.
+ If parameters are already updated, lets set them
+ up to what has been requested during connection establishement */
+ if (p_lcb->conn_update_mask & L2C_BLE_NOT_DEFAULT_PARAM &&
+ /* current connection interval is greater than default min */
+ p_lcb->current_used_conn_interval > BTM_BLE_CONN_INT_MAX_DEF) {
+ /* use 6 * 1.25 = 7.5 ms as fast connection parameter, 0 slave latency */
+ min_conn_int = max_conn_int = BTM_BLE_CONN_INT_MIN;
+ slave_latency = BTM_BLE_CONN_SLAVE_LATENCY_DEF;
+ supervision_tout = BTM_BLE_CONN_TIMEOUT_DEF;
+
+ /* if both side 4.1, or we are master device, send HCI command */
+ if (p_lcb->link_role == HCI_ROLE_MASTER
+#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) && (BLE_SLAVE_UPD_CONN_PARAMS == TRUE)
+ || (HCI_LE_CONN_PARAM_REQ_SUPPORTED(controller_get_interface()->get_features_ble()->as_array) &&
+ HCI_LE_CONN_PARAM_REQ_SUPPORTED(p_acl_cb->peer_le_features))
+#endif
+ ) {
+ btsnd_hcic_ble_upd_ll_conn_params(p_lcb->handle, min_conn_int, max_conn_int,
+ slave_latency, supervision_tout, BLE_CE_LEN_MIN, BLE_CE_LEN_MIN);
+ } else {
+ l2cu_send_peer_ble_par_req (p_lcb, min_conn_int, max_conn_int, slave_latency, supervision_tout);
+ }
+
+ //cache save
+ p_lcb->updating_conn_min_interval = min_conn_int;
+ p_lcb->updating_conn_max_interval = max_conn_int;
+ p_lcb->updating_param_flag = true;
+
+ p_lcb->conn_update_mask |= L2C_BLE_UPDATE_PENDING;
+ p_lcb->conn_update_mask &= ~L2C_BLE_NOT_DEFAULT_PARAM;
+ p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM;
+ return TRUE;
+ }else {
+ return FALSE;
+ }
+ } else {
+ /* application allows to do update, if we were delaying one do it now */
+ if (p_lcb->conn_update_mask & L2C_BLE_NEW_CONN_PARAM) {
+ /* if both side 4.1, or we are master device, send HCI command */
+ if (p_lcb->link_role == HCI_ROLE_MASTER
+#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE) && (BLE_SLAVE_UPD_CONN_PARAMS == TRUE)
+ || (HCI_LE_CONN_PARAM_REQ_SUPPORTED(controller_get_interface()->get_features_ble()->as_array) &&
+ HCI_LE_CONN_PARAM_REQ_SUPPORTED(p_acl_cb->peer_le_features))
+#endif
+ ) {
+ btsnd_hcic_ble_upd_ll_conn_params(p_lcb->handle, p_lcb->waiting_update_conn_min_interval,
+ p_lcb->waiting_update_conn_max_interval, p_lcb->waiting_update_conn_latency, p_lcb->waiting_update_conn_timeout, BLE_CE_LEN_MIN, BLE_CE_LEN_MIN);
+ } else {
+ l2cu_send_peer_ble_par_req (p_lcb, p_lcb->waiting_update_conn_min_interval, p_lcb->waiting_update_conn_max_interval,
+ p_lcb->waiting_update_conn_latency, p_lcb->waiting_update_conn_timeout);
+ }
+
+ //cache save
+ p_lcb->updating_conn_min_interval = p_lcb->waiting_update_conn_min_interval;
+ p_lcb->updating_conn_max_interval = p_lcb->waiting_update_conn_max_interval;
+ p_lcb->updating_param_flag = true;
+
+ p_lcb->conn_update_mask |= L2C_BLE_UPDATE_PENDING;
+ p_lcb->conn_update_mask &= ~L2C_BLE_NEW_CONN_PARAM;
+ p_lcb->conn_update_mask |= L2C_BLE_NOT_DEFAULT_PARAM;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cble_process_conn_update_evt
+**
+** Description This function enables the connection update request from remote
+** after a successful connection update response is received.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_process_conn_update_evt (UINT16 handle, UINT8 status, UINT16 conn_interval,
+ UINT16 conn_latency, UINT16 conn_timeout)
+{
+ tL2C_LCB *p_lcb;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_handle(handle);
+ if (!p_lcb) {
+ L2CAP_TRACE_WARNING("le con upd: inv hdl=%d", handle);
+ return;
+ }
+ if (status == HCI_SUCCESS){
+ p_lcb->current_used_conn_interval = conn_interval;
+ p_lcb->current_used_conn_latency = conn_latency;
+ p_lcb->current_used_conn_timeout = conn_timeout;
+ }else{
+ L2CAP_TRACE_WARNING("le con upd: err_stat=0x%x", status);
+ }
+
+ p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PENDING;
+ p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PARAM_FULL;
+ btu_stop_timer(&p_lcb->upda_con_timer);
+
+ if (conn_param_update_cb.update_conn_param_cb != NULL) {
+ l2c_send_update_conn_params_cb(p_lcb, status);
+ }
+
+ if (l2cble_start_conn_update(p_lcb) == TRUE) {
+ UINT32 time = CalConnectParamTimeout(p_lcb);
+ btu_start_timer(&p_lcb->upda_con_timer, BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS, time);
+ }
+
+ btu_stop_timer (&p_lcb->timer_entry);
+
+ L2CAP_TRACE_DEBUG("le con upd: conn_update_mask=%d", p_lcb->conn_update_mask);
+}
+
+/*******************************************************************************
+**
+** Function l2cble_get_conn_param_format_err_from_contoller
+**
+** Description This function is called when host get illegal connection paramrters
+** format status from controller
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_get_conn_param_format_err_from_contoller (UINT8 status, UINT16 handle)
+{
+ tL2C_LCB *p_lcb;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_handle(handle);
+ if (!p_lcb) {
+ L2CAP_TRACE_ERROR("%s: Invalid handle: %d", __FUNCTION__, handle);
+ return;
+ }
+
+ p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PENDING;
+
+ btu_stop_timer (&p_lcb->upda_con_timer);
+
+ if (conn_param_update_cb.update_conn_param_cb != NULL) {
+ l2c_send_update_conn_params_cb(p_lcb, status);
+ }
+ if ((p_lcb->conn_update_mask & L2C_BLE_UPDATE_PARAM_FULL) != 0){
+ p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PARAM_FULL;
+ if (l2cble_start_conn_update(p_lcb) == TRUE) {
+ UINT32 time = CalConnectParamTimeout(p_lcb);
+ btu_start_timer(&p_lcb->upda_con_timer, BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS, time);
+ }
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function l2cble_process_sig_cmd
+**
+** Description This function is called when a signalling packet is received
+** on the BLE signalling CID
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_process_sig_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
+{
+ UINT8 *p_pkt_end;
+ UINT8 cmd_code, id;
+ UINT16 cmd_len;
+ UINT16 min_interval, max_interval, latency, timeout;
+
+ p_pkt_end = p + pkt_len;
+
+ STREAM_TO_UINT8 (cmd_code, p);
+ STREAM_TO_UINT8 (id, p);
+ STREAM_TO_UINT16 (cmd_len, p);
+
+ /* Check command length does not exceed packet length */
+ if ((p + cmd_len) > p_pkt_end) {
+ L2CAP_TRACE_WARNING ("L2CAP - LE - format error, pkt_len: %d cmd_len: %d code: %d", pkt_len, cmd_len, cmd_code);
+ return;
+ }
+
+ switch (cmd_code) {
+ case L2CAP_CMD_REJECT:
+ case L2CAP_CMD_ECHO_RSP:
+ case L2CAP_CMD_INFO_RSP:
+ p += 2;
+ break;
+ case L2CAP_CMD_ECHO_REQ:
+ case L2CAP_CMD_INFO_REQ:
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
+ break;
+
+ case L2CAP_CMD_BLE_UPDATE_REQ:
+ STREAM_TO_UINT16 (min_interval, p); /* 0x0006 - 0x0C80 */
+ STREAM_TO_UINT16 (max_interval, p); /* 0x0006 - 0x0C80 */
+ STREAM_TO_UINT16 (latency, p); /* 0x0000 - 0x03E8 */
+ STREAM_TO_UINT16 (timeout, p); /* 0x000A - 0x0C80 */
+ /* If we are a master, the slave wants to update the parameters */
+ if (p_lcb->link_role == HCI_ROLE_MASTER) {
+ if (min_interval < BTM_BLE_CONN_INT_MIN || min_interval > BTM_BLE_CONN_INT_MAX ||
+ max_interval < BTM_BLE_CONN_INT_MIN || max_interval > BTM_BLE_CONN_INT_MAX ||
+ latency > BTM_BLE_CONN_LATENCY_MAX ||
+ timeout < BTM_BLE_CONN_SUP_TOUT_MIN || timeout > BTM_BLE_CONN_SUP_TOUT_MAX ||
+ /* The supervision_timeout parameter defines the link supervision timeout for the connection.
+ The supervision_timeout in milliseconds shall be large than (1 + latency) * max_interval * 2,
+ where max_interval is given in milliseconds. (See [Vol 6] Part B, Section 4.5.2).
+ supervision_timeout (mult of 10ms); conn_interval (mult of 1.25ms)
+ (max_interval * 1.25 * 2) replaced by ((max_interval * 5) >> 1).
+ */
+ ((timeout * 10) < ((1 + latency) *((max_interval * 5) >> 1))) ||
+ max_interval < min_interval) {
+ l2cu_send_peer_ble_par_rsp (p_lcb, L2CAP_CFG_UNACCEPTABLE_PARAMS, id);
+
+ L2CAP_TRACE_ERROR("slave connection parameters update failed, the parameters are out of range");
+
+ } else {
+
+ l2cu_send_peer_ble_par_rsp (p_lcb, L2CAP_CFG_OK, id);
+ p_lcb->waiting_update_conn_min_interval = min_interval;
+ p_lcb->waiting_update_conn_max_interval = max_interval;
+ p_lcb->waiting_update_conn_latency = latency;
+ p_lcb->waiting_update_conn_timeout = timeout;
+ p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM;
+
+ if (l2cble_start_conn_update(p_lcb) == TRUE) {
+ UINT32 time = CalConnectParamTimeout(p_lcb);
+ btu_start_timer(&p_lcb->upda_con_timer, BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS, time);
+ }
+ }
+ } else {
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
+ }
+ break;
+
+ case L2CAP_CMD_BLE_UPDATE_RSP: {
+ UINT16 result = 0;
+ STREAM_TO_UINT16(result, p); //result = 0 connection param accepted, result = 1 connection param rejected.
+ UINT8 status = (result == 0) ? HCI_SUCCESS : HCI_ERR_PARAM_OUT_OF_RANGE;
+ if (status != HCI_SUCCESS) {
+ btu_stop_timer(&p_lcb->upda_con_timer);
+ p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PENDING;
+ p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PARAM_FULL;
+ l2c_send_update_conn_params_cb(p_lcb, status);
+ }
+ break;
+ }
+ case L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ: {
+ tL2C_CCB *p_ccb = NULL;
+ tL2C_RCB *p_rcb = NULL;
+ UINT16 spsm;
+ UINT16 scid;
+ UINT16 mtu;
+ UINT16 mps;
+ UINT16 credits;
+ STREAM_TO_UINT16(spsm, p);
+ STREAM_TO_UINT16(scid, p);
+ STREAM_TO_UINT16(mtu, p);
+ STREAM_TO_UINT16(mps, p);
+ STREAM_TO_UINT16(credits, p);
+ L2CAP_TRACE_DEBUG("%s spsm %x, scid %x", __func__, spsm, scid);
+ UNUSED(spsm);
+
+ p_ccb = l2cu_find_ccb_by_remote_cid(p_lcb, scid);
+ if (p_ccb) {
+ l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_SOURCE_CID_ALREADY_ALLOCATED);
+ break;
+ }
+
+ #if 0
+ p_rcb = l2cu_find_ble_rcb_by_psm(spsm);
+ if (p_rcb == NULL) {
+ break;
+ }
+ #endif
+
+ p_ccb = l2cu_allocate_ccb(p_lcb, 0);
+ if (p_ccb == NULL) {
+ l2cu_reject_ble_connection(p_lcb, id, L2CAP_LE_RESULT_NO_RESOURCES);
+ break;
+ }
+
+ p_ccb->remote_id = id;
+ p_ccb->p_rcb = p_rcb;
+ p_ccb->remote_cid = scid;
+ p_ccb->local_conn_cfg.mtu = mtu;
+ p_ccb->local_conn_cfg.mps = controller_get_interface()->get_acl_data_size_ble();
+ p_ccb->local_conn_cfg.credits = credits;
+ p_ccb->peer_conn_cfg.mtu = mtu;
+ p_ccb->peer_conn_cfg.mps = mps;
+ p_ccb->peer_conn_cfg.credits = credits;
+
+ l2cu_send_peer_ble_credit_based_conn_res(p_ccb, L2CAP_LE_RESULT_CONN_OK);
+ break;
+ }
+ case L2CAP_CMD_DISC_REQ: {
+ tL2C_CCB *p_ccb = NULL;
+ UINT16 lcid;
+ UINT16 rcid;
+ STREAM_TO_UINT16(lcid, p);
+ STREAM_TO_UINT16(rcid, p);
+
+ p_ccb = l2cu_find_ccb_by_cid(p_lcb, lcid);
+ if (p_ccb) {
+ p_ccb->remote_id = id;
+ // TODO
+ }
+
+ l2cu_send_peer_disc_rsp(p_lcb, id, lcid, rcid);
+ break;
+ }
+ default:
+ L2CAP_TRACE_WARNING ("L2CAP - LE - unknown cmd code: %d", cmd_code);
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
+ return;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cble_init_direct_conn
+**
+** Description This function is to initate a direct connection
+**
+** Returns TRUE connection initiated, FALSE otherwise.
+**
+*******************************************************************************/
+BOOLEAN l2cble_init_direct_conn (tL2C_LCB *p_lcb)
+{
+#if ( (defined BLE_PRIVACY_SPT) && (BLE_PRIVACY_SPT == TRUE) && (!CONTROLLER_RPA_LIST_ENABLE))
+ //check for security device information in the cache
+ bool dev_rec_exist = true;
+ tBTM_SEC_DEV_REC *find_dev_rec = btm_find_dev (p_lcb->remote_bd_addr);
+ if(find_dev_rec == NULL) {
+ dev_rec_exist = false;
+ }
+
+#endif // ( (defined BLE_PRIVACY_SPT) && (BLE_PRIVACY_SPT == TRUE) && (!CONTROLLER_RPA_LIST_ENABLE))
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_lcb->remote_bd_addr);
+ tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb;
+ UINT16 scan_int;
+ UINT16 scan_win;
+ BD_ADDR peer_addr;
+ UINT8 peer_addr_type = BLE_ADDR_PUBLIC;
+ UINT8 own_addr_type = BLE_ADDR_PUBLIC;
+
+ /* There can be only one BLE connection request outstanding at a time */
+ if (p_dev_rec == NULL) {
+ L2CAP_TRACE_WARNING ("unknown device, can not initate connection");
+ return (FALSE);
+ }
+
+ scan_int = (p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF) ? BTM_BLE_SCAN_FAST_INT : p_cb->scan_int;
+ scan_win = (p_cb->scan_win == BTM_BLE_SCAN_PARAM_UNDEF) ? BTM_BLE_SCAN_FAST_WIN : p_cb->scan_win;
+
+ peer_addr_type = p_lcb->ble_addr_type;
+ memcpy(peer_addr, p_lcb->remote_bd_addr, BD_ADDR_LEN);
+
+#if ( (defined BLE_PRIVACY_SPT) && (BLE_PRIVACY_SPT == TRUE))
+ own_addr_type = btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type;
+#if (!CONTROLLER_RPA_LIST_ENABLE)
+ if(dev_rec_exist) {
+ // if the current address information is valid, get the real address information
+ if(p_dev_rec->ble.current_addr_valid) {
+ peer_addr_type = p_dev_rec->ble.current_addr_type;
+ memcpy(peer_addr, p_dev_rec->ble.current_addr, 6);
+ } else {
+ /* find security device information but not find the real address information
+ * This state may be directly open without scanning. In this case, you must
+ * use the current adv address of the device to open*/
+ }
+ } else {
+ //not find security device information, We think this is a new device, connect directly
+ }
+
+ /* It will cause that scanner doesn't send scan request to advertiser
+ * which has sent IRK to us and we have stored the IRK in controller.
+ * It is a hardware limitation. The preliminary solution is not to
+ * send key to the controller, but to resolve the random address in host.
+ * so we need send the real address information to controller. */
+
+#endif // (!CONTROLLER_RPA_LIST_ENABLE)
+
+#if (CONTROLLER_RPA_LIST_ENABLE)
+
+ if (p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) {
+ if (btm_cb.ble_ctr_cb.privacy_mode >= BTM_PRIVACY_1_2) {
+ own_addr_type |= BLE_ADDR_TYPE_ID_BIT;
+ }
+
+ //btm_ble_enable_resolving_list(BTM_BLE_RL_INIT);
+ btm_random_pseudo_to_identity_addr(peer_addr, &peer_addr_type);
+ } else {
+ btm_ble_disable_resolving_list(BTM_BLE_RL_INIT, TRUE);
+ }
+
+#endif // CONTROLLER_RPA_LIST_ENABLE
+#endif // (defined BLE_PRIVACY_SPT) && (BLE_PRIVACY_SPT == TRUE)
+
+ if (!btm_ble_topology_check(BTM_BLE_STATE_INIT)) {
+ l2cu_release_lcb (p_lcb);
+ L2CAP_TRACE_ERROR("initate direct connection fail, topology limitation");
+ return FALSE;
+ }
+ uint32_t link_timeout = L2CAP_BLE_LINK_CONNECT_TOUT;
+ if(GATTC_CONNECT_RETRY_COUNT) {
+ if(!p_lcb->retry_create_con) {
+ p_lcb->start_time_s = (esp_system_get_time()/1000);
+ }
+ uint32_t current_time = (esp_system_get_time()/1000);
+ link_timeout = (L2CAP_BLE_LINK_CONNECT_TOUT*1000 - (current_time - p_lcb->start_time_s))/1000;
+
+ if(link_timeout == 0 || link_timeout > L2CAP_BLE_LINK_CONNECT_TOUT) {
+ link_timeout = L2CAP_BLE_LINK_CONNECT_TOUT;
+ }
+ }
+
+ if (!p_lcb->is_aux) {
+ if (!btsnd_hcic_ble_create_ll_conn (scan_int,/* UINT16 scan_int */
+ scan_win, /* UINT16 scan_win */
+ FALSE, /* UINT8 white_list */
+ peer_addr_type, /* UINT8 addr_type_peer */
+ peer_addr, /* BD_ADDR bda_peer */
+ own_addr_type, /* UINT8 addr_type_own */
+ (UINT16) ((p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ?
+ p_dev_rec->conn_params.min_conn_int : BTM_BLE_CONN_INT_MIN_DEF), /* UINT16 conn_int_min */
+ (UINT16) ((p_dev_rec->conn_params.max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) ?
+ p_dev_rec->conn_params.max_conn_int : BTM_BLE_CONN_INT_MAX_DEF), /* UINT16 conn_int_max */
+ (UINT16) ((p_dev_rec->conn_params.slave_latency != BTM_BLE_CONN_PARAM_UNDEF) ?
+ p_dev_rec->conn_params.slave_latency : BTM_BLE_CONN_SLAVE_LATENCY_DEF), /* UINT16 conn_latency */
+ (UINT16) ((p_dev_rec->conn_params.supervision_tout != BTM_BLE_CONN_PARAM_UNDEF) ?
+ p_dev_rec->conn_params.supervision_tout : BTM_BLE_CONN_TIMEOUT_DEF), /* conn_timeout */
+ BLE_CE_LEN_MIN, /* UINT16 min_len */
+ BLE_CE_LEN_MIN)) { /* UINT16 max_len */
+ l2cu_release_lcb (p_lcb);
+ L2CAP_TRACE_ERROR("initate direct connection fail, no resources");
+ return (FALSE);
+ } else {
+ p_lcb->link_state = LST_CONNECTING;
+ l2cb.is_ble_connecting = TRUE;
+ memcpy (l2cb.ble_connecting_bda, p_lcb->remote_bd_addr, BD_ADDR_LEN);
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, link_timeout);
+ btm_ble_set_conn_st (BLE_DIR_CONN);
+
+ return (TRUE);
+ }
+ } else {
+#if (BLE_50_FEATURE_SUPPORT == TRUE)
+
+ /*
+ * 0x00 Public Device Address
+ * 0x01 Random Device Address
+ * 0x02 Public Identity Address (corresponds to Resolved Private Address)
+ * 0x03 Random (static) Identity Address (corresponds to Resolved Private Address)
+ * 0xFF No address provided (anonymous advertisement)
+ */
+
+ if ((peer_addr_type & BLE_ADDR_RANDOM) == BLE_ADDR_RANDOM) {
+ peer_addr_type = BLE_ADDR_RANDOM;
+ } else {
+ peer_addr_type = BLE_ADDR_PUBLIC;
+ }
+
+ tHCI_CreatExtConn aux_conn = {0};
+ aux_conn.filter_policy = FALSE;
+ aux_conn.own_addr_type = own_addr_type;
+ aux_conn.peer_addr_type = peer_addr_type;
+ memcpy(aux_conn.peer_addr, peer_addr, sizeof(BD_ADDR));
+ if (p_dev_rec->ext_conn_params.phy_mask == BLE_PHY_NO_PREF) {
+ L2CAP_TRACE_WARNING("No extend connection parameters set, use default parameters");
+ aux_conn.init_phy_mask = BLE_PHY_PREF_MASK;
+ memcpy(&aux_conn.params[0], &ext_conn_params_1m_phy, sizeof(tHCI_ExtConnParams));
+ memcpy(&aux_conn.params[1], &ext_conn_params_2m_phy, sizeof(tHCI_ExtConnParams));
+ memcpy(&aux_conn.params[2], &ext_conn_params_coded_phy, sizeof(tHCI_ExtConnParams));
+ } else {
+ aux_conn.init_phy_mask = p_dev_rec->ext_conn_params.phy_mask;
+ memcpy(&aux_conn.params[0], &p_dev_rec->ext_conn_params.phy_1m_conn_params, sizeof(tHCI_ExtConnParams));
+ memcpy(&aux_conn.params[1], &p_dev_rec->ext_conn_params.phy_2m_conn_params, sizeof(tHCI_ExtConnParams));
+ memcpy(&aux_conn.params[2], &p_dev_rec->ext_conn_params.phy_coded_conn_params, sizeof(tHCI_ExtConnParams));
+ }
+ p_lcb->link_state = LST_CONNECTING;
+ l2cb.is_ble_connecting = TRUE;
+ memcpy (l2cb.ble_connecting_bda, p_lcb->remote_bd_addr, BD_ADDR_LEN);
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, link_timeout);
+ btm_ble_set_conn_st (BLE_DIR_CONN);
+ if(!btsnd_hcic_ble_create_ext_conn(&aux_conn)) {
+ l2cu_release_lcb (p_lcb);
+ L2CAP_TRACE_ERROR("initate Aux connection failed, no resources");
+ }
+#else
+ L2CAP_TRACE_ERROR("BLE 5.0 not support!\n");
+#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
+ return (TRUE);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cble_create_conn
+**
+** Description This function initiates an acl connection via HCI
+**
+** Returns TRUE if successful, FALSE if connection not started.
+**
+*******************************************************************************/
+BOOLEAN l2cble_create_conn (tL2C_LCB *p_lcb)
+{
+ tBTM_BLE_CONN_ST conn_st = btm_ble_get_conn_st();
+ BOOLEAN rt = FALSE;
+
+ /* There can be only one BLE connection request outstanding at a time */
+ if (conn_st == BLE_CONN_IDLE) {
+ rt = l2cble_init_direct_conn(p_lcb);
+ } else {
+ L2CAP_TRACE_WARNING ("L2CAP - LE - cannot start new connection at conn st: %d", conn_st);
+
+ btm_ble_enqueue_direct_conn_req(p_lcb);
+
+ if (conn_st == BLE_BG_CONN) {
+ btm_ble_suspend_bg_conn();
+ }
+
+ rt = TRUE;
+ }
+ return rt;
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_processs_ble_num_bufs
+**
+** Description This function is called when a "controller buffer size"
+** event is first received from the controller. It updates
+** the L2CAP values.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_processs_ble_num_bufs (UINT16 num_lm_ble_bufs)
+{
+ if (num_lm_ble_bufs == 0) {
+ num_lm_ble_bufs = L2C_DEF_NUM_BLE_BUF_SHARED;
+ l2cb.num_lm_acl_bufs -= L2C_DEF_NUM_BLE_BUF_SHARED;
+ }
+ L2CAP_TRACE_DEBUG("num_lm_ble_bufs = %d",num_lm_ble_bufs);
+ l2cb.num_lm_ble_bufs = l2cb.controller_le_xmit_window = num_lm_ble_bufs;
+}
+
+/*******************************************************************************
+**
+** Function l2c_ble_link_adjust_allocation
+**
+** Description This function is called when a link is created or removed
+** to calculate the amount of packets each link may send to
+** the HCI without an ack coming back.
+**
+** Currently, this is a simple allocation, dividing the
+** number of Controller Packets by the number of links. In
+** the future, QOS configuration should be examined.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_ble_link_adjust_allocation (void)
+{
+ UINT16 qq, qq_remainder;
+ tL2C_LCB *p_lcb;
+ UINT16 hi_quota, low_quota;
+ UINT16 num_lowpri_links = 0;
+ UINT16 num_hipri_links = 0;
+ UINT16 controller_xmit_quota = l2cb.num_lm_ble_bufs;
+ UINT16 high_pri_link_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A;
+ list_node_t *p_node = NULL;
+
+ /* If no links active, reset buffer quotas and controller buffers */
+ if (l2cb.num_ble_links_active == 0) {
+ l2cb.controller_le_xmit_window = l2cb.num_lm_ble_bufs;
+ l2cb.ble_round_robin_quota = l2cb.ble_round_robin_unacked = 0;
+ return;
+ }
+
+ /* First, count the links */
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (p_lcb->in_use && p_lcb->transport == BT_TRANSPORT_LE) {
+ if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) {
+ num_hipri_links++;
+ } else {
+ num_lowpri_links++;
+ }
+ }
+ }
+
+ /* now adjust high priority link quota */
+ low_quota = num_lowpri_links ? 1 : 0;
+ while ( (num_hipri_links * high_pri_link_quota + low_quota) > controller_xmit_quota ) {
+ high_pri_link_quota--;
+ }
+
+
+ /* Work out the xmit quota and buffer quota high and low priorities */
+ hi_quota = num_hipri_links * high_pri_link_quota;
+ low_quota = (hi_quota < controller_xmit_quota) ? controller_xmit_quota - hi_quota : 1;
+
+ /* Work out and save the HCI xmit quota for each low priority link */
+
+ /* If each low priority link cannot have at least one buffer */
+ if (num_lowpri_links > low_quota) {
+ l2cb.ble_round_robin_quota = low_quota;
+ qq = qq_remainder = 0;
+ }
+ /* If each low priority link can have at least one buffer */
+ else if (num_lowpri_links > 0) {
+ l2cb.ble_round_robin_quota = 0;
+ l2cb.ble_round_robin_unacked = 0;
+ qq = low_quota / num_lowpri_links;
+ qq_remainder = low_quota % num_lowpri_links;
+ }
+ /* If no low priority link */
+ else {
+ l2cb.ble_round_robin_quota = 0;
+ l2cb.ble_round_robin_unacked = 0;
+ qq = qq_remainder = 0;
+ }
+ L2CAP_TRACE_EVENT ("l2c_ble_link_adjust_allocation num_hipri: %u num_lowpri: %u low_quota: %u round_robin_quota: %u qq: %u",
+ num_hipri_links, num_lowpri_links, low_quota,
+ l2cb.ble_round_robin_quota, qq);
+
+ /* Now, assign the quotas to each link */
+ p_node = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (p_lcb->in_use && p_lcb->transport == BT_TRANSPORT_LE) {
+ if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) {
+ p_lcb->link_xmit_quota = high_pri_link_quota;
+ } else {
+ /* Safety check in case we switched to round-robin with something outstanding */
+ /* if sent_not_acked is added into round_robin_unacked then don't add it again */
+ /* l2cap keeps updating sent_not_acked for exiting from round robin */
+ if (( p_lcb->link_xmit_quota > 0 ) && ( qq == 0 )) {
+ l2cb.ble_round_robin_unacked += p_lcb->sent_not_acked;
+ }
+
+ p_lcb->link_xmit_quota = qq;
+ if (qq_remainder > 0) {
+ p_lcb->link_xmit_quota++;
+ qq_remainder--;
+ }
+ }
+
+ L2CAP_TRACE_EVENT("l2c_ble_link_adjust_allocation Priority: %d XmitQuota: %d",
+ p_lcb->acl_priority, p_lcb->link_xmit_quota);
+
+ L2CAP_TRACE_EVENT(" SentNotAcked: %d RRUnacked: %d",
+ p_lcb->sent_not_acked, l2cb.round_robin_unacked);
+
+ /* There is a special case where we have readjusted the link quotas and */
+ /* this link may have sent anything but some other link sent packets so */
+ /* so we may need a timer to kick off this link's transmissions. */
+ if ( (p_lcb->link_state == LST_CONNECTED)
+ && (!list_is_empty(p_lcb->link_xmit_data_q))
+ && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) ) {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT);
+ }
+ }
+ }
+}
+
+#if (defined BLE_LLT_INCLUDED) && (BLE_LLT_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function l2cble_process_rc_param_request_evt
+**
+** Description process LE Remote Connection Parameter Request Event.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_process_rc_param_request_evt(UINT16 handle, UINT16 int_min, UINT16 int_max,
+ UINT16 latency, UINT16 timeout)
+{
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle (handle);
+
+ if (p_lcb != NULL) {
+
+ /* if update is enabled, always accept connection parameter update */
+ if ((p_lcb->conn_update_mask & L2C_BLE_CONN_UPDATE_DISABLE) == 0) {
+ p_lcb->conn_update_mask |= L2C_BLE_UPDATE_PENDING;
+ btsnd_hcic_ble_rc_param_req_reply(handle, int_min, int_max, latency, timeout, BLE_CE_LEN_MIN, BLE_CE_LEN_MIN);
+ }else {
+ /* always accept connection parameters request which is sent by itself */
+ if (int_max == BTM_BLE_CONN_INT_MIN) {
+ p_lcb->conn_update_mask |= L2C_BLE_UPDATE_PENDING;
+ btsnd_hcic_ble_rc_param_req_reply(handle, int_min, int_max, latency, timeout, BLE_CE_LEN_MIN, BLE_CE_LEN_MIN);
+ }else {
+ L2CAP_TRACE_EVENT ("L2CAP - LE - update currently disabled");
+ p_lcb->conn_update_mask |= L2C_BLE_NEW_CONN_PARAM;
+ btsnd_hcic_ble_rc_param_req_neg_reply (handle, HCI_ERR_UNACCEPT_CONN_INTERVAL);
+ }
+ }
+
+ } else {
+ L2CAP_TRACE_WARNING("No link to update connection parameter")
+ }
+}
+#endif
+
+/*******************************************************************************
+**
+** Function l2cble_update_data_length
+**
+** Description This function update link tx data length if applicable
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_update_data_length(tL2C_LCB *p_lcb)
+{
+ UINT16 tx_mtu = 0;
+ UINT16 i = 0;
+
+ L2CAP_TRACE_DEBUG("%s", __FUNCTION__);
+
+ /* See if we have a link control block for the connection */
+ if (p_lcb == NULL) {
+ return;
+ }
+
+ for (i = 0; i < L2CAP_NUM_FIXED_CHNLS; i++) {
+ if (i + L2CAP_FIRST_FIXED_CHNL != L2CAP_BLE_SIGNALLING_CID) {
+ if ((p_lcb->p_fixed_ccbs[i] != NULL) &&
+ (tx_mtu < (p_lcb->p_fixed_ccbs[i]->tx_data_len + L2CAP_PKT_OVERHEAD))) {
+ tx_mtu = p_lcb->p_fixed_ccbs[i]->tx_data_len + L2CAP_PKT_OVERHEAD;
+ }
+ }
+ }
+
+ if (tx_mtu > BTM_BLE_DATA_SIZE_MAX) {
+ tx_mtu = BTM_BLE_DATA_SIZE_MAX;
+ }
+
+ /* update TX data length if changed */
+ if (p_lcb->tx_data_len != tx_mtu) {
+ BTM_SetBleDataLength(p_lcb->remote_bd_addr, tx_mtu);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function l2cble_process_data_length_change_evt
+**
+** Description This function process the data length change event
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_process_data_length_change_event(UINT16 handle, UINT16 tx_data_len, UINT16 rx_data_len)
+{
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(handle);
+ tACL_CONN *p_acl = btm_handle_to_acl(handle);
+ tBTM_LE_SET_PKT_DATA_LENGTH_PARAMS data_length_params;
+
+ L2CAP_TRACE_DEBUG("%s TX data len = %d", __FUNCTION__, tx_data_len);
+ if (p_lcb == NULL) {
+ return;
+ }
+
+ if (tx_data_len > 0) {
+ p_lcb->tx_data_len = tx_data_len;
+ }
+
+ data_length_params.rx_len = rx_data_len;
+ data_length_params.tx_len = tx_data_len;
+
+ if(p_acl) {
+ p_acl->data_length_params = data_length_params;
+ if (p_acl->p_set_pkt_data_cback) {
+ (*p_acl->p_set_pkt_data_cback)(BTM_SUCCESS, &data_length_params);
+ }
+
+ p_acl->data_len_updating = false;
+ if(p_acl->data_len_waiting) {
+ p_acl->data_len_waiting = false;
+ p_acl->p_set_pkt_data_cback = p_acl->p_set_data_len_cback_waiting;
+ p_acl->p_set_data_len_cback_waiting = NULL;
+ // if value is same, triger callback directly
+ if(p_acl->tx_len_waiting == p_acl->data_length_params.tx_len) {
+ if(p_acl->p_set_pkt_data_cback) {
+ (*p_acl->p_set_pkt_data_cback)(BTM_SUCCESS, &p_acl->data_length_params);
+ }
+ return;
+ }
+ p_acl->data_len_updating = true;
+ /* always set the TxTime to be max, as controller does not care for now */
+ btsnd_hcic_ble_set_data_length(handle, p_acl->tx_len_waiting,
+ BTM_BLE_DATA_TX_TIME_MAX);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cble_set_fixed_channel_tx_data_length
+**
+** Description This function update max fixed channel tx data length if applicable
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_set_fixed_channel_tx_data_length(BD_ADDR remote_bda, UINT16 fix_cid, UINT16 tx_mtu)
+{
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(remote_bda, BT_TRANSPORT_LE);
+ UINT16 cid = fix_cid - L2CAP_FIRST_FIXED_CHNL;
+
+ L2CAP_TRACE_DEBUG("%s TX MTU = %d", __FUNCTION__, tx_mtu);
+
+ if (!controller_get_interface()->supports_ble_packet_extension()) {
+ L2CAP_TRACE_WARNING("%s, request not supported", __FUNCTION__);
+ return;
+ }
+
+ /* See if we have a link control block for the connection */
+ if (p_lcb == NULL) {
+ return;
+ }
+
+ if (p_lcb->p_fixed_ccbs[cid] != NULL) {
+ if (tx_mtu > BTM_BLE_DATA_SIZE_MAX) {
+ tx_mtu = BTM_BLE_DATA_SIZE_MAX;
+ }
+
+ p_lcb->p_fixed_ccbs[cid]->tx_data_len = tx_mtu;
+ }
+
+ l2cble_update_data_length(p_lcb);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_send_update_conn_params_cb
+**
+** Description This function send the update connection parameter callback to the uplayer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_send_update_conn_params_cb(tL2C_LCB *p_lcb, UINT8 status)
+{
+ if(conn_param_update_cb.update_conn_param_cb != NULL){
+ tBTM_LE_UPDATE_CONN_PRAMS update_param;
+ //if myself update the connection parameters
+ if (p_lcb->updating_param_flag){
+ update_param.max_conn_int = p_lcb->updating_conn_max_interval;
+ update_param.min_conn_int = p_lcb->updating_conn_min_interval;
+ p_lcb->updating_param_flag = false;
+ }else{
+ // remote device update the connection parameters
+ update_param.max_conn_int = update_param.min_conn_int = 0;
+ }
+ // current connection parameters
+ update_param.conn_int = p_lcb->current_used_conn_interval;
+ update_param.slave_latency = p_lcb->current_used_conn_latency;
+ update_param.supervision_tout = p_lcb->current_used_conn_timeout;
+
+ (conn_param_update_cb.update_conn_param_cb)(status, p_lcb->remote_bd_addr, &update_param);
+ }
+}
+
+/*******************************************************************************
+**
+** Function CalConnectParamTimeout
+**
+** Description This function is called to calculate the connection parameter timeout.
+**
+** Returns timeout
+**
+*******************************************************************************/
+UINT32 CalConnectParamTimeout(tL2C_LCB *p_lcb)
+{
+ UINT32 timeout = 6;
+ if (p_lcb != NULL){
+ //1.25 * conn_int *(1+ latency) *32
+ timeout = (40 * ( 1 + p_lcb->current_used_conn_latency) * p_lcb->current_used_conn_interval + 1.25 * p_lcb->waiting_update_conn_max_interval + 1000) / 1000;
+ if (timeout < 1){
+ timeout = 1;
+ }else if (timeout > 120){
+ timeout = 120;
+ }
+ }
+ return timeout;
+}
+
+/*******************************************************************************
+**
+** Function l2cble_credit_based_conn_req
+**
+** Description This function sends LE Credit Based Connection Request for
+** LE connection oriented channels.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_credit_based_conn_req (tL2C_CCB *p_ccb)
+{
+ if (!p_ccb) {
+ return;
+ }
+
+ if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE)
+ {
+ L2CAP_TRACE_WARNING ("LE link doesn't exist");
+ return;
+ }
+
+ l2cu_send_peer_ble_credit_based_conn_req (p_ccb);
+ return;
+}
+
+/*******************************************************************************
+**
+** Function l2cble_credit_based_conn_res
+**
+** Description This function sends LE Credit Based Connection Response for
+** LE connection oriented channels.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result)
+{
+ if (!p_ccb) {
+ return;
+ }
+
+ if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE)
+ {
+ L2CAP_TRACE_WARNING ("LE link doesn't exist");
+ return;
+ }
+
+ l2cu_send_peer_ble_credit_based_conn_res (p_ccb, result);
+ return;
+}
+
+/*******************************************************************************
+**
+** Function l2cble_send_flow_control_credit
+**
+** Description This function sends flow control credits for
+** LE connection oriented channels.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_send_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value)
+{
+ if (!p_ccb) {
+ return;
+ }
+
+ if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE)
+ {
+ L2CAP_TRACE_WARNING ("LE link doesn't exist");
+ return;
+ }
+
+ l2cu_send_peer_ble_flow_control_credit(p_ccb, credit_value);
+ return;
+
+}
+
+/*******************************************************************************
+**
+** Function l2cble_send_peer_disc_req
+**
+** Description This function sends disconnect request
+** to the peer LE device
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_send_peer_disc_req(tL2C_CCB *p_ccb)
+{
+ L2CAP_TRACE_DEBUG ("%s",__func__);
+ if (!p_ccb) {
+ return;
+ }
+
+ if (p_ccb->p_lcb && p_ccb->p_lcb->transport != BT_TRANSPORT_LE)
+ {
+ L2CAP_TRACE_WARNING ("LE link doesn't exist");
+ return;
+ }
+
+ l2cu_send_peer_ble_credit_based_disconn_req(p_ccb);
+ return;
+}
+
+#if (SMP_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function l2cble_sec_comp
+**
+** Description This function is called when security procedure for an LE COC
+** link is done
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cble_sec_comp(BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data, UINT8 status)
+{
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_bda, BT_TRANSPORT_LE);
+ tL2CAP_SEC_DATA *p_buf = NULL;
+ UINT8 sec_flag;
+ UINT8 sec_act;
+
+ if (!p_lcb)
+ {
+ L2CAP_TRACE_WARNING ("%s security complete for unknown device", __func__);
+ return;
+ }
+
+ sec_act = p_lcb->sec_act;
+ p_lcb->sec_act = 0;
+
+ if (!fixed_queue_is_empty(p_lcb->le_sec_pending_q))
+ {
+ p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q, FIXED_QUEUE_MAX_TIMEOUT);
+ if (!p_buf)
+ {
+ L2CAP_TRACE_WARNING ("%s Security complete for request not initiated from L2CAP",
+ __func__);
+ return;
+ }
+
+ if (status != BTM_SUCCESS)
+ {
+ (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status);
+ }
+ else
+ {
+ if (sec_act == BTM_SEC_ENCRYPT_MITM)
+ {
+ BTM_GetSecurityFlagsByTransport(p_bda, &sec_flag, transport);
+ if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) {
+ (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status);
+ }
+ else
+ {
+ L2CAP_TRACE_DEBUG ("%s MITM Protection Not present", __func__);
+ (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data,
+ BTM_FAILED_ON_SECURITY);
+ }
+ }
+ else
+ {
+ L2CAP_TRACE_DEBUG ("%s MITM Protection not required sec_act = %d",
+ __func__, p_lcb->sec_act);
+
+ (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status);
+ }
+ }
+ }
+ else
+ {
+ L2CAP_TRACE_WARNING ("%s Security complete for request not initiated from L2CAP", __func__);
+ return;
+ }
+ osi_free(p_buf);
+
+ while (!fixed_queue_is_empty(p_lcb->le_sec_pending_q))
+ {
+ p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q, FIXED_QUEUE_MAX_TIMEOUT);
+
+ if (status != BTM_SUCCESS) {
+ (*(p_buf->p_callback))(p_bda, BT_TRANSPORT_LE, p_buf->p_ref_data, status);
+ } else {
+ l2ble_sec_access_req(p_bda, p_buf->psm, p_buf->is_originator,
+ p_buf->p_callback, p_buf->p_ref_data);
+ }
+
+ osi_free(p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2ble_sec_access_req
+**
+** Description This function is called by LE COC link to meet the
+** security requirement for the link
+**
+** Returns TRUE - security procedures are started
+** FALSE - failure
+**
+*******************************************************************************/
+BOOLEAN l2ble_sec_access_req(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, tL2CAP_SEC_CBACK *p_callback, void *p_ref_data)
+{
+ L2CAP_TRACE_DEBUG ("%s", __func__);
+ BOOLEAN status;
+ tL2C_LCB *p_lcb = NULL;
+
+ if (!p_callback)
+ {
+ L2CAP_TRACE_ERROR("%s No callback function", __func__);
+ return FALSE;
+ }
+
+ p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE);
+
+ if (!p_lcb)
+ {
+ L2CAP_TRACE_ERROR ("%s Security check for unknown device", __func__);
+ p_callback(bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_UNKNOWN_ADDR);
+ return FALSE;
+ }
+
+ tL2CAP_SEC_DATA *p_buf = (tL2CAP_SEC_DATA*) osi_malloc((UINT16)sizeof(tL2CAP_SEC_DATA));
+ if (!p_buf)
+ {
+ p_callback(bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_NO_RESOURCES);
+ return FALSE;
+ }
+
+ p_buf->psm = psm;
+ p_buf->is_originator = is_originator;
+ p_buf->p_callback = p_callback;
+ p_buf->p_ref_data = p_ref_data;
+ fixed_queue_enqueue(p_lcb->le_sec_pending_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT);
+ status = btm_ble_start_sec_check(bd_addr, psm, is_originator, &l2cble_sec_comp, p_ref_data);
+
+ return status;
+}
+#endif /* #if (SMP_INCLUDED == TRUE) */
+#endif /* (BLE_INCLUDED == TRUE) */
+/*******************************************************************************
+**
+** Function L2CA_GetDisconnectReason
+**
+** Description This function returns the disconnect reason code.
+**
+** Returns disconnect reason
+**
+*******************************************************************************/
+UINT16 L2CA_GetDisconnectReason (BD_ADDR remote_bda, tBT_TRANSPORT transport)
+{
+ tL2C_LCB *p_lcb;
+ UINT16 reason = 0;
+
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (remote_bda, transport)) != NULL) {
+ reason = p_lcb->disc_reason;
+ }
+
+ L2CAP_TRACE_DEBUG ("L2CA_GetDisconnectReason=%d ", reason);
+
+ return reason;
+}
diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_csm.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_csm.c
new file mode 100644
index 00000000..9abe72e2
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_csm.c
@@ -0,0 +1,1263 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-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 L2CAP channel state machine
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "common/bt_target.h"
+#include "stack/hcidefs.h"
+#include "stack/hcimsgs.h"
+#include "stack/l2cdefs.h"
+#include "l2c_int.h"
+#include "btm_int.h"
+#include "stack/btu.h"
+#include "stack/hcimsgs.h"
+#include "osi/allocator.h"
+
+#if (L2CAP_COC_INCLUDED == TRUE)
+/********************************************************************************/
+/* L O C A L F U N C T I O N P R O T O T Y P E S */
+/********************************************************************************/
+static void l2c_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_orig_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_term_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_w4_l2cap_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_w4_l2ca_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_config (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_w4_l2cap_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+static void l2c_csm_w4_l2ca_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data);
+
+#if (BT_TRACE_VERBOSE == TRUE)
+static char *l2c_csm_get_event_name (UINT16 event);
+#endif
+
+/*******************************************************************************
+**
+** Function l2c_csm_execute
+**
+** Description This function executes the state machine.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ switch (p_ccb->chnl_state) {
+ case CST_CLOSED:
+ l2c_csm_closed (p_ccb, event, p_data);
+ break;
+
+ case CST_ORIG_W4_SEC_COMP:
+ l2c_csm_orig_w4_sec_comp (p_ccb, event, p_data);
+ break;
+
+ case CST_TERM_W4_SEC_COMP:
+ l2c_csm_term_w4_sec_comp (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CAP_CONNECT_RSP:
+ l2c_csm_w4_l2cap_connect_rsp (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CA_CONNECT_RSP:
+ l2c_csm_w4_l2ca_connect_rsp (p_ccb, event, p_data);
+ break;
+
+ case CST_CONFIG:
+ l2c_csm_config (p_ccb, event, p_data);
+ break;
+
+ case CST_OPEN:
+ l2c_csm_open (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CAP_DISCONNECT_RSP:
+ l2c_csm_w4_l2cap_disconnect_rsp (p_ccb, event, p_data);
+ break;
+
+ case CST_W4_L2CA_DISCONNECT_RSP:
+ l2c_csm_w4_l2ca_disconnect_rsp (p_ccb, event, p_data);
+ break;
+
+ default:
+ L2CAP_TRACE_DEBUG("Unhandled event! event = %d", event);
+ break;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_csm_closed
+**
+** Description This function handles events when the channel is in
+** CLOSED state. This state exists only when the link is
+** being initially established.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_closed (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2C_CONN_INFO *p_ci = (tL2C_CONN_INFO *)p_data;
+ UINT16 local_cid = p_ccb->local_cid;
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind;
+ tL2CA_CONNECT_CFM_CB *connect_cfm;
+
+ if (p_ccb->p_rcb == NULL) {
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_ERROR ("L2CAP - LCID: 0x%04x st: CLOSED evt: %s p_rcb == NULL", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_ERROR ("L2CAP - LCID: 0x%04x st: CLOSED evt: 0x%04x p_rcb == NULL", p_ccb->local_cid, event);
+#endif
+ return;
+ }
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ if ( local_cid == L2CAP_CONNECTIONLESS_CID ) {
+ /* check if this event can be processed by UCD */
+ if ( l2c_ucd_process_event (p_ccb, event, p_data) ) {
+ /* The event is processed by UCD state machine */
+ return;
+ }
+ }
+#endif
+
+ disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: CLOSED evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT ("L2CAP - st: CLOSED evt: %d", event);
+#endif
+
+ switch (event) {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_LP_CONNECT_CFM: /* Link came up */
+ p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP;
+ btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
+ p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb);
+ break;
+
+ case L2CEVT_LP_CONNECT_CFM_NEG: /* Link failed */
+ /* Disconnect unless ACL collision and upper layer wants to handle it */
+ if (p_ci->status != HCI_ERR_CONNECTION_EXISTS
+ || !btm_acl_notif_conn_collision(p_ccb->p_lcb->remote_bd_addr)) {
+ L2CAP_TRACE_API ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, p_ci->status);
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, p_ci->status);
+ }
+ break;
+
+ case L2CEVT_L2CA_CONNECT_REQ: /* API connect request */
+ /* Cancel sniff mode if needed */
+ {
+ tBTM_PM_PWR_MD settings;
+// btla-specific ++
+ memset((void *)&settings, 0, sizeof(settings));
+// btla-specific --
+ settings.mode = BTM_PM_MD_ACTIVE;
+ /* COVERITY
+ Event uninit_use_in_call: Using uninitialized value "settings" (field "settings".timeout uninitialized) in call to function "BTM_SetPowerMode" [details]
+ Event uninit_use_in_call: Using uninitialized value "settings.max" in call to function "BTM_SetPowerMode" [details]
+ Event uninit_use_in_call: Using uninitialized value "settings.min" in call to function "BTM_SetPowerMode"
+ // FALSE-POSITIVE error from Coverity test-tool. Please do NOT remove following comment.
+ // coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode the other data members of tBTM_PM_PWR_MD are ignored
+ */
+ BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
+ }
+
+ /* If sec access does not result in started SEC_COM or COMP_NEG are already processed */
+ if (btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
+ p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb) == BTM_CMD_STARTED) {
+ p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP;
+ }
+ break;
+
+ case L2CEVT_SEC_COMP:
+ p_ccb->chnl_state = CST_W4_L2CAP_CONNECT_RSP;
+
+ /* Wait for the info resp in this state before sending connect req (if needed) */
+ if (!p_ccb->p_lcb->w4_info_rsp) {
+ /* Need to have at least one compatible channel to continue */
+ if (!l2c_fcr_chk_chan_modes(p_ccb)) {
+ l2cu_release_ccb (p_ccb);
+ (*p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb)(local_cid, L2CAP_CONN_NO_LINK);
+ } else {
+ l2cu_send_peer_connect_req (p_ccb);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT);
+ }
+ }
+ break;
+
+ case L2CEVT_SEC_COMP_NEG: /* something is really bad with security */
+ L2CAP_TRACE_API ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, L2CAP_CONN_TIMEOUT);
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, L2CAP_CONN_SECURITY_BLOCK);
+ break;
+
+ case L2CEVT_L2CAP_CONNECT_REQ: /* Peer connect request */
+ /* stop link timer to avoid race condition between A2MP, Security, and L2CAP */
+ btu_stop_timer (&p_ccb->p_lcb->timer_entry);
+
+ /* Cancel sniff mode if needed */
+ {
+ tBTM_PM_PWR_MD settings;
+// btla-specific ++
+ memset((void *)&settings, 0, sizeof(settings));
+// btla-specific --
+ settings.mode = BTM_PM_MD_ACTIVE;
+ /* COVERITY
+ Event uninit_use_in_call: Using uninitialized value "settings" (field "settings".timeout uninitialized) in call to function "BTM_SetPowerMode" [details]
+ Event uninit_use_in_call: Using uninitialized value "settings.max" in call to function "BTM_SetPowerMode" [details]
+ Event uninit_use_in_call: Using uninitialized value "settings.min" in call to function "BTM_SetPowerMode"
+ // FALSE-POSITIVE error from Coverity test-tool. Please do NOT remove following comment.
+ // coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode the other data members of tBTM_PM_PWR_MD are ignored
+ */
+ BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
+ }
+
+ p_ccb->chnl_state = CST_TERM_W4_SEC_COMP;
+ if (btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
+ p_ccb->p_lcb->handle, FALSE, &l2c_link_sec_comp, p_ccb) == BTM_CMD_STARTED) {
+ /* started the security process, tell the peer to set a longer timer */
+ l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0);
+ }
+ break;
+
+ case L2CEVT_TIMEOUT:
+ L2CAP_TRACE_API ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, L2CAP_CONN_TIMEOUT);
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, L2CAP_CONN_TIMEOUT);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ osi_free (p_data);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ l2cu_release_ccb (p_ccb);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_orig_w4_sec_comp
+**
+** Description This function handles events when the channel is in
+** CST_ORIG_W4_SEC_COMP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_orig_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ tL2CA_CONNECT_CFM_CB *connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: ORIG_W4_SEC_COMP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT ("L2CAP - st: ORIG_W4_SEC_COMP evt: %d", event);
+#endif
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ if ( local_cid == L2CAP_CONNECTIONLESS_CID ) {
+ /* check if this event can be processed by UCD */
+ if ( l2c_ucd_process_event (p_ccb, event, p_data) ) {
+ /* The event is processed by UCD state machine */
+ return;
+ }
+ }
+#endif
+
+ switch (event) {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */
+ case L2CEVT_LP_CONNECT_CFM: /* Link came up */
+ btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
+ p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb);
+ break;
+
+ case L2CEVT_SEC_COMP: /* Security completed success */
+ /* Wait for the info resp in this state before sending connect req (if needed) */
+ p_ccb->chnl_state = CST_W4_L2CAP_CONNECT_RSP;
+ if (!p_ccb->p_lcb->w4_info_rsp) {
+ /* Need to have at least one compatible channel to continue */
+ if (!l2c_fcr_chk_chan_modes(p_ccb)) {
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, L2CAP_CONN_NO_LINK);
+ } else {
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT);
+ l2cu_send_peer_connect_req (p_ccb); /* Start Connection */
+ }
+ }
+ break;
+
+ case L2CEVT_SEC_COMP_NEG:
+ L2CAP_TRACE_API ("L2CAP - Calling ConnectCfm_Cb(), CID: 0x%04x Status: %d", p_ccb->local_cid, HCI_ERR_AUTH_FAILURE);
+
+ /* If last channel immediately disconnect the ACL for better security.
+ Also prevents a race condition between BTM and L2CAP */
+ if ( (p_ccb == p_ccb->p_lcb->ccb_queue.p_first_ccb) && (p_ccb == p_ccb->p_lcb->ccb_queue.p_last_ccb) ) {
+ p_ccb->p_lcb->idle_timeout = 0;
+ }
+
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, HCI_ERR_AUTH_FAILURE);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ osi_free (p_data);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ /* Tell security manager to abort */
+ btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr);
+
+ l2cu_release_ccb (p_ccb);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_term_w4_sec_comp
+**
+** Description This function handles events when the channel is in
+** CST_TERM_W4_SEC_COMP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_term_w4_sec_comp (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: TERM_W4_SEC_COMP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT ("L2CAP - st: TERM_W4_SEC_COMP evt: %d", event);
+#endif
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) {
+ /* check if this event can be processed by UCD */
+ if ( l2c_ucd_process_event (p_ccb, event, p_data) ) {
+ /* The event is processed by UCD state machine */
+ return;
+ }
+ }
+#endif
+
+ switch (event) {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ /* Tell security manager to abort */
+ btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr);
+
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_SEC_COMP:
+ p_ccb->chnl_state = CST_W4_L2CA_CONNECT_RSP;
+
+ /* Wait for the info resp in next state before sending connect ind (if needed) */
+ if (!p_ccb->p_lcb->w4_info_rsp) {
+ /* Don't need to get info from peer or already retrieved so continue */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT);
+ L2CAP_TRACE_API ("L2CAP - Calling Connect_Ind_Cb(), CID: 0x%04x", p_ccb->local_cid);
+
+ (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb) (p_ccb->p_lcb->remote_bd_addr, p_ccb->local_cid,
+ p_ccb->p_rcb->psm, p_ccb->remote_id);
+ } else {
+ /*
+ ** L2CAP Connect Response will be sent out by 3 sec timer expiration
+ ** because Bluesoleil doesn't respond to L2CAP Information Request.
+ ** Bluesoleil seems to disconnect ACL link as failure case, because
+ ** it takes too long (4~7secs) to get response.
+ ** product version : Bluesoleil 2.1.1.0 EDR Release 060123
+ ** stack version : 05.04.11.20060119
+ */
+
+ /* Waiting for the info resp, tell the peer to set a longer timer */
+ l2cu_send_peer_connect_rsp(p_ccb, L2CAP_CONN_PENDING, 0);
+ }
+ break;
+
+ case L2CEVT_SEC_COMP_NEG:
+ if (((tL2C_CONN_INFO *)p_data)->status == BTM_DELAY_CHECK) {
+ /* start a timer - encryption change not received before L2CAP connect req */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_DELAY_CHECK_SM4);
+ } else {
+ l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_SECURITY_BLOCK, 0);
+ l2cu_release_ccb (p_ccb);
+ }
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ osi_free (p_data);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */
+ l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid);
+
+ /* Tell security manager to abort */
+ btm_sec_abort_access_req (p_ccb->p_lcb->remote_bd_addr);
+
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ /* SM4 related. */
+ if (!btsnd_hcic_disconnect (p_ccb->p_lcb->handle, HCI_ERR_AUTH_FAILURE)) {
+ L2CAP_TRACE_API ("L2CAP - Calling btsnd_hcic_disconnect for handle %i failed", p_ccb->p_lcb->handle);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 1);
+ }
+ break;
+
+ case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */
+ btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
+ p_ccb->p_lcb->handle, FALSE, &l2c_link_sec_comp, p_ccb);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_w4_l2cap_connect_rsp
+**
+** Description This function handles events when the channel is in
+** CST_W4_L2CAP_CONNECT_RSP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_w4_l2cap_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2C_CONN_INFO *p_ci = (tL2C_CONN_INFO *)p_data;
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ tL2CA_CONNECT_CFM_CB *connect_cfm = p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: W4_L2CAP_CON_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT ("L2CAP - st: W4_L2CAP_CON_RSP evt: %d", event);
+#endif
+
+ switch (event) {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ /* Send disc indication unless peer to peer race condition AND normal disconnect */
+ /* *((UINT8 *)p_data) != HCI_ERR_PEER_USER happens when peer device try to disconnect for normal reason */
+ p_ccb->chnl_state = CST_CLOSED;
+ if ((p_ccb->flags & CCB_FLAG_NO_RETRY) || !p_data || (*((UINT8 *)p_data) != HCI_ERR_PEER_USER)) {
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed",
+ p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ }
+ p_ccb->flags |= CCB_FLAG_NO_RETRY;
+ break;
+
+ case L2CEVT_L2CAP_CONNECT_RSP: /* Got peer connect confirm */
+ p_ccb->remote_cid = p_ci->remote_cid;
+ p_ccb->chnl_state = CST_CONFIG;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ L2CAP_TRACE_API ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Success", p_ccb->local_cid);
+
+ (*p_ccb->p_rcb->api.pL2CA_ConnectCfm_Cb)(local_cid, L2CAP_CONN_OK);
+ break;
+
+ case L2CEVT_L2CAP_CONNECT_RSP_PND: /* Got peer connect pending */
+ p_ccb->remote_cid = p_ci->remote_cid;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT_EXT);
+ if (p_ccb->p_rcb->api.pL2CA_ConnectPnd_Cb) {
+ L2CAP_TRACE_API ("L2CAP - Calling Connect_Pnd_Cb(), CID: 0x%04x", p_ccb->local_cid);
+ (*p_ccb->p_rcb->api.pL2CA_ConnectPnd_Cb)(p_ccb->local_cid);
+ }
+ break;
+
+ case L2CEVT_L2CAP_CONNECT_RSP_NEG: /* Peer rejected connection */
+ L2CAP_TRACE_API ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Failure Code: %d", p_ccb->local_cid, p_ci->l2cap_result);
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, p_ci->l2cap_result);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ L2CAP_TRACE_API ("L2CAP - Calling Connect_Cfm_Cb(), CID: 0x%04x, Timeout", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, L2CAP_CONN_TIMEOUT);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ /* If we know peer CID from connect pending, we can send disconnect */
+ if (p_ccb->remote_cid != 0) {
+ l2cu_send_peer_disc_req (p_ccb);
+ p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
+ } else {
+ l2cu_release_ccb (p_ccb);
+ }
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ osi_free (p_data);
+ break;
+
+ case L2CEVT_L2CAP_INFO_RSP:
+ /* Need to have at least one compatible channel to continue */
+ if (!l2c_fcr_chk_chan_modes(p_ccb)) {
+ l2cu_release_ccb (p_ccb);
+ (*connect_cfm)(local_cid, L2CAP_CONN_NO_LINK);
+ } else {
+ /* We have feature info, so now send peer connect request */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT);
+ l2cu_send_peer_connect_req (p_ccb); /* Start Connection */
+ }
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_w4_l2ca_connect_rsp
+**
+** Description This function handles events when the channel is in
+** CST_W4_L2CA_CONNECT_RSP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_w4_l2ca_connect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2C_CONN_INFO *p_ci;
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: W4_L2CA_CON_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT ("L2CAP - st: W4_L2CA_CON_RSP evt: %d", event);
+#endif
+
+ switch (event) {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CA_CONNECT_RSP:
+ p_ci = (tL2C_CONN_INFO *)p_data;
+
+ /* Result should be OK or PENDING */
+ if ((!p_ci) || (p_ci->l2cap_result == L2CAP_CONN_OK)) {
+ l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_OK, 0);
+ p_ccb->chnl_state = CST_CONFIG;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ } else {
+ /* If pending, stay in same state and start extended timer */
+ l2cu_send_peer_connect_rsp (p_ccb, p_ci->l2cap_result, p_ci->l2cap_status);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT_EXT);
+ }
+ break;
+
+ case L2CEVT_L2CA_CONNECT_RSP_NEG:
+ p_ci = (tL2C_CONN_INFO *)p_data;
+ l2cu_send_peer_connect_rsp (p_ccb, p_ci->l2cap_result, p_ci->l2cap_status);
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ l2cu_send_peer_connect_rsp (p_ccb, L2CAP_CONN_NO_PSM, 0);
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ osi_free (p_data);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ l2cu_send_peer_disc_req (p_ccb);
+ p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
+ break;
+
+ case L2CEVT_L2CAP_INFO_RSP:
+ /* We have feature info, so now give the upper layer connect IND */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CONNECT_TOUT);
+ L2CAP_TRACE_API ("L2CAP - Calling Connect_Ind_Cb(), CID: 0x%04x", p_ccb->local_cid);
+
+ (*p_ccb->p_rcb->api.pL2CA_ConnectInd_Cb) (p_ccb->p_lcb->remote_bd_addr,
+ p_ccb->local_cid,
+ p_ccb->p_rcb->psm,
+ p_ccb->remote_id);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_config
+**
+** Description This function handles events when the channel is in
+** CONFIG state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_config (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2CAP_CFG_INFO *p_cfg = (tL2CAP_CFG_INFO *)p_data;
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+ UINT8 cfg_result;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: CONFIG evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT ("L2CAP - st: CONFIG evt: %d", event);
+#endif
+
+ switch (event) {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */
+
+ if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK) {
+ L2CAP_TRACE_EVENT ("L2CAP - Calling Config_Req_Cb(), CID: 0x%04x, C-bit %d",
+ p_ccb->local_cid, (p_cfg->flags & L2CAP_CFG_FLAGS_MASK_CONT));
+ (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg);
+ } else if (cfg_result == L2CAP_PEER_CFG_DISCONNECT) {
+ /* Disconnect if channels are incompatible */
+ L2CAP_TRACE_EVENT ("L2CAP - incompatible configurations disconnect");
+ l2cu_disconnect_chnl (p_ccb);
+ } else { /* Return error to peer so he can renegotiate if possible */
+ L2CAP_TRACE_EVENT ("L2CAP - incompatible configurations trying reconfig");
+ l2cu_send_peer_config_rsp (p_ccb, p_cfg);
+ }
+ break;
+
+ case L2CEVT_L2CAP_CONFIG_RSP: /* Peer config response */
+ l2cu_process_peer_cfg_rsp (p_ccb, p_cfg);
+
+ if (p_cfg->result != L2CAP_CFG_PENDING) {
+ /* TBD: When config options grow beyong minimum MTU (48 bytes)
+ * logic needs to be added to handle responses with
+ * continuation bit set in flags field.
+ * 1. Send additional config request out until C-bit is cleared in response
+ */
+ p_ccb->config_done |= OB_CFG_DONE;
+
+ if (p_ccb->config_done & IB_CFG_DONE) {
+ /* Verify two sides are in compatible modes before continuing */
+ if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) {
+ l2cu_send_peer_disc_req (p_ccb);
+ L2CAP_TRACE_WARNING ("L2CAP - Calling Disconnect_Ind_Cb(Incompatible CFG), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+ }
+
+ p_ccb->config_done |= RECONFIG_FLAG;
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_link_adjust_chnl_allocation ();
+ btu_stop_timer (&p_ccb->timer_entry);
+
+ /* If using eRTM and waiting for an ACK, restart the ACK timer */
+ if (p_ccb->fcrb.wait_ack) {
+ l2c_fcr_start_timer(p_ccb);
+ }
+
+ /*
+ ** check p_ccb->our_cfg.fcr.mon_tout and p_ccb->our_cfg.fcr.rtrans_tout
+ ** we may set them to zero when sending config request during renegotiation
+ */
+ if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ && ((p_ccb->our_cfg.fcr.mon_tout == 0) || (p_ccb->our_cfg.fcr.rtrans_tout))) {
+ l2c_fcr_adj_monitor_retran_timeout (p_ccb);
+ }
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.connect_tick_count = osi_time_get_os_boottime_ms();
+#endif
+ /* See if we can forward anything on the hold queue */
+ if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ }
+ }
+ }
+
+ L2CAP_TRACE_API ("L2CAP - Calling Config_Rsp_Cb(), CID: 0x%04x", p_ccb->local_cid);
+ (*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(p_ccb->local_cid, p_cfg);
+ break;
+
+ case L2CEVT_L2CAP_CONFIG_RSP_NEG: /* Peer config error rsp */
+ /* Disable the Timer */
+ btu_stop_timer (&p_ccb->timer_entry);
+
+ /* If failure was channel mode try to renegotiate */
+ if (l2c_fcr_renegotiate_chan(p_ccb, p_cfg) == FALSE) {
+ L2CAP_TRACE_API ("L2CAP - Calling Config_Rsp_Cb(), CID: 0x%04x, Failure: %d", p_ccb->local_cid, p_cfg->result);
+ (*p_ccb->p_rcb->api.pL2CA_ConfigCfm_Cb)(p_ccb->local_cid, p_cfg);
+ }
+ break;
+
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
+ p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP;
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x Conf Needed", p_ccb->local_cid);
+ (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE);
+ break;
+
+ case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */
+ l2cu_process_our_cfg_req (p_ccb, p_cfg);
+ l2cu_send_peer_config_req (p_ccb, p_cfg);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ break;
+
+ case L2CEVT_L2CA_CONFIG_RSP: /* Upper layer config rsp */
+ l2cu_process_our_cfg_rsp (p_ccb, p_cfg);
+
+ /* Not finished if continuation flag is set */
+ if ( (p_cfg->flags & L2CAP_CFG_FLAGS_MASK_CONT) || (p_cfg->result == L2CAP_CFG_PENDING) ) {
+ /* Send intermediate response; remain in cfg state */
+ l2cu_send_peer_config_rsp (p_ccb, p_cfg);
+ break;
+ }
+
+ /* Local config done; clear cached configuration in case reconfig takes place later */
+ p_ccb->peer_cfg.mtu_present = FALSE;
+ p_ccb->peer_cfg.flush_to_present = FALSE;
+ p_ccb->peer_cfg.qos_present = FALSE;
+
+ p_ccb->config_done |= IB_CFG_DONE;
+
+ if (p_ccb->config_done & OB_CFG_DONE) {
+ /* Verify two sides are in compatible modes before continuing */
+ if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) {
+ l2cu_send_peer_disc_req (p_ccb);
+ L2CAP_TRACE_WARNING ("L2CAP - Calling Disconnect_Ind_Cb(Incompatible CFG), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+ }
+
+ p_ccb->config_done |= RECONFIG_FLAG;
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_link_adjust_chnl_allocation ();
+ btu_stop_timer (&p_ccb->timer_entry);
+ }
+
+ l2cu_send_peer_config_rsp (p_ccb, p_cfg);
+
+ /* If using eRTM and waiting for an ACK, restart the ACK timer */
+ if (p_ccb->fcrb.wait_ack) {
+ l2c_fcr_start_timer(p_ccb);
+ }
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.connect_tick_count = osi_time_get_os_boottime_ms();
+#endif
+
+ /* See if we can forward anything on the hold queue */
+ if ( (p_ccb->chnl_state == CST_OPEN) &&
+ (!fixed_queue_is_empty(p_ccb->xmit_hold_q))) {
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ }
+ break;
+
+ case L2CEVT_L2CA_CONFIG_RSP_NEG: /* Upper layer config reject */
+ l2cu_send_peer_config_rsp (p_ccb, p_cfg);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ l2cu_send_peer_disc_req (p_ccb);
+ p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ L2CAP_TRACE_API ("L2CAP - Calling DataInd_Cb(), CID: 0x%04x", p_ccb->local_cid);
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ if (p_ccb->local_cid >= L2CAP_FIRST_FIXED_CHNL &&
+ p_ccb->local_cid <= L2CAP_LAST_FIXED_CHNL) {
+ if (p_ccb->local_cid < L2CAP_BASE_APPL_CID) {
+ if (l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb) {
+ (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
+ (p_ccb->local_cid, p_ccb->p_lcb->remote_bd_addr, (BT_HDR *)p_data);
+ } else {
+ osi_free (p_data);
+ }
+ break;
+ }
+ }
+#endif
+ (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ if (p_ccb->config_done & OB_CFG_DONE) {
+ l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data);
+ } else {
+ osi_free (p_data);
+ }
+ break;
+
+ case L2CEVT_TIMEOUT:
+ l2cu_send_peer_disc_req (p_ccb);
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed",
+ p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_open
+**
+** Description This function handles events when the channel is in
+** OPEN state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ UINT16 local_cid = p_ccb->local_cid;
+ tL2CAP_CFG_INFO *p_cfg;
+ tL2C_CHNL_STATE tempstate;
+ UINT8 tempcfgdone;
+ UINT8 cfg_result;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: OPEN evt: %s",
+ p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT ("L2CAP - st: OPEN evt: %d", event);
+#endif
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ if ( local_cid == L2CAP_CONNECTIONLESS_CID ) {
+ /* check if this event can be processed by UCD */
+ if ( l2c_ucd_process_event (p_ccb, event, p_data) ) {
+ /* The event is processed by UCD state machine */
+ return;
+ }
+ }
+#endif
+
+ switch (event) {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed",
+ p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ if (p_ccb->p_rcb) {
+ (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(local_cid, FALSE);
+ }
+ break;
+
+ case L2CEVT_LP_QOS_VIOLATION_IND: /* QOS violation */
+ /* Tell upper layer. If service guaranteed, then clear the channel */
+ if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb) {
+ (*p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)(p_ccb->p_lcb->remote_bd_addr);
+ }
+ break;
+
+ case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */
+ p_cfg = (tL2CAP_CFG_INFO *)p_data;
+
+ tempstate = p_ccb->chnl_state;
+ tempcfgdone = p_ccb->config_done;
+ p_ccb->chnl_state = CST_CONFIG;
+ p_ccb->config_done &= ~CFG_DONE_MASK;
+
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+
+ if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK) {
+ (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg);
+ }
+
+ /* Error in config parameters: reset state and config flag */
+ else if (cfg_result == L2CAP_PEER_CFG_UNACCEPTABLE) {
+ btu_stop_timer(&p_ccb->timer_entry);
+ p_ccb->chnl_state = tempstate;
+ p_ccb->config_done = tempcfgdone;
+ l2cu_send_peer_config_rsp (p_ccb, p_cfg);
+ } else { /* L2CAP_PEER_CFG_DISCONNECT */
+ /* Disconnect if channels are incompatible
+ * Note this should not occur if reconfigure
+ * since this should have never passed original config.
+ */
+ l2cu_disconnect_chnl (p_ccb);
+ }
+ break;
+
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */
+// btla-specific ++
+ /* Make sure we are not in sniff mode */
+ {
+ tBTM_PM_PWR_MD settings;
+ memset((void *)&settings, 0, sizeof(settings));
+ settings.mode = BTM_PM_MD_ACTIVE;
+ BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
+ }
+// btla-specific --
+
+ p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x Conf Needed", p_ccb->local_cid);
+ (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ if ((p_ccb->p_rcb) && (p_ccb->p_rcb->api.pL2CA_DataInd_Cb)) {
+ (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data);
+ }
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
+ /* Make sure we are not in sniff mode */
+ {
+ tBTM_PM_PWR_MD settings;
+ memset((void *)&settings, 0, sizeof(settings));
+ settings.mode = BTM_PM_MD_ACTIVE;
+ BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
+ }
+
+ l2cu_send_peer_disc_req (p_ccb);
+ p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data);
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ break;
+
+ case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */
+ p_ccb->chnl_state = CST_CONFIG;
+ p_ccb->config_done &= ~CFG_DONE_MASK;
+ l2cu_process_our_cfg_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
+ l2cu_send_peer_config_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ /* Process the monitor/retransmission time-outs in flow control/retrans mode */
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) {
+ l2c_fcr_proc_tout (p_ccb);
+ }
+ break;
+
+ case L2CEVT_ACK_TIMEOUT:
+ l2c_fcr_proc_ack_tout (p_ccb);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_w4_l2cap_disconnect_rsp
+**
+** Description This function handles events when the channel is in
+** CST_W4_L2CAP_DISCONNECT_RSP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_w4_l2cap_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2CA_DISCONNECT_CFM_CB *disconnect_cfm = p_ccb->p_rcb->api.pL2CA_DisconnectCfm_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: W4_L2CAP_DISC_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT ("L2CAP - st: W4_L2CAP_DISC_RSP evt: %d", event);
+#endif
+
+ switch (event) {
+ case L2CEVT_L2CAP_DISCONNECT_RSP: /* Peer disconnect response */
+ l2cu_release_ccb (p_ccb);
+ if (disconnect_cfm) {
+ L2CAP_TRACE_API ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid);
+ (*disconnect_cfm)(local_cid, L2CAP_DISC_OK);
+ }
+ break;
+
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnect request */
+ l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid);
+ l2cu_release_ccb (p_ccb);
+ if (disconnect_cfm) {
+ L2CAP_TRACE_API ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid);
+ (*disconnect_cfm)(local_cid, L2CAP_DISC_OK);
+ }
+ break;
+
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ case L2CEVT_TIMEOUT: /* Timeout */
+ l2cu_release_ccb (p_ccb);
+ if (disconnect_cfm) {
+ L2CAP_TRACE_API ("L2CAP - Calling DisconnectCfm_Cb(), CID: 0x%04x", local_cid);
+ (*disconnect_cfm)(local_cid, L2CAP_DISC_TIMEOUT);
+ }
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ osi_free (p_data);
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_csm_w4_l2ca_disconnect_rsp
+**
+** Description This function handles events when the channel is in
+** CST_W4_L2CA_DISCONNECT_RSP state.
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_csm_w4_l2ca_disconnect_rsp (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ tL2CA_DISCONNECT_IND_CB *disconnect_ind = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+ UINT16 local_cid = p_ccb->local_cid;
+
+#if (BT_TRACE_VERBOSE == TRUE)
+ L2CAP_TRACE_EVENT ("L2CAP - LCID: 0x%04x st: W4_L2CA_DISC_RSP evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
+#else
+ L2CAP_TRACE_EVENT ("L2CAP - st: W4_L2CA_DISC_RSP evt: %d", event);
+#endif
+
+ switch (event) {
+ case L2CEVT_LP_DISCONNECT_IND: /* Link was disconnected */
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid);
+ L2CAP_TRACE_API ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
+ l2cu_release_ccb (p_ccb);
+ (*disconnect_ind)(local_cid, FALSE);
+ break;
+
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper disconnect request */
+ case L2CEVT_L2CA_DISCONNECT_RSP: /* Upper disconnect response */
+ l2cu_send_peer_disc_rsp (p_ccb->p_lcb, p_ccb->remote_id, p_ccb->local_cid, p_ccb->remote_cid);
+ l2cu_release_ccb (p_ccb);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ osi_free (p_data);
+ break;
+ }
+}
+#endif /// (L2CAP_COC_INCLUDED == TRUE)
+
+#if (BT_TRACE_VERBOSE == TRUE)
+/*******************************************************************************
+**
+** Function l2c_csm_get_event_name
+**
+** Description This function returns the event name.
+**
+** NOTE conditionally compiled to save memory.
+**
+** Returns pointer to the name
+**
+*******************************************************************************/
+static char *l2c_csm_get_event_name (UINT16 event)
+{
+ switch (event) {
+ case L2CEVT_LP_CONNECT_CFM: /* Lower layer connect confirm */
+ return ("LOWER_LAYER_CONNECT_CFM");
+ case L2CEVT_LP_CONNECT_CFM_NEG: /* Lower layer connect confirm (failed) */
+ return ("LOWER_LAYER_CONNECT_CFM_NEG");
+ case L2CEVT_LP_CONNECT_IND: /* Lower layer connect indication */
+ return ("LOWER_LAYER_CONNECT_IND");
+ case L2CEVT_LP_DISCONNECT_IND: /* Lower layer disconnect indication */
+ return ("LOWER_LAYER_DISCONNECT_IND");
+ case L2CEVT_LP_QOS_CFM: /* Lower layer QOS confirmation */
+ return ("LOWER_LAYER_QOS_CFM");
+ case L2CEVT_LP_QOS_CFM_NEG: /* Lower layer QOS confirmation (failed)*/
+ return ("LOWER_LAYER_QOS_CFM_NEG");
+ case L2CEVT_LP_QOS_VIOLATION_IND: /* Lower layer QOS violation indication */
+ return ("LOWER_LAYER_QOS_VIOLATION_IND");
+
+ case L2CEVT_SEC_COMP: /* Security cleared successfully */
+ return ("SECURITY_COMPLETE");
+ case L2CEVT_SEC_COMP_NEG: /* Security procedure failed */
+ return ("SECURITY_COMPLETE_NEG");
+
+ case L2CEVT_L2CAP_CONNECT_REQ: /* Peer connection request */
+ return ("PEER_CONNECT_REQ");
+ case L2CEVT_L2CAP_CONNECT_RSP: /* Peer connection response */
+ return ("PEER_CONNECT_RSP");
+ case L2CEVT_L2CAP_CONNECT_RSP_PND: /* Peer connection response pending */
+ return ("PEER_CONNECT_RSP_PND");
+ case L2CEVT_L2CAP_CONNECT_RSP_NEG: /* Peer connection response (failed) */
+ return ("PEER_CONNECT_RSP_NEG");
+ case L2CEVT_L2CAP_CONFIG_REQ: /* Peer configuration request */
+ return ("PEER_CONFIG_REQ");
+ case L2CEVT_L2CAP_CONFIG_RSP: /* Peer configuration response */
+ return ("PEER_CONFIG_RSP");
+ case L2CEVT_L2CAP_CONFIG_RSP_NEG: /* Peer configuration response (failed) */
+ return ("PEER_CONFIG_RSP_NEG");
+ case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnect request */
+ return ("PEER_DISCONNECT_REQ");
+ case L2CEVT_L2CAP_DISCONNECT_RSP: /* Peer disconnect response */
+ return ("PEER_DISCONNECT_RSP");
+ case L2CEVT_L2CAP_DATA: /* Peer data */
+ return ("PEER_DATA");
+
+ case L2CEVT_L2CA_CONNECT_REQ: /* Upper layer connect request */
+ return ("UPPER_LAYER_CONNECT_REQ");
+ case L2CEVT_L2CA_CONNECT_RSP: /* Upper layer connect response */
+ return ("UPPER_LAYER_CONNECT_RSP");
+ case L2CEVT_L2CA_CONNECT_RSP_NEG: /* Upper layer connect response (failed)*/
+ return ("UPPER_LAYER_CONNECT_RSP_NEG");
+ case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config request */
+ return ("UPPER_LAYER_CONFIG_REQ");
+ case L2CEVT_L2CA_CONFIG_RSP: /* Upper layer config response */
+ return ("UPPER_LAYER_CONFIG_RSP");
+ case L2CEVT_L2CA_CONFIG_RSP_NEG: /* Upper layer config response (failed) */
+ return ("UPPER_LAYER_CONFIG_RSP_NEG");
+ case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper layer disconnect request */
+ return ("UPPER_LAYER_DISCONNECT_REQ");
+ case L2CEVT_L2CA_DISCONNECT_RSP: /* Upper layer disconnect response */
+ return ("UPPER_LAYER_DISCONNECT_RSP");
+ case L2CEVT_L2CA_DATA_READ: /* Upper layer data read */
+ return ("UPPER_LAYER_DATA_READ");
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data write */
+ return ("UPPER_LAYER_DATA_WRITE");
+ case L2CEVT_TIMEOUT: /* Timeout */
+ return ("TIMEOUT");
+ case L2CEVT_SEC_RE_SEND_CMD:
+ return ("SEC_RE_SEND_CMD");
+ case L2CEVT_L2CAP_INFO_RSP: /* Peer information response */
+ return ("L2CEVT_L2CAP_INFO_RSP");
+ case L2CEVT_ACK_TIMEOUT:
+ return ("L2CEVT_ACK_TIMEOUT");
+
+ default:
+ return ("???? UNKNOWN EVENT");
+ }
+}
+#endif /* (BT_TRACE_VERBOSE == TRUE) */
+
+
+/*******************************************************************************
+**
+** Function l2c_enqueue_peer_data
+**
+** Description Enqueues data destined for the peer in the ccb. Handles
+** FCR segmentation and checks for congestion.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_enqueue_peer_data (tL2C_CCB *p_ccb, BT_HDR *p_buf)
+{
+ UINT8 *p;
+
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) {
+ p_buf->event = 0;
+ } else {
+ /* Save the channel ID for faster counting */
+ p_buf->event = p_ccb->local_cid;
+
+ /* Step back to add the L2CAP header */
+ p_buf->offset -= L2CAP_PKT_OVERHEAD;
+ p_buf->len += L2CAP_PKT_OVERHEAD;
+
+ /* Set the pointer to the beginning of the data */
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+
+ /* Now the L2CAP header */
+ UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD);
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ }
+
+ fixed_queue_enqueue(p_ccb->xmit_hold_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT);
+
+ l2cu_check_channel_congestion (p_ccb);
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ /* if new packet is higher priority than serving ccb and it is not overrun */
+ if (( p_ccb->p_lcb->rr_pri > p_ccb->ccb_priority )
+ && ( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota > 0)) {
+ /* send out higher priority packet */
+ p_ccb->p_lcb->rr_pri = p_ccb->ccb_priority;
+ }
+#endif
+
+ /* if we are doing a round robin scheduling, set the flag */
+ if (p_ccb->p_lcb->link_xmit_quota == 0) {
+ l2cb.check_round_robin = TRUE;
+ }
+}
diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_fcr.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_fcr.c
new file mode 100644
index 00000000..1da2459d
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_fcr.c
@@ -0,0 +1,2229 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2004-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 L2CAP 1.2 Flow Control and retransmissions
+ * functions
+ *
+ ******************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "common/bt_trace.h"
+#include "stack/bt_types.h"
+#include "stack/hcimsgs.h"
+#include "stack/l2c_api.h"
+#include "l2c_int.h"
+#include "stack/l2cdefs.h"
+#include "stack/btm_api.h"
+#include "btm_int.h"
+#include "stack/btu.h"
+#include "osi/allocator.h"
+
+#if (L2CAP_COC_INCLUDED == TRUE)
+
+/* Flag passed to retransmit_i_frames() when all packets should be retransmitted */
+#define L2C_FCR_RETX_ALL_PKTS 0xFF
+
+#if BT_TRACE_VERBOSE == TRUE
+static char *SAR_types[] = { "Unsegmented", "Start", "End", "Continuation" };
+static char *SUP_types[] = { "RR", "REJ", "RNR", "SREJ" };
+#endif
+
+/* Look-up table for the CRC calculation */
+static const unsigned short crctab[256] = {
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+ 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+ 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+ 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+ 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+ 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+ 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+ 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+ 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+ 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+ 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+ 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+ 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+ 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+ 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+ 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040,
+};
+
+
+/*******************************************************************************
+** Static local functions
+*/
+static BOOLEAN process_reqseq (tL2C_CCB *p_ccb, UINT16 ctrl_word);
+static void process_s_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word);
+static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, BOOLEAN delay_ack);
+static BOOLEAN retransmit_i_frames (tL2C_CCB *p_ccb, UINT8 tx_seq);
+static void prepare_I_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, BOOLEAN is_retransmission);
+static void process_stream_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf);
+static BOOLEAN do_sar_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word);
+
+#if (L2CAP_ERTM_STATS == TRUE)
+static void l2c_fcr_collect_ack_delay (tL2C_CCB *p_ccb, UINT8 num_bufs_acked);
+#endif
+
+/*******************************************************************************
+**
+** Function l2c_fcr_updcrc
+**
+** Description This function computes the CRC using the look-up table.
+**
+** Returns CRC
+**
+*******************************************************************************/
+static unsigned short l2c_fcr_updcrc(unsigned short icrc, unsigned char *icp, int icnt)
+{
+ register unsigned short crc = icrc;
+ register unsigned char *cp = icp;
+ register int cnt = icnt;
+
+ while (cnt--) {
+ crc = ((crc >> 8) & 0xff) ^ crctab[(crc & 0xff) ^ *cp++];
+ }
+
+ return (crc);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_tx_get_fcs
+**
+** Description This function computes the CRC for a frame to be TXed.
+**
+** Returns CRC
+**
+*******************************************************************************/
+static UINT16 l2c_fcr_tx_get_fcs (BT_HDR *p_buf)
+{
+ UINT8 *p = ((UINT8 *) (p_buf + 1)) + p_buf->offset;
+
+ return (l2c_fcr_updcrc (L2CAP_FCR_INIT_CRC, p, p_buf->len));
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_rx_get_fcs
+**
+** Description This function computes the CRC for a received frame.
+**
+** Returns CRC
+**
+*******************************************************************************/
+static UINT16 l2c_fcr_rx_get_fcs (BT_HDR *p_buf)
+{
+ UINT8 *p = ((UINT8 *) (p_buf + 1)) + p_buf->offset;
+
+ /* offset points past the L2CAP header, but the CRC check includes it */
+ p -= L2CAP_PKT_OVERHEAD;
+
+ return (l2c_fcr_updcrc (L2CAP_FCR_INIT_CRC, p, p_buf->len + L2CAP_PKT_OVERHEAD));
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_start_timer
+**
+** Description This function starts the (monitor or retransmission) timer.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_start_timer (tL2C_CCB *p_ccb)
+{
+ assert(p_ccb != NULL);
+ UINT32 tout;
+
+ /* The timers which are in milliseconds */
+ if (p_ccb->fcrb.wait_ack) {
+ tout = (UINT32)p_ccb->our_cfg.fcr.mon_tout;
+ } else {
+ tout = (UINT32)p_ccb->our_cfg.fcr.rtrans_tout;
+ }
+
+ /* Only start a timer that was not started */
+ if (p_ccb->fcrb.mon_retrans_timer.in_use == 0) {
+ btu_start_quick_timer (&p_ccb->fcrb.mon_retrans_timer, BTU_TTYPE_L2CAP_CHNL, tout * QUICK_TIMER_TICKS_PER_SEC / 1000);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_stop_timer
+**
+** Description This function stops the (monitor or transmission) timer.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_stop_timer (tL2C_CCB *p_ccb)
+{
+ assert(p_ccb != NULL);
+ if (p_ccb->fcrb.mon_retrans_timer.in_use) {
+ btu_stop_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_free_timer
+**
+** Description This function releases the (monitor or transmission) timer.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_free_timer (tL2C_CCB *p_ccb)
+{
+ assert(p_ccb != NULL);
+ btu_free_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_cleanup
+**
+** Description This function cleans up the variable used for flow-control/retrans.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_cleanup (tL2C_CCB *p_ccb)
+{
+ assert(p_ccb != NULL);
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+
+ l2c_fcr_stop_timer (p_ccb);
+
+ if (p_fcrb->p_rx_sdu) {
+ osi_free(p_fcrb->p_rx_sdu);
+ p_fcrb->p_rx_sdu = NULL;
+ }
+
+
+ fixed_queue_free(p_fcrb->waiting_for_ack_q, osi_free_func);
+ p_fcrb->waiting_for_ack_q = NULL;
+
+ fixed_queue_free(p_fcrb->srej_rcv_hold_q, osi_free_func);
+ p_fcrb->srej_rcv_hold_q = NULL;
+
+ fixed_queue_free(p_fcrb->retrans_q, osi_free_func);
+ p_fcrb->retrans_q = NULL;
+
+ btu_free_quick_timer (&p_fcrb->ack_timer);
+ memset(&p_fcrb->ack_timer, 0, sizeof(TIMER_LIST_ENT));
+
+ btu_free_quick_timer (&p_ccb->fcrb.mon_retrans_timer);
+ memset(&p_fcrb->mon_retrans_timer, 0, sizeof(TIMER_LIST_ENT));
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ if ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID) && (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) ) {
+ UINT32 dur = osi_time_get_os_boottime_ms() - p_ccb->fcrb.connect_tick_count;
+ char *p_str = (char *)osi_malloc(120);
+ UINT16 i;
+ UINT32 throughput_avg, ack_delay_avg, ack_q_count_avg;
+
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "--- L2CAP ERTM Stats for CID: 0x%04x Duration: %08ums", p_ccb->local_cid, dur);
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "Retransmissions:%08u Times Flow Controlled:%08u Retrans Touts:%08u Ack Touts:%08u",
+ p_ccb->fcrb.pkts_retransmitted, p_ccb->fcrb.xmit_window_closed, p_ccb->fcrb.retrans_touts, p_ccb->fcrb.xmit_ack_touts);
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "Times there is less than 2 packets in controller when flow controlled:%08u", p_ccb->fcrb.controller_idle);
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "max_held_acks:%08u, in_cfg.fcr.tx_win_sz:%08u", p_ccb->fcrb.max_held_acks, p_ccb->peer_cfg.fcr.tx_win_sz );
+ if (p_str) {
+ sprintf(p_str, "Sent Pkts:%08u Bytes:%10u(%06u/sec) RR:%08u REJ:%08u RNR:%08u SREJ:%08u",
+ p_ccb->fcrb.ertm_pkt_counts[0], p_ccb->fcrb.ertm_byte_counts[0],
+ (dur >= 10 ? (p_ccb->fcrb.ertm_byte_counts[0] * 100) / (dur / 10) : 0),
+ p_ccb->fcrb.s_frames_sent[0], p_ccb->fcrb.s_frames_sent[1], p_ccb->fcrb.s_frames_sent[2], p_ccb->fcrb.s_frames_sent[3]);
+
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str);
+
+ sprintf(p_str, "Rcvd Pkts:%08u Bytes:%10u(%06u/sec) RR:%08u REJ:%08u RNR:%08u SREJ:%08u",
+ p_ccb->fcrb.ertm_pkt_counts[1], p_ccb->fcrb.ertm_byte_counts[1],
+ (dur >= 10 ? (p_ccb->fcrb.ertm_byte_counts[1] * 100) / (dur / 10) : 0),
+ p_ccb->fcrb.s_frames_rcvd[0], p_ccb->fcrb.s_frames_rcvd[1], p_ccb->fcrb.s_frames_rcvd[2], p_ccb->fcrb.s_frames_rcvd[3]);
+
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str);
+
+ throughput_avg = 0;
+ ack_delay_avg = 0;
+ ack_q_count_avg = 0;
+
+ for (i = 0; i < L2CAP_ERTM_STATS_NUM_AVG; i++ ) {
+ if (i == p_ccb->fcrb.ack_delay_avg_index ) {
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "[%02u] collecting data ...", i );
+ continue;
+ }
+
+ sprintf(p_str, "[%02u] throughput: %5u, ack_delay avg:%3u, min:%3u, max:%3u, ack_q_count avg:%3u, min:%3u, max:%3u",
+ i, p_ccb->fcrb.throughput[i],
+ p_ccb->fcrb.ack_delay_avg[i], p_ccb->fcrb.ack_delay_min[i], p_ccb->fcrb.ack_delay_max[i],
+ p_ccb->fcrb.ack_q_count_avg[i], p_ccb->fcrb.ack_q_count_min[i], p_ccb->fcrb.ack_q_count_max[i] );
+
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", p_str);
+
+ throughput_avg += p_ccb->fcrb.throughput[i];
+ ack_delay_avg += p_ccb->fcrb.ack_delay_avg[i];
+ ack_q_count_avg += p_ccb->fcrb.ack_q_count_avg[i];
+ }
+
+ throughput_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1);
+ ack_delay_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1);
+ ack_q_count_avg /= (L2CAP_ERTM_STATS_NUM_AVG - 1);
+
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "throughput_avg: %8u (kbytes/sec), ack_delay_avg: %8u ms, ack_q_count_avg: %8u",
+ throughput_avg, ack_delay_avg, ack_q_count_avg );
+
+ osi_free(p_str);
+ }
+
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC,
+ "---");
+ }
+#endif
+
+ memset (p_fcrb, 0, sizeof (tL2C_FCRB));
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_clone_buf
+**
+** Description This function allocates and copies requested part of a buffer
+** at a new-offset.
+**
+** Returns pointer to new buffer
+**
+*******************************************************************************/
+BT_HDR *l2c_fcr_clone_buf (BT_HDR *p_buf, UINT16 new_offset, UINT16 no_of_bytes)
+{
+ assert(p_buf != NULL);
+ /*
+ * NOTE: We allocate extra L2CAP_FCS_LEN octets, in case we need to put
+ * the FCS (Frame Check Sequence) at the end of the buffer.
+ */
+ uint16_t buf_size = no_of_bytes + sizeof(BT_HDR) + new_offset + L2CAP_FCS_LEN;
+#if (L2CAP_ERTM_STATS == TRUE)
+ /*
+ * NOTE: If L2CAP_ERTM_STATS is enabled, we need 4 extra octets at the
+ * end for a timestamp at the end of an I-frame.
+ */
+ buf_size += sizeof(uint32_t);
+#endif
+ BT_HDR *p_buf2 = (BT_HDR *)osi_malloc(buf_size);
+
+ p_buf2->offset = new_offset;
+ p_buf2->len = no_of_bytes;
+ memcpy(((UINT8 *)(p_buf2 + 1)) + p_buf2->offset,
+ ((UINT8 *)(p_buf + 1)) + p_buf->offset,
+ no_of_bytes);
+
+ return (p_buf2);
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_is_flow_controlled
+**
+** Description This function checks if the CCB is flow controlled by peer.
+**
+** Returns The control word
+**
+*******************************************************************************/
+BOOLEAN l2c_fcr_is_flow_controlled (tL2C_CCB *p_ccb)
+{
+ assert(p_ccb != NULL);
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) {
+ /* Check if remote side flowed us off or the transmit window is full */
+ if ( (p_ccb->fcrb.remote_busy == TRUE)
+ || (fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q) >= p_ccb->peer_cfg.fcr.tx_win_sz) ) {
+#if (L2CAP_ERTM_STATS == TRUE)
+ if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
+ p_ccb->fcrb.xmit_window_closed++;
+
+ if ((p_ccb->p_lcb->sent_not_acked < 2) && (l2cb.controller_xmit_window > 0)) {
+ p_ccb->fcrb.controller_idle++;
+ }
+ }
+#endif
+ return (TRUE);
+ }
+ }
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function prepare_I_frame
+**
+** Description This function sets the FCR variables in an I-frame that is
+** about to be sent to HCI for transmission. This may be the
+** first time the I-frame is sent, or a retransmission
+**
+** Returns -
+**
+*******************************************************************************/
+static void prepare_I_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, BOOLEAN is_retransmission)
+{
+ assert(p_ccb != NULL);
+ assert(p_buf != NULL);
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ UINT8 *p;
+ UINT16 fcs;
+ UINT16 ctrl_word;
+ BOOLEAN set_f_bit = p_fcrb->send_f_rsp;
+
+ p_fcrb->send_f_rsp = FALSE;
+
+ if (is_retransmission) {
+ /* Get the old control word and clear out the old req_seq and F bits */
+ p = ((UINT8 *) (p_buf + 1)) + p_buf->offset + L2CAP_PKT_OVERHEAD;
+
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ ctrl_word &= ~(L2CAP_FCR_REQ_SEQ_BITS + L2CAP_FCR_F_BIT);
+ } else {
+ ctrl_word = p_buf->layer_specific & L2CAP_FCR_SEG_BITS; /* SAR bits */
+ ctrl_word |= (p_fcrb->next_tx_seq << L2CAP_FCR_TX_SEQ_BITS_SHIFT); /* Tx Seq */
+
+ p_fcrb->next_tx_seq = (p_fcrb->next_tx_seq + 1) & L2CAP_FCR_SEQ_MODULO;
+ }
+
+ /* Set the F-bit and reqseq only if using re-transmission mode */
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) {
+ if (set_f_bit) {
+ ctrl_word |= L2CAP_FCR_F_BIT;
+ }
+
+ ctrl_word |= (p_fcrb->next_seq_expected) << L2CAP_FCR_REQ_SEQ_BITS_SHIFT;
+
+ p_fcrb->last_ack_sent = p_ccb->fcrb.next_seq_expected;
+
+ if (p_ccb->fcrb.ack_timer.in_use) {
+ btu_stop_quick_timer (&p_ccb->fcrb.ack_timer);
+ }
+ }
+
+ /* Set the control word */
+ p = ((UINT8 *) (p_buf + 1)) + p_buf->offset + L2CAP_PKT_OVERHEAD;
+
+ UINT16_TO_STREAM (p, ctrl_word);
+
+ /* Compute the FCS and add to the end of the buffer if not bypassed */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) {
+ /* length field in l2cap header has to include FCS length */
+ p = ((UINT8 *) (p_buf + 1)) + p_buf->offset;
+ UINT16_TO_STREAM (p, p_buf->len + L2CAP_FCS_LEN - L2CAP_PKT_OVERHEAD);
+
+ /* Calculate the FCS */
+ fcs = l2c_fcr_tx_get_fcs(p_buf);
+
+ /* Point to the end of the buffer and put the FCS there */
+ p = ((UINT8 *) (p_buf + 1)) + p_buf->offset + p_buf->len;
+
+ UINT16_TO_STREAM (p, fcs);
+
+ p_buf->len += L2CAP_FCS_LEN;
+ }
+
+#if BT_TRACE_VERBOSE == TRUE
+ if (is_retransmission) {
+ L2CAP_TRACE_EVENT ("L2CAP eRTM ReTx I-frame CID: 0x%04x Len: %u SAR: %s TxSeq: %u ReqSeq: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT],
+ (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ } else {
+ L2CAP_TRACE_EVENT ("L2CAP eRTM Tx I-frame CID: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT],
+ (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ }
+#endif
+
+ /* Start the retransmission timer if not already running */
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) {
+ l2c_fcr_start_timer (p_ccb);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_send_S_frame
+**
+** Description This function formats and sends an S-frame for transmission.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_send_S_frame (tL2C_CCB *p_ccb, UINT16 function_code, UINT16 pf_bit)
+{
+ assert(p_ccb != NULL);
+ BT_HDR *p_buf;
+ UINT8 *p;
+ UINT16 ctrl_word;
+ UINT16 fcs;
+
+ if ((!p_ccb->in_use) || (p_ccb->chnl_state != CST_OPEN)) {
+ return;
+ }
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.s_frames_sent[function_code]++;
+#endif
+
+ if (pf_bit == L2CAP_FCR_P_BIT) {
+ p_ccb->fcrb.wait_ack = TRUE;
+
+ l2c_fcr_stop_timer (p_ccb); /* Restart the monitor timer */
+ l2c_fcr_start_timer (p_ccb);
+ }
+
+ /* Create the control word to use */
+ ctrl_word = (function_code << L2CAP_FCR_SUP_SHIFT) | L2CAP_FCR_S_FRAME_BIT;
+ ctrl_word |= (p_ccb->fcrb.next_seq_expected << L2CAP_FCR_REQ_SEQ_BITS_SHIFT);
+ ctrl_word |= pf_bit;
+
+ if ((p_buf = (BT_HDR *)osi_malloc(L2CAP_CMD_BUF_SIZE)) != NULL) {
+ p_buf->offset = HCI_DATA_PREAMBLE_SIZE;
+ p_buf->len = L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD;
+
+ /* Set the pointer to the beginning of the data */
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+
+ /* Put in the L2CAP header */
+ UINT16_TO_STREAM (p, L2CAP_FCR_OVERHEAD + L2CAP_FCS_LEN);
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ UINT16_TO_STREAM (p, ctrl_word);
+
+ /* Compute the FCS and add to the end of the buffer if not bypassed */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) {
+ fcs = l2c_fcr_tx_get_fcs (p_buf);
+
+ UINT16_TO_STREAM (p, fcs);
+ p_buf->len += L2CAP_FCS_LEN;
+ } else { /* rewrite the length without FCS length */
+ p -= 6;
+ UINT16_TO_STREAM (p, L2CAP_FCR_OVERHEAD);
+ }
+
+ /* Now, the HCI transport header */
+ p_buf->layer_specific = L2CAP_NON_FLUSHABLE_PKT;
+ l2cu_set_acl_hci_header (p_buf, p_ccb);
+
+#if BT_TRACE_VERBOSE == TRUE
+ if ((((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 1)
+ || (((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 3)) {
+ L2CAP_TRACE_WARNING ("L2CAP eRTM Tx S-frame CID: 0x%04x ctrlword: 0x%04x Type: %s ReqSeq: %u P: %u F: %u",
+ p_ccb->local_cid, ctrl_word,
+ SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT],
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ L2CAP_TRACE_WARNING (" Buf Len: %u", p_buf->len);
+ } else {
+ L2CAP_TRACE_EVENT ("L2CAP eRTM Tx S-frame CID: 0x%04x ctrlword: 0x%04x Type: %s ReqSeq: %u P: %u F: %u",
+ p_ccb->local_cid, ctrl_word,
+ SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT],
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ L2CAP_TRACE_EVENT (" Buf Len: %u", p_buf->len);
+ }
+#endif /* BT_TRACE_VERBOSE */
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+
+ p_ccb->fcrb.last_ack_sent = p_ccb->fcrb.next_seq_expected;
+
+ if (p_ccb->fcrb.ack_timer.in_use) {
+ btu_stop_quick_timer (&p_ccb->fcrb.ack_timer);
+ }
+ } else {
+ L2CAP_TRACE_ERROR ("l2c_fcr_send_S_frame(No Resources) cid 0x%04x, Type: 0x%4x",
+ p_ccb->local_cid, function_code);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_proc_pdu
+**
+** Description This function is the entry point for processing of a
+** received PDU when in flow control and/or retransmission modes.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_proc_pdu (tL2C_CCB *p_ccb, BT_HDR *p_buf)
+{
+ assert(p_ccb != NULL);
+ assert(p_buf != NULL);
+ UINT8 *p;
+ UINT16 fcs;
+ UINT16 min_pdu_len;
+ UINT16 ctrl_word;
+
+ /* Check the length */
+ min_pdu_len = (p_ccb->bypass_fcs == L2CAP_BYPASS_FCS) ?
+ (UINT16)L2CAP_FCR_OVERHEAD : (UINT16)(L2CAP_FCS_LEN + L2CAP_FCR_OVERHEAD);
+
+ if (p_buf->len < min_pdu_len) {
+ L2CAP_TRACE_WARNING ("Rx L2CAP PDU: CID: 0x%04x Len too short: %u", p_ccb->local_cid, p_buf->len);
+ osi_free (p_buf);
+ return;
+ }
+
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_STREAM_MODE) {
+ process_stream_frame (p_ccb, p_buf);
+ return;
+ }
+
+#if BT_TRACE_VERBOSE == TRUE
+ /* Get the control word */
+ p = ((UINT8 *)(p_buf + 1)) + p_buf->offset;
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) {
+ if ((((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 1)
+ || (((ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT) == 3)) {
+ /* REJ or SREJ */
+ L2CAP_TRACE_WARNING ("L2CAP eRTM Rx S-frame: cid: 0x%04x Len: %u Type: %s ReqSeq: %u P: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT],
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ } else {
+ L2CAP_TRACE_EVENT ("L2CAP eRTM Rx S-frame: cid: 0x%04x Len: %u Type: %s ReqSeq: %u P: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SUP_types[(ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT],
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_P_BIT) >> L2CAP_FCR_P_BIT_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ }
+ } else {
+ L2CAP_TRACE_EVENT ("L2CAP eRTM Rx I-frame: cid: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT],
+ (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+ }
+
+ L2CAP_TRACE_EVENT (" eRTM Rx Nxt_tx_seq %u, Lst_rx_ack %u, Nxt_seq_exp %u, Lst_ack_snt %u, wt_q.cnt %u, tries %u",
+ p_ccb->fcrb.next_tx_seq, p_ccb->fcrb.last_rx_ack,
+ p_ccb->fcrb.next_seq_expected,
+ p_ccb->fcrb.last_ack_sent,
+ fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q),
+ p_ccb->fcrb.num_tries);
+
+#endif /* BT_TRACE_VERBOSE */
+
+ /* Verify FCS if using */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) {
+ p = ((UINT8 *)(p_buf + 1)) + p_buf->offset + p_buf->len - L2CAP_FCS_LEN;
+
+ /* Extract and drop the FCS from the packet */
+ STREAM_TO_UINT16 (fcs, p);
+ p_buf->len -= L2CAP_FCS_LEN;
+
+ if (l2c_fcr_rx_get_fcs(p_buf) != fcs) {
+ L2CAP_TRACE_WARNING ("Rx L2CAP PDU: CID: 0x%04x BAD FCS", p_ccb->local_cid);
+ osi_free(p_buf);
+ return;
+ }
+ }
+
+ /* Get the control word */
+ p = ((UINT8 *)(p_buf + 1)) + p_buf->offset;
+
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ p_buf->len -= L2CAP_FCR_OVERHEAD;
+ p_buf->offset += L2CAP_FCR_OVERHEAD;
+
+ /* If we had a poll bit outstanding, check if we got a final response */
+ if (p_ccb->fcrb.wait_ack) {
+ /* If final bit not set, ignore the frame unless it is a polled S-frame */
+ if ( !(ctrl_word & L2CAP_FCR_F_BIT) ) {
+ if ( (ctrl_word & L2CAP_FCR_P_BIT) && (ctrl_word & L2CAP_FCR_S_FRAME_BIT) ) {
+ if (p_ccb->fcrb.srej_sent) {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, L2CAP_FCR_F_BIT);
+ } else if (p_ccb->fcrb.local_busy) {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_F_BIT);
+ } else {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_F_BIT);
+ }
+
+ /* Got a poll while in wait_ack state, so re-start our timer with 1-second */
+ /* This is a small optimization... the monitor timer is 12 secs, but we saw */
+ /* that if the other side sends us a poll when we are waiting for a final, */
+ /* then it speeds up recovery significantly if we poll him back soon after his poll. */
+ btu_start_quick_timer (&p_ccb->fcrb.mon_retrans_timer, BTU_TTYPE_L2CAP_CHNL, QUICK_TIMER_TICKS_PER_SEC);
+ }
+ osi_free (p_buf);
+ return;
+ }
+
+ p_ccb->fcrb.wait_ack = FALSE;
+
+ /* P and F are mutually exclusive */
+ if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) {
+ ctrl_word &= ~L2CAP_FCR_P_BIT;
+ }
+
+ if (fixed_queue_is_empty(p_ccb->fcrb.waiting_for_ack_q)) {
+ p_ccb->fcrb.num_tries = 0;
+ }
+
+ l2c_fcr_stop_timer (p_ccb);
+ } else {
+ /* Otherwise, ensure the final bit is ignored */
+ ctrl_word &= ~L2CAP_FCR_F_BIT;
+ }
+
+ /* Process receive sequence number */
+ if (!process_reqseq (p_ccb, ctrl_word)) {
+ osi_free (p_buf);
+ return;
+ }
+
+ /* Process based on whether it is an S-frame or an I-frame */
+ if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) {
+ process_s_frame (p_ccb, p_buf, ctrl_word);
+ } else {
+ process_i_frame (p_ccb, p_buf, ctrl_word, FALSE);
+ }
+
+ /* Return if the channel got disconnected by a bad packet or max retransmissions */
+ if ( (!p_ccb->in_use) || (p_ccb->chnl_state != CST_OPEN) ) {
+ return;
+ }
+
+ /* If we have some buffers held while doing SREJ, and SREJ has cleared, process them now */
+ if ( (!p_ccb->fcrb.local_busy) && (!p_ccb->fcrb.srej_sent) &&
+ (!fixed_queue_is_empty(p_ccb->fcrb.srej_rcv_hold_q))) {
+ fixed_queue_t *temp_q = p_ccb->fcrb.srej_rcv_hold_q;
+ p_ccb->fcrb.srej_rcv_hold_q = fixed_queue_new(QUEUE_SIZE_MAX);
+
+ while ((p_buf = (BT_HDR *)fixed_queue_dequeue(temp_q, 0)) != NULL) {
+ if (p_ccb->in_use && (p_ccb->chnl_state == CST_OPEN)) {
+ /* Get the control word */
+ p = ((UINT8 *)(p_buf + 1)) + p_buf->offset - L2CAP_FCR_OVERHEAD;
+
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ L2CAP_TRACE_DEBUG ("l2c_fcr_proc_pdu() CID: 0x%04x Process Buffer from SREJ_Hold_Q TxSeq: %u Expected_Seq: %u",
+ p_ccb->local_cid, (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT,
+ p_ccb->fcrb.next_seq_expected);
+
+ /* Process the SREJ held I-frame, but do not send an RR for each individual frame */
+ process_i_frame (p_ccb, p_buf, ctrl_word, TRUE);
+ } else {
+ osi_free (p_buf);
+ }
+
+ /* If more frames were lost during SREJ, send a REJ */
+ if (p_ccb->fcrb.rej_after_srej) {
+ p_ccb->fcrb.rej_after_srej = FALSE;
+ p_ccb->fcrb.rej_sent = TRUE;
+
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_REJ, 0);
+ }
+ }
+ fixed_queue_free(temp_q, NULL);
+
+ /* Now, if needed, send one RR for the whole held queue */
+ if ( (!p_ccb->fcrb.local_busy) && (!p_ccb->fcrb.rej_sent) && (!p_ccb->fcrb.srej_sent)
+ && (p_ccb->fcrb.next_seq_expected != p_ccb->fcrb.last_ack_sent) ) {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0);
+ } else {
+ L2CAP_TRACE_DEBUG ("l2c_fcr_proc_pdu() not sending RR CID: 0x%04x local_busy:%d rej_sent:%d srej_sent:%d Expected_Seq:%u Last_Ack:%u",
+ p_ccb->local_cid, p_ccb->fcrb.local_busy, p_ccb->fcrb.rej_sent, p_ccb->fcrb.srej_sent, p_ccb->fcrb.next_seq_expected,
+ p_ccb->fcrb.last_ack_sent);
+ }
+ }
+
+ /* If a window has opened, check if we can send any more packets */
+ if ( (!fixed_queue_is_empty(p_ccb->fcrb.retrans_q) ||
+ !fixed_queue_is_empty(p_ccb->xmit_hold_q))
+ && (p_ccb->fcrb.wait_ack == FALSE)
+ && (l2c_fcr_is_flow_controlled (p_ccb) == FALSE) ) {
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_proc_tout
+**
+** Description Handle a timeout. We should be in error recovery state.
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_proc_tout (tL2C_CCB *p_ccb)
+{
+ assert(p_ccb != NULL);
+ L2CAP_TRACE_DEBUG ("l2c_fcr_proc_tout: CID: 0x%04x num_tries: %u (max: %u) wait_ack: %u ack_q_count: %u",
+ p_ccb->local_cid, p_ccb->fcrb.num_tries,
+ p_ccb->peer_cfg.fcr.max_transmit,
+ p_ccb->fcrb.wait_ack,
+ fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q));
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.retrans_touts++;
+#endif
+
+ if ( (p_ccb->peer_cfg.fcr.max_transmit != 0) && (++p_ccb->fcrb.num_tries > p_ccb->peer_cfg.fcr.max_transmit) ) {
+ l2cu_disconnect_chnl (p_ccb);
+ } else {
+ if (!p_ccb->fcrb.srej_sent && !p_ccb->fcrb.rej_sent) {
+ if (p_ccb->fcrb.local_busy) {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_P_BIT);
+ } else {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_P_BIT);
+ }
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_proc_ack_tout
+**
+** Description Send RR/RNR if we have not acked I frame
+**
+** Returns -
+**
+*******************************************************************************/
+void l2c_fcr_proc_ack_tout (tL2C_CCB *p_ccb)
+{
+ assert(p_ccb != NULL);
+ L2CAP_TRACE_DEBUG ("l2c_fcr_proc_ack_tout: CID: 0x%04x State: %u Wack:%u Rq:%d Acked:%d", p_ccb->local_cid,
+ p_ccb->chnl_state, p_ccb->fcrb.wait_ack, p_ccb->fcrb.next_seq_expected, p_ccb->fcrb.last_ack_sent);
+
+ if ( (p_ccb->chnl_state == CST_OPEN) && (!p_ccb->fcrb.wait_ack)
+ && (p_ccb->fcrb.last_ack_sent != p_ccb->fcrb.next_seq_expected) ) {
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.xmit_ack_touts++;
+#endif
+ if (p_ccb->fcrb.local_busy) {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0);
+ } else {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0);
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function process_reqseq
+**
+** Description Handle receive sequence number
+**
+** Returns -
+**
+*******************************************************************************/
+static BOOLEAN process_reqseq (tL2C_CCB *p_ccb, UINT16 ctrl_word)
+{
+ assert(p_ccb != NULL);
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ UINT8 req_seq, num_bufs_acked, xx;
+ UINT16 ls;
+ UINT16 full_sdus_xmitted;
+
+ /* Receive sequence number does not ack anything for SREJ with P-bit set to zero */
+ if ( (ctrl_word & L2CAP_FCR_S_FRAME_BIT)
+ && ((ctrl_word & L2CAP_FCR_SUP_BITS) == (L2CAP_FCR_SUP_SREJ << L2CAP_FCR_SUP_SHIFT))
+ && ((ctrl_word & L2CAP_FCR_P_BIT) == 0) ) {
+ /* If anything still waiting for ack, restart the timer if it was stopped */
+ if (!fixed_queue_is_empty(p_fcrb->waiting_for_ack_q)) {
+ l2c_fcr_start_timer(p_ccb);
+ }
+
+ return (TRUE);
+ }
+
+ /* Extract the receive sequence number from the control word */
+ req_seq = (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT;
+
+ num_bufs_acked = (req_seq - p_fcrb->last_rx_ack) & L2CAP_FCR_SEQ_MODULO;
+
+ /* Verify the request sequence is in range before proceeding */
+ if (num_bufs_acked > fixed_queue_length(p_fcrb->waiting_for_ack_q)) {
+ /* The channel is closed if ReqSeq is not in range */
+ L2CAP_TRACE_WARNING ("L2CAP eRTM Frame BAD Req_Seq - ctrl_word: 0x%04x req_seq 0x%02x last_rx_ack: 0x%02x QCount: %u",
+ ctrl_word, req_seq, p_fcrb->last_rx_ack,
+ fixed_queue_length(p_fcrb->waiting_for_ack_q));
+
+ l2cu_disconnect_chnl (p_ccb);
+ return (FALSE);
+ }
+
+ p_fcrb->last_rx_ack = req_seq;
+
+ /* Now we can release all acknowledged frames, and restart the retransmission timer if needed */
+ if (num_bufs_acked != 0) {
+ p_fcrb->num_tries = 0;
+ full_sdus_xmitted = 0;
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ l2c_fcr_collect_ack_delay (p_ccb, num_bufs_acked);
+#endif
+
+ for (xx = 0; xx < num_bufs_acked; xx++) {
+ BT_HDR *p_tmp = (BT_HDR *)fixed_queue_dequeue(p_fcrb->waiting_for_ack_q, 0);
+ ls = p_tmp->layer_specific & L2CAP_FCR_SAR_BITS;
+
+ if ( (ls == L2CAP_FCR_UNSEG_SDU) || (ls == L2CAP_FCR_END_SDU) ) {
+ full_sdus_xmitted++;
+ }
+ osi_free(p_tmp);
+ if (p_ccb->cong_sent) {
+ l2cu_check_channel_congestion(p_ccb);
+ }
+ }
+
+ /* If we are still in a wait_ack state, do not mess with the timer */
+ if (!p_ccb->fcrb.wait_ack) {
+ l2c_fcr_stop_timer (p_ccb);
+ }
+
+ /* Check if we need to call the "packet_sent" callback */
+ if ( (p_ccb->p_rcb) && (p_ccb->p_rcb->api.pL2CA_TxComplete_Cb) && (full_sdus_xmitted) ) {
+ /* Special case for eRTM, if all packets sent, send 0xFFFF */
+ if (fixed_queue_is_empty(p_fcrb->waiting_for_ack_q) &&
+ fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
+ full_sdus_xmitted = 0xFFFF;
+ }
+
+ (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, full_sdus_xmitted);
+ }
+ }
+
+ /* If anything still waiting for ack, restart the timer if it was stopped */
+ if (!fixed_queue_is_empty(p_fcrb->waiting_for_ack_q)) {
+ l2c_fcr_start_timer (p_ccb);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function process_s_frame
+**
+** Description Process an S frame
+**
+** Returns -
+**
+*******************************************************************************/
+static void process_s_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word)
+{
+ assert(p_ccb != NULL);
+ assert(p_buf != NULL);
+
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ UINT16 s_frame_type = (ctrl_word & L2CAP_FCR_SUP_BITS) >> L2CAP_FCR_SUP_SHIFT;
+ BOOLEAN remote_was_busy;
+ BOOLEAN all_ok = TRUE;
+
+ if (p_buf->len != 0) {
+ L2CAP_TRACE_WARNING ("Incorrect S-frame Length (%d)", p_buf->len);
+ }
+
+ L2CAP_TRACE_DEBUG ("process_s_frame ctrl_word 0x%04x fcrb_remote_busy:%d", ctrl_word, p_fcrb->remote_busy);
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.s_frames_rcvd[s_frame_type]++;
+#endif
+
+ if (ctrl_word & L2CAP_FCR_P_BIT) {
+ p_fcrb->rej_sent = FALSE; /* After checkpoint, we can send anoher REJ */
+ p_fcrb->send_f_rsp = TRUE; /* Set a flag in case an I-frame is pending */
+ }
+
+ switch (s_frame_type) {
+ case L2CAP_FCR_SUP_RR:
+ remote_was_busy = p_fcrb->remote_busy;
+ p_fcrb->remote_busy = FALSE;
+
+ if ( (ctrl_word & L2CAP_FCR_F_BIT) || (remote_was_busy) ) {
+ all_ok = retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS);
+ }
+ break;
+
+ case L2CAP_FCR_SUP_REJ:
+ p_fcrb->remote_busy = FALSE;
+ all_ok = retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS);
+ break;
+
+ case L2CAP_FCR_SUP_RNR:
+ p_fcrb->remote_busy = TRUE;
+ l2c_fcr_stop_timer (p_ccb);
+ break;
+
+ case L2CAP_FCR_SUP_SREJ:
+ p_fcrb->remote_busy = FALSE;
+ all_ok = retransmit_i_frames (p_ccb, (UINT8)((ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT));
+ break;
+ }
+
+ if (all_ok) {
+ /* If polled, we need to respond with F-bit. Note, we may have sent a I-frame with the F-bit */
+ if (p_fcrb->send_f_rsp) {
+ if (p_fcrb->srej_sent) {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, L2CAP_FCR_F_BIT);
+ } else if (p_fcrb->local_busy) {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, L2CAP_FCR_F_BIT);
+ } else {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, L2CAP_FCR_F_BIT);
+ }
+
+ p_fcrb->send_f_rsp = FALSE;
+ }
+ } else {
+ L2CAP_TRACE_DEBUG ("process_s_frame hit_max_retries");
+ }
+
+ osi_free (p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function process_i_frame
+**
+** Description Process an I frame
+**
+** Returns -
+**
+*******************************************************************************/
+static void process_i_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word, BOOLEAN delay_ack)
+{
+ assert(p_ccb != NULL);
+ assert(p_buf != NULL);
+
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ UINT8 tx_seq, num_lost, num_to_ack, next_srej;
+
+ /* If we were doing checkpoint recovery, first retransmit all unacked I-frames */
+ if (ctrl_word & L2CAP_FCR_F_BIT) {
+ if (!retransmit_i_frames (p_ccb, L2C_FCR_RETX_ALL_PKTS)) {
+ osi_free(p_buf);
+ return;
+ }
+ }
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.ertm_pkt_counts[1]++;
+ p_ccb->fcrb.ertm_byte_counts[1] += p_buf->len;
+#endif
+
+ /* Extract the sequence number */
+ tx_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT;
+
+ /* If we have flow controlled the peer, ignore any bad I-frames from him */
+ if ( (tx_seq != p_fcrb->next_seq_expected) && (p_fcrb->local_busy) ) {
+ L2CAP_TRACE_WARNING ("Dropping bad I-Frame since we flowed off, tx_seq:%u", tx_seq);
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0);
+ osi_free(p_buf);
+ return;
+ }
+
+ /* Check if tx-sequence is the expected one */
+ if (tx_seq != p_fcrb->next_seq_expected) {
+ num_lost = (tx_seq - p_fcrb->next_seq_expected) & L2CAP_FCR_SEQ_MODULO;
+
+ /* Is the frame a duplicate ? If so, just drop it */
+ if (num_lost >= p_ccb->our_cfg.fcr.tx_win_sz) {
+ /* Duplicate - simply drop it */
+ L2CAP_TRACE_WARNING ("process_i_frame() Dropping Duplicate Frame tx_seq:%u ExpectedTxSeq %u", tx_seq, p_fcrb->next_seq_expected);
+ osi_free(p_buf);
+ } else {
+ L2CAP_TRACE_WARNING ("process_i_frame() CID: 0x%04x Lost: %u tx_seq:%u ExpTxSeq %u Rej: %u SRej: %u",
+ p_ccb->local_cid, num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent, p_fcrb->srej_sent);
+
+ if (p_fcrb->srej_sent) {
+ /* If SREJ sent, save the frame for later processing as long as it is in sequence */
+ next_srej = (((BT_HDR *)fixed_queue_try_peek_last(p_fcrb->srej_rcv_hold_q))->layer_specific + 1) & L2CAP_FCR_SEQ_MODULO;
+
+ if ( (tx_seq == next_srej) && (fixed_queue_length(p_fcrb->srej_rcv_hold_q) < p_ccb->our_cfg.fcr.tx_win_sz) ) {
+ /* If user gave us a pool for held rx buffers, use that */
+ /* TODO: Could that happen? Get rid of this code. */
+ if (p_ccb->ertm_info.fcr_rx_buf_size != L2CAP_FCR_RX_BUF_SIZE) {
+ BT_HDR *p_buf2;
+
+ /* Adjust offset and len so that control word is copied */
+ p_buf->offset -= L2CAP_FCR_OVERHEAD;
+ p_buf->len += L2CAP_FCR_OVERHEAD;
+
+ p_buf2 = l2c_fcr_clone_buf (p_buf, p_buf->offset, p_buf->len);
+
+ if (p_buf2) {
+ osi_free (p_buf);
+ p_buf = p_buf2;
+ }
+ p_buf->offset += L2CAP_FCR_OVERHEAD;
+ p_buf->len -= L2CAP_FCR_OVERHEAD;
+ }
+ L2CAP_TRACE_DEBUG ("process_i_frame() Lost: %u tx_seq:%u ExpTxSeq %u Rej: %u SRej1",
+ num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent);
+
+ p_buf->layer_specific = tx_seq;
+ fixed_queue_enqueue(p_fcrb->srej_rcv_hold_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT);
+ } else {
+ L2CAP_TRACE_WARNING ("process_i_frame() CID: 0x%04x frame dropped in Srej Sent next_srej:%u hold_q.count:%u win_sz:%u",
+ p_ccb->local_cid, next_srej, fixed_queue_length(p_fcrb->srej_rcv_hold_q), p_ccb->our_cfg.fcr.tx_win_sz);
+
+ p_fcrb->rej_after_srej = TRUE;
+ osi_free (p_buf);
+ }
+ } else if (p_fcrb->rej_sent) {
+ L2CAP_TRACE_WARNING ("process_i_frame() CID: 0x%04x Lost: %u tx_seq:%u ExpTxSeq %u Rej: 1 SRej: %u",
+ p_ccb->local_cid, num_lost, tx_seq, p_fcrb->next_seq_expected, p_fcrb->srej_sent);
+
+ /* If REJ sent, just drop the frame */
+ osi_free (p_buf);
+ } else {
+ L2CAP_TRACE_DEBUG ("process_i_frame() CID: 0x%04x tx_seq:%u ExpTxSeq %u Rej: %u",
+ p_ccb->local_cid, tx_seq, p_fcrb->next_seq_expected, p_fcrb->rej_sent);
+
+ /* If only one lost, we will send SREJ, otherwise we will send REJ */
+ if (num_lost > 1) {
+ osi_free (p_buf);
+ p_fcrb->rej_sent = TRUE;
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_REJ, 0);
+ } else {
+ if (!fixed_queue_is_empty(p_fcrb->srej_rcv_hold_q)) {
+ L2CAP_TRACE_ERROR ("process_i_frame() CID: 0x%04x sending SREJ tx_seq:%d hold_q.count:%u",
+ p_ccb->local_cid, tx_seq, fixed_queue_length(p_fcrb->srej_rcv_hold_q));
+ }
+ p_buf->layer_specific = tx_seq;
+ fixed_queue_enqueue(p_fcrb->srej_rcv_hold_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT);
+ p_fcrb->srej_sent = TRUE;
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_SREJ, 0);
+ }
+ btu_stop_quick_timer (&p_ccb->fcrb.ack_timer);
+ }
+ }
+ return;
+ }
+
+ /* Seq number is the next expected. Clear possible reject exception in case it occured */
+ p_fcrb->rej_sent = p_fcrb->srej_sent = FALSE;
+
+ /* Adjust the next_seq, so that if the upper layer sends more data in the callback
+ context, the received frame is acked by an I-frame. */
+ p_fcrb->next_seq_expected = (tx_seq + 1) & L2CAP_FCR_SEQ_MODULO;
+
+ /* If any SAR problem in eRTM mode, spec says disconnect. */
+ if (!do_sar_reassembly (p_ccb, p_buf, ctrl_word)) {
+ L2CAP_TRACE_WARNING ("process_i_frame() CID: 0x%04x reassembly failed", p_ccb->local_cid);
+ l2cu_disconnect_chnl (p_ccb);
+ return;
+ }
+
+ /* RR optimization - if peer can still send us more, then start an ACK timer */
+ num_to_ack = (p_fcrb->next_seq_expected - p_fcrb->last_ack_sent) & L2CAP_FCR_SEQ_MODULO;
+
+ if ( (num_to_ack < p_ccb->fcrb.max_held_acks) && (!p_fcrb->local_busy) ) {
+ delay_ack = TRUE;
+ }
+
+ /* We should neve never ack frame if we are not in OPEN state */
+ if ((num_to_ack != 0) && p_ccb->in_use && (p_ccb->chnl_state == CST_OPEN)) {
+ /* If no frames are awaiting transmission or are held, send an RR or RNR S-frame for ack */
+ if (delay_ack) {
+ /* If it is the first I frame we did not ack, start ack timer */
+ if (!p_ccb->fcrb.ack_timer.in_use) {
+ btu_start_quick_timer (&p_ccb->fcrb.ack_timer, BTU_TTYPE_L2CAP_FCR_ACK,
+ (L2CAP_FCR_ACK_TOUT * QUICK_TIMER_TICKS_PER_SEC) / 1000);
+ }
+ } else if ((fixed_queue_is_empty(p_ccb->xmit_hold_q) ||
+ l2c_fcr_is_flow_controlled(p_ccb))
+ && fixed_queue_is_empty(p_ccb->fcrb.srej_rcv_hold_q)) {
+ if (p_fcrb->local_busy) {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RNR, 0);
+ } else {
+ l2c_fcr_send_S_frame (p_ccb, L2CAP_FCR_SUP_RR, 0);
+ }
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function process_stream_frame
+**
+** Description This function processes frames in streaming mode
+**
+** Returns -
+**
+*******************************************************************************/
+static void process_stream_frame (tL2C_CCB *p_ccb, BT_HDR *p_buf)
+{
+ assert(p_ccb != NULL);
+ assert(p_buf != NULL);
+
+ UINT16 ctrl_word;
+ UINT16 fcs;
+ UINT8 *p;
+ UINT8 tx_seq;
+
+ /* Verify FCS if using */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) {
+ p = ((UINT8 *)(p_buf + 1)) + p_buf->offset + p_buf->len - L2CAP_FCS_LEN;
+
+ /* Extract and drop the FCS from the packet */
+ STREAM_TO_UINT16 (fcs, p);
+ p_buf->len -= L2CAP_FCS_LEN;
+
+ if (l2c_fcr_rx_get_fcs(p_buf) != fcs) {
+ L2CAP_TRACE_WARNING ("Rx L2CAP PDU: CID: 0x%04x BAD FCS", p_ccb->local_cid);
+ osi_free(p_buf);
+ return;
+ }
+ }
+
+ /* Get the control word */
+ p = ((UINT8 *)(p_buf + 1)) + p_buf->offset;
+
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ p_buf->len -= L2CAP_FCR_OVERHEAD;
+ p_buf->offset += L2CAP_FCR_OVERHEAD;
+
+ /* Make sure it is an I-frame */
+ if (ctrl_word & L2CAP_FCR_S_FRAME_BIT) {
+ L2CAP_TRACE_WARNING ("Rx L2CAP PDU: CID: 0x%04x BAD S-frame in streaming mode ctrl_word: 0x%04x", p_ccb->local_cid, ctrl_word);
+ osi_free (p_buf);
+ return;
+ }
+
+#if BT_TRACE_VERBOSE == TRUE
+ L2CAP_TRACE_EVENT ("L2CAP eRTM Rx I-frame: cid: 0x%04x Len: %u SAR: %-12s TxSeq: %u ReqSeq: %u F: %u",
+ p_ccb->local_cid, p_buf->len,
+ SAR_types[(ctrl_word & L2CAP_FCR_SAR_BITS) >> L2CAP_FCR_SAR_BITS_SHIFT],
+ (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_REQ_SEQ_BITS) >> L2CAP_FCR_REQ_SEQ_BITS_SHIFT,
+ (ctrl_word & L2CAP_FCR_F_BIT) >> L2CAP_FCR_F_BIT_SHIFT);
+#endif
+
+ /* Extract the sequence number */
+ tx_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT;
+
+ /* Check if tx-sequence is the expected one */
+ if (tx_seq != p_ccb->fcrb.next_seq_expected) {
+ L2CAP_TRACE_WARNING ("Rx L2CAP PDU: CID: 0x%04x Lost frames Exp: %u Got: %u p_rx_sdu: %p",
+ p_ccb->local_cid, p_ccb->fcrb.next_seq_expected, tx_seq, p_ccb->fcrb.p_rx_sdu);
+
+ /* Lost one or more packets, so flush the SAR queue */
+ if (p_ccb->fcrb.p_rx_sdu != NULL) {
+ osi_free(p_ccb->fcrb.p_rx_sdu);
+ p_ccb->fcrb.p_rx_sdu = NULL;
+ }
+ }
+
+ p_ccb->fcrb.next_seq_expected = (tx_seq + 1) & L2CAP_FCR_SEQ_MODULO;
+
+ if (!do_sar_reassembly (p_ccb, p_buf, ctrl_word)) {
+ /* Some sort of SAR error, so flush the SAR queue */
+ if (p_ccb->fcrb.p_rx_sdu != NULL) {
+ osi_free(p_ccb->fcrb.p_rx_sdu);
+ p_ccb->fcrb.p_rx_sdu = NULL;
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function do_sar_reassembly
+**
+** Description Process SAR bits and re-assemble frame
+**
+** Returns TRUE if all OK, else FALSE
+**
+*******************************************************************************/
+static BOOLEAN do_sar_reassembly (tL2C_CCB *p_ccb, BT_HDR *p_buf, UINT16 ctrl_word)
+{
+ assert(p_ccb != NULL);
+ assert(p_buf != NULL);
+
+ tL2C_FCRB *p_fcrb = &p_ccb->fcrb;
+ UINT16 sar_type = ctrl_word & L2CAP_FCR_SEG_BITS;
+ BOOLEAN packet_ok = TRUE;
+ UINT8 *p;
+
+ /* Check if the SAR state is correct */
+ if ((sar_type == L2CAP_FCR_UNSEG_SDU) || (sar_type == L2CAP_FCR_START_SDU)) {
+ if (p_fcrb->p_rx_sdu != NULL) {
+ L2CAP_TRACE_WARNING ("SAR - got unexpected unsegmented or start SDU Expected len: %u Got so far: %u",
+ p_fcrb->rx_sdu_len, p_fcrb->p_rx_sdu->len);
+
+ packet_ok = FALSE;
+ }
+ /* Check the length of the packet */
+ if ( (sar_type == L2CAP_FCR_START_SDU) && (p_buf->len < L2CAP_SDU_LEN_OVERHEAD) ) {
+ L2CAP_TRACE_WARNING ("SAR start packet too short: %u", p_buf->len);
+ packet_ok = FALSE;
+ }
+ } else {
+ if (p_fcrb->p_rx_sdu == NULL) {
+ L2CAP_TRACE_WARNING ("SAR - got unexpected cont or end SDU");
+ packet_ok = FALSE;
+ }
+ }
+
+ if ( (packet_ok) && (sar_type != L2CAP_FCR_UNSEG_SDU) ) {
+ p = ((UINT8 *)(p_buf + 1)) + p_buf->offset;
+
+ /* For start SDU packet, extract the SDU length */
+ if (sar_type == L2CAP_FCR_START_SDU) {
+ /* Get the SDU length */
+ STREAM_TO_UINT16 (p_fcrb->rx_sdu_len, p);
+ p_buf->offset += 2;
+ p_buf->len -= 2;
+
+ if (p_fcrb->rx_sdu_len > p_ccb->max_rx_mtu) {
+ L2CAP_TRACE_WARNING ("SAR - SDU len: %u larger than MTU: %u", p_fcrb->rx_sdu_len, p_fcrb->rx_sdu_len);
+ packet_ok = FALSE;
+ } else if ((p_fcrb->p_rx_sdu = (BT_HDR *)osi_malloc(L2CAP_MAX_BUF_SIZE)) == NULL) {
+ L2CAP_TRACE_ERROR ("SAR - no buffer for SDU start user_rx_buf_size:%d", p_ccb->ertm_info.user_rx_buf_size);
+ packet_ok = FALSE;
+ } else {
+ p_fcrb->p_rx_sdu->offset = 4; /* this is the minimal offset required by OBX to process incoming packets */
+ p_fcrb->p_rx_sdu->len = 0;
+ }
+ }
+
+ if (packet_ok) {
+ if ((p_fcrb->p_rx_sdu->len + p_buf->len) > p_fcrb->rx_sdu_len) {
+ L2CAP_TRACE_ERROR ("SAR - SDU len exceeded Type: %u Lengths: %u %u %u",
+ sar_type, p_fcrb->p_rx_sdu->len, p_buf->len, p_fcrb->rx_sdu_len);
+ packet_ok = FALSE;
+ } else if ( (sar_type == L2CAP_FCR_END_SDU) && ((p_fcrb->p_rx_sdu->len + p_buf->len) != p_fcrb->rx_sdu_len) ) {
+ L2CAP_TRACE_WARNING ("SAR - SDU end rcvd but SDU incomplete: %u %u %u",
+ p_fcrb->p_rx_sdu->len, p_buf->len, p_fcrb->rx_sdu_len);
+ packet_ok = FALSE;
+ } else {
+ memcpy (((UINT8 *) (p_fcrb->p_rx_sdu + 1)) + p_fcrb->p_rx_sdu->offset + p_fcrb->p_rx_sdu->len, p, p_buf->len);
+
+ p_fcrb->p_rx_sdu->len += p_buf->len;
+
+ osi_free (p_buf);
+ p_buf = NULL;
+
+ if (sar_type == L2CAP_FCR_END_SDU) {
+ p_buf = p_fcrb->p_rx_sdu;
+ p_fcrb->p_rx_sdu = NULL;
+ }
+ }
+ }
+ }
+
+ if (packet_ok == FALSE) {
+ osi_free (p_buf);
+ } else if (p_buf != NULL) {
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ if (p_ccb->local_cid < L2CAP_BASE_APPL_CID &&
+ (p_ccb->local_cid >= L2CAP_FIRST_FIXED_CHNL && p_ccb->local_cid <= L2CAP_LAST_FIXED_CHNL)) {
+ if (l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
+ (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
+ (p_ccb->local_cid, p_ccb->p_lcb->remote_bd_addr, p_buf);
+ } else
+#endif
+ {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_buf);
+ }
+ }
+
+ return (packet_ok);
+}
+
+
+/*******************************************************************************
+**
+** Function retransmit_i_frames
+**
+** Description This function retransmits i-frames awaiting acks.
+**
+** Returns BOOLEAN - TRUE if retransmitted
+**
+*******************************************************************************/
+static BOOLEAN retransmit_i_frames (tL2C_CCB *p_ccb, UINT8 tx_seq)
+{
+ assert(p_ccb != NULL);
+
+ BT_HDR *p_buf = NULL;
+ UINT8 *p;
+ UINT8 buf_seq;
+ UINT16 ctrl_word;
+
+ if ( (!fixed_queue_is_empty(p_ccb->fcrb.waiting_for_ack_q))
+ && (p_ccb->peer_cfg.fcr.max_transmit != 0)
+ && (p_ccb->fcrb.num_tries >= p_ccb->peer_cfg.fcr.max_transmit) ) {
+ L2CAP_TRACE_EVENT ("Max Tries Exceeded: (last_acq: %d CID: 0x%04x num_tries: %u (max: %u) ack_q_count: %u",
+ p_ccb->fcrb.last_rx_ack, p_ccb->local_cid, p_ccb->fcrb.num_tries, p_ccb->peer_cfg.fcr.max_transmit,
+ fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q));
+
+ l2cu_disconnect_chnl (p_ccb);
+ return (FALSE);
+ }
+
+ /* tx_seq indicates whether to retransmit a specific sequence or all (if == L2C_FCR_RETX_ALL_PKTS) */
+ list_t *list_ack = NULL;
+ const list_node_t *node_ack = NULL;
+ if (! fixed_queue_is_empty(p_ccb->fcrb.waiting_for_ack_q)) {
+ list_ack = fixed_queue_get_list(p_ccb->fcrb.waiting_for_ack_q);
+ node_ack = list_begin(list_ack);
+ }
+ if (tx_seq != L2C_FCR_RETX_ALL_PKTS) {
+ /* If sending only one, the sequence number tells us which one. Look for it.
+ */
+ if (list_ack != NULL) {
+ for ( ; node_ack != list_end(list_ack); node_ack = list_next(node_ack)) {
+ p_buf = (BT_HDR *)list_node(node_ack);
+ /* Get the old control word */
+ p = ((UINT8 *) (p_buf+1)) + p_buf->offset + L2CAP_PKT_OVERHEAD;
+
+ STREAM_TO_UINT16 (ctrl_word, p);
+
+ buf_seq = (ctrl_word & L2CAP_FCR_TX_SEQ_BITS) >> L2CAP_FCR_TX_SEQ_BITS_SHIFT;
+
+ L2CAP_TRACE_DEBUG ("retransmit_i_frames() cur seq: %u looking for: %u", buf_seq, tx_seq);
+
+ if (tx_seq == buf_seq) {
+ break;
+ }
+ }
+ }
+
+ if (!p_buf) {
+ L2CAP_TRACE_ERROR ("retransmit_i_frames() UNKNOWN seq: %u q_count: %u",
+ tx_seq,
+ fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q));
+ return (TRUE);
+ }
+ } else {
+ // Iterate though list and flush the amount requested from
+ // the transmit data queue that satisfy the layer and event conditions.
+ for (const list_node_t *node = list_begin(p_ccb->p_lcb->link_xmit_data_q);
+ node != list_end(p_ccb->p_lcb->link_xmit_data_q);) {
+ BT_HDR *p_buf = (BT_HDR *)list_node(node);
+ node = list_next(node);
+
+ /* Do not flush other CIDs or partial segments */
+ if ((p_buf->layer_specific == 0) && (p_buf->event == p_ccb->local_cid)) {
+ list_remove(p_ccb->p_lcb->link_xmit_data_q, p_buf);
+ osi_free(p_buf);
+ }
+ }
+
+ /* Also flush our retransmission queue */
+ while (!fixed_queue_is_empty(p_ccb->fcrb.retrans_q)) {
+ osi_free(fixed_queue_dequeue(p_ccb->fcrb.retrans_q, 0));
+ }
+
+ if (list_ack != NULL) {
+ node_ack = list_begin(list_ack);
+ }
+ }
+
+ if (list_ack != NULL) {
+ while (node_ack != list_end(list_ack))
+ {
+ p_buf = (BT_HDR *)list_node(node_ack);
+ node_ack = list_next(node_ack);
+
+ BT_HDR *p_buf2 = l2c_fcr_clone_buf(p_buf, p_buf->offset, p_buf->len);
+ if (p_buf2)
+ {
+ p_buf2->layer_specific = p_buf->layer_specific;
+
+ fixed_queue_enqueue(p_ccb->fcrb.retrans_q, p_buf2, FIXED_QUEUE_MAX_TIMEOUT);
+ }
+
+ if ( (tx_seq != L2C_FCR_RETX_ALL_PKTS) || (p_buf2 == NULL) ) {
+ break;
+ }
+ }
+ }
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+
+ if (fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q))
+ {
+ p_ccb->fcrb.num_tries++;
+ l2c_fcr_start_timer (p_ccb);
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_get_next_xmit_sdu_seg
+**
+** Description Get the next SDU segment to transmit.
+**
+** Returns pointer to buffer with segment or NULL
+**
+*******************************************************************************/
+BT_HDR *l2c_fcr_get_next_xmit_sdu_seg (tL2C_CCB *p_ccb, UINT16 max_packet_length)
+{
+ assert(p_ccb != NULL);
+
+ BOOLEAN first_seg = FALSE, /* The segment is the first part of data */
+ mid_seg = FALSE, /* The segment is the middle part of data */
+ last_seg = FALSE; /* The segment is the last part of data */
+ UINT16 sdu_len = 0;
+ BT_HDR *p_buf, *p_xmit;
+ UINT8 *p;
+ UINT16 max_pdu = p_ccb->tx_mps /* Needed? - L2CAP_MAX_HEADER_FCS*/;
+
+ /* If there is anything in the retransmit queue, that goes first
+ */
+ p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->fcrb.retrans_q, 0);
+ if (p_buf != NULL) {
+ /* Update Rx Seq and FCS if we acked some packets while this one was queued */
+ prepare_I_frame (p_ccb, p_buf, TRUE);
+
+ p_buf->event = p_ccb->local_cid;
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.pkts_retransmitted++;
+ p_ccb->fcrb.ertm_pkt_counts[0]++;
+ p_ccb->fcrb.ertm_byte_counts[0] += (p_buf->len - 8);
+#endif
+ return (p_buf);
+ }
+
+ /* For BD/EDR controller, max_packet_length is set to 0 */
+ /* For AMP controller, max_packet_length is set by available blocks */
+ if ( (max_packet_length > L2CAP_MAX_HEADER_FCS)
+ && (max_pdu + L2CAP_MAX_HEADER_FCS > max_packet_length) ) {
+ max_pdu = max_packet_length - L2CAP_MAX_HEADER_FCS;
+ }
+
+ p_buf = (BT_HDR *)fixed_queue_try_peek_first(p_ccb->xmit_hold_q);
+
+ /* If there is more data than the MPS, it requires segmentation */
+ if (p_buf->len > max_pdu) {
+ /* We are using the "event" field to tell is if we already started segmentation */
+ if (p_buf->event == 0) {
+ first_seg = TRUE;
+ sdu_len = p_buf->len;
+ } else {
+ mid_seg = TRUE;
+ }
+
+ /* Get a new buffer and copy the data that can be sent in a PDU */
+ p_xmit = l2c_fcr_clone_buf (p_buf, L2CAP_MIN_OFFSET + L2CAP_SDU_LEN_OFFSET,
+ max_pdu);
+
+ if (p_xmit != NULL) {
+ p_buf->event = p_ccb->local_cid;
+ p_xmit->event = p_ccb->local_cid;
+
+ p_buf->len -= max_pdu;
+ p_buf->offset += max_pdu;
+
+ /* copy PBF setting */
+ p_xmit->layer_specific = p_buf->layer_specific;
+ } else { /* Should never happen if the application has configured buffers correctly */
+ L2CAP_TRACE_ERROR ("L2CAP - cannot get buffer for segmentation, max_pdu: %u", max_pdu);
+ return (NULL);
+ }
+ } else { /* Use the original buffer if no segmentation, or the last segment */
+ p_xmit = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0);
+
+ if (p_xmit->event != 0) {
+ last_seg = TRUE;
+ }
+
+ p_xmit->event = p_ccb->local_cid;
+ }
+
+ /* Step back to add the L2CAP headers */
+ p_xmit->offset -= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD);
+ p_xmit->len += L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD;
+
+ if (first_seg) {
+ p_xmit->offset -= L2CAP_SDU_LEN_OVERHEAD;
+ p_xmit->len += L2CAP_SDU_LEN_OVERHEAD;
+ }
+
+ /* Set the pointer to the beginning of the data */
+ p = (UINT8 *)(p_xmit + 1) + p_xmit->offset;
+
+ /* Now the L2CAP header */
+
+ /* Note: if FCS has to be included then the length is recalculated later */
+ UINT16_TO_STREAM (p, p_xmit->len - L2CAP_PKT_OVERHEAD);
+
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+
+ if (first_seg) {
+ /* Skip control word and add SDU length */
+ p += 2;
+ UINT16_TO_STREAM (p, sdu_len);
+
+ /* We will store the SAR type in layer-specific */
+ /* layer_specific is shared with flushable flag(bits 0-1), don't clear it */
+ p_xmit->layer_specific |= L2CAP_FCR_START_SDU;
+
+ first_seg = FALSE;
+ } else if (mid_seg) {
+ p_xmit->layer_specific |= L2CAP_FCR_CONT_SDU;
+ } else if (last_seg) {
+ p_xmit->layer_specific |= L2CAP_FCR_END_SDU;
+ } else {
+ p_xmit->layer_specific |= L2CAP_FCR_UNSEG_SDU;
+ }
+
+ prepare_I_frame (p_ccb, p_xmit, FALSE);
+
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) {
+ BT_HDR *p_wack = l2c_fcr_clone_buf (p_xmit, HCI_DATA_PREAMBLE_SIZE, p_xmit->len);
+
+ if (!p_wack) {
+ L2CAP_TRACE_ERROR("L2CAP - no buffer for xmit cloning, CID: 0x%04x Length: %u",
+ p_ccb->local_cid, p_xmit->len);
+
+ /* We will not save the FCS in case we reconfigure and change options */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) {
+ p_xmit->len -= L2CAP_FCS_LEN;
+ }
+
+ /* Pretend we sent it and it got lost */
+ fixed_queue_enqueue(p_ccb->fcrb.waiting_for_ack_q, p_xmit, FIXED_QUEUE_MAX_TIMEOUT);
+ return (NULL);
+ } else {
+#if (L2CAP_ERTM_STATS == TRUE)
+ /* set timestamp at the end of tx I-frame to get acking delay */
+ p = ((UINT8 *) (p_wack + 1)) + p_wack->offset + p_wack->len;
+ UINT32_TO_STREAM (p, osi_time_get_os_boottime_ms());
+#endif
+ /* We will not save the FCS in case we reconfigure and change options */
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) {
+ p_wack->len -= L2CAP_FCS_LEN;
+ }
+
+ p_wack->layer_specific = p_xmit->layer_specific;
+ fixed_queue_enqueue(p_ccb->fcrb.waiting_for_ack_q, p_wack, FIXED_QUEUE_MAX_TIMEOUT);
+ }
+
+#if (L2CAP_ERTM_STATS == TRUE)
+ p_ccb->fcrb.ertm_pkt_counts[0]++;
+ p_ccb->fcrb.ertm_byte_counts[0] += (p_xmit->len - 8);
+#endif
+
+ }
+
+ return (p_xmit);
+}
+
+
+/*******************************************************************************
+** Configuration negotiation functions
+**
+** The following functions are used in negotiating channel modes during
+** configuration
+********************************************************************************/
+
+/*******************************************************************************
+**
+** Function l2c_fcr_chk_chan_modes
+**
+** Description Validates and adjusts if necessary, the FCR options
+** based on remote EXT features.
+**
+** Note: This assumes peer EXT Features have been received.
+** Basic mode is used if FCR Options have not been received
+**
+** Returns UINT8 - nonzero if can continue, '0' if no compatible channels
+**
+*******************************************************************************/
+UINT8 l2c_fcr_chk_chan_modes (tL2C_CCB *p_ccb)
+{
+ assert(p_ccb != NULL);
+
+ /* Remove nonbasic options that the peer does not support */
+ if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_ENH_RETRANS)) {
+ p_ccb->ertm_info.allowed_modes &= ~L2CAP_FCR_CHAN_OPT_ERTM;
+ }
+
+ if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_STREAM_MODE)) {
+ p_ccb->ertm_info.allowed_modes &= ~L2CAP_FCR_CHAN_OPT_STREAM;
+ }
+
+ /* At least one type needs to be set (Basic, ERTM, STM) to continue */
+ if (!p_ccb->ertm_info.allowed_modes) {
+ L2CAP_TRACE_WARNING ("L2CAP - Peer does not support our desired channel types");
+ }
+
+ return (p_ccb->ertm_info.allowed_modes);
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_adj_our_req_options
+**
+** Description Validates and sets up the FCR options passed in from
+** L2CA_ConfigReq based on remote device's features.
+**
+** Returns TRUE if no errors, Otherwise FALSE
+**
+*******************************************************************************/
+BOOLEAN l2c_fcr_adj_our_req_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ assert(p_ccb != NULL);
+ assert(p_cfg != NULL);
+
+ tL2CAP_FCR_OPTS *p_fcr = &p_cfg->fcr;
+
+ if (p_fcr->mode != p_ccb->ertm_info.preferred_mode) {
+ L2CAP_TRACE_WARNING ("l2c_fcr_adj_our_req_options - preferred_mode (%d), does not match mode (%d)",
+ p_ccb->ertm_info.preferred_mode, p_fcr->mode);
+
+ /* The preferred mode is passed in through tL2CAP_ERTM_INFO, so override this one */
+ p_fcr->mode = p_ccb->ertm_info.preferred_mode;
+ }
+
+ /* If upper layer did not request eRTM mode, BASIC must be used */
+ if (p_ccb->ertm_info.allowed_modes == L2CAP_FCR_CHAN_OPT_BASIC) {
+ if (p_cfg->fcr_present && p_fcr->mode != L2CAP_FCR_BASIC_MODE) {
+ L2CAP_TRACE_WARNING ("l2c_fcr_adj_our_req_options (mode %d): ERROR: No FCR options set using BASIC mode", p_fcr->mode);
+ }
+ p_fcr->mode = L2CAP_FCR_BASIC_MODE;
+ }
+
+ /* Process the FCR options if initial channel bring-up (not a reconfig request)
+ ** Determine initial channel mode to try based on our options and remote's features
+ */
+ if (p_cfg->fcr_present && !(p_ccb->config_done & RECONFIG_FLAG)) {
+ /* We need to have at least one mode type common with the peer */
+ if (!l2c_fcr_chk_chan_modes(p_ccb)) {
+ /* Two channels have incompatible supported types */
+ l2cu_disconnect_chnl (p_ccb);
+ return (FALSE);
+ }
+
+ /* Basic is the only common channel mode between the two devices */
+ else if (p_ccb->ertm_info.allowed_modes == L2CAP_FCR_CHAN_OPT_BASIC) {
+ /* We only want to try Basic, so bypass sending the FCR options entirely */
+ p_cfg->fcr_present = FALSE;
+ p_cfg->fcs_present = FALSE; /* Illegal to use FCS option in basic mode */
+ p_cfg->ext_flow_spec_present = FALSE; /* Illegal to use extended flow spec in basic mode */
+ }
+
+ /* We have at least one non-basic mode available
+ * Override mode from available mode options based on preference, if needed
+ */
+ else {
+ /* If peer does not support STREAMING, try ERTM */
+ if (p_fcr->mode == L2CAP_FCR_STREAM_MODE && !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_STREAM)) {
+ L2CAP_TRACE_DEBUG ("L2C CFG: mode is STREAM, but peer does not support; Try ERTM");
+ p_fcr->mode = L2CAP_FCR_ERTM_MODE;
+ }
+
+ /* If peer does not support ERTM, try BASIC (will support this if made it here in the code) */
+ if (p_fcr->mode == L2CAP_FCR_ERTM_MODE && !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM)) {
+ L2CAP_TRACE_DEBUG ("L2C CFG: mode is ERTM, but peer does not support; Try BASIC");
+ p_fcr->mode = L2CAP_FCR_BASIC_MODE;
+ }
+ }
+
+ if (p_fcr->mode != L2CAP_FCR_BASIC_MODE) {
+ /* MTU must be smaller than buffer size */
+ if ( (p_cfg->mtu_present) && (p_cfg->mtu > p_ccb->max_rx_mtu) ) {
+ L2CAP_TRACE_WARNING ("L2CAP - MTU: %u larger than buf size: %u", p_cfg->mtu, p_ccb->max_rx_mtu);
+ return (FALSE);
+ }
+
+ /* application want to use the default MPS */
+ if (p_fcr->mps == L2CAP_DEFAULT_ERM_MPS) {
+ p_fcr->mps = L2CAP_MPS_OVER_BR_EDR;
+ }
+ /* MPS must be less than MTU */
+ else if (p_fcr->mps > p_ccb->max_rx_mtu) {
+ L2CAP_TRACE_WARNING ("L2CAP - MPS %u invalid MTU: %u", p_fcr->mps, p_ccb->max_rx_mtu);
+ return (FALSE);
+ }
+
+ /* We always initially read into the HCI buffer pool, so make sure it fits */
+ if (p_fcr->mps > (L2CAP_MTU_SIZE - L2CAP_MAX_HEADER_FCS)) {
+ p_fcr->mps = L2CAP_MTU_SIZE - L2CAP_MAX_HEADER_FCS;
+ }
+ } else {
+ p_cfg->fcs_present = FALSE; /* Illegal to use FCS option in basic mode */
+ p_cfg->ext_flow_spec_present = FALSE; /* Illegal to use extended flow spec in basic mode */
+ }
+
+ p_ccb->our_cfg.fcr = *p_fcr;
+ } else { /* Not sure how to send a reconfiguration(??) should fcr be included? */
+ p_ccb->our_cfg.fcr_present = FALSE;
+ }
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_adj_monitor_retran_timeout
+**
+** Description Overrides monitor/retrans timer value based on controller
+**
+** Returns None
+**
+*******************************************************************************/
+void l2c_fcr_adj_monitor_retran_timeout (tL2C_CCB *p_ccb)
+{
+ assert(p_ccb != NULL);
+
+ /* adjust our monitor/retran timeout */
+ if (p_ccb->out_cfg_fcr_present) {
+ /*
+ ** if we requestd ERTM or accepted ERTM
+ ** We may accept ERTM even if we didn't request ERTM, in case of requesting STREAM
+ */
+ if ((p_ccb->our_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
+ || (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)) {
+ /* upper layer setting is ignored */
+ p_ccb->our_cfg.fcr.mon_tout = L2CAP_MIN_MONITOR_TOUT;
+ p_ccb->our_cfg.fcr.rtrans_tout = L2CAP_MIN_RETRANS_TOUT;
+ } else {
+ p_ccb->our_cfg.fcr.mon_tout = 0;
+ p_ccb->our_cfg.fcr.rtrans_tout = 0;
+ }
+
+ L2CAP_TRACE_DEBUG ("l2c_fcr_adj_monitor_retran_timeout: mon_tout:%d, rtrans_tout:%d",
+ p_ccb->our_cfg.fcr.mon_tout, p_ccb->our_cfg.fcr.rtrans_tout);
+ }
+}
+/*******************************************************************************
+**
+** Function l2c_fcr_adj_our_rsp_options
+**
+** Description Overrides any neccesary FCR options passed in from
+** L2CA_ConfigRsp based on our FCR options.
+** Only makes adjustments if channel is in ERTM mode.
+**
+** Returns None
+**
+*******************************************************************************/
+void l2c_fcr_adj_our_rsp_options (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ assert(p_ccb != NULL);
+ assert(p_cfg != NULL);
+
+ /* adjust our monitor/retran timeout */
+ l2c_fcr_adj_monitor_retran_timeout(p_ccb);
+
+ p_cfg->fcr_present = p_ccb->out_cfg_fcr_present;
+
+ if (p_cfg->fcr_present) {
+// btla-specific ++
+ /* Temporary - until a better algorithm is implemented */
+ /* If peer's tx_wnd_sz requires too many buffers for us to support, then adjust it. For now, respond with our own tx_wnd_sz. */
+ /* Note: peer is not guaranteed to obey our adjustment */
+ if (p_ccb->peer_cfg.fcr.tx_win_sz > p_ccb->our_cfg.fcr.tx_win_sz) {
+ L2CAP_TRACE_DEBUG ("%s: adjusting requested tx_win_sz from %i to %i", __FUNCTION__, p_ccb->peer_cfg.fcr.tx_win_sz, p_ccb->our_cfg.fcr.tx_win_sz);
+ p_ccb->peer_cfg.fcr.tx_win_sz = p_ccb->our_cfg.fcr.tx_win_sz;
+ }
+// btla-specific --
+
+ p_cfg->fcr.mode = p_ccb->peer_cfg.fcr.mode;
+ p_cfg->fcr.tx_win_sz = p_ccb->peer_cfg.fcr.tx_win_sz;
+ p_cfg->fcr.max_transmit = p_ccb->peer_cfg.fcr.max_transmit;
+ p_cfg->fcr.mps = p_ccb->peer_cfg.fcr.mps;
+ p_cfg->fcr.rtrans_tout = p_ccb->our_cfg.fcr.rtrans_tout;
+ p_cfg->fcr.mon_tout = p_ccb->our_cfg.fcr.mon_tout;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_fcr_renegotiate_chan
+**
+** Description Called upon unsuccessful peer response to config request.
+** If the error is because of the channel mode, it will try
+** to resend using another supported optional channel.
+**
+** Returns TRUE if resent configuration, False if channel matches or
+** cannot match.
+**
+*******************************************************************************/
+BOOLEAN l2c_fcr_renegotiate_chan(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ assert(p_ccb != NULL);
+ assert(p_cfg != NULL);
+
+ UINT8 peer_mode = p_ccb->our_cfg.fcr.mode;
+ BOOLEAN can_renegotiate;
+
+ /* Skip if this is a reconfiguration from OPEN STATE or if FCR is not returned */
+ if (!p_cfg->fcr_present || (p_ccb->config_done & RECONFIG_FLAG)) {
+ return (FALSE);
+ }
+
+ /* Only retry if there are more channel options to try */
+ if (p_cfg->result == L2CAP_CFG_UNACCEPTABLE_PARAMS) {
+ peer_mode = (p_cfg->fcr_present) ? p_cfg->fcr.mode : L2CAP_FCR_BASIC_MODE;
+
+ if (p_ccb->our_cfg.fcr.mode != peer_mode) {
+
+ if ((--p_ccb->fcr_cfg_tries) == 0) {
+ p_cfg->result = L2CAP_CFG_FAILED_NO_REASON;
+ L2CAP_TRACE_WARNING ("l2c_fcr_renegotiate_chan (Max retries exceeded)");
+ }
+
+ can_renegotiate = FALSE;
+
+ /* Try another supported mode if available based on our last attempted channel */
+ switch (p_ccb->our_cfg.fcr.mode) {
+ /* Our Streaming mode request was unnacceptable; try ERTM or Basic */
+ case L2CAP_FCR_STREAM_MODE:
+ /* Peer wants ERTM and we support it */
+ if ( (peer_mode == L2CAP_FCR_ERTM_MODE) && (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM) ) {
+ L2CAP_TRACE_DEBUG ("l2c_fcr_renegotiate_chan(Trying ERTM)");
+ p_ccb->our_cfg.fcr.mode = L2CAP_FCR_ERTM_MODE;
+ can_renegotiate = TRUE;
+ } else /* Falls through */
+
+ case L2CAP_FCR_ERTM_MODE: {
+ /* We can try basic for any other peer mode if we support it */
+ if (p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_BASIC) {
+ L2CAP_TRACE_DEBUG ("l2c_fcr_renegotiate_chan(Trying Basic)");
+ can_renegotiate = TRUE;
+ p_ccb->our_cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
+ }
+ }
+ break;
+
+ default:
+ /* All other scenarios cannot be renegotiated */
+ break;
+ }
+
+ if (can_renegotiate) {
+ p_ccb->our_cfg.fcr_present = TRUE;
+
+ if (p_ccb->our_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) {
+ p_ccb->our_cfg.fcs_present = FALSE;
+ p_ccb->our_cfg.ext_flow_spec_present = FALSE;
+
+ /* Basic Mode uses ACL Data Pool, make sure the MTU fits */
+ if ( (p_cfg->mtu_present) && (p_cfg->mtu > L2CAP_MTU_SIZE) ) {
+ L2CAP_TRACE_WARNING ("L2CAP - adjust MTU: %u too large", p_cfg->mtu);
+ p_cfg->mtu = L2CAP_MTU_SIZE;
+ }
+ }
+
+ l2cu_process_our_cfg_req (p_ccb, &p_ccb->our_cfg);
+ l2cu_send_peer_config_req (p_ccb, &p_ccb->our_cfg);
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
+ return (TRUE);
+ }
+ }
+ }
+
+ /* Disconnect if the channels do not match */
+ if (p_ccb->our_cfg.fcr.mode != peer_mode) {
+ L2CAP_TRACE_WARNING ("L2C CFG: Channels incompatible (local %d, peer %d)",
+ p_ccb->our_cfg.fcr.mode, peer_mode);
+ l2cu_disconnect_chnl (p_ccb);
+ }
+
+ return (FALSE);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_fcr_process_peer_cfg_req
+**
+** Description This function is called to process the FCR options passed
+** in the peer's configuration request.
+**
+** Returns UINT8 - L2CAP_PEER_CFG_OK, L2CAP_PEER_CFG_UNACCEPTABLE,
+** or L2CAP_PEER_CFG_DISCONNECT.
+**
+*******************************************************************************/
+UINT8 l2c_fcr_process_peer_cfg_req(tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ assert(p_ccb != NULL);
+ assert(p_cfg != NULL);
+
+ UINT16 max_retrans_size;
+ UINT8 fcr_ok = L2CAP_PEER_CFG_OK;
+
+ p_ccb->p_lcb->w4_info_rsp = FALSE; /* Handles T61x SonyEricsson Bug in Info Request */
+
+ L2CAP_TRACE_EVENT ("l2c_fcr_process_peer_cfg_req() CFG fcr_present:%d fcr.mode:%d CCB FCR mode:%d preferred: %u allowed:%u",
+ p_cfg->fcr_present, p_cfg->fcr.mode, p_ccb->our_cfg.fcr.mode, p_ccb->ertm_info.preferred_mode,
+ p_ccb->ertm_info.allowed_modes);
+
+ /* If Peer wants basic, we are done (accept it or disconnect) */
+ if (p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE) {
+ /* If we do not allow basic, disconnect */
+ if ( !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_BASIC) ) {
+ fcr_ok = L2CAP_PEER_CFG_DISCONNECT;
+ }
+ }
+
+ /* Need to negotiate if our modes are not the same */
+ else if (p_cfg->fcr.mode != p_ccb->ertm_info.preferred_mode) {
+ /* If peer wants a mode that we don't support then retry our mode (ex. rtx/flc), OR
+ ** If we want ERTM and they wanted streaming retry our mode.
+ ** Note: If we have already determined they support our mode previously
+ ** from their EXF mask.
+ */
+ if ( (((1 << p_cfg->fcr.mode) & L2CAP_FCR_CHAN_OPT_ALL_MASK) == 0)
+ || (p_ccb->ertm_info.preferred_mode == L2CAP_FCR_ERTM_MODE) ) {
+ p_cfg->fcr.mode = p_ccb->our_cfg.fcr.mode;
+ p_cfg->fcr.tx_win_sz = p_ccb->our_cfg.fcr.tx_win_sz;
+ p_cfg->fcr.max_transmit = p_ccb->our_cfg.fcr.max_transmit;
+ fcr_ok = L2CAP_PEER_CFG_UNACCEPTABLE;
+ }
+
+ /* If we wanted basic, then try to renegotiate it */
+ else if (p_ccb->ertm_info.preferred_mode == L2CAP_FCR_BASIC_MODE) {
+ p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE;
+ p_cfg->fcr.max_transmit = p_cfg->fcr.tx_win_sz = 0;
+ p_cfg->fcr.rtrans_tout = p_cfg->fcr.mon_tout = p_cfg->fcr.mps = 0;
+ p_ccb->our_cfg.fcr.rtrans_tout = p_ccb->our_cfg.fcr.mon_tout = p_ccb->our_cfg.fcr.mps = 0;
+ fcr_ok = L2CAP_PEER_CFG_UNACCEPTABLE;
+ }
+
+ /* Only other valid case is if they want ERTM and we wanted STM which should be
+ accepted if we support it; otherwise the channel should be disconnected */
+ else if ( (p_cfg->fcr.mode != L2CAP_FCR_ERTM_MODE)
+ || !(p_ccb->ertm_info.allowed_modes & L2CAP_FCR_CHAN_OPT_ERTM) ) {
+ fcr_ok = L2CAP_PEER_CFG_DISCONNECT;
+ }
+ }
+
+ /* Configuration for FCR channels so make any adjustments and fwd to upper layer */
+ if (fcr_ok == L2CAP_PEER_CFG_OK) {
+ /* by default don't need to send params in the response */
+ p_ccb->out_cfg_fcr_present = FALSE;
+
+ /* Make any needed adjustments for the response to the peer */
+ if (p_cfg->fcr_present && p_cfg->fcr.mode != L2CAP_FCR_BASIC_MODE) {
+ /* Peer desires to bypass FCS check, and streaming or ERTM mode */
+ if (p_cfg->fcs_present) {
+ p_ccb->peer_cfg.fcs = p_cfg->fcs;
+ p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FCS;
+ if ( p_cfg->fcs == L2CAP_CFG_FCS_BYPASS) {
+ p_ccb->bypass_fcs |= L2CAP_CFG_FCS_PEER;
+ }
+ }
+
+ max_retrans_size = p_ccb->ertm_info.fcr_tx_buf_size - sizeof(BT_HDR)
+ - L2CAP_MIN_OFFSET - L2CAP_SDU_LEN_OFFSET - L2CAP_FCS_LEN;
+
+ /* Ensure the MPS is not bigger than the MTU */
+ if ( (p_cfg->fcr.mps == 0) || (p_cfg->fcr.mps > p_ccb->peer_cfg.mtu) ) {
+ p_cfg->fcr.mps = p_ccb->peer_cfg.mtu;
+ p_ccb->out_cfg_fcr_present = TRUE;
+ }
+
+ /* Ensure the MPS is not bigger than our retransmission buffer */
+ if (p_cfg->fcr.mps > max_retrans_size) {
+ L2CAP_TRACE_DEBUG("CFG: Overriding MPS to %d (orig %d)", max_retrans_size, p_cfg->fcr.mps);
+
+ p_cfg->fcr.mps = max_retrans_size;
+ p_ccb->out_cfg_fcr_present = TRUE;
+ }
+
+ if (p_cfg->fcr.mode == L2CAP_FCR_ERTM_MODE || p_cfg->fcr.mode == L2CAP_FCR_STREAM_MODE) {
+ /* Always respond with FCR ERTM parameters */
+ p_ccb->out_cfg_fcr_present = TRUE;
+ }
+ }
+
+ /* Everything ok, so save the peer's adjusted fcr options */
+ p_ccb->peer_cfg.fcr = p_cfg->fcr;
+
+ if (p_cfg->fcr_present) {
+ p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FCR;
+ }
+ } else if (fcr_ok == L2CAP_PEER_CFG_UNACCEPTABLE) {
+ /* Allow peer only one retry for mode */
+ if (p_ccb->peer_cfg_already_rejected) {
+ fcr_ok = L2CAP_PEER_CFG_DISCONNECT;
+ } else {
+ p_ccb->peer_cfg_already_rejected = TRUE;
+ }
+ }
+
+ return (fcr_ok);
+}
+
+#if (L2CAP_ERTM_STATS == TRUE)
+/*******************************************************************************
+**
+** Function l2c_fcr_collect_ack_delay
+**
+** Description collect throughput, delay, queue size of waiting ack
+**
+** Parameters
+** tL2C_CCB
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_fcr_collect_ack_delay (tL2C_CCB *p_ccb, UINT8 num_bufs_acked)
+{
+ UINT32 index;
+ BT_HDR *p_buf;
+ UINT8 *p;
+ UINT32 timestamp, delay;
+ UINT8 xx;
+ UINT8 str[120];
+
+ index = p_ccb->fcrb.ack_delay_avg_index;
+
+ /* update sum, max and min of waiting for ack queue size */
+ p_ccb->fcrb.ack_q_count_avg[index] +=
+ fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q);
+
+ if (fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q) > p_ccb->fcrb.ack_q_count_max[index]) {
+ p_ccb->fcrb.ack_q_count_max[index] = fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q);
+ }
+
+ if (fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q) < p_ccb->fcrb.ack_q_count_min[index]) {
+ p_ccb->fcrb.ack_q_count_min[index] = fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q);
+ }
+
+ /* update sum, max and min of round trip delay of acking */
+ list_t *list = NULL;
+ if (! fixed_queue_is_empty(p_ccb->fcrb.waiting_for_ack_q)) {
+ list = fixed_queue_get_list(p_ccb->fcrb.waiting_for_ack_q);
+ }
+ if (list != NULL) {
+ for (const list_node_t *node = list_begin(list), xx = 0;
+ (node != list_end(list)) && (xx < num_bufs_acked);
+ node = list_next(node), xx++) {
+ p_buf = list_node(node);
+ /* adding up length of acked I-frames to get throughput */
+ p_ccb->fcrb.throughput[index] += p_buf->len - 8;
+
+ if ( xx == num_bufs_acked - 1 ) {
+ /* get timestamp from tx I-frame that receiver is acking */
+ p = ((UINT8 *) (p_buf+1)) + p_buf->offset + p_buf->len;
+ if (p_ccb->bypass_fcs != L2CAP_BYPASS_FCS) {
+ p += L2CAP_FCS_LEN;
+ }
+
+ STREAM_TO_UINT32(timestamp, p);
+ delay = osi_time_get_os_boottime_ms() - timestamp;
+
+ p_ccb->fcrb.ack_delay_avg[index] += delay;
+ if ( delay > p_ccb->fcrb.ack_delay_max[index] ) {
+ p_ccb->fcrb.ack_delay_max[index] = delay;
+ }
+ if ( delay < p_ccb->fcrb.ack_delay_min[index] ) {
+ p_ccb->fcrb.ack_delay_min[index] = delay;
+ }
+ }
+ }
+ }
+
+ p_ccb->fcrb.ack_delay_avg_count++;
+
+ /* calculate average and initialize next avg, min and max */
+ if (p_ccb->fcrb.ack_delay_avg_count > L2CAP_ERTM_STATS_AVG_NUM_SAMPLES) {
+ p_ccb->fcrb.ack_delay_avg_count = 0;
+
+ p_ccb->fcrb.ack_q_count_avg[index] /= L2CAP_ERTM_STATS_AVG_NUM_SAMPLES;
+ p_ccb->fcrb.ack_delay_avg[index] /= L2CAP_ERTM_STATS_AVG_NUM_SAMPLES;
+
+ /* calculate throughput */
+ timestamp = osi_time_get_os_boottime_ms();
+ if (timestamp - p_ccb->fcrb.throughput_start > 0 ) {
+ p_ccb->fcrb.throughput[index] /= (timestamp - p_ccb->fcrb.throughput_start);
+ }
+
+ p_ccb->fcrb.throughput_start = timestamp;
+
+ sprintf(str, "[%02u] throughput: %5u, ack_delay avg:%3u, min:%3u, max:%3u, ack_q_count avg:%3u, min:%3u, max:%3u",
+ index, p_ccb->fcrb.throughput[index],
+ p_ccb->fcrb.ack_delay_avg[index], p_ccb->fcrb.ack_delay_min[index], p_ccb->fcrb.ack_delay_max[index],
+ p_ccb->fcrb.ack_q_count_avg[index], p_ccb->fcrb.ack_q_count_min[index], p_ccb->fcrb.ack_q_count_max[index] );
+
+ BT_TRACE(TRACE_CTRL_GENERAL | TRACE_LAYER_GKI | TRACE_ORG_GKI , TRACE_TYPE_GENERIC, "%s", str);
+
+ index = (index + 1) % L2CAP_ERTM_STATS_NUM_AVG;
+ p_ccb->fcrb.ack_delay_avg_index = index;
+
+ p_ccb->fcrb.ack_q_count_max[index] = 0;
+ p_ccb->fcrb.ack_q_count_min[index] = 0xFFFFFFFF;
+ p_ccb->fcrb.ack_q_count_avg[index] = 0;
+
+
+ p_ccb->fcrb.ack_delay_max[index] = 0;
+ p_ccb->fcrb.ack_delay_min[index] = 0xFFFFFFFF;
+ p_ccb->fcrb.ack_delay_avg[index] = 0;
+
+ p_ccb->fcrb.throughput[index] = 0;
+ }
+}
+#endif
+#endif /// (L2CAP_COC_INCLUDED == TRUE)
diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_link.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_link.c
new file mode 100644
index 00000000..4b81b4b3
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_link.c
@@ -0,0 +1,1509 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-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 functions relating to link management. A "link"
+ * is a connection between this device and another device. Only ACL links
+ * are managed.
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+//#include <stdio.h>
+
+#include "device/controller.h"
+//#include "btcore/include/counter.h"
+#include "stack/bt_types.h"
+//#include "bt_utils.h"
+#include "stack/hcimsgs.h"
+#include "stack/l2cdefs.h"
+#include "l2c_int.h"
+#include "stack/l2c_api.h"
+#include "stack/btu.h"
+#include "stack/btm_api.h"
+#include "btm_int.h"
+
+static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf);
+
+#if (BLE_50_FEATURE_SUPPORT == TRUE)
+extern tBTM_STATUS BTM_BleStartExtAdvRestart(uint8_t handle);
+#endif// #if (BLE_50_FEATURE_SUPPORT == TRUE)
+extern bool btm_ble_inter_get(void);
+
+/*******************************************************************************
+**
+** Function l2c_link_hci_conn_req
+**
+** Description This function is called when an HCI Connection Request
+** event is received.
+**
+** Returns TRUE, if accept conn
+**
+*******************************************************************************/
+BOOLEAN l2c_link_hci_conn_req (BD_ADDR bd_addr)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_LCB *p_lcb_cur;
+ BOOLEAN no_links;
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_BR_EDR);
+
+ /* If we don't have one, create one and accept the connection. */
+ if (!p_lcb) {
+ p_lcb = l2cu_allocate_lcb (bd_addr, FALSE, BT_TRANSPORT_BR_EDR);
+ if (!p_lcb) {
+ btsnd_hcic_reject_conn (bd_addr, HCI_ERR_HOST_REJECT_RESOURCES);
+ L2CAP_TRACE_ERROR ("L2CAP failed to allocate LCB");
+ return FALSE;
+ }
+
+ no_links = TRUE;
+
+ /* If we already have connection, accept as a master */
+ list_node_t *p_node = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb_cur = list_node(p_node);
+ if (p_lcb_cur == p_lcb) {
+ continue;
+ }
+
+ if (p_lcb_cur->in_use) {
+ no_links = FALSE;
+ p_lcb->link_role = HCI_ROLE_MASTER;
+ break;
+ }
+ }
+
+ if (no_links) {
+ if (!btm_dev_support_switch (bd_addr)) {
+ p_lcb->link_role = HCI_ROLE_SLAVE;
+ } else {
+ p_lcb->link_role = l2cu_get_conn_role(p_lcb);
+ }
+ }
+
+ //counter_add("l2cap.conn.accept", 1);
+
+ /* Tell the other side we accept the connection */
+ btsnd_hcic_accept_conn (bd_addr, p_lcb->link_role);
+
+ p_lcb->link_state = LST_CONNECTING;
+
+ /* Start a timer waiting for connect complete */
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_CONNECT_TOUT);
+ return (TRUE);
+ }
+
+ /* We already had a link control block to the guy. Check what state it is in */
+ if ((p_lcb->link_state == LST_CONNECTING) || (p_lcb->link_state == LST_CONNECT_HOLDING)) {
+ /* Connection collision. Accept the connection anyways. */
+
+ if (!btm_dev_support_switch (bd_addr)) {
+ p_lcb->link_role = HCI_ROLE_SLAVE;
+ } else {
+ p_lcb->link_role = l2cu_get_conn_role(p_lcb);
+ }
+
+ //counter_add("l2cap.conn.accept", 1);
+ btsnd_hcic_accept_conn (bd_addr, p_lcb->link_role);
+
+ p_lcb->link_state = LST_CONNECTING;
+ return (TRUE);
+ } else if (p_lcb->link_state == LST_DISCONNECTING) {
+ /* In disconnecting state, reject the connection. */
+ //counter_add("l2cap.conn.reject.disconn", 1);
+ btsnd_hcic_reject_conn (bd_addr, HCI_ERR_HOST_REJECT_DEVICE);
+ } else {
+ L2CAP_TRACE_ERROR("L2CAP got conn_req while connected (state:%d). Reject it\n",
+ p_lcb->link_state);
+ /* Reject the connection with ACL Connection Already exist reason */
+ //counter_add("l2cap.conn.reject.exists", 1);
+ btsnd_hcic_reject_conn (bd_addr, HCI_ERR_CONNECTION_EXISTS);
+ }
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_hci_conn_comp
+**
+** Description This function is called when an HCI Connection Complete
+** event is received.
+**
+** Returns void
+**
+*******************************************************************************/
+BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda)
+{
+ tL2C_CONN_INFO ci;
+ tL2C_LCB *p_lcb;
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ tL2C_CCB *p_ccb;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ tBTM_SEC_DEV_REC *p_dev_info = NULL;
+
+ btm_acl_update_busy_level (BTM_BLI_PAGE_DONE_EVT);
+
+ /* Save the parameters */
+ ci.status = status;
+ memcpy (ci.bd_addr, p_bda, BD_ADDR_LEN);
+
+ /* See if we have a link control block for the remote device */
+ p_lcb = l2cu_find_lcb_by_bd_addr (ci.bd_addr, BT_TRANSPORT_BR_EDR);
+
+ /* If we don't have one, this is an error */
+ if (!p_lcb) {
+ L2CAP_TRACE_WARNING ("L2CAP got conn_comp for unknown BD_ADDR\n");
+ return (FALSE);
+ }
+
+ if (p_lcb->link_state != LST_CONNECTING) {
+ L2CAP_TRACE_ERROR ("L2CAP got conn_comp in bad state: %d status: 0x%d\n", p_lcb->link_state, status);
+
+ if (status != HCI_SUCCESS) {
+ l2c_link_hci_disc_comp (p_lcb->handle, status);
+ }
+
+ return (FALSE);
+ }
+
+ /* Save the handle */
+ p_lcb->handle = handle;
+
+ if (ci.status == HCI_SUCCESS) {
+ /* Connected OK. Change state to connected */
+ p_lcb->link_state = LST_CONNECTED;
+ //counter_add("l2cap.conn.ok", 1);
+
+ /* Get the peer information if the l2cap flow-control/rtrans is supported */
+ l2cu_send_peer_info_req (p_lcb, L2CAP_EXTENDED_FEATURES_INFO_TYPE);
+
+ /* Tell BTM Acl management about the link */
+ if ((p_dev_info = btm_find_dev (p_bda)) != NULL) {
+ btm_acl_created (ci.bd_addr, p_dev_info->dev_class,
+ p_dev_info->sec_bd_name, handle,
+ p_lcb->link_role, BT_TRANSPORT_BR_EDR);
+ } else {
+ btm_acl_created (ci.bd_addr, NULL, NULL, handle, p_lcb->link_role, BT_TRANSPORT_BR_EDR);
+ }
+
+ BTM_SetLinkSuperTout (ci.bd_addr, btm_cb.btm_def_link_super_tout);
+
+ /* If dedicated bonding do not process any further */
+ if (p_lcb->is_bonding) {
+ if (l2cu_start_post_bond_timer(handle)) {
+ return (TRUE);
+ }
+ }
+
+ /* Update the timeouts in the hold queue */
+ l2c_process_held_packets(FALSE);
+
+ btu_stop_timer (&p_lcb->timer_entry);
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ /* For all channels, send the event through their FSMs */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) {
+ l2c_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM, &ci);
+ }
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ if (p_lcb->p_echo_rsp_cb) {
+ l2cu_send_peer_echo_req (p_lcb, NULL, 0);
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_ECHO_RSP_TOUT);
+ } else if (!p_lcb->ccb_queue.p_first_ccb) {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_STARTUP_TOUT);
+ }
+ }
+ /* Max number of acl connections. */
+ /* If there's an lcb disconnecting set this one to holding */
+ else if ((ci.status == HCI_ERR_MAX_NUM_OF_CONNECTIONS) && l2cu_lcb_disconnecting()) {
+ p_lcb->link_state = LST_CONNECT_HOLDING;
+ p_lcb->handle = HCI_INVALID_HANDLE;
+ } else {
+ /* Just in case app decides to try again in the callback context */
+ p_lcb->link_state = LST_DISCONNECTING;
+#if(CLASSIC_BT_INCLUDED == TRUE)
+ /* Connection failed. For all channels, send the event through */
+ /* their FSMs. The CCBs should remove themselves from the LCB */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) {
+ tL2C_CCB *pn = p_ccb->p_next_ccb;
+
+ l2c_csm_execute (p_ccb, L2CEVT_LP_CONNECT_CFM_NEG, &ci);
+
+ p_ccb = pn;
+ }
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ p_lcb->disc_reason = status;
+ /* Release the LCB */
+ if (p_lcb->ccb_queue.p_first_ccb == NULL) {
+ l2cu_release_lcb (p_lcb);
+ } else { /* there are any CCBs remaining */
+ if (ci.status == HCI_ERR_CONNECTION_EXISTS) {
+ /* we are in collision situation, wait for connection request from controller */
+ p_lcb->link_state = LST_CONNECTING;
+ } else {
+ l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR);
+ }
+ }
+ }
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_link_sec_comp
+**
+** Description This function is called when required security procedures
+** are completed.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_sec_comp (BD_ADDR p_bda, tBT_TRANSPORT transport, void *p_ref_data, UINT8 status)
+{
+ tL2C_CONN_INFO ci;
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_CCB *p_next_ccb;
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ UINT8 event;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ UNUSED(transport);
+
+ L2CAP_TRACE_DEBUG ("l2c_link_sec_comp: %d, %p", status, p_ref_data);
+
+ if (status == BTM_SUCCESS_NO_SECURITY) {
+ status = BTM_SUCCESS;
+ }
+
+ /* Save the parameters */
+ ci.status = status;
+ memcpy (ci.bd_addr, p_bda, BD_ADDR_LEN);
+
+ p_lcb = l2cu_find_lcb_by_bd_addr (p_bda, BT_TRANSPORT_BR_EDR);
+
+ /* If we don't have one, this is an error */
+ if (!p_lcb) {
+ L2CAP_TRACE_WARNING ("L2CAP got sec_comp for unknown BD_ADDR\n");
+ return;
+ }
+
+ /* Match p_ccb with p_ref_data returned by sec manager */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb) {
+ p_next_ccb = p_ccb->p_next_ccb;
+
+ if (p_ccb == p_ref_data) {
+ switch (status) {
+ case BTM_SUCCESS:
+ L2CAP_TRACE_DEBUG ("ccb timer ticks: %u", p_ccb->timer_entry.ticks);
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ event = L2CEVT_SEC_COMP;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ break;
+
+ case BTM_DELAY_CHECK:
+ /* start a timer - encryption change not received before L2CAP connect req */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_DELAY_CHECK_SM4);
+ return;
+
+ default:
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ event = L2CEVT_SEC_COMP_NEG;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ break;
+ }
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ l2c_csm_execute (p_ccb, event, &ci);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_hci_disc_comp
+**
+** Description This function is called when an HCI Disconnect Complete
+** event is received.
+**
+** Returns TRUE if the link is known about, else FALSE
+**
+*******************************************************************************/
+BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
+{
+ tL2C_LCB *p_lcb;
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ tL2C_CCB *p_ccb;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ BOOLEAN status = TRUE;
+ BOOLEAN lcb_is_free = TRUE;
+ tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
+
+ /* See if we have a link control block for the connection */
+ p_lcb = l2cu_find_lcb_by_handle (handle);
+ /* If we don't have one, maybe an SCO link. Send to MM */
+ if (!p_lcb) {
+#if (BLE_INCLUDED == TRUE)
+ /* The Directed Advertising Timeout error code indicates that directed advertising completed */
+ if (reason != HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT) {
+ BTM_Recovery_Pre_State();
+ }
+#endif ///BLE_INCLUDED == TRUE
+ status = FALSE;
+ } else {
+ /* There can be a case when we rejected PIN code authentication */
+ /* otherwise save a new reason */
+ if (btm_cb.acl_disc_reason != HCI_ERR_HOST_REJECT_SECURITY) {
+ btm_cb.acl_disc_reason = reason;
+ }
+
+ p_lcb->disc_reason = btm_cb.acl_disc_reason;
+
+ /* Just in case app decides to try again in the callback context */
+ p_lcb->link_state = LST_DISCONNECTING;
+
+#if (BLE_INCLUDED == TRUE)
+ /* Check for BLE and handle that differently */
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ btm_ble_update_link_topology_mask(p_lcb->link_role, FALSE);
+ }
+#endif
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ /* Link is disconnected. For all channels, send the event through */
+ /* their FSMs. The CCBs should remove themselves from the LCB */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) {
+ tL2C_CCB *pn = p_ccb->p_next_ccb;
+
+ /* Keep connect pending control block (if exists)
+ * Possible Race condition when a reconnect occurs
+ * on the channel during a disconnect of link. This
+ * ccb will be automatically retried after link disconnect
+ * arrives
+ */
+ if (p_ccb != p_lcb->p_pending_ccb) {
+ l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, &reason);
+ }
+ p_ccb = pn;
+ }
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+#if (BTM_SCO_INCLUDED == TRUE)
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_BR_EDR)
+#endif
+ {
+ /* Tell SCO management to drop any SCOs on this ACL */
+ btm_sco_acl_removed (p_lcb->remote_bd_addr);
+ }
+#endif
+
+ /* If waiting for disconnect and reconnect is pending start the reconnect now
+ race condition where layer above issued connect request on link that was
+ disconnecting
+ */
+ if (p_lcb->ccb_queue.p_first_ccb != NULL || p_lcb->p_pending_ccb) {
+ L2CAP_TRACE_DEBUG("l2c_link_hci_disc_comp: Restarting pending ACL request");
+ transport = p_lcb->transport;
+#if BLE_INCLUDED == TRUE
+ /* for LE link, always drop and re-open to ensure to get LE remote feature */
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ l2cb.is_ble_connecting = FALSE;
+ btm_acl_removed (p_lcb->remote_bd_addr, p_lcb->transport);
+ /* Release any held buffers */
+ BT_HDR *p_buf;
+ while (!list_is_empty(p_lcb->link_xmit_data_q)) {
+ p_buf = list_front(p_lcb->link_xmit_data_q);
+ list_remove(p_lcb->link_xmit_data_q, p_buf);
+ osi_free(p_buf);
+ }
+ } else
+#endif
+ {
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ /* If we are going to re-use the LCB without dropping it, release all fixed channels
+ here */
+ int xx;
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) {
+ if (p_lcb->p_fixed_ccbs[xx] && p_lcb->p_fixed_ccbs[xx] != p_lcb->p_pending_ccb) {
+#if BLE_INCLUDED == TRUE
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
+#else
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
+#endif
+ l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]);
+
+ p_lcb->p_fixed_ccbs[xx] = NULL;
+ }
+ }
+#endif
+ }
+ if (l2cu_create_conn(p_lcb, transport)) {
+ lcb_is_free = FALSE; /* still using this lcb */
+ }
+ }
+
+ p_lcb->p_pending_ccb = NULL;
+#if (BLE_INCLUDED == TRUE && GATTC_CONNECT_RETRY_EN == TRUE)
+ if(reason == HCI_ERR_CONN_FAILED_ESTABLISHMENT && p_lcb->transport == BT_TRANSPORT_LE) {
+
+ if(p_lcb->link_role == HCI_ROLE_MASTER && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) {
+ L2CAP_TRACE_DEBUG("master retry connect, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason);
+ p_lcb->retry_create_con ++;
+ // create connection retry
+ if (l2cu_create_conn(p_lcb, BT_TRANSPORT_LE)) {
+ btm_acl_removed (p_lcb->remote_bd_addr, BT_TRANSPORT_LE);
+ lcb_is_free = FALSE; /* still using this lcb */
+ }
+ }
+
+ #if (BLE_50_FEATURE_SUPPORT == TRUE)
+ if(btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) {
+ p_lcb->retry_create_con ++;
+ L2CAP_TRACE_DEBUG("slave restart extend adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason);
+ BTM_BleStartExtAdvRestart(handle);
+ }
+ #endif // #if (BLE_50_FEATURE_SUPPORT == TRUE)
+
+ #if (BLE_42_FEATURE_SUPPORT == TRUE)
+ if(!btm_ble_inter_get() && p_lcb->link_role == HCI_ROLE_SLAVE && p_lcb->retry_create_con < GATTC_CONNECT_RETRY_COUNT) {
+ p_lcb->retry_create_con ++;
+ L2CAP_TRACE_DEBUG("slave resatrt adv, retry count %d reason 0x%x\n", p_lcb->retry_create_con, reason);
+ btm_ble_start_adv();
+ }
+ #endif // #if (BLE_42_FEATURE_SUPPORT == TRUE)
+ }
+
+
+#endif // #if (BLE_INCLUDED == TRUE)
+ /* Release the LCB */
+ if (lcb_is_free) {
+ l2cu_release_lcb (p_lcb);
+ }
+ }
+
+ /* Now that we have a free acl connection, see if any lcbs are pending */
+ if (lcb_is_free && ((p_lcb = l2cu_find_lcb_by_state(LST_CONNECT_HOLDING)) != NULL)) {
+ /* we found one-- create a connection */
+ l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR);
+ }
+
+ return status;
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_link_hci_qos_violation
+**
+** Description This function is called when an HCI QOS Violation
+** event is received.
+**
+** Returns TRUE if the link is known about, else FALSE
+**
+*******************************************************************************/
+BOOLEAN l2c_link_hci_qos_violation (UINT16 handle)
+{
+ tL2C_LCB *p_lcb;
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ tL2C_CCB *p_ccb;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ /* See if we have a link control block for the connection */
+ p_lcb = l2cu_find_lcb_by_handle (handle);
+
+ /* If we don't have one, maybe an SCO link. */
+ if (!p_lcb) {
+ return (FALSE);
+ }
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ /* For all channels, tell the upper layer about it */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) {
+ if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb) {
+ l2c_csm_execute (p_ccb, L2CEVT_LP_QOS_VIOLATION_IND, NULL);
+ }
+ }
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ return (TRUE);
+}
+
+
+
+/*******************************************************************************
+**
+** Function l2c_link_timeout
+**
+** Description This function is called when a link timer expires
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_timeout (tL2C_LCB *p_lcb)
+{
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ tL2C_CCB *p_ccb;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+#if (SMP_INCLUDED == TRUE)
+ UINT16 timeout;
+ tBTM_STATUS rc;
+#endif ///SMP_INCLUDED == TRUE
+ L2CAP_TRACE_EVENT ("L2CAP - l2c_link_timeout() link state %d first CCB %p is_bonding:%d",
+ p_lcb->link_state, p_lcb->ccb_queue.p_first_ccb, p_lcb->is_bonding);
+
+ /* If link was connecting or disconnecting, clear all channels and drop the LCB */
+ if ((p_lcb->link_state == LST_CONNECTING_WAIT_SWITCH) ||
+ (p_lcb->link_state == LST_CONNECTING) ||
+ (p_lcb->link_state == LST_CONNECT_HOLDING) ||
+ (p_lcb->link_state == LST_DISCONNECTING)) {
+ p_lcb->p_pending_ccb = NULL;
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ /* For all channels, send a disconnect indication event through */
+ /* their FSMs. The CCBs should remove themselves from the LCB */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) {
+ tL2C_CCB *pn = p_ccb->p_next_ccb;
+
+ l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL);
+
+ p_ccb = pn;
+ }
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->link_state == LST_CONNECTING &&
+ l2cb.is_ble_connecting == TRUE) {
+ L2CA_CancelBleConnectReq(l2cb.ble_connecting_bda);
+ }
+#endif
+ /* Release the LCB */
+ l2cu_release_lcb (p_lcb);
+ }
+
+ /* If link is connected, check for inactivity timeout */
+ if (p_lcb->link_state == LST_CONNECTED) {
+ /* Check for ping outstanding */
+ if (p_lcb->p_echo_rsp_cb) {
+ tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb;
+
+ /* Zero out the callback in case app immediately calls us again */
+ p_lcb->p_echo_rsp_cb = NULL;
+
+ (*p_cb) (L2CAP_PING_RESULT_NO_RESP);
+
+ L2CAP_TRACE_WARNING ("L2CAP - ping timeout");
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ /* For all channels, send a disconnect indication event through */
+ /* their FSMs. The CCBs should remove themselves from the LCB */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; ) {
+ tL2C_CCB *pn = p_ccb->p_next_ccb;
+
+ l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL);
+
+ p_ccb = pn;
+ }
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ }
+
+#if (SMP_INCLUDED == TRUE)
+ /* If no channels in use, drop the link. */
+ if (!p_lcb->ccb_queue.p_first_ccb) {
+ rc = btm_sec_disconnect (p_lcb->handle, HCI_ERR_PEER_USER);
+
+ if (rc == BTM_CMD_STORED) {
+ /* Security Manager will take care of disconnecting, state will be updated at that time */
+ timeout = 0xFFFF;
+ } else if (rc == BTM_CMD_STARTED) {
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ } else if (rc == BTM_SUCCESS) {
+ l2cu_process_fixed_disc_cback(p_lcb);
+ /* BTM SEC will make sure that link is release (probably after pairing is done) */
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = 0xFFFF;
+ } else if (rc == BTM_BUSY) {
+ /* BTM is still executing security process. Let lcb stay as connected */
+ timeout = 0xFFFF;
+ } else if ((p_lcb->is_bonding)
+ && (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER))) {
+ l2cu_process_fixed_disc_cback(p_lcb);
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ } else {
+ /* probably no buffer to send disconnect */
+ timeout = BT_1SEC_TIMEOUT;
+ }
+
+ if (timeout != 0xFFFF) {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout);
+ }
+ } else {
+ /* Check in case we were flow controlled */
+ l2c_link_check_send_pkts (p_lcb, NULL, NULL);
+ }
+#endif ///SMP_INCLUDED == TRUE
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_info_timeout
+**
+** Description This function is called when an info request times out
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_info_timeout (tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_ccb;
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ tL2C_CONN_INFO ci;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ /* If we timed out waiting for info response, just continue using basic if allowed */
+ if (p_lcb->w4_info_rsp) {
+ /* If waiting for security complete, restart the info response timer */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) {
+ if ( (p_ccb->chnl_state == CST_ORIG_W4_SEC_COMP) || (p_ccb->chnl_state == CST_TERM_W4_SEC_COMP) ) {
+ btu_start_timer (&p_lcb->info_timer_entry, BTU_TTYPE_L2CAP_INFO, L2CAP_WAIT_INFO_RSP_TOUT);
+ return;
+ }
+ }
+
+ p_lcb->w4_info_rsp = FALSE;
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ /* If link is in process of being brought up */
+ if ((p_lcb->link_state != LST_DISCONNECTED) &&
+ (p_lcb->link_state != LST_DISCONNECTING)) {
+ /* Notify active channels that peer info is finished */
+ if (p_lcb->ccb_queue.p_first_ccb) {
+ ci.status = HCI_SUCCESS;
+ memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR));
+
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci);
+ }
+ }
+ }
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_adjust_allocation
+**
+** Description This function is called when a link is created or removed
+** to calculate the amount of packets each link may send to
+** the HCI without an ack coming back.
+**
+** Currently, this is a simple allocation, dividing the
+** number of Controller Packets by the number of links. In
+** the future, QOS configuration should be examined.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_adjust_allocation (void)
+{
+ UINT16 qq, qq_remainder;
+ tL2C_LCB *p_lcb;
+ UINT16 hi_quota, low_quota;
+ UINT16 num_lowpri_links = 0;
+ UINT16 num_hipri_links = 0;
+ UINT16 controller_xmit_quota = l2cb.num_lm_acl_bufs;
+ UINT16 high_pri_link_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA_A;
+ list_node_t *p_node = NULL;
+
+ /* If no links active, reset buffer quotas and controller buffers */
+ if (l2cb.num_links_active == 0) {
+ l2cb.controller_xmit_window = l2cb.num_lm_acl_bufs;
+ l2cb.round_robin_quota = l2cb.round_robin_unacked = 0;
+ return;
+ }
+
+ /* First, count the links */
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (p_lcb->in_use) {
+ if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) {
+ num_hipri_links++;
+ } else {
+ num_lowpri_links++;
+ }
+ }
+ }
+
+ /* now adjust high priority link quota */
+ low_quota = num_lowpri_links ? 1 : 0;
+ while ( (num_hipri_links * high_pri_link_quota + low_quota) > controller_xmit_quota ) {
+ high_pri_link_quota--;
+ }
+
+ /* Work out the xmit quota and buffer quota high and low priorities */
+ hi_quota = num_hipri_links * high_pri_link_quota;
+ low_quota = (hi_quota < controller_xmit_quota) ? controller_xmit_quota - hi_quota : 1;
+
+ /* Work out and save the HCI xmit quota for each low priority link */
+
+ /* If each low priority link cannot have at least one buffer */
+ if (num_lowpri_links > low_quota) {
+ l2cb.round_robin_quota = low_quota;
+ qq = qq_remainder = 1;
+ }
+ /* If each low priority link can have at least one buffer */
+ else if (num_lowpri_links > 0) {
+ l2cb.round_robin_quota = 0;
+ l2cb.round_robin_unacked = 0;
+ qq = low_quota / num_lowpri_links;
+ qq_remainder = low_quota % num_lowpri_links;
+ }
+ /* If no low priority link */
+ else {
+ l2cb.round_robin_quota = 0;
+ l2cb.round_robin_unacked = 0;
+ qq = qq_remainder = 1;
+ }
+
+ L2CAP_TRACE_EVENT ("l2c_link_adjust_allocation num_hipri: %u num_lowpri: %u low_quota: %u round_robin_quota: %u qq: %u\n",
+ num_hipri_links, num_lowpri_links, low_quota,
+ l2cb.round_robin_quota, qq);
+
+ /* Now, assign the quotas to each link */
+ p_node = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (p_lcb->in_use) {
+ if (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH) {
+ p_lcb->link_xmit_quota = high_pri_link_quota;
+ } else {
+ /* Safety check in case we switched to round-robin with something outstanding */
+ /* if sent_not_acked is added into round_robin_unacked then don't add it again */
+ /* l2cap keeps updating sent_not_acked for exiting from round robin */
+ if (( p_lcb->link_xmit_quota > 0 ) && ( qq == 0 )) {
+ l2cb.round_robin_unacked += p_lcb->sent_not_acked;
+ }
+
+ p_lcb->link_xmit_quota = qq;
+ if (qq_remainder > 0) {
+ p_lcb->link_xmit_quota++;
+ qq_remainder--;
+ }
+ }
+
+ L2CAP_TRACE_EVENT ("l2c_link_adjust_allocation Priority: %d XmitQuota: %d\n",
+ p_lcb->acl_priority, p_lcb->link_xmit_quota);
+
+ L2CAP_TRACE_EVENT (" SentNotAcked: %d RRUnacked: %d\n",
+ p_lcb->sent_not_acked, l2cb.round_robin_unacked);
+
+ /* There is a special case where we have readjusted the link quotas and */
+ /* this link may have sent anything but some other link sent packets so */
+ /* so we may need a timer to kick off this link's transmissions. */
+ if ( (p_lcb->link_state == LST_CONNECTED)
+ && (!list_is_empty(p_lcb->link_xmit_data_q))
+ && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) ) {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT);
+ }
+ }
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_adjust_chnl_allocation
+**
+** Description This function is called to calculate the amount of packets each
+** non-F&EC channel may have outstanding.
+**
+** Currently, this is a simple allocation, dividing the number
+** of packets allocated to the link by the number of channels. In
+** the future, QOS configuration should be examined.
+**
+** Returns void
+**
+*******************************************************************************/
+
+bool l2c_chnl_allocation_in_ccb_list (void *p_ccb_node, void *context)
+{
+ UNUSED(context);
+ tL2C_CCB *p_ccb = (tL2C_CCB *)p_ccb_node;
+ if (p_ccb->in_use) {
+ tL2CAP_CHNL_DATA_RATE data_rate = p_ccb->tx_data_rate + p_ccb->rx_data_rate;
+ p_ccb->buff_quota = L2CAP_CBB_DEFAULT_DATA_RATE_BUFF_QUOTA * data_rate;
+ L2CAP_TRACE_EVENT("CID:0x%04x FCR Mode:%u Priority:%u TxDataRate:%u RxDataRate:%u Quota:%u",
+ p_ccb->local_cid, p_ccb->peer_cfg.fcr.mode,
+ p_ccb->ccb_priority, p_ccb->tx_data_rate,
+ p_ccb->rx_data_rate, p_ccb->buff_quota);
+
+ /* quota may be change so check congestion */
+ l2cu_check_channel_congestion (p_ccb);
+ }
+ return false;
+}
+void l2c_link_adjust_chnl_allocation (void)
+{
+
+ L2CAP_TRACE_DEBUG ("l2c_link_adjust_chnl_allocation");
+
+ /* assign buffer quota to each channel based on its data rate requirement */
+ list_foreach(l2cb.p_ccb_pool, l2c_chnl_allocation_in_ccb_list, NULL);
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_processs_num_bufs
+**
+** Description This function is called when a "controller buffer size"
+** event is first received from the controller. It updates
+** the L2CAP values.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_processs_num_bufs (UINT16 num_lm_acl_bufs)
+{
+ l2cb.num_lm_acl_bufs = l2cb.controller_xmit_window = num_lm_acl_bufs;
+
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_pkts_rcvd
+**
+** Description This function is called from the HCI transport when it is time
+** tto send a "Host ready for packets" command. This is only when
+** host to controller flow control is used. If fills in the arrays
+** of numbers of packets and handles.
+**
+** Returns count of number of entries filled in
+**
+*******************************************************************************/
+UINT8 l2c_link_pkts_rcvd (UINT16 *num_pkts, UINT16 *handles)
+{
+ UINT8 num_found = 0;
+
+ UNUSED(num_pkts);
+ UNUSED(handles);
+
+ return (num_found);
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_role_changed
+**
+** Description This function is called whan a link's master/slave role change
+** event is received. It simply updates the link control block.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_role_changed (BD_ADDR bd_addr, UINT8 new_role, UINT8 hci_status)
+{
+ tL2C_LCB *p_lcb;
+
+ /* Make sure not called from HCI Command Status (bd_addr and new_role are invalid) */
+ if (bd_addr) {
+ /* If here came form hci role change event */
+ p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_BR_EDR);
+ if (p_lcb) {
+ p_lcb->link_role = new_role;
+
+ /* Reset high priority link if needed */
+ if (hci_status == HCI_SUCCESS) {
+ l2cu_set_acl_priority(bd_addr, p_lcb->acl_priority, TRUE);
+ }
+ }
+ }
+
+ /* Check if any LCB was waiting for switch to be completed */
+ list_node_t *p_node = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if ((p_lcb->in_use) && (p_lcb->link_state == LST_CONNECTING_WAIT_SWITCH)) {
+ l2cu_create_conn_after_switch (p_lcb);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_pin_code_request
+**
+** Description This function is called whan a pin-code request is received
+** on a connection. If there are no channels active yet on the
+** link, it extends the link first connection timer. Make sure
+** that inactivity timer is not extended if PIN code happens
+** to be after last ccb released.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_pin_code_request (BD_ADDR bd_addr)
+{
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (bd_addr, BT_TRANSPORT_BR_EDR);
+
+ if ( (p_lcb) && (!p_lcb->ccb_queue.p_first_ccb) ) {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_CONNECT_TOUT_EXT);
+ }
+}
+
+#if L2CAP_WAKE_PARKED_LINK == TRUE
+/*******************************************************************************
+**
+** Function l2c_link_check_power_mode
+**
+** Description This function is called to check power mode.
+**
+** Returns TRUE if link is going to be active from park
+** FALSE if nothing to send or not in park mode
+**
+*******************************************************************************/
+BOOLEAN l2c_link_check_power_mode (tL2C_LCB *p_lcb)
+{
+ tBTM_PM_MODE mode;
+ tL2C_CCB *p_ccb;
+ BOOLEAN need_to_active = FALSE;
+
+ /*
+ * We only switch park to active only if we have unsent packets
+ */
+ if (list_is_empty(p_lcb->link_xmit_data_q)) {
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) {
+ if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
+ need_to_active = TRUE;
+ break;
+ }
+ }
+ } else {
+ need_to_active = TRUE;
+ }
+
+ /* if we have packets to send */
+ if ( need_to_active ) {
+ /* check power mode */
+ if (BTM_ReadPowerMode(p_lcb->remote_bd_addr, &mode) == BTM_SUCCESS) {
+ if ( mode == BTM_PM_STS_PENDING ) {
+ L2CAP_TRACE_DEBUG ("LCB(0x%x) is in PM pending state\n", p_lcb->handle);
+
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+#endif /* L2CAP_WAKE_PARKED_LINK == TRUE) */
+
+/*******************************************************************************
+**
+** Function l2c_link_check_send_pkts
+**
+** Description This function is called to check if it can send packets
+** to the Host Controller. It may be passed the address of
+** a packet to send.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_check_send_pkts (tL2C_LCB *p_lcb, tL2C_CCB *p_ccb, BT_HDR *p_buf)
+{
+ BOOLEAN single_write = FALSE;
+ L2CAP_TRACE_DEBUG("%s",__func__);
+ /* Save the channel ID for faster counting */
+ if (p_buf) {
+ if (p_ccb != NULL) {
+ p_buf->event = p_ccb->local_cid;
+ single_write = TRUE;
+ } else {
+ p_buf->event = 0;
+ }
+
+ p_buf->layer_specific = 0;
+ list_append(p_lcb->link_xmit_data_q, p_buf);
+
+ if (p_lcb->link_xmit_quota == 0) {
+#if BLE_INCLUDED == TRUE
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ l2cb.ble_check_round_robin = TRUE;
+ } else
+#endif
+ {
+ l2cb.check_round_robin = TRUE;
+ }
+ }
+ }
+
+ /* If this is called from uncongested callback context break recursive calling.
+ ** This LCB will be served when receiving number of completed packet event.
+ */
+ if (l2cb.is_cong_cback_context) {
+ L2CAP_TRACE_ERROR("l2cab is_cong_cback_context");
+ return;
+ }
+
+ /* If we are in a scenario where there are not enough buffers for each link to
+ ** have at least 1, then do a round-robin for all the LCBs
+ */
+ if ( (p_lcb == NULL) || (p_lcb->link_xmit_quota == 0) ) {
+ list_node_t *p_node = NULL;
+ tL2C_LCB *p_lcb_cur = NULL;
+ if (p_lcb == NULL) {
+ p_node = list_begin(l2cb.p_lcb_pool);
+ p_lcb = list_node(p_node);
+ } else if (!single_write) {
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb_cur = list_node(p_node);
+ if (p_lcb_cur == p_lcb) {
+ p_node = list_next(p_node);
+ p_lcb = list_node(p_node);
+ break;
+ }
+ }
+ }
+
+ /* Loop through, starting at the next */
+ for ( ; p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+#if (BLE_INCLUDED == TRUE)
+ L2CAP_TRACE_DEBUG("window = %d,robin_unacked = %d,robin_quota=%d",l2cb.controller_le_xmit_window,l2cb.ble_round_robin_unacked,l2cb.ble_round_robin_quota);
+#endif ///BLE_INCLUDED == TRUE
+ /* If controller window is full, nothing to do */
+ if (((l2cb.controller_xmit_window == 0 ||
+ (l2cb.round_robin_unacked >= l2cb.round_robin_quota))
+#if (BLE_INCLUDED == TRUE)
+ && (p_lcb->transport == BT_TRANSPORT_BR_EDR)
+ )
+ || (p_lcb->transport == BT_TRANSPORT_LE &&
+ (l2cb.ble_round_robin_unacked >= l2cb.ble_round_robin_quota ||
+ l2cb.controller_le_xmit_window == 0 )))
+#else
+ ))
+#endif ///BLE_INCLUDED == TRUE
+ break;
+
+
+ /* Check for wraparound */
+ if (p_node == list_end(l2cb.p_lcb_pool)) {
+ p_node = list_begin(l2cb.p_lcb_pool);
+ p_lcb = list_node(p_node);
+ }
+ L2CAP_TRACE_DEBUG("in_use=%d,segment_being_sent=%d,link_state=%d,link_xmit_quota=%d",p_lcb->in_use,p_lcb->partial_segment_being_sent,p_lcb->link_state,p_lcb->link_xmit_quota);
+ if ( (!p_lcb->in_use)
+ || (p_lcb->partial_segment_being_sent)
+ || (p_lcb->link_state != LST_CONNECTED)
+ || (p_lcb->link_xmit_quota != 0)
+ || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) {
+ continue;
+ }
+
+ /* See if we can send anything from the Link Queue */
+ if (!list_is_empty(p_lcb->link_xmit_data_q)) {
+ p_buf = (BT_HDR *)list_front(p_lcb->link_xmit_data_q);
+ list_remove(p_lcb->link_xmit_data_q, p_buf);
+ l2c_link_send_to_lower (p_lcb, p_buf);
+ } else if (single_write) {
+ /* If only doing one write, break out */
+ break;
+ }
+ /* If nothing on the link queue, check the channel queue */
+ else if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) != NULL) {
+ l2c_link_send_to_lower (p_lcb, p_buf);
+ }
+ }
+
+ /* If we finished without using up our quota, no need for a safety check */
+ if ( (l2cb.controller_xmit_window > 0)
+ && (l2cb.round_robin_unacked < l2cb.round_robin_quota)
+#if (BLE_INCLUDED == TRUE)
+ && (p_lcb->transport == BT_TRANSPORT_BR_EDR)
+#endif
+ ) {
+ l2cb.check_round_robin = FALSE;
+ }
+
+#if (BLE_INCLUDED == TRUE)
+ if ( (l2cb.controller_le_xmit_window > 0)
+ && (l2cb.ble_round_robin_unacked < l2cb.ble_round_robin_quota)
+ && (p_lcb->transport == BT_TRANSPORT_LE)) {
+ l2cb.ble_check_round_robin = FALSE;
+ }
+#endif
+ } else { /* if this is not round-robin service */
+ /* If a partial segment is being sent, can't send anything else */
+ L2CAP_TRACE_DEBUG("partial_segment_being_sent=%d,link_state=%d,power_mode=%d",p_lcb->partial_segment_being_sent,p_lcb->link_state,L2C_LINK_CHECK_POWER_MODE (p_lcb));
+ if ( (p_lcb->partial_segment_being_sent)
+ || (p_lcb->link_state != LST_CONNECTED)
+ || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) {
+ return;
+ }
+
+ /* See if we can send anything from the link queue */
+#if (BLE_INCLUDED == TRUE)
+ while ( ((l2cb.controller_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) ||
+ (l2cb.controller_le_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_LE)))
+ && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
+#else
+ while ( (l2cb.controller_xmit_window != 0)
+ && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
+#endif
+ {
+ if (list_is_empty(p_lcb->link_xmit_data_q)) {
+ break;
+ }
+
+ p_buf = (BT_HDR *)list_front(p_lcb->link_xmit_data_q);
+ list_remove(p_lcb->link_xmit_data_q, p_buf);
+ if (!l2c_link_send_to_lower (p_lcb, p_buf)) {
+ break;
+ }
+ }
+
+ if (!single_write) {
+ /* See if we can send anything for any channel */
+#if (BLE_INCLUDED == TRUE)
+ while ( ((l2cb.controller_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) ||
+ (l2cb.controller_le_xmit_window != 0 && (p_lcb->transport == BT_TRANSPORT_LE)))
+ && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
+#else
+ while ((l2cb.controller_xmit_window != 0) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota))
+#endif
+ {
+ //need check flag: partial_segment_being_sent
+ if ( (p_lcb->partial_segment_being_sent)
+ || (p_lcb->link_state != LST_CONNECTED)
+ || (L2C_LINK_CHECK_POWER_MODE (p_lcb)) ) {
+ break;
+ }
+ //L2CAP_TRACE_DEBUG("l2cu_get_next_buffer_to_send = %p",l2cu_get_next_buffer_to_send(p_lcb));
+ if ((p_buf = l2cu_get_next_buffer_to_send (p_lcb)) == NULL) {
+ break;
+ }
+
+ if (!l2c_link_send_to_lower (p_lcb, p_buf)) {
+ break;
+ }
+ }
+ }
+
+ /* There is a special case where we have readjusted the link quotas and */
+ /* this link may have sent anything but some other link sent packets so */
+ /* so we may need a timer to kick off this link's transmissions. */
+ if ( (!list_is_empty(p_lcb->link_xmit_data_q)) && (p_lcb->sent_not_acked < p_lcb->link_xmit_quota) ) {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_FLOW_CONTROL_TOUT);
+ }
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_send_to_lower
+**
+** Description This function queues the buffer for HCI transmission
+**
+** Returns TRUE for success, FALSE for fail
+**
+*******************************************************************************/
+static BOOLEAN l2c_link_send_to_lower (tL2C_LCB *p_lcb, BT_HDR *p_buf)
+{
+ UINT16 num_segs;
+ UINT16 xmit_window, acl_data_size;
+ const controller_t *controller = controller_get_interface();
+ L2CAP_TRACE_DEBUG("%s",__func__);
+ if ((p_buf->len <= controller->get_acl_packet_size_classic()
+#if (BLE_INCLUDED == TRUE)
+ && (p_lcb->transport == BT_TRANSPORT_BR_EDR)) ||
+ ((p_lcb->transport == BT_TRANSPORT_LE) && (p_buf->len <= controller->get_acl_packet_size_ble()))
+#else
+ )
+#endif
+ ) {
+ if (p_lcb->link_xmit_quota == 0) {
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ l2cb.ble_round_robin_unacked++;
+ } else
+#endif
+ l2cb.round_robin_unacked++;
+ }
+ p_lcb->sent_not_acked++;
+ p_buf->layer_specific = 0;
+
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ l2cb.controller_le_xmit_window--;
+ bte_main_hci_send(p_buf, (UINT16)(BT_EVT_TO_LM_HCI_ACL | LOCAL_BLE_CONTROLLER_ID));
+ } else
+#endif
+ {
+ l2cb.controller_xmit_window--;
+ bte_main_hci_send(p_buf, BT_EVT_TO_LM_HCI_ACL);
+ }
+ } else {
+#if BLE_INCLUDED == TRUE
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ acl_data_size = controller->get_acl_data_size_ble();
+ xmit_window = l2cb.controller_le_xmit_window;
+
+ } else
+#endif
+ {
+ acl_data_size = controller->get_acl_data_size_classic();
+ xmit_window = l2cb.controller_xmit_window;
+ }
+ num_segs = (p_buf->len - HCI_DATA_PREAMBLE_SIZE + acl_data_size - 1) / acl_data_size;
+
+
+ /* If doing round-robin, then only 1 segment each time */
+ if (p_lcb->link_xmit_quota == 0) {
+ num_segs = 1;
+ p_lcb->partial_segment_being_sent = TRUE;
+ } else {
+ /* Multi-segment packet. Make sure it can fit */
+ if (num_segs > xmit_window) {
+ num_segs = xmit_window;
+ p_lcb->partial_segment_being_sent = TRUE;
+ }
+
+ if (num_segs > (p_lcb->link_xmit_quota - p_lcb->sent_not_acked)) {
+ num_segs = (p_lcb->link_xmit_quota - p_lcb->sent_not_acked);
+ p_lcb->partial_segment_being_sent = TRUE;
+ }
+ }
+
+ p_buf->layer_specific = num_segs;
+#if BLE_INCLUDED == TRUE
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ l2cb.controller_le_xmit_window -= num_segs;
+ if (p_lcb->link_xmit_quota == 0) {
+ l2cb.ble_round_robin_unacked += num_segs;
+ }
+ } else
+#endif
+ {
+ l2cb.controller_xmit_window -= num_segs;
+
+ if (p_lcb->link_xmit_quota == 0) {
+ l2cb.round_robin_unacked += num_segs;
+ }
+ }
+
+ p_lcb->sent_not_acked += num_segs;
+#if BLE_INCLUDED == TRUE
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ bte_main_hci_send(p_buf, (UINT16)(BT_EVT_TO_LM_HCI_ACL | LOCAL_BLE_CONTROLLER_ID));
+ } else
+#endif
+ {
+ bte_main_hci_send(p_buf, BT_EVT_TO_LM_HCI_ACL);
+ }
+ }
+
+#if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE)
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ L2CAP_TRACE_DEBUG ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d",
+ l2cb.controller_le_xmit_window,
+ p_lcb->handle,
+ p_lcb->link_xmit_quota, p_lcb->sent_not_acked,
+ l2cb.ble_round_robin_quota, l2cb.ble_round_robin_unacked);
+ } else
+#endif
+ {
+ L2CAP_TRACE_DEBUG ("TotalWin=%d,Hndl=0x%x,Quota=%d,Unack=%d,RRQuota=%d,RRUnack=%d",
+ l2cb.controller_xmit_window,
+ p_lcb->handle,
+ p_lcb->link_xmit_quota, p_lcb->sent_not_acked,
+ l2cb.round_robin_quota, l2cb.round_robin_unacked);
+ }
+#endif
+
+ return TRUE;
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_process_num_completed_pkts
+**
+** Description This function is called when a "number-of-completed-packets"
+** event is received from the controller. It updates all the
+** LCB transmit counts.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_process_num_completed_pkts (UINT8 *p)
+{
+ UINT8 num_handles, xx;
+ UINT16 handle;
+ UINT16 num_sent;
+ tL2C_LCB *p_lcb;
+
+ STREAM_TO_UINT8 (num_handles, p);
+
+ for (xx = 0; xx < num_handles; xx++) {
+ STREAM_TO_UINT16 (handle, p);
+ STREAM_TO_UINT16 (num_sent, p);
+
+ p_lcb = l2cu_find_lcb_by_handle (handle);
+
+ /* Callback for number of completed packet event */
+ /* Originally designed for [3DSG] */
+ if ((p_lcb != NULL) && (p_lcb->p_nocp_cb)) {
+ L2CAP_TRACE_DEBUG ("L2CAP - calling NoCP callback");
+ (*p_lcb->p_nocp_cb)(p_lcb->remote_bd_addr);
+ }
+
+ if (p_lcb) {
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb && (p_lcb->transport == BT_TRANSPORT_LE)) {
+ l2cb.controller_le_xmit_window += num_sent;
+ } else
+#endif
+ {
+ /* Maintain the total window to the controller */
+ l2cb.controller_xmit_window += num_sent;
+ }
+ /* If doing round-robin, adjust communal counts */
+ if (p_lcb->link_xmit_quota == 0) {
+#if BLE_INCLUDED == TRUE
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ /* Don't go negative */
+ if (l2cb.ble_round_robin_unacked > num_sent) {
+ l2cb.ble_round_robin_unacked -= num_sent;
+ } else {
+ l2cb.ble_round_robin_unacked = 0;
+ }
+ } else
+#endif
+ {
+ /* Don't go negative */
+ if (l2cb.round_robin_unacked > num_sent) {
+ l2cb.round_robin_unacked -= num_sent;
+ } else {
+ l2cb.round_robin_unacked = 0;
+ }
+ }
+ }
+
+ /* Don't go negative */
+ if (p_lcb->sent_not_acked > num_sent) {
+ p_lcb->sent_not_acked -= num_sent;
+ } else {
+ p_lcb->sent_not_acked = 0;
+ }
+
+ l2c_link_check_send_pkts (p_lcb, NULL, NULL);
+
+ /* If we were doing round-robin for low priority links, check 'em */
+ if ( (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)
+ && (l2cb.check_round_robin)
+ && (l2cb.round_robin_unacked < l2cb.round_robin_quota) ) {
+ l2c_link_check_send_pkts (NULL, NULL, NULL);
+ }
+#if BLE_INCLUDED == TRUE
+ if ((p_lcb->transport == BT_TRANSPORT_LE)
+ && (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)
+ && ((l2cb.ble_check_round_robin)
+ && (l2cb.ble_round_robin_unacked < l2cb.ble_round_robin_quota))) {
+ l2c_link_check_send_pkts (NULL, NULL, NULL);
+ }
+#endif
+ }
+
+#if (L2CAP_HCI_FLOW_CONTROL_DEBUG == TRUE)
+ if (p_lcb) {
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ L2CAP_TRACE_DEBUG ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d\n",
+ l2cb.controller_le_xmit_window,
+ p_lcb->handle, p_lcb->sent_not_acked,
+ l2cb.ble_check_round_robin, l2cb.ble_round_robin_unacked);
+ } else
+#endif
+ {
+ L2CAP_TRACE_DEBUG ("TotalWin=%d,LinkUnack(0x%x)=%d,RRCheck=%d,RRUnack=%d\n",
+ l2cb.controller_xmit_window,
+ p_lcb->handle, p_lcb->sent_not_acked,
+ l2cb.check_round_robin, l2cb.round_robin_unacked);
+
+ }
+ } else {
+#if (BLE_INCLUDED == TRUE)
+ L2CAP_TRACE_DEBUG ("TotalWin=%d LE_Win: %d, Handle=0x%x, RRCheck=%d, RRUnack=%d\n",
+ l2cb.controller_xmit_window,
+ l2cb.controller_le_xmit_window,
+ handle,
+ l2cb.ble_check_round_robin, l2cb.ble_round_robin_unacked);
+#else
+ L2CAP_TRACE_DEBUG ("TotalWin=%d Handle=0x%x RRCheck=%d RRUnack=%d\n",
+ l2cb.controller_xmit_window,
+ handle,
+ l2cb.check_round_robin, l2cb.round_robin_unacked);
+#endif
+ }
+#endif
+ }
+
+#if (defined(HCILP_INCLUDED) && HCILP_INCLUDED == TRUE)
+ /* only full stack can enable sleep mode */
+ btu_check_bt_sleep ();
+#endif
+}
+
+/*******************************************************************************
+**
+** Function l2c_link_segments_xmitted
+**
+** Description This function is called from the HCI Interface when an ACL
+** data packet segment is transmitted.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_link_segments_xmitted (BT_HDR *p_msg)
+{
+ UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset;
+ UINT16 handle;
+ tL2C_LCB *p_lcb;
+
+ /* Extract the handle */
+ STREAM_TO_UINT16 (handle, p);
+ handle = HCID_GET_HANDLE (handle);
+
+ /* Find the LCB based on the handle */
+ if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - rcvd segment complete, unknown handle: %d\n", handle);
+ osi_free (p_msg);
+ return;
+ }
+
+ if (p_lcb->link_state == LST_CONNECTED) {
+ /* Enqueue the buffer to the head of the transmit queue, and see */
+ /* if we can transmit anything more. */
+ list_prepend(p_lcb->link_xmit_data_q, p_msg);
+
+ p_lcb->partial_segment_being_sent = FALSE;
+
+ l2c_link_check_send_pkts (p_lcb, NULL, NULL);
+ } else {
+ osi_free (p_msg);
+ }
+}
diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_main.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_main.c
new file mode 100644
index 00000000..dfdef99b
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_main.c
@@ -0,0 +1,1065 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-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 main L2CAP entry points
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+//#include <stdio.h>
+
+#include "device/controller.h"
+//#include "btcore/include/counter.h"
+#include "common/bt_target.h"
+#include "btm_int.h"
+#include "stack/btu.h"
+#include "stack/hcimsgs.h"
+#include "stack/l2c_api.h"
+#include "l2c_int.h"
+#include "stack/l2cdefs.h"
+//#include "osi/include/log.h"
+
+/********************************************************************************/
+/* L O C A L F U N C T I O N P R O T O T Y P E S */
+/********************************************************************************/
+#if (CLASSIC_BT_INCLUDED == TRUE)
+static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+/********************************************************************************/
+/* G L O B A L L 2 C A P D A T A */
+/********************************************************************************/
+#if L2C_DYNAMIC_MEMORY == FALSE
+tL2C_CB l2cb;
+#else
+tL2C_CB *l2c_cb_ptr;
+#endif
+
+#if BT_CLASSIC_BQB_INCLUDED
+static BOOLEAN s_l2cap_bqb_bad_cmd_len_rej_flag = FALSE;
+#endif /* BT_CLASSIC_BQB_INCLUDED */
+
+#if 0 //Unused
+/*******************************************************************************
+**
+** Function l2c_bcst_msg
+**
+** Description
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_bcst_msg( BT_HDR *p_buf, UINT16 psm )
+{
+ UINT8 *p;
+
+ /* Ensure we have enough space in the buffer for the L2CAP and HCI headers */
+ if (p_buf->offset < L2CAP_BCST_MIN_OFFSET) {
+ L2CAP_TRACE_ERROR ("L2CAP - cannot send buffer, offset: %d", p_buf->offset);
+ osi_free (p_buf);
+ return;
+ }
+
+ /* Step back some bytes to add the headers */
+ p_buf->offset -= (HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_BCST_OVERHEAD);
+ p_buf->len += L2CAP_PKT_OVERHEAD + L2CAP_BCST_OVERHEAD;
+
+ /* Set the pointer to the beginning of the data */
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+
+ /* First, the HCI transport header */
+ UINT16_TO_STREAM (p, 0x0050 | (L2CAP_PKT_START << 12) | (2 << 14));
+
+ uint16_t acl_data_size = controller_get_interface()->get_acl_data_size_classic();
+ /* The HCI transport will segment the buffers. */
+ if (p_buf->len > acl_data_size) {
+ UINT16_TO_STREAM (p, acl_data_size);
+ } else {
+ UINT16_TO_STREAM (p, p_buf->len);
+ }
+
+ /* Now the L2CAP header */
+ UINT16_TO_STREAM (p, p_buf->len - L2CAP_PKT_OVERHEAD);
+ UINT16_TO_STREAM (p, L2CAP_CONNECTIONLESS_CID);
+ UINT16_TO_STREAM (p, psm);
+
+ p_buf->len += HCI_DATA_PREAMBLE_SIZE;
+
+ if (p_buf->len <= controller_get_interface()->get_acl_packet_size_classic()) {
+ //counter_add("l2cap.ch2.tx.bytes", p_buf->len);
+ //counter_add("l2cap.ch2.tx.pkts", 1);
+
+ bte_main_hci_send(p_buf, BT_EVT_TO_LM_HCI_ACL);
+ }
+}
+#endif
+
+/*******************************************************************************
+**
+** Function l2cap_bqb_bad_cmd_len_rej_ctrl
+**
+** Description Control rejecting L2CAP signaling PDUs with incorrect length
+** for BQB test.
+**
+** Returns void
+**
+*******************************************************************************/
+#if BT_CLASSIC_BQB_INCLUDED
+void l2cap_bqb_bad_cmd_len_rej_ctrl(BOOLEAN enable)
+{
+ s_l2cap_bqb_bad_cmd_len_rej_flag = enable;
+}
+#endif /* BT_CLASSIC_BQB_INCLUDED */
+
+
+/*******************************************************************************
+**
+** Function l2c_rcv_acl_data
+**
+** Description This function is called from the HCI Interface when an ACL
+** data packet is received.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_rcv_acl_data (BT_HDR *p_msg)
+{
+ UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset;
+ UINT16 handle, hci_len;
+ UINT8 pkt_type;
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb = NULL;
+ UINT16 l2cap_len, rcv_cid;
+#if (!CONFIG_BT_STACK_NO_LOG)
+ UINT16 psm;
+#endif
+ UINT16 credit;
+
+ /* Extract the handle */
+ STREAM_TO_UINT16 (handle, p);
+ pkt_type = HCID_GET_EVENT (handle);
+ handle = HCID_GET_HANDLE (handle);
+
+ /* Since the HCI Transport is putting segmented packets back together, we */
+ /* should never get a valid packet with the type set to "continuation" */
+ if (pkt_type != L2CAP_PKT_CONTINUE) {
+ /* Find the LCB based on the handle */
+ if ((p_lcb = l2cu_find_lcb_by_handle (handle)) == NULL) {
+ UINT8 cmd_code;
+
+ /* There is a slight possibility (specifically with USB) that we get an */
+ /* L2CAP connection request before we get the HCI connection complete. */
+ /* So for these types of messages, hold them for up to 2 seconds. */
+ STREAM_TO_UINT16 (hci_len, p);
+ STREAM_TO_UINT16 (l2cap_len, p);
+ STREAM_TO_UINT16 (rcv_cid, p);
+ STREAM_TO_UINT8 (cmd_code, p);
+
+ if ((p_msg->layer_specific == 0) && (rcv_cid == L2CAP_SIGNALLING_CID)
+ && (cmd_code == L2CAP_CMD_INFO_REQ || cmd_code == L2CAP_CMD_CONN_REQ)) {
+ L2CAP_TRACE_WARNING ("L2CAP - holding ACL for unknown handle:%d ls:%d"
+ " cid:%d opcode:%d cur count:%d", handle, p_msg->layer_specific,
+ rcv_cid, cmd_code, list_length(l2cb.rcv_pending_q));
+ p_msg->layer_specific = 2;
+ list_append(l2cb.rcv_pending_q, p_msg);
+
+ if (list_length(l2cb.rcv_pending_q) == 1) {
+ btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT);
+ }
+
+ return;
+ } else {
+ L2CAP_TRACE_ERROR ("L2CAP - rcvd ACL for unknown handle:%d ls:%d cid:%d"
+ " opcode:%d cur count:%d", handle, p_msg->layer_specific, rcv_cid,
+ cmd_code, list_length(l2cb.rcv_pending_q));
+ }
+ osi_free (p_msg);
+ return;
+ }
+ } else {
+ L2CAP_TRACE_WARNING ("L2CAP - expected pkt start or complete, got: %d", pkt_type);
+ osi_free (p_msg);
+ return;
+ }
+
+ /* Extract the length and update the buffer header */
+ STREAM_TO_UINT16 (hci_len, p);
+ p_msg->offset += 4;
+
+ /* Extract the length and CID */
+ STREAM_TO_UINT16 (l2cap_len, p);
+ STREAM_TO_UINT16 (rcv_cid, p);
+
+#if BLE_INCLUDED == TRUE
+ /* for BLE channel, always notify connection when ACL data received on the link */
+ if (p_lcb && p_lcb->transport == BT_TRANSPORT_LE && p_lcb->link_state != LST_DISCONNECTING)
+ /* only process fixed channel data as channel open indication when link is not in disconnecting mode */
+ {
+ l2cble_notify_le_connection(p_lcb->remote_bd_addr);
+ }
+#endif
+ L2CAP_TRACE_DEBUG ("L2CAP - rcv_cid CID: 0x%04x\n", rcv_cid);
+ /* Find the CCB for this CID */
+ if (rcv_cid >= L2CAP_BASE_APPL_CID) {
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, rcv_cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - unknown CID: 0x%04x", rcv_cid);
+ osi_free (p_msg);
+ return;
+ }
+ }
+
+ if (hci_len >= L2CAP_PKT_OVERHEAD) { /* Must receive at least the L2CAP length and CID.*/
+ p_msg->len = hci_len - L2CAP_PKT_OVERHEAD;
+ p_msg->offset += L2CAP_PKT_OVERHEAD;
+ } else {
+ L2CAP_TRACE_WARNING ("L2CAP - got incorrect hci header" );
+ osi_free (p_msg);
+ return;
+ }
+
+ if (l2cap_len != p_msg->len) {
+ L2CAP_TRACE_WARNING ("L2CAP - bad length in pkt. Exp: %d Act: %d",
+ l2cap_len, p_msg->len);
+
+ osi_free (p_msg);
+ return;
+ }
+
+ /* Send the data through the channel state machine */
+ if (rcv_cid == L2CAP_SIGNALLING_CID) {
+ //counter_add("l2cap.sig.rx.bytes", l2cap_len);
+ //counter_add("l2cap.sig.rx.pkts", 1);
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ process_l2cap_cmd (p_lcb, p, l2cap_len);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ osi_free (p_msg);
+ } else if (rcv_cid == L2CAP_CONNECTIONLESS_CID) {
+ //counter_add("l2cap.ch2.rx.bytes", l2cap_len);
+ //counter_add("l2cap.ch2.rx.pkts", 1);
+ /* process_connectionless_data (p_lcb); */
+#if !CONFIG_BT_STACK_NO_LOG
+ STREAM_TO_UINT16 (psm, p);
+#endif
+ L2CAP_TRACE_DEBUG( "GOT CONNECTIONLESS DATA PSM:%d", psm ) ;
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ /* if it is not broadcast, check UCD registration */
+ if ( l2c_ucd_check_rx_pkts( p_lcb, p_msg ) ) {
+ /* nothing to do */
+ } else
+#endif
+ {
+ osi_free (p_msg);
+ }
+ }
+#if (BLE_INCLUDED == TRUE)
+ else if (rcv_cid == L2CAP_BLE_SIGNALLING_CID) {
+ //counter_add("l2cap.ble.rx.bytes", l2cap_len);
+ //counter_add("l2cap.ble.rx.pkts", 1);
+ l2cble_process_sig_cmd (p_lcb, p, l2cap_len);
+ osi_free (p_msg);
+ }
+#endif
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ else if ((rcv_cid >= L2CAP_FIRST_FIXED_CHNL) && (rcv_cid <= L2CAP_LAST_FIXED_CHNL) &&
+ (l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb != NULL) ) {
+ //counter_add("l2cap.fix.rx.bytes", l2cap_len);
+ //counter_add("l2cap.fix.rx.pkts", 1);
+ /* If no CCB for this channel, allocate one */
+ if (p_lcb &&
+ /* only process fixed channel data when link is open or wait for data indication */
+ (p_lcb->link_state != LST_DISCONNECTING) &&
+ l2cu_initialize_fixed_ccb (p_lcb, rcv_cid, &l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) {
+ p_ccb = p_lcb->p_fixed_ccbs[rcv_cid - L2CAP_FIRST_FIXED_CHNL];
+
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) {
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ l2c_fcr_proc_pdu (p_ccb, p_msg);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ } else {
+ (*l2cb.fixed_reg[rcv_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)
+ (rcv_cid, p_lcb->remote_bd_addr, p_msg);
+ }
+ } else {
+ osi_free (p_msg);
+ }
+ }
+#endif
+
+ else {
+ //counter_add("l2cap.dyn.rx.bytes", l2cap_len);
+ //counter_add("l2cap.dyn.rx.pkts", 1);
+ if (p_ccb == NULL) {
+ osi_free (p_msg);
+ } else {
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ // Got a pkt, valid send out credits to the peer device
+ credit = L2CAP_LE_DEFAULT_CREDIT;
+ L2CAP_TRACE_DEBUG("%s Credits received %d",__func__, credit);
+ if((p_ccb->peer_conn_cfg.credits + credit) > L2CAP_LE_MAX_CREDIT) {
+ /* we have received credits more than max coc credits,
+ * so disconnecting the Le Coc Channel
+ */
+#if (BLE_INCLUDED == TRUE)
+ l2cble_send_peer_disc_req (p_ccb);
+#endif ///BLE_INCLUDED == TRUE
+ } else {
+ p_ccb->peer_conn_cfg.credits += credit;
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ }
+ }
+ /* Basic mode packets go straight to the state machine */
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) {
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DATA, p_msg);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ } else {
+ /* eRTM or streaming mode, so we need to validate states first */
+ if ((p_ccb->chnl_state == CST_OPEN) || (p_ccb->chnl_state == CST_CONFIG)) {
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ l2c_fcr_proc_pdu (p_ccb, p_msg);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ } else {
+ osi_free (p_msg);
+ }
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function process_l2cap_cmd
+**
+** Description This function is called when a packet is received on the
+** L2CAP signalling CID
+**
+** Returns void
+**
+*******************************************************************************/
+#if (CLASSIC_BT_INCLUDED == TRUE)
+static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len)
+{
+ UINT8 *p_pkt_end, *p_next_cmd, *p_cfg_end, *p_cfg_start;
+ UINT8 cmd_code, cfg_code, cfg_len, id;
+ tL2C_CONN_INFO con_info;
+ tL2CAP_CFG_INFO cfg_info;
+ UINT16 rej_reason, rej_mtu, lcid, rcid, info_type;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+ BOOLEAN cfg_rej, pkt_size_rej = FALSE;
+ UINT16 cfg_rej_len, cmd_len;
+ UINT16 result;
+ tL2C_CONN_INFO ci;
+
+#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE)
+ /* if l2cap command received in CID 1 on top of an LE link, ignore this command */
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ return;
+ }
+#endif
+
+ /* Reject the packet if it exceeds the default Signalling Channel MTU */
+ if (pkt_len > L2CAP_DEFAULT_MTU) {
+ /* Core Spec requires a single response to the first command found in a multi-command
+ ** L2cap packet. If only responses in the packet, then it will be ignored.
+ ** Here we simply mark the bad packet and decide which cmd ID to reject later
+ */
+ pkt_size_rej = TRUE;
+ L2CAP_TRACE_ERROR ("L2CAP SIG MTU Pkt Len Exceeded (672) -> pkt_len: %d", pkt_len);
+ }
+
+ p_next_cmd = p;
+ p_pkt_end = p + pkt_len;
+
+ memset (&cfg_info, 0, sizeof(cfg_info));
+
+ /* An L2CAP packet may contain multiple commands */
+ while (TRUE) {
+ /* Smallest command is 4 bytes */
+ if ((p = p_next_cmd) > (p_pkt_end - 4)) {
+ break;
+ }
+
+ STREAM_TO_UINT8 (cmd_code, p);
+ STREAM_TO_UINT8 (id, p);
+ STREAM_TO_UINT16 (cmd_len, p);
+
+ /* Check command length does not exceed packet length */
+ if ((p_next_cmd = p + cmd_len) > p_pkt_end) {
+ L2CAP_TRACE_WARNING ("Command len bad pkt_len: %d cmd_len: %d code: %d",
+ pkt_len, cmd_len, cmd_code);
+ break;
+ }
+
+ L2CAP_TRACE_DEBUG ("cmd_code: %d, id:%d, cmd_len:%d", cmd_code, id, cmd_len);
+
+ /* Bad L2CAP packet length, look or cmd to reject */
+ if (pkt_size_rej) {
+ /* If command found rejected it and we're done, otherwise keep looking */
+ if (l2c_is_cmd_rejected(cmd_code, id, p_lcb)) {
+ return;
+ } else {
+ continue; /* Look for next cmd/response in current packet */
+ }
+ }
+
+ switch (cmd_code) {
+ case L2CAP_CMD_REJECT:
+ STREAM_TO_UINT16 (rej_reason, p);
+ if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
+ STREAM_TO_UINT16 (rej_mtu, p);
+ /* What to do with the MTU reject ? We have negotiated an MTU. For now */
+ /* we will ignore it and let a higher protocol timeout take care of it */
+ L2CAP_TRACE_WARNING ("L2CAP - MTU rej Handle: %d MTU: %d", p_lcb->handle, rej_mtu);
+ }
+ if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) {
+ STREAM_TO_UINT16 (rcid, p);
+ STREAM_TO_UINT16 (lcid, p);
+
+ L2CAP_TRACE_WARNING ("L2CAP - rej with CID invalid, LCID: 0x%04x RCID: 0x%04x", lcid, rcid);
+
+ /* Remote CID invalid. Treat as a disconnect */
+ if (((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL)
+ && (p_ccb->remote_cid == rcid)) {
+ /* Fake link disconnect - no reply is generated */
+ l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL);
+ }
+ }
+
+ /* SonyEricsson Info request Bug workaround (Continue connection) */
+ else if (rej_reason == L2CAP_CMD_REJ_NOT_UNDERSTOOD && p_lcb->w4_info_rsp) {
+ btu_stop_timer (&p_lcb->info_timer_entry);
+
+ p_lcb->w4_info_rsp = FALSE;
+ ci.status = HCI_SUCCESS;
+ memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR));
+
+ /* For all channels, send the event through their FSMs */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci);
+ }
+ }
+ break;
+
+ case L2CAP_CMD_CONN_REQ:
+ STREAM_TO_UINT16 (con_info.psm, p);
+ STREAM_TO_UINT16 (rcid, p);
+ if ((p_rcb = l2cu_find_rcb_by_psm (con_info.psm)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - rcvd conn req for unknown PSM: %d", con_info.psm);
+ l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM);
+ break;
+ } else {
+ if (!p_rcb->api.pL2CA_ConnectInd_Cb) {
+ L2CAP_TRACE_WARNING ("L2CAP - rcvd conn req for outgoing-only connection PSM: %d", con_info.psm);
+ l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM);
+ break;
+ }
+ }
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) {
+ L2CAP_TRACE_ERROR ("L2CAP - unable to allocate CCB");
+ l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_RESOURCES);
+ break;
+ }
+ p_ccb->remote_id = id;
+ p_ccb->p_rcb = p_rcb;
+ p_ccb->remote_cid = rcid;
+
+ l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_REQ, &con_info);
+#if BT_CLASSIC_BQB_INCLUDED
+ // L2CAP/COS/CED/BI-02-C
+ if (s_l2cap_bqb_bad_cmd_len_rej_flag) {
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
+ }
+#endif /* BT_CLASSIC_BQB_INCLUDED */
+ break;
+
+ case L2CAP_CMD_CONN_RSP:
+ STREAM_TO_UINT16 (con_info.remote_cid, p);
+ STREAM_TO_UINT16 (lcid, p);
+ STREAM_TO_UINT16 (con_info.l2cap_result, p);
+ STREAM_TO_UINT16 (con_info.l2cap_status, p);
+
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for conn rsp, LCID: %d RCID: %d",
+ lcid, con_info.remote_cid);
+ break;
+ }
+ if (p_ccb->local_id != id) {
+ L2CAP_TRACE_WARNING ("L2CAP - con rsp - bad ID. Exp: %d Got: %d",
+ p_ccb->local_id, id);
+ break;
+ }
+
+ if (con_info.l2cap_result == L2CAP_CONN_OK) {
+ l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP, &con_info);
+ } else if (con_info.l2cap_result == L2CAP_CONN_PENDING) {
+ l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP_PND, &con_info);
+ } else {
+ l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP_NEG, &con_info);
+ }
+
+ break;
+
+ case L2CAP_CMD_CONFIG_REQ:
+ p_cfg_end = p + cmd_len;
+ cfg_rej = FALSE;
+ cfg_rej_len = 0;
+
+ STREAM_TO_UINT16 (lcid, p);
+ STREAM_TO_UINT16 (cfg_info.flags, p);
+
+ p_cfg_start = p;
+
+ cfg_info.flush_to_present = cfg_info.mtu_present = cfg_info.qos_present =
+ cfg_info.fcr_present = cfg_info.fcs_present = FALSE;
+
+ while (p < p_cfg_end) {
+ STREAM_TO_UINT8 (cfg_code, p);
+ STREAM_TO_UINT8 (cfg_len, p);
+
+ switch (cfg_code & 0x7F) {
+ case L2CAP_CFG_TYPE_MTU:
+ cfg_info.mtu_present = TRUE;
+ STREAM_TO_UINT16 (cfg_info.mtu, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FLUSH_TOUT:
+ cfg_info.flush_to_present = TRUE;
+ STREAM_TO_UINT16 (cfg_info.flush_to, p);
+ break;
+
+ case L2CAP_CFG_TYPE_QOS:
+ cfg_info.qos_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.qos.qos_flags, p);
+ STREAM_TO_UINT8 (cfg_info.qos.service_type, p);
+ STREAM_TO_UINT32 (cfg_info.qos.token_rate, p);
+ STREAM_TO_UINT32 (cfg_info.qos.token_bucket_size, p);
+ STREAM_TO_UINT32 (cfg_info.qos.peak_bandwidth, p);
+ STREAM_TO_UINT32 (cfg_info.qos.latency, p);
+ STREAM_TO_UINT32 (cfg_info.qos.delay_variation, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FCR:
+ cfg_info.fcr_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.fcr.mode, p);
+ STREAM_TO_UINT8 (cfg_info.fcr.tx_win_sz, p);
+ STREAM_TO_UINT8 (cfg_info.fcr.max_transmit, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.rtrans_tout, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.mon_tout, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.mps, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FCS:
+ cfg_info.fcs_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.fcs, p);
+ break;
+
+ case L2CAP_CFG_TYPE_EXT_FLOW:
+ cfg_info.ext_flow_spec_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.ext_flow_spec.id, p);
+ STREAM_TO_UINT8 (cfg_info.ext_flow_spec.stype, p);
+ STREAM_TO_UINT16 (cfg_info.ext_flow_spec.max_sdu_size, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.sdu_inter_time, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.access_latency, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.flush_timeout, p);
+ break;
+
+ default:
+ /* sanity check option length */
+ if ((cfg_len + L2CAP_CFG_OPTION_OVERHEAD) <= cmd_len) {
+ p += cfg_len;
+ if ((cfg_code & 0x80) == 0) {
+ cfg_rej_len += cfg_len + L2CAP_CFG_OPTION_OVERHEAD;
+ cfg_rej = TRUE;
+ }
+ }
+ /* bad length; force loop exit */
+ else {
+ p = p_cfg_end;
+ cfg_rej = TRUE;
+ }
+ break;
+ }
+ }
+
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) {
+ p_ccb->remote_id = id;
+ if (cfg_rej) {
+ l2cu_send_peer_config_rej (p_ccb, p_cfg_start, (UINT16) (cmd_len - L2CAP_CONFIG_REQ_LEN), cfg_rej_len);
+ } else {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_REQ, &cfg_info);
+ }
+ } else {
+ /* updated spec says send command reject on invalid cid */
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_INVALID_CID, id, 0, 0);
+ }
+ break;
+
+ case L2CAP_CMD_CONFIG_RSP:
+ p_cfg_end = p + cmd_len;
+ STREAM_TO_UINT16 (lcid, p);
+ STREAM_TO_UINT16 (cfg_info.flags, p);
+ STREAM_TO_UINT16 (cfg_info.result, p);
+
+ cfg_info.flush_to_present = cfg_info.mtu_present = cfg_info.qos_present =
+ cfg_info.fcr_present = cfg_info.fcs_present = FALSE;
+
+ while (p < p_cfg_end) {
+ STREAM_TO_UINT8 (cfg_code, p);
+ STREAM_TO_UINT8 (cfg_len, p);
+
+ switch (cfg_code & 0x7F) {
+ case L2CAP_CFG_TYPE_MTU:
+ cfg_info.mtu_present = TRUE;
+ STREAM_TO_UINT16 (cfg_info.mtu, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FLUSH_TOUT:
+ cfg_info.flush_to_present = TRUE;
+ STREAM_TO_UINT16 (cfg_info.flush_to, p);
+ break;
+
+ case L2CAP_CFG_TYPE_QOS:
+ cfg_info.qos_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.qos.qos_flags, p);
+ STREAM_TO_UINT8 (cfg_info.qos.service_type, p);
+ STREAM_TO_UINT32 (cfg_info.qos.token_rate, p);
+ STREAM_TO_UINT32 (cfg_info.qos.token_bucket_size, p);
+ STREAM_TO_UINT32 (cfg_info.qos.peak_bandwidth, p);
+ STREAM_TO_UINT32 (cfg_info.qos.latency, p);
+ STREAM_TO_UINT32 (cfg_info.qos.delay_variation, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FCR:
+ cfg_info.fcr_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.fcr.mode, p);
+ STREAM_TO_UINT8 (cfg_info.fcr.tx_win_sz, p);
+ STREAM_TO_UINT8 (cfg_info.fcr.max_transmit, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.rtrans_tout, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.mon_tout, p);
+ STREAM_TO_UINT16 (cfg_info.fcr.mps, p);
+ break;
+
+ case L2CAP_CFG_TYPE_FCS:
+ cfg_info.fcs_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.fcs, p);
+ break;
+
+ case L2CAP_CFG_TYPE_EXT_FLOW:
+ cfg_info.ext_flow_spec_present = TRUE;
+ STREAM_TO_UINT8 (cfg_info.ext_flow_spec.id, p);
+ STREAM_TO_UINT8 (cfg_info.ext_flow_spec.stype, p);
+ STREAM_TO_UINT16 (cfg_info.ext_flow_spec.max_sdu_size, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.sdu_inter_time, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.access_latency, p);
+ STREAM_TO_UINT32 (cfg_info.ext_flow_spec.flush_timeout, p);
+ break;
+ }
+ }
+
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) {
+ if (p_ccb->local_id != id) {
+ L2CAP_TRACE_WARNING ("L2CAP - cfg rsp - bad ID. Exp: %d Got: %d",
+ p_ccb->local_id, id);
+ break;
+ }
+ if ( (cfg_info.result == L2CAP_CFG_OK) || (cfg_info.result == L2CAP_CFG_PENDING) ) {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_RSP, &cfg_info);
+ } else {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_RSP_NEG, &cfg_info);
+ }
+ } else {
+ L2CAP_TRACE_WARNING ("L2CAP - rcvd cfg rsp for unknown CID: 0x%04x", lcid);
+ }
+ break;
+
+
+ case L2CAP_CMD_DISC_REQ:
+ STREAM_TO_UINT16 (lcid, p);
+ STREAM_TO_UINT16 (rcid, p);
+
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) {
+ if (p_ccb->remote_cid == rcid) {
+ p_ccb->remote_id = id;
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_REQ, &con_info);
+ }
+ } else {
+ l2cu_send_peer_disc_rsp (p_lcb, id, lcid, rcid);
+ }
+
+ break;
+
+ case L2CAP_CMD_DISC_RSP:
+ STREAM_TO_UINT16 (rcid, p);
+ STREAM_TO_UINT16 (lcid, p);
+
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) {
+ if ((p_ccb->remote_cid == rcid) && (p_ccb->local_id == id)) {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_RSP, &con_info);
+ }
+ }
+ break;
+
+ case L2CAP_CMD_ECHO_REQ:
+ l2cu_send_peer_echo_rsp (p_lcb, id, NULL, 0);
+ break;
+
+ case L2CAP_CMD_ECHO_RSP:
+ if (p_lcb->p_echo_rsp_cb) {
+ tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb;
+
+ /* Zero out the callback in case app immediately calls us again */
+ p_lcb->p_echo_rsp_cb = NULL;
+
+ (*p_cb) (L2CAP_PING_RESULT_OK);
+ }
+ break;
+
+ case L2CAP_CMD_INFO_REQ:
+ STREAM_TO_UINT16 (info_type, p);
+ l2cu_send_peer_info_rsp (p_lcb, id, info_type);
+ break;
+
+ case L2CAP_CMD_INFO_RSP:
+ /* Stop the link connect timer if sent before L2CAP connection is up */
+ if (p_lcb->w4_info_rsp) {
+ btu_stop_timer (&p_lcb->info_timer_entry);
+ p_lcb->w4_info_rsp = FALSE;
+ }
+
+ STREAM_TO_UINT16 (info_type, p);
+ STREAM_TO_UINT16 (result, p);
+
+ p_lcb->info_rx_bits |= (1 << info_type);
+
+ if ( (info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE)
+ && (result == L2CAP_INFO_RESP_RESULT_SUCCESS) ) {
+ STREAM_TO_UINT32( p_lcb->peer_ext_fea, p );
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ if (p_lcb->peer_ext_fea & L2CAP_EXTFEA_FIXED_CHNLS) {
+ l2cu_send_peer_info_req (p_lcb, L2CAP_FIXED_CHANNELS_INFO_TYPE);
+ break;
+ } else {
+ l2cu_process_fixed_chnl_resp (p_lcb);
+ }
+#endif
+ }
+
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE) {
+ if (result == L2CAP_INFO_RESP_RESULT_SUCCESS) {
+ memcpy (p_lcb->peer_chnl_mask, p, L2CAP_FIXED_CHNL_ARRAY_SIZE);
+ }
+
+ l2cu_process_fixed_chnl_resp (p_lcb);
+ }
+#endif
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE) {
+ if (result == L2CAP_INFO_RESP_RESULT_SUCCESS) {
+ STREAM_TO_UINT16 (p_lcb->ucd_mtu, p);
+ }
+ }
+#endif
+
+ ci.status = HCI_SUCCESS;
+ memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR));
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) {
+ l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci);
+ }
+ break;
+
+ default:
+ L2CAP_TRACE_WARNING ("L2CAP - bad cmd code: %d", cmd_code);
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0);
+ return;
+ }
+ }
+
+ UNUSED(rej_mtu);
+}
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+
+/*******************************************************************************
+**
+** Function l2c_process_held_packets
+**
+** Description This function processes any L2CAP packets that arrived before
+** the HCI connection complete arrived. It is a work around for
+** badly behaved controllers.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_process_held_packets(BOOLEAN timed_out)
+{
+ if (list_is_empty(l2cb.rcv_pending_q)) {
+ return;
+ }
+
+ if (!timed_out) {
+ btu_stop_timer(&l2cb.rcv_hold_tle);
+ L2CAP_TRACE_WARNING("L2CAP HOLD CONTINUE");
+ } else {
+ L2CAP_TRACE_WARNING("L2CAP HOLD TIMEOUT");
+ }
+
+ for (const list_node_t *node = list_begin(l2cb.rcv_pending_q);
+ node != list_end(l2cb.rcv_pending_q);) {
+ BT_HDR *p_buf = list_node(node);
+ node = list_next(node);
+ if (!timed_out || (!p_buf->layer_specific) || (--p_buf->layer_specific == 0)) {
+ list_remove(l2cb.rcv_pending_q, p_buf);
+ p_buf->layer_specific = 0xFFFF;
+ l2c_rcv_acl_data(p_buf);
+ }
+ }
+
+ /* If anyone still in the queue, restart the timeout */
+ if (!list_is_empty(l2cb.rcv_pending_q)) {
+ btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2c_init
+**
+** Description This function is called once at startup to initialize
+** all the L2CAP structures
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_init (void)
+{
+#if L2C_DYNAMIC_MEMORY
+ l2c_cb_ptr = (tL2C_CB *)osi_malloc(sizeof(tL2C_CB));
+#endif /* #if L2C_DYNAMIC_MEMORY */
+ memset (&l2cb, 0, sizeof (tL2C_CB));
+ /* the psm is increased by 2 before being used */
+ l2cb.dyn_psm = 0xFFF;
+
+ l2cb.p_ccb_pool = list_new(osi_free_func);
+ if (l2cb.p_ccb_pool == NULL) {
+ L2CAP_TRACE_ERROR("%s unable to allocate memory for L2CAP channel control block", __func__);
+ }
+ l2cb.p_lcb_pool = list_new(osi_free_func);
+ if (l2cb.p_lcb_pool == NULL) {
+ L2CAP_TRACE_ERROR("%s unable to allocate memory for L2CAP Link control block", __func__);
+ }
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ /* it will be set to L2CAP_PKT_START_NON_FLUSHABLE if controller supports */
+ l2cb.non_flushable_pbf = L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT;
+#endif
+
+
+
+#ifdef L2CAP_DESIRED_LINK_ROLE
+ l2cb.desire_role = L2CAP_DESIRED_LINK_ROLE;
+#else
+ l2cb.desire_role = HCI_ROLE_SLAVE;
+#endif
+
+ /* Set the default idle timeout */
+ l2cb.idle_timeout = L2CAP_LINK_INACTIVITY_TOUT;
+
+#if defined(L2CAP_INITIAL_TRACE_LEVEL)
+ l2cb.l2cap_trace_level = L2CAP_INITIAL_TRACE_LEVEL;
+#else
+ l2cb.l2cap_trace_level = BT_TRACE_LEVEL_NONE; /* No traces */
+#endif
+
+#if L2CAP_CONFORMANCE_TESTING == TRUE
+ /* Conformance testing needs a dynamic response */
+ l2cb.test_info_resp = L2CAP_EXTFEA_SUPPORTED_MASK;
+#endif
+
+ /* Number of ACL buffers to use for high priority channel */
+#if (defined(L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE) && (L2CAP_HIGH_PRI_CHAN_QUOTA_IS_CONFIGURABLE == TRUE))
+ l2cb.high_pri_min_xmit_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA;
+#endif
+
+#if BLE_INCLUDED == TRUE
+ l2cb.l2c_ble_fixed_chnls_mask =
+ L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT;
+#endif
+
+ l2cb.rcv_pending_q = list_new(NULL);
+ if (l2cb.rcv_pending_q == NULL) {
+ L2CAP_TRACE_ERROR("%s unable to allocate memory for link layer control block", __func__);
+ }
+#if BLE_INCLUDED == TRUE
+ l2ble_update_att_acl_pkt_num(L2CA_BUFF_INI, NULL);
+#endif
+}
+void l2c_free_p_lcb_pool(void)
+{
+ list_node_t *p_node = NULL;
+ tL2C_LCB *p_lcb = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (p_lcb) {
+ l2cu_release_lcb (p_lcb);
+ }
+ }
+
+ list_free(l2cb.p_lcb_pool);
+}
+
+void l2c_free_p_ccb_pool(void)
+{
+ list_node_t *p_node = NULL;
+ tL2C_CCB *p_ccb = NULL;
+ for (p_node = list_begin(l2cb.p_ccb_pool); p_node; p_node = list_next(p_node)) {
+ p_ccb = list_node(p_node);
+ if (p_ccb) {
+ l2cu_release_ccb (p_ccb);
+ }
+ }
+
+ list_free(l2cb.p_ccb_pool);
+}
+
+void l2c_free(void)
+{
+ list_free(l2cb.rcv_pending_q);
+ l2cb.rcv_pending_q = NULL;
+ l2c_free_p_lcb_pool();
+ l2c_free_p_ccb_pool();
+#if L2C_DYNAMIC_MEMORY
+ FREE_AND_RESET(l2c_cb_ptr);
+#endif
+#if BLE_INCLUDED == TRUE
+ l2ble_update_att_acl_pkt_num(L2CA_BUFF_DEINIT, NULL);
+#endif
+}
+
+/*******************************************************************************
+**
+** Function l2c_process_timeout
+**
+** Description This function is called when an L2CAP-related timeout occurs
+**
+** Returns void
+**
+*******************************************************************************/
+void l2c_process_timeout (TIMER_LIST_ENT *p_tle)
+{
+ /* What type of timeout ? */
+ switch (p_tle->event) {
+ case BTU_TTYPE_L2CAP_LINK:
+ l2c_link_timeout ((tL2C_LCB *)p_tle->param);
+ break;
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ case BTU_TTYPE_L2CAP_CHNL:
+ l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_TIMEOUT, NULL);
+ break;
+
+ case BTU_TTYPE_L2CAP_FCR_ACK:
+ l2c_csm_execute (((tL2C_CCB *)p_tle->param), L2CEVT_ACK_TIMEOUT, NULL);
+ break;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ case BTU_TTYPE_L2CAP_HOLD:
+ /* Update the timeouts in the hold queue */
+ l2c_process_held_packets(TRUE);
+ break;
+
+ case BTU_TTYPE_L2CAP_INFO:
+ l2c_info_timeout((tL2C_LCB *)p_tle->param);
+ break;
+ case BTU_TTYPE_L2CAP_UPDA_CONN_PARAMS: {
+#if (BLE_INCLUDED == TRUE)
+ UINT8 status = HCI_ERR_HOST_TIMEOUT;
+ tL2C_LCB *p_lcb = (tL2C_LCB *)p_tle->param;
+ if (p_lcb){
+ p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PENDING;
+ p_lcb->conn_update_mask &= ~L2C_BLE_UPDATE_PARAM_FULL;
+ l2c_send_update_conn_params_cb(p_lcb, status);
+ }
+#endif ///BLE_INCLUDED == TRUE
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_data_write
+**
+** Description API functions call this function to write data.
+**
+** Returns L2CAP_DW_SUCCESS, if data accepted, else FALSE
+** L2CAP_DW_CONGESTED, if data accepted and the channel is congested
+** L2CAP_DW_FAILED, if error
+**
+*******************************************************************************/
+#if (CLASSIC_BT_INCLUDED == TRUE)
+UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags)
+{
+ tL2C_CCB *p_ccb;
+
+ /* Find the channel control block. We don't know the link it is on. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_DataWrite, CID: %d", cid);
+ osi_free (p_data);
+ return (L2CAP_DW_FAILED);
+ }
+
+#ifndef TESTER /* Tester may send any amount of data. otherwise sending message
+ bigger than mtu size of peer is a violation of protocol */
+ if (p_data->len > p_ccb->peer_cfg.mtu) {
+ L2CAP_TRACE_WARNING ("L2CAP - CID: 0x%04x cannot send message bigger than peer's mtu size", cid);
+ osi_free (p_data);
+ return (L2CAP_DW_FAILED);
+ }
+#endif
+
+ /* channel based, packet based flushable or non-flushable */
+ p_data->layer_specific = flags;
+
+ /* If already congested, do not accept any more packets */
+ if (p_ccb->cong_sent) {
+ L2CAP_TRACE_DEBUG ("L2CAP - CID: 0x%04x cannot send, already congested xmit_hold_q.count: %u buff_quota: %u",
+ p_ccb->local_cid,
+ fixed_queue_length(p_ccb->xmit_hold_q),
+ p_ccb->buff_quota);
+
+ osi_free (p_data);
+ return (L2CAP_DW_FAILED);
+ }
+
+ //counter_add("l2cap.dyn.tx.bytes", p_data->len);
+ //counter_add("l2cap.dyn.tx.pkts", 1);
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data);
+
+ if (p_ccb->cong_sent) {
+ return (L2CAP_DW_CONGESTED);
+ }
+ return (L2CAP_DW_SUCCESS);
+}
+#endif ///CLASSIC_BT_INCLUDED == TRUE
diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_ucd.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_ucd.c
new file mode 100644
index 00000000..d2de138b
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_ucd.c
@@ -0,0 +1,1079 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-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 L2CAP UCD code
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+//#include <stdio.h>
+
+#include "stack/bt_types.h"
+#include "stack/hcidefs.h"
+#include "stack/hcimsgs.h"
+#include "stack/l2cdefs.h"
+#include "l2c_int.h"
+#include "stack/btu.h"
+#include "stack/btm_api.h"
+#include "btm_int.h"
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+static BOOLEAN l2c_ucd_connect ( BD_ADDR rem_bda );
+
+/*******************************************************************************
+**
+** Function l2c_ucd_discover_cback
+**
+** Description UCD Discover callback
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_discover_cback (BD_ADDR rem_bda, UINT8 info_type, UINT32 data)
+{
+ tL2C_RCB *p_rcb = &l2cb.rcb_pool[0];
+ UINT16 xx;
+
+ L2CAP_TRACE_DEBUG ("L2CAP - l2c_ucd_discover_cback");
+
+ for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) {
+ if (p_rcb->in_use) {
+ /* if this application is waiting UCD reception info */
+ if (( info_type == L2CAP_UCD_INFO_TYPE_RECEPTION )
+ && ( p_rcb->ucd.state & L2C_UCD_STATE_W4_RECEPTION )) {
+ p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (rem_bda, info_type, data);
+ p_rcb->ucd.state &= ~(L2C_UCD_STATE_W4_RECEPTION);
+ }
+
+ /* if this application is waiting UCD MTU info */
+ if (( info_type == L2CAP_UCD_INFO_TYPE_MTU )
+ && ( p_rcb->ucd.state & L2C_UCD_STATE_W4_MTU )) {
+ p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (rem_bda, info_type, data);
+ p_rcb->ucd.state &= ~(L2C_UCD_STATE_W4_MTU);
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_data_ind_cback
+**
+** Description UCD Data callback
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_data_ind_cback (BD_ADDR rem_bda, BT_HDR *p_buf)
+{
+ UINT8 *p;
+ UINT16 psm;
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_DEBUG ("L2CAP - l2c_ucd_data_ind_cback");
+
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+ STREAM_TO_UINT16(psm, p)
+
+ p_buf->offset += L2CAP_UCD_OVERHEAD;
+ p_buf->len -= L2CAP_UCD_OVERHEAD;
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) {
+ L2CAP_TRACE_ERROR ("L2CAP - no RCB for l2c_ucd_data_ind_cback, PSM: 0x%04x", psm);
+ osi_free (p_buf);
+ } else {
+ p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb(rem_bda, p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_congestion_status_cback
+**
+** Description UCD Congestion Status callback
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_congestion_status_cback (BD_ADDR rem_bda, BOOLEAN is_congested)
+{
+ tL2C_RCB *p_rcb = &l2cb.rcb_pool[0];
+ UINT16 xx;
+
+ L2CAP_TRACE_DEBUG ("L2CAP - l2c_ucd_congestion_status_cback");
+
+ for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) {
+ if (( p_rcb->in_use )
+ && ( p_rcb->ucd.state != L2C_UCD_STATE_UNUSED )) {
+ if ( p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) {
+ L2CAP_TRACE_DEBUG ("L2CAP - Calling UCDCongestionStatus_Cb (%d), PSM=0x%04x, BDA: %08x%04x,",
+ is_congested, p_rcb->psm,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5]);
+
+ p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ( rem_bda, is_congested );
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_disconnect_ind_cback
+**
+** Description UCD disconnect callback (This prevent to access null pointer)
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_disconnect_ind_cback (UINT16 cid, BOOLEAN result)
+{
+ /* do nothing */
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_config_ind_cback
+**
+** Description UCD config callback (This prevent to access null pointer)
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_config_ind_cback (UINT16 cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ /* do nothing */
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_config_cfm_cback
+**
+** Description UCD config callback (This prevent to access null pointer)
+**
+** Returns void
+**
+*******************************************************************************/
+static void l2c_ucd_config_cfm_cback (UINT16 cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ /* do nothing */
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UcdRegister
+**
+** Description Register PSM on UCD.
+**
+** Parameters: tL2CAP_UCD_CB_INFO
+**
+** Return value: TRUE if successs
+**
+*******************************************************************************/
+BOOLEAN L2CA_UcdRegister ( UINT16 psm, tL2CAP_UCD_CB_INFO *p_cb_info )
+{
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_API ("L2CA_UcdRegister() PSM: 0x%04x", psm);
+
+ if ((!p_cb_info->pL2CA_UCD_Discover_Cb)
+ || (!p_cb_info->pL2CA_UCD_Data_Cb)) {
+ L2CAP_TRACE_ERROR ("L2CAP - no callback registering PSM(0x%04x) on UCD", psm);
+ return (FALSE);
+ }
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) {
+ L2CAP_TRACE_ERROR ("L2CAP - no RCB for L2CA_UcdRegister, PSM: 0x%04x", psm);
+ return (FALSE);
+ }
+
+ p_rcb->ucd.state = L2C_UCD_STATE_W4_DATA;
+ p_rcb->ucd.cb_info = *p_cb_info;
+
+ /* check if master rcb is created for UCD */
+ if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) == NULL) {
+ if ((p_rcb = l2cu_allocate_rcb (L2C_UCD_RCB_ID)) == NULL) {
+ L2CAP_TRACE_ERROR ("L2CAP - no RCB available for L2CA_UcdRegister");
+ return (FALSE);
+ } else {
+ /* these callback functions will forward data to each UCD application */
+ p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb = l2c_ucd_discover_cback;
+ p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb = l2c_ucd_data_ind_cback;
+ p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb = l2c_ucd_congestion_status_cback;
+
+ memset (&p_rcb->api, 0, sizeof(tL2CAP_APPL_INFO));
+ p_rcb->api.pL2CA_DisconnectInd_Cb = l2c_ucd_disconnect_ind_cback;
+
+ /* This will make L2CAP check UCD congestion callback */
+ p_rcb->api.pL2CA_CongestionStatus_Cb = NULL;
+
+ /* do nothing but prevent crash */
+ p_rcb->api.pL2CA_ConfigInd_Cb = l2c_ucd_config_ind_cback;
+ p_rcb->api.pL2CA_ConfigCfm_Cb = l2c_ucd_config_cfm_cback;
+ }
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UcdDeregister
+**
+** Description Deregister PSM on UCD.
+**
+** Parameters: PSM
+**
+** Return value: TRUE if successs
+**
+*******************************************************************************/
+BOOLEAN L2CA_UcdDeregister_In_CCB_List (void *p_ccb_node, void * context)
+{
+ p_ccb = (tL2C_CCB *)p_ccb_node;
+ if (( p_ccb->in_use )
+ && ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID )) {
+ l2cu_release_ccb (p_ccb);
+ }
+ return false;
+}
+
+BOOLEAN L2CA_UcdDeregister ( UINT16 psm )
+{
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+ UINT16 xx;
+
+ L2CAP_TRACE_API ("L2CA_UcdDeregister() PSM: 0x%04x", psm);
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL) {
+ L2CAP_TRACE_ERROR ("L2CAP - no RCB for L2CA_UcdDeregister, PSM: 0x%04x", psm);
+ return (FALSE);
+ }
+
+ p_rcb->ucd.state = L2C_UCD_STATE_UNUSED;
+
+ /* check this was the last UCD registration */
+ p_rcb = &l2cb.rcb_pool[0];
+
+ for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) {
+ if ((p_rcb->in_use) && (p_rcb->ucd.state != L2C_UCD_STATE_UNUSED)) {
+ return (TRUE);
+ }
+ }
+
+ /* delete master rcb for UCD */
+ if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) != NULL) {
+ l2cu_release_rcb (p_rcb);
+ }
+
+ /* delete CCB for UCD */
+ list_foreach(l2cb.p_ccb_pool, L2CA_UcdDeregister_In_CCB_List, NULL);
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UcdDiscover
+**
+** Description Discover UCD of remote device.
+**
+** Parameters: PSM
+** BD_ADDR of remote device
+** info_type : L2CAP_UCD_INFO_TYPE_RECEPTION
+** L2CAP_UCD_INFO_TYPE_MTU
+**
+**
+** Return value: TRUE if successs
+**
+*******************************************************************************/
+BOOLEAN L2CA_UcdDiscover ( UINT16 psm, BD_ADDR rem_bda, UINT8 info_type )
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_API ("L2CA_UcdDiscover() PSM: 0x%04x BDA: %08x%04x, InfoType=0x%02x", psm,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5], info_type);
+
+ /* Fail if the PSM is not registered */
+ if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+ || ( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED )) {
+ L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_UcdDiscover, PSM: 0x%04x", psm);
+ return (FALSE);
+ }
+
+ /* First, see if we already have a link to the remote */
+ /* then find the channel control block for UCD. */
+ if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL)
+ || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) {
+ if ( l2c_ucd_connect (rem_bda) == FALSE ) {
+ return (FALSE);
+ }
+ }
+
+ /* set waiting flags in rcb */
+
+ if ( info_type & L2CAP_UCD_INFO_TYPE_RECEPTION ) {
+ p_rcb->ucd.state |= L2C_UCD_STATE_W4_RECEPTION;
+ }
+
+ if ( info_type & L2CAP_UCD_INFO_TYPE_MTU ) {
+ p_rcb->ucd.state |= L2C_UCD_STATE_W4_MTU;
+ }
+
+ /* if link is already established */
+ if ((p_lcb) && (p_lcb->link_state == LST_CONNECTED)) {
+ if (!p_ccb) {
+ p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID);
+ }
+ l2c_ucd_check_pending_info_req(p_ccb);
+ }
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UcdDataWrite
+**
+** Description Send UCD to remote device
+**
+** Parameters: PSM
+** BD Address of remote
+** Pointer to buffer of type BT_HDR
+** flags : L2CAP_FLUSHABLE_CH_BASED
+** L2CAP_FLUSHABLE_PKT
+** L2CAP_NON_FLUSHABLE_PKT
+**
+** Return value L2CAP_DW_SUCCESS, if data accepted
+** L2CAP_DW_FAILED, if error
+**
+*******************************************************************************/
+UINT16 L2CA_UcdDataWrite (UINT16 psm, BD_ADDR rem_bda, BT_HDR *p_buf, UINT16 flags)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+ UINT8 *p;
+
+ L2CAP_TRACE_API ("L2CA_UcdDataWrite() PSM: 0x%04x BDA: %08x%04x", psm,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5]);
+
+ /* Fail if the PSM is not registered */
+ if (((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
+ || ( p_rcb->ucd.state == L2C_UCD_STATE_UNUSED )) {
+ L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_UcdDataWrite, PSM: 0x%04x", psm);
+ osi_free (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ /* First, see if we already have a link to the remote */
+ /* then find the channel control block for UCD */
+ if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL)
+ || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) {
+ if ( l2c_ucd_connect (rem_bda) == FALSE ) {
+ osi_free (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ /* If we still don't have lcb and ccb after connect attempt, then can't proceed */
+ if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL)
+ || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) {
+ osi_free (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+ }
+
+ /* write PSM */
+ p_buf->offset -= L2CAP_UCD_OVERHEAD;
+ p_buf->len += L2CAP_UCD_OVERHEAD;
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+
+ UINT16_TO_STREAM (p, psm);
+
+ /* UCD MTU check */
+ if ((p_lcb->ucd_mtu) && (p_buf->len > p_lcb->ucd_mtu)) {
+ L2CAP_TRACE_WARNING ("L2CAP - Handle: 0x%04x UCD bigger than peer's UCD mtu size cannot be sent", p_lcb->handle);
+ osi_free (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ /* If already congested, do not accept any more packets */
+ if (p_ccb->cong_sent) {
+ L2CAP_TRACE_ERROR ("L2CAP - Handle: 0x%04x UCD cannot be sent, already congested count: %u buff_quota: %u",
+ p_lcb->handle,
+ (fixed_queue_length(p_ccb->xmit_hold_q) +
+ fixed_queue_length(p_lcb->ucd_out_sec_pending_q)),
+ p_ccb->buff_quota);
+
+ osi_free (p_buf);
+ return (L2CAP_DW_FAILED);
+ }
+
+ /* channel based, packet based flushable or non-flushable */
+ p_buf->layer_specific = flags;
+
+ l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_buf);
+
+ if (p_ccb->cong_sent) {
+ return (L2CAP_DW_CONGESTED);
+ } else {
+ return (L2CAP_DW_SUCCESS);
+ }
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UcdSetIdleTimeout
+**
+** Description Set UCD Idle timeout.
+**
+** Parameters: BD Addr
+** Timeout in second
+**
+** Return value: TRUE if successs
+**
+*******************************************************************************/
+BOOLEAN L2CA_UcdSetIdleTimeout ( BD_ADDR rem_bda, UINT16 timeout )
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API ("L2CA_UcdSetIdleTimeout() Timeout: 0x%04x BDA: %08x%04x", timeout,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5]);
+
+ /* First, see if we already have a link to the remote */
+ /* then find the channel control block. */
+ if (((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL)
+ || ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL)) {
+ L2CAP_TRACE_WARNING ("L2CAP - no UCD channel");
+ return (FALSE);
+ } else {
+ p_ccb->fixed_chnl_idle_tout = timeout;
+ return (TRUE);
+ }
+}
+
+/*******************************************************************************
+**
+** Function L2CA_UCDSetTxPriority
+**
+** Description Sets the transmission priority for a connectionless channel.
+**
+** Returns TRUE if a valid channel, else FALSE
+**
+*******************************************************************************/
+BOOLEAN L2CA_UCDSetTxPriority ( BD_ADDR rem_bda, tL2CAP_CHNL_PRIORITY priority )
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+
+ L2CAP_TRACE_API ("L2CA_UCDSetTxPriority() priority: 0x%02x BDA: %08x%04x", priority,
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5]);
+
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no LCB for L2CA_UCDSetTxPriority");
+ return (FALSE);
+ }
+
+ /* Find the channel control block */
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_UCDSetTxPriority");
+ return (FALSE);
+ }
+
+ /* it will update the order of CCB in LCB by priority and update round robin service variables */
+ l2cu_change_pri_ccb (p_ccb, priority);
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_connect
+**
+** Description Connect UCD to remote device.
+**
+** Parameters: BD_ADDR of remote device
+**
+** Return value: TRUE if successs
+**
+*******************************************************************************/
+static BOOLEAN l2c_ucd_connect ( BD_ADDR rem_bda )
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+
+ L2CAP_TRACE_DEBUG ("l2c_ucd_connect() BDA: %08x%04x",
+ (rem_bda[0] << 24) + (rem_bda[1] << 16) + (rem_bda[2] << 8) + rem_bda[3],
+ (rem_bda[4] << 8) + rem_bda[5]);
+
+ /* Fail if we have not established communications with the controller */
+ if (!BTM_IsDeviceUp()) {
+ L2CAP_TRACE_WARNING ("l2c_ucd_connect - BTU not ready");
+ return (FALSE);
+ }
+
+ /* First, see if we already have a link to the remote */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, BT_TRANSPORT_BR_EDR)) == NULL) {
+ /* No link. Get an LCB and start link establishment */
+ if ( ((p_lcb = l2cu_allocate_lcb (rem_bda, FALSE, BT_TRANSPORT_BR_EDR)) == NULL)
+ || (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == FALSE) ) {
+ L2CAP_TRACE_WARNING ("L2CAP - conn not started l2c_ucd_connect");
+ return (FALSE);
+ }
+ } else if ( p_lcb->info_rx_bits & (1 << L2CAP_EXTENDED_FEATURES_INFO_TYPE) ) {
+ if (!(p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION)) {
+ L2CAP_TRACE_WARNING ("L2CAP - UCD is not supported by peer, l2c_ucd_connect");
+ return (FALSE);
+ }
+ }
+
+ /* Find the channel control block. */
+ if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) == NULL) {
+ /* Allocate a channel control block */
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for l2c_ucd_connect");
+ return (FALSE);
+ } else {
+ /* Set CID for the connection */
+ p_ccb->local_cid = L2CAP_CONNECTIONLESS_CID;
+ p_ccb->remote_cid = L2CAP_CONNECTIONLESS_CID;
+
+ /* Set the default idle timeout value to use */
+ p_ccb->fixed_chnl_idle_tout = L2CAP_UCD_IDLE_TIMEOUT;
+
+ /* Set the default channel priority value to use */
+ l2cu_change_pri_ccb (p_ccb, L2CAP_UCD_CH_PRIORITY);
+
+ if ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no UCD registered, l2c_ucd_connect");
+ return (FALSE);
+ }
+ /* Save UCD registration info */
+ p_ccb->p_rcb = p_rcb;
+
+ /* There is no configuration, so if the link is up, the channel is up */
+ if (p_lcb->link_state == LST_CONNECTED) {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ }
+ }
+
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_delete_sec_pending_q
+**
+** Description discard all of UCD packets in security pending queue
+**
+** Returns None
+**
+*******************************************************************************/
+void l2c_ucd_delete_sec_pending_q(tL2C_LCB *p_lcb)
+{
+ /* clean up any security pending UCD */
+ while (p_lcb->ucd_out_sec_pending_q.p_first) {
+ osi_free(fixed_queue_dequeue(p_lcb->ucd_out_sec_pending_q, 0));
+ }
+ fixed_queue_free(p_lcb->ucd_out_sec_pending_q, NULL);
+ p_lcb->ucd_out_sec_pending_q = NULL;
+
+ while (! fixed_queue_is_empty(p_lcb->ucd_in_sec_pending_q)) {
+ osi_free(fixed_queue_dequeue(p_lcb->ucd_in_sec_pending_q, 0));
+ }
+ fixed_queue_free(p_lcb->ucd_in_sec_pending_q);
+ p_lcb->ucd_in_sec_pending_q = NULL;
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_check_pending_info_req
+**
+** Description check if any application is waiting for UCD information
+**
+** Return TRUE if any pending UCD info request
+**
+*******************************************************************************/
+BOOLEAN l2c_ucd_check_pending_info_req(tL2C_CCB *p_ccb)
+{
+ tL2C_RCB *p_rcb = &l2cb.rcb_pool[0];
+ UINT16 xx;
+ BOOLEAN pending = FALSE;
+
+ if (p_ccb == NULL) {
+ L2CAP_TRACE_ERROR ("L2CAP - NULL p_ccb in l2c_ucd_check_pending_info_req");
+ return (FALSE);
+ }
+
+ for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) {
+ if (p_rcb->in_use) {
+ /* if application is waiting UCD reception info */
+ if (p_rcb->ucd.state & L2C_UCD_STATE_W4_RECEPTION) {
+ /* if this information is available */
+ if ( p_ccb->p_lcb->info_rx_bits & (1 << L2CAP_EXTENDED_FEATURES_INFO_TYPE) ) {
+ if (!(p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION)) {
+ L2CAP_TRACE_WARNING ("L2CAP - UCD is not supported by peer, l2c_ucd_check_pending_info_req");
+
+ l2c_ucd_delete_sec_pending_q(p_ccb->p_lcb);
+ l2cu_release_ccb (p_ccb);
+ }
+
+ p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (p_ccb->p_lcb->remote_bd_addr,
+ L2CAP_UCD_INFO_TYPE_RECEPTION,
+ p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_UCD_RECEPTION);
+ } else {
+ pending = TRUE;
+ if (p_ccb->p_lcb->w4_info_rsp == FALSE) {
+ l2cu_send_peer_info_req (p_ccb->p_lcb, L2CAP_EXTENDED_FEATURES_INFO_TYPE);
+ }
+ }
+ }
+
+ /* if application is waiting for UCD MTU */
+ if (p_rcb->ucd.state & L2C_UCD_STATE_W4_MTU) {
+ /* if this information is available */
+ if ( p_ccb->p_lcb->info_rx_bits & (1 << L2CAP_CONNLESS_MTU_INFO_TYPE)) {
+ p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Discover_Cb (p_ccb->p_lcb->remote_bd_addr,
+ L2CAP_UCD_INFO_TYPE_MTU,
+ p_ccb->p_lcb->ucd_mtu);
+ } else {
+ pending = TRUE;
+ if (p_ccb->p_lcb->w4_info_rsp == FALSE) {
+ l2cu_send_peer_info_req (p_ccb->p_lcb, L2CAP_CONNLESS_MTU_INFO_TYPE);
+ }
+ }
+ }
+ }
+ }
+ return (pending);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_enqueue_pending_out_sec_q
+**
+** Description enqueue outgoing UCD packet into security pending queue
+** and check congestion
+**
+** Return None
+**
+*******************************************************************************/
+void l2c_ucd_enqueue_pending_out_sec_q(tL2C_CCB *p_ccb, void *p_data)
+{
+ fixed_queue_enqueue(p_ccb->p_lcb->ucd_out_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT);
+ l2cu_check_channel_congestion (p_ccb);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_check_pending_out_sec_q
+**
+** Description check outgoing security
+**
+** Return TRUE if any UCD packet for security
+**
+*******************************************************************************/
+BOOLEAN l2c_ucd_check_pending_out_sec_q(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf = (BT_HDR*)fixed_queue_try_peek_first(p_ccb->p_lcb->ucd_out_sec_pending_q);
+
+ if (p_buf != NULL) {
+ UINT16 psm;
+ UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+
+ STREAM_TO_UINT16(psm, p)
+
+ p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP;
+ btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, psm,
+ p_ccb->p_lcb->handle, CONNLESS_ORIG, &l2c_link_sec_comp, p_ccb);
+
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_send_pending_out_sec_q
+**
+** Description dequeue UCD packet from security pending queue and
+** enqueue it into CCB
+**
+** Return None
+**
+*******************************************************************************/
+void l2c_ucd_send_pending_out_sec_q(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_out_sec_pending_q, 0);
+
+ if (p_buf != NULL) {
+ l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_buf);
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_discard_pending_out_sec_q
+**
+** Description dequeue UCD packet from security pending queue and
+** discard it.
+**
+** Return None
+**
+*******************************************************************************/
+void l2c_ucd_discard_pending_out_sec_q(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_out_sec_pending_q, 0);
+
+ /* we may need to report to application */
+
+ if (p_buf) {
+ osi_free (p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_check_pending_in_sec_q
+**
+** Description check incoming security
+**
+** Return TRUE if any UCD packet for security
+**
+*******************************************************************************/
+BOOLEAN l2c_ucd_check_pending_in_sec_q(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q, 0);
+
+ if (p_buf != NULL) {
+ UINT16 psm;
+ UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset;
+ STREAM_TO_UINT16(psm, p)
+
+ p_ccb->chnl_state = CST_TERM_W4_SEC_COMP;
+ btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, psm,
+ p_ccb->p_lcb->handle, CONNLESS_TERM, &l2c_link_sec_comp, p_ccb);
+
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_send_pending_in_sec_q
+**
+** Description dequeue UCD packet from security pending queue and
+** send it to application
+**
+** Return None
+**
+*******************************************************************************/
+void l2c_ucd_send_pending_in_sec_q(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q, 0)
+
+ if (p_buf != NULL) {
+ p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Data_Cb(p_ccb->p_lcb->remote_bd_addr, (BT_HDR *)p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_discard_pending_in_sec_q
+**
+** Description dequeue UCD packet from security pending queue and
+** discard it.
+**
+** Return None
+**
+*******************************************************************************/
+void l2c_ucd_discard_pending_in_sec_q(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf = (BT_HDR*)fixed_queue_dequeue(p_ccb->p_lcb->ucd_in_sec_pending_q, 0);
+
+ if (p_buf) {
+ osi_free (p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_check_rx_pkts
+**
+** Description Check if UCD reception is registered.
+** Process received UCD packet if application is expecting.
+**
+** Return TRUE if UCD reception is registered
+**
+*******************************************************************************/
+BOOLEAN l2c_ucd_check_rx_pkts(tL2C_LCB *p_lcb, BT_HDR *p_msg)
+{
+ tL2C_CCB *p_ccb;
+ tL2C_RCB *p_rcb;
+
+ if (((p_ccb = l2cu_find_ccb_by_cid (p_lcb, L2CAP_CONNECTIONLESS_CID)) != NULL)
+ || ((p_rcb = l2cu_find_rcb_by_psm (L2C_UCD_RCB_ID)) != NULL)) {
+ if (p_ccb == NULL) {
+ /* Allocate a channel control block */
+ if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no CCB for UCD reception");
+ osi_free (p_msg);
+ return TRUE;
+ } else {
+ /* Set CID for the connection */
+ p_ccb->local_cid = L2CAP_CONNECTIONLESS_CID;
+ p_ccb->remote_cid = L2CAP_CONNECTIONLESS_CID;
+
+ /* Set the default idle timeout value to use */
+ p_ccb->fixed_chnl_idle_tout = L2CAP_UCD_IDLE_TIMEOUT;
+
+ /* Set the default channel priority value to use */
+ l2cu_change_pri_ccb (p_ccb, L2CAP_UCD_CH_PRIORITY);
+
+ /* Save registration info */
+ p_ccb->p_rcb = p_rcb;
+
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ }
+ l2c_csm_execute(p_ccb, L2CEVT_L2CAP_DATA, p_msg);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2c_ucd_process_event
+**
+** Description This is called from main state machine when LCID is connectionless
+** Process the event if it is for UCD.
+**
+** Return TRUE if the event is consumed by UCD
+** FALSE if the event needs to be processed by main state machine
+**
+*******************************************************************************/
+BOOLEAN l2c_ucd_process_event(tL2C_CCB *p_ccb, UINT16 event, void *p_data)
+{
+ /* if the event is not processed by this function, this variable will be set to FALSE */
+ BOOLEAN done = TRUE;
+
+ switch (p_ccb->chnl_state) {
+ case CST_CLOSED:
+ switch (event) {
+ case L2CEVT_LP_CONNECT_CFM: /* Link came up */
+ /* check if waiting for UCD info */
+ if (!l2c_ucd_check_pending_info_req (p_ccb)) {
+ /* check if any outgoing UCD packet is waiting security check */
+ if (!l2c_ucd_check_pending_out_sec_q(p_ccb)) {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ }
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data);
+ break;
+
+ case L2CEVT_L2CAP_INFO_RSP:
+ /* check if waiting for UCD info */
+ if (!l2c_ucd_check_pending_info_req (p_ccb)) {
+ /* check if any outgoing UCD packet is waiting security check */
+ if (!l2c_ucd_check_pending_out_sec_q(p_ccb)) {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ }
+ break;
+
+ default:
+ done = FALSE; /* main state machine continues to process event */
+ break;
+ }
+ break;
+
+ case CST_ORIG_W4_SEC_COMP:
+ switch (event) {
+ case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */
+ /* check if any outgoing UCD packet is waiting security check */
+ if (!l2c_ucd_check_pending_out_sec_q(p_ccb)) {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ break;
+
+ case L2CEVT_SEC_COMP: /* Security completed success */
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_ucd_send_pending_out_sec_q(p_ccb);
+
+ if (! fixed_queue_is_empty(p_ccb->p_lcb->ucd_out_sec_pending_q))
+ {
+ /* start a timer to send next UCD packet in OPEN state */
+ /* it will prevent stack overflow */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 0);
+ } else {
+ /* start a timer for idle timeout of UCD */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout);
+ }
+ break;
+
+ case L2CEVT_SEC_COMP_NEG:
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_ucd_discard_pending_out_sec_q(p_ccb);
+
+ /* start a timer for idle timeout of UCD */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT);
+ break;
+
+ case L2CEVT_L2CAP_INFO_RSP:
+ /* check if waiting for UCD info */
+ l2c_ucd_check_pending_info_req (p_ccb);
+ break;
+
+ default:
+ done = FALSE; /* main state machine continues to process event */
+ break;
+ }
+ break;
+
+
+ case CST_TERM_W4_SEC_COMP:
+ switch (event) {
+ case L2CEVT_SEC_COMP:
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_ucd_send_pending_in_sec_q (p_ccb);
+
+ if (! fixed_queue_is_empty(p_ccb->p_lcb->ucd_in_sec_pending_q)) {
+ /* start a timer to check next UCD packet in OPEN state */
+ /* it will prevent stack overflow */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, 0);
+ } else {
+ /* start a timer for idle timeout of UCD */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout);
+ }
+ break;
+
+ case L2CEVT_SEC_COMP_NEG:
+ if (((tL2C_CONN_INFO *)p_data)->status == BTM_DELAY_CHECK) {
+ done = FALSE;
+ break;
+ }
+ p_ccb->chnl_state = CST_OPEN;
+ l2c_ucd_discard_pending_in_sec_q (p_ccb);
+
+ /* start a timer for idle timeout of UCD */
+ btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, p_ccb->fixed_chnl_idle_tout);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data);
+ break;
+
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT);
+ break;
+
+ case L2CEVT_SEC_RE_SEND_CMD: /* BTM has enough info to proceed */
+ /* check if any incoming UCD packet is waiting security check */
+ if (!l2c_ucd_check_pending_in_sec_q(p_ccb)) {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+ break;
+
+ case L2CEVT_L2CAP_INFO_RSP:
+ /* check if waiting for UCD info */
+ l2c_ucd_check_pending_info_req (p_ccb);
+ break;
+
+ default:
+ done = FALSE; /* main state machine continues to process event */
+ break;
+ }
+ break;
+
+ case CST_OPEN:
+ switch (event) {
+ case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
+ /* stop idle timer of UCD */
+ btu_stop_timer (&p_ccb->timer_entry);
+
+ fixed_queue_enqueue(p_ccb->p_lcb->ucd_in_sec_pending_q, p_data, FIXED_QUEUE_MAX_TIMEOUT);
+ l2c_ucd_check_pending_in_sec_q (p_ccb);
+ break;
+
+ case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */
+ /* stop idle timer of UCD */
+ btu_stop_timer (&p_ccb->timer_entry);
+
+ l2c_ucd_enqueue_pending_out_sec_q(p_ccb, p_data);
+
+ /* coverity[check_return] */ /* coverity[unchecked_value] */
+ /* success changes state, failure stays in current state */
+ l2c_ucd_check_pending_out_sec_q (p_ccb);
+ break;
+
+ case L2CEVT_TIMEOUT:
+ /* check if any UCD packet is waiting security check */
+ if ((!l2c_ucd_check_pending_in_sec_q(p_ccb))
+ && (!l2c_ucd_check_pending_out_sec_q(p_ccb))) {
+ l2cu_release_ccb (p_ccb);
+ }
+ break;
+
+ case L2CEVT_L2CAP_INFO_RSP:
+ /* check if waiting for UCD info */
+ l2c_ucd_check_pending_info_req (p_ccb);
+ break;
+
+ default:
+ done = FALSE; /* main state machine continues to process event */
+ break;
+ }
+ break;
+
+ default:
+ done = FALSE; /* main state machine continues to process event */
+ break;
+ }
+
+ return done;
+}
+#endif /* (L2CAP_UCD_INCLUDED == TRUE) */
diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2c_utils.c b/lib/bt/host/bluedroid/stack/l2cap/l2c_utils.c
new file mode 100644
index 00000000..992d4b68
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/l2cap/l2c_utils.c
@@ -0,0 +1,3731 @@
+/******************************************************************************
+ *
+ * Copyright (C) 1999-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 L2CAP utility functions
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "osi/allocator.h"
+#include "device/controller.h"
+#include "stack/bt_types.h"
+#include "stack/hcimsgs.h"
+#include "stack/l2cdefs.h"
+#include "l2c_int.h"
+#include "stack/hcidefs.h"
+#include "stack/btu.h"
+#include "stack/btm_api.h"
+#include "btm_int.h"
+#include "stack/hcidefs.h"
+#include "osi/allocator.h"
+#include "osi/list.h"
+
+#if BT_SDP_BQB_INCLUDED
+extern BOOLEAN l2cap_bqb_ertm_mode_included_flag;
+#endif /* BT_SDP_BQB_INCLUDED */
+
+/*******************************************************************************
+**
+** Function l2cu_allocate_lcb
+**
+** Description Look for an unused LCB
+**
+** Returns LCB address or NULL if none found
+**
+*******************************************************************************/
+tL2C_LCB *l2cu_allocate_lcb (BD_ADDR p_bd_addr, BOOLEAN is_bonding, tBT_TRANSPORT transport)
+{
+ tL2C_LCB *p_lcb = NULL;
+ bool list_ret = false;
+ extern tL2C_LCB *l2cu_find_free_lcb (void);
+ // temp solution
+ p_lcb = l2cu_find_free_lcb();
+ if(p_lcb != NULL) {
+ list_ret = true;
+ }
+
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ /* Check if peer device's and our BD_ADDR is same or not. It
+ should be different to avoid 'Impersonation in the Pin Pairing
+ Protocol' (CVE-2020-26555) vulnerability. */
+ if (memcmp((uint8_t *)p_bd_addr, (uint8_t *)&controller_get_interface()->get_address()->address, sizeof (BD_ADDR)) == 0) {
+ L2CAP_TRACE_ERROR ("%s connection rejected due to same BD ADDR", __func__);
+ return (NULL);
+ }
+#endif
+
+ if(p_lcb == NULL && list_length(l2cb.p_lcb_pool) < MAX_L2CAP_LINKS) {
+ p_lcb = (tL2C_LCB *)osi_malloc(sizeof(tL2C_LCB));
+ if (p_lcb) {
+ memset (p_lcb, 0, sizeof(tL2C_LCB));
+ list_ret = list_append(l2cb.p_lcb_pool, p_lcb);
+ }else {
+ L2CAP_TRACE_ERROR("Error in allocating L2CAP Link Control Block");
+ }
+ }
+ if (list_ret) {
+ if (p_lcb) {
+ btu_free_timer(&p_lcb->timer_entry);
+ btu_free_timer(&p_lcb->info_timer_entry);
+ btu_free_timer(&p_lcb->upda_con_timer);
+
+ memset (p_lcb, 0, sizeof (tL2C_LCB));
+ memcpy (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN);
+
+ p_lcb->in_use = TRUE;
+ p_lcb->link_state = LST_DISCONNECTED;
+ p_lcb->handle = HCI_INVALID_HANDLE;
+ p_lcb->link_flush_tout = 0xFFFF;
+ p_lcb->timer_entry.param = (TIMER_PARAM_TYPE)p_lcb;
+ p_lcb->info_timer_entry.param = (TIMER_PARAM_TYPE)p_lcb;
+ p_lcb->upda_con_timer.param = (TIMER_PARAM_TYPE)p_lcb;
+ p_lcb->idle_timeout = l2cb.idle_timeout;
+ p_lcb->id = 1; /* spec does not allow '0' */
+ p_lcb->is_bonding = is_bonding;
+#if (BLE_INCLUDED == TRUE)
+ p_lcb->transport = transport;
+ p_lcb->tx_data_len = controller_get_interface()->get_ble_default_data_packet_length();
+ p_lcb->le_sec_pending_q = fixed_queue_new(QUEUE_SIZE_MAX);
+
+ if (transport == BT_TRANSPORT_LE) {
+ l2cb.num_ble_links_active++;
+ l2c_ble_link_adjust_allocation();
+ } else
+#endif
+ {
+ l2cb.num_links_active++;
+ l2c_link_adjust_allocation();
+ }
+ p_lcb->link_xmit_data_q = list_new(NULL);
+ return (p_lcb);
+ }
+ }
+
+ /* If here, no free LCB found */
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_update_lcb_4_bonding
+**
+** Description Mark the lcb for bonding. Used when bonding takes place on
+** an existing ACL connection. (Pre-Lisbon devices)
+**
+** Returns Nothing
+**
+*******************************************************************************/
+void l2cu_update_lcb_4_bonding (BD_ADDR p_bd_addr, BOOLEAN is_bonding)
+{
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR);
+
+ if (p_lcb) {
+ p_lcb->is_bonding = is_bonding;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_release_lcb
+**
+** Description Release an LCB. All timers will be stopped, channels
+** dropped, buffers returned etc.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_release_lcb (tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_ccb;
+
+ p_lcb->in_use = FALSE;
+ p_lcb->is_bonding = FALSE;
+#if (BLE_INCLUDED == TRUE)
+ p_lcb->retry_create_con = 0;
+ p_lcb->start_time_s = 0;
+#endif // #if (BLE_INCLUDED == TRUE)
+
+ /* Stop and release timers */
+ btu_free_timer (&p_lcb->timer_entry);
+ memset(&p_lcb->timer_entry, 0, sizeof(TIMER_LIST_ENT));
+ btu_free_timer (&p_lcb->info_timer_entry);
+ memset(&p_lcb->info_timer_entry, 0, sizeof(TIMER_LIST_ENT));
+ btu_free_timer(&p_lcb->upda_con_timer);
+ memset(&p_lcb->upda_con_timer, 0, sizeof(TIMER_LIST_ENT));
+
+ /* Release any unfinished L2CAP packet on this link */
+ if (p_lcb->p_hcit_rcv_acl) {
+ osi_free(p_lcb->p_hcit_rcv_acl);
+ p_lcb->p_hcit_rcv_acl = NULL;
+ }
+
+#if BTM_SCO_INCLUDED == TRUE
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_BR_EDR)
+#endif
+ {
+ /* Release all SCO links */
+ btm_remove_sco_links(p_lcb->remote_bd_addr);
+ }
+#endif
+
+ if (p_lcb->sent_not_acked > 0) {
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ l2cb.controller_le_xmit_window += p_lcb->sent_not_acked;
+ if (l2cb.controller_le_xmit_window > l2cb.num_lm_ble_bufs) {
+ l2cb.controller_le_xmit_window = l2cb.num_lm_ble_bufs;
+ }
+ } else
+#endif
+ {
+ l2cb.controller_xmit_window += p_lcb->sent_not_acked;
+ if (l2cb.controller_xmit_window > l2cb.num_lm_acl_bufs) {
+ l2cb.controller_xmit_window = l2cb.num_lm_acl_bufs;
+ }
+ }
+ }
+
+#if (BLE_INCLUDED == TRUE)
+ // Reset BLE connecting flag only if the address matches
+ if (!memcmp(l2cb.ble_connecting_bda, p_lcb->remote_bd_addr, BD_ADDR_LEN)) {
+ l2cb.is_ble_connecting = FALSE;
+ }
+#endif
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ l2cu_process_fixed_disc_cback(p_lcb);
+#endif
+
+ /* Ensure no CCBs left on this LCB */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_lcb->ccb_queue.p_first_ccb) {
+ l2cu_release_ccb (p_ccb);
+ }
+
+ /* Tell BTM Acl management the link was removed */
+ if ((p_lcb->link_state == LST_CONNECTED) || (p_lcb->link_state == LST_DISCONNECTING)) {
+#if (BLE_INCLUDED == TRUE)
+ btm_acl_removed (p_lcb->remote_bd_addr, p_lcb->transport);
+#else
+ btm_acl_removed (p_lcb->remote_bd_addr, BT_TRANSPORT_BR_EDR);
+#endif
+ }
+
+ /* Release any held buffers */
+ if (p_lcb->link_xmit_data_q) {
+ while (!list_is_empty(p_lcb->link_xmit_data_q)) {
+ BT_HDR *p_buf = list_front(p_lcb->link_xmit_data_q);
+ list_remove(p_lcb->link_xmit_data_q, p_buf);
+ osi_free(p_buf);
+ }
+ list_free(p_lcb->link_xmit_data_q);
+ p_lcb->link_xmit_data_q = NULL;
+ }
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ /* clean up any security pending UCD */
+ l2c_ucd_delete_sec_pending_q(p_lcb);
+#endif
+
+#if BLE_INCLUDED == TRUE
+ /* Re-adjust flow control windows make sure it does not go negative */
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ if (l2cb.num_ble_links_active >= 1) {
+ l2cb.num_ble_links_active--;
+ }
+
+ l2c_ble_link_adjust_allocation();
+ } else
+#endif
+ {
+ if (l2cb.num_links_active >= 1) {
+ l2cb.num_links_active--;
+ }
+
+ l2c_link_adjust_allocation();
+ }
+
+ /* Check for ping outstanding */
+ if (p_lcb->p_echo_rsp_cb) {
+ tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb;
+
+ /* Zero out the callback in case app immediately calls us again */
+ p_lcb->p_echo_rsp_cb = NULL;
+
+ (*p_cb) (L2CAP_PING_RESULT_NO_LINK);
+ }
+
+#if (BLE_INCLUDED == TRUE)
+ /* Check and release all the LE COC connections waiting for security */
+ if (p_lcb->le_sec_pending_q)
+ {
+ while (!fixed_queue_is_empty(p_lcb->le_sec_pending_q))
+ {
+ tL2CAP_SEC_DATA *p_buf = (tL2CAP_SEC_DATA*) fixed_queue_dequeue(p_lcb->le_sec_pending_q, FIXED_QUEUE_MAX_TIMEOUT);
+ if (p_buf->p_callback) {
+ p_buf->p_callback(p_lcb->remote_bd_addr, p_lcb->transport, p_buf->p_ref_data, BTM_DEV_RESET);
+ }
+ osi_free(p_buf);
+ }
+ fixed_queue_free(p_lcb->le_sec_pending_q, NULL);
+ p_lcb->le_sec_pending_q = NULL;
+ }
+#endif ///BLE_INCLUDED == TRUE
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_find_lcb_by_bd_addr
+**
+** Description Look through all active LCBs for a match based on the
+** remote BD address.
+**
+** Returns pointer to matched LCB, or NULL if no match
+**
+*******************************************************************************/
+tL2C_LCB *l2cu_find_lcb_by_bd_addr (BD_ADDR p_bd_addr, tBT_TRANSPORT transport)
+{
+ list_node_t *p_node = NULL;
+ tL2C_LCB *p_lcb = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if ((p_lcb->in_use) &&
+#if BLE_INCLUDED == TRUE
+ p_lcb->transport == transport &&
+#endif
+ (!memcmp (p_lcb->remote_bd_addr, p_bd_addr, BD_ADDR_LEN))) {
+ return (p_lcb);
+ }
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+tL2C_LCB *l2cu_find_free_lcb (void)
+{
+ list_node_t *p_node = NULL;
+ tL2C_LCB *p_lcb = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (!p_lcb->in_use) {
+ return (p_lcb);
+ }
+ }
+ /* If here, no match found */
+ return (NULL);
+}
+
+uint8_t l2cu_plcb_active_count(void)
+{
+ list_node_t *p_node = NULL;
+ tL2C_LCB *p_lcb = NULL;
+ uint8_t active_count = 0;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (p_lcb && p_lcb->in_use) {
+ active_count ++;
+ }
+ }
+ if (active_count >= MAX_L2CAP_CHANNELS) {
+ L2CAP_TRACE_ERROR("error active count");
+ active_count = 0;
+ }
+ L2CAP_TRACE_DEBUG("plcb active count %d", active_count);
+ return active_count;
+
+}
+
+/*******************************************************************************
+**
+** Function l2cu_get_conn_role
+**
+** Description Determine the desired role (master or slave) of a link.
+** If already got a slave link, this one must be a master. If
+** already got at least 1 link where we are the master, make this
+** also a master.
+**
+** Returns HCI_ROLE_MASTER or HCI_ROLE_SLAVE
+**
+*******************************************************************************/
+UINT8 l2cu_get_conn_role (tL2C_LCB *p_this_lcb)
+{
+ return l2cb.desire_role;
+}
+
+/*******************************************************************************
+**
+** Function l2c_is_cmd_rejected
+**
+** Description Checks if cmd_code is command or response
+** If a command it will be rejected per spec.
+** This function is used when a illegal packet length is detected
+**
+** Returns BOOLEAN - TRUE if cmd_code is a command and it is rejected,
+** FALSE if response code. (command not rejected)
+**
+*******************************************************************************/
+BOOLEAN l2c_is_cmd_rejected (UINT8 cmd_code, UINT8 id, tL2C_LCB *p_lcb)
+{
+ switch (cmd_code) {
+ case L2CAP_CMD_CONN_REQ:
+ case L2CAP_CMD_CONFIG_REQ:
+ case L2CAP_CMD_DISC_REQ:
+ case L2CAP_CMD_ECHO_REQ:
+ case L2CAP_CMD_INFO_REQ:
+ case L2CAP_CMD_AMP_CONN_REQ:
+ case L2CAP_CMD_AMP_MOVE_REQ:
+ case L2CAP_CMD_BLE_UPDATE_REQ:
+ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_MTU_EXCEEDED, id, L2CAP_DEFAULT_MTU, 0);
+ L2CAP_TRACE_WARNING ("Dumping first Command (%d)", cmd_code);
+ return TRUE;
+
+ default: /* Otherwise a response */
+ return FALSE;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_build_header
+**
+** Description Builds the L2CAP command packet header
+**
+** Returns Pointer to allocated packet or NULL if no resources
+**
+*******************************************************************************/
+BT_HDR *l2cu_build_header (tL2C_LCB *p_lcb, UINT16 len, UINT8 cmd, UINT8 id)
+{
+ BT_HDR *p_buf = (BT_HDR *)osi_malloc(L2CAP_CMD_BUF_SIZE);
+ UINT8 *p;
+
+ if (!p_buf) {
+ return (NULL);
+ }
+
+ p_buf->offset = L2CAP_SEND_CMD_OFFSET;
+ p_buf->len = len + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET;
+
+ /* Put in HCI header - handle + pkt boundary */
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ UINT16_TO_STREAM (p, (p_lcb->handle | (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT)));
+ } else
+#endif
+ {
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ UINT16_TO_STREAM (p, p_lcb->handle | l2cb.non_flushable_pbf);
+#else
+ UINT16_TO_STREAM (p, (p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT)));
+#endif
+ }
+
+ UINT16_TO_STREAM (p, len + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD);
+ UINT16_TO_STREAM (p, len + L2CAP_CMD_OVERHEAD);
+
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ //counter_add("l2cap.ble.tx.bytes", p_buf->len);
+ //counter_add("l2cap.ble.tx.pkts", 1);
+
+ UINT16_TO_STREAM (p, L2CAP_BLE_SIGNALLING_CID);
+ } else
+#endif
+ {
+ //counter_add("l2cap.sig.tx.bytes", p_buf->len);
+ //counter_add("l2cap.sig.tx.pkts", 1);
+ UINT16_TO_STREAM (p, L2CAP_SIGNALLING_CID);
+ }
+
+ /* Put in L2CAP command header */
+ UINT8_TO_STREAM (p, cmd);
+ UINT8_TO_STREAM (p, id);
+ UINT16_TO_STREAM (p, len);
+
+ return (p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_adj_id
+**
+** Description Checks for valid ID based on specified mask
+** and adjusts the id if invalid.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_adj_id (tL2C_LCB *p_lcb, UINT8 adj_mask)
+{
+ if ((adj_mask & L2CAP_ADJ_ZERO_ID) && !p_lcb->id) {
+ p_lcb->id++;
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_cmd_reject
+**
+** Description Build and send an L2CAP "command reject" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_cmd_reject (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id,
+ UINT16 p1, UINT16 p2)
+{
+ UINT16 param_len;
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ /* Put in L2CAP packet header */
+ if (reason == L2CAP_CMD_REJ_MTU_EXCEEDED) {
+ param_len = 2;
+ } else if (reason == L2CAP_CMD_REJ_INVALID_CID) {
+ param_len = 4;
+ } else {
+ param_len = 0;
+ }
+
+ if ((p_buf = l2cu_build_header (p_lcb, (UINT16) (L2CAP_CMD_REJECT_LEN + param_len), L2CAP_CMD_REJECT, rem_id)) == NULL ) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer cmd_rej");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, reason);
+
+ if (param_len >= 2) {
+ UINT16_TO_STREAM (p, p1);
+ }
+
+ if (param_len >= 4) {
+ UINT16_TO_STREAM (p, p2);
+ }
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_connect_req
+**
+** Description Build and send an L2CAP "connection request" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_connect_req (tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ /* Create an identifier for this packet */
+ p_ccb->p_lcb->id++;
+ l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID);
+
+ p_ccb->local_id = p_ccb->p_lcb->id;
+
+ if ((p_buf = l2cu_build_header (p_ccb->p_lcb, L2CAP_CONN_REQ_LEN, L2CAP_CMD_CONN_REQ,
+ p_ccb->local_id)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for conn_req");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->p_rcb->real_psm);
+ UINT16_TO_STREAM (p, p_ccb->local_cid);
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_connect_rsp
+**
+** Description Build and send an L2CAP "connection response" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_connect_rsp (tL2C_CCB *p_ccb, UINT16 result, UINT16 status)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if (result == L2CAP_CONN_PENDING) {
+ /* if we already sent pending response */
+ if (p_ccb->flags & CCB_FLAG_SENT_PENDING) {
+ return;
+ } else {
+ p_ccb->flags |= CCB_FLAG_SENT_PENDING;
+ }
+ }
+
+ if ((p_buf = l2cu_build_header(p_ccb->p_lcb, L2CAP_CONN_RSP_LEN, L2CAP_CMD_CONN_RSP, p_ccb->remote_id)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for conn_rsp");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->local_cid);
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ UINT16_TO_STREAM (p, result);
+ UINT16_TO_STREAM (p, status);
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_reject_connection
+**
+** Description Build and send an L2CAP "connection response neg" message
+** to the peer. This function is called when there is no peer
+** CCB (non-existant PSM or no resources).
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_reject_connection (tL2C_LCB *p_lcb, UINT16 remote_cid, UINT8 rem_id, UINT16 result)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if ((p_buf = l2cu_build_header(p_lcb, L2CAP_CONN_RSP_LEN, L2CAP_CMD_CONN_RSP, rem_id)) == NULL ) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for conn_req");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, 0); /* Local CID of 0 */
+ UINT16_TO_STREAM (p, remote_cid);
+ UINT16_TO_STREAM (p, result);
+ UINT16_TO_STREAM (p, 0); /* Status of 0 */
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_config_req
+**
+** Description Build and send an L2CAP "configuration request" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_config_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ BT_HDR *p_buf;
+ UINT16 cfg_len = 0;
+ UINT8 *p;
+
+ /* Create an identifier for this packet */
+ p_ccb->p_lcb->id++;
+ l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID);
+
+ p_ccb->local_id = p_ccb->p_lcb->id;
+
+ if (p_cfg->mtu_present) {
+ cfg_len += L2CAP_CFG_MTU_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ if (p_cfg->flush_to_present) {
+ cfg_len += L2CAP_CFG_FLUSH_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ if (p_cfg->qos_present) {
+ cfg_len += L2CAP_CFG_QOS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ if (p_cfg->fcr_present) {
+ cfg_len += L2CAP_CFG_FCR_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ if (p_cfg->fcs_present) {
+ cfg_len += L2CAP_CFG_FCS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ if (p_cfg->ext_flow_spec_present) {
+ cfg_len += L2CAP_CFG_EXT_FLOW_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+
+ if ((p_buf = l2cu_build_header (p_ccb->p_lcb, (UINT16) (L2CAP_CONFIG_REQ_LEN + cfg_len),
+ L2CAP_CMD_CONFIG_REQ, p_ccb->local_id)) == NULL ) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for conn_req");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ UINT16_TO_STREAM (p, p_cfg->flags); /* Flags (continuation) */
+
+ /* Now, put the options */
+ if (p_cfg->mtu_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_MTU);
+ UINT8_TO_STREAM (p, L2CAP_CFG_MTU_OPTION_LEN);
+ UINT16_TO_STREAM (p, p_cfg->mtu);
+ }
+ if (p_cfg->flush_to_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FLUSH_TOUT);
+ UINT8_TO_STREAM (p, L2CAP_CFG_FLUSH_OPTION_LEN);
+ UINT16_TO_STREAM (p, p_cfg->flush_to);
+ }
+ if (p_cfg->qos_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_QOS);
+ UINT8_TO_STREAM (p, L2CAP_CFG_QOS_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->qos.qos_flags);
+ UINT8_TO_STREAM (p, p_cfg->qos.service_type);
+ UINT32_TO_STREAM (p, p_cfg->qos.token_rate);
+ UINT32_TO_STREAM (p, p_cfg->qos.token_bucket_size);
+ UINT32_TO_STREAM (p, p_cfg->qos.peak_bandwidth);
+ UINT32_TO_STREAM (p, p_cfg->qos.latency);
+ UINT32_TO_STREAM (p, p_cfg->qos.delay_variation);
+ }
+ if (p_cfg->fcr_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCR);
+ UINT8_TO_STREAM (p, L2CAP_CFG_FCR_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->fcr.mode);
+ UINT8_TO_STREAM (p, p_cfg->fcr.tx_win_sz);
+ UINT8_TO_STREAM (p, p_cfg->fcr.max_transmit);
+ UINT16_TO_STREAM (p, p_cfg->fcr.rtrans_tout);
+ UINT16_TO_STREAM (p, p_cfg->fcr.mon_tout);
+ UINT16_TO_STREAM (p, p_cfg->fcr.mps);
+ }
+
+ if (p_cfg->fcs_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCS);
+ UINT8_TO_STREAM (p, L2CAP_CFG_FCS_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->fcs);
+ }
+
+ if (p_cfg->ext_flow_spec_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_EXT_FLOW);
+ UINT8_TO_STREAM (p, L2CAP_CFG_EXT_FLOW_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.id);
+ UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.stype);
+ UINT16_TO_STREAM (p, p_cfg->ext_flow_spec.max_sdu_size);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.sdu_inter_time);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.access_latency);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.flush_timeout);
+ }
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_config_rsp
+**
+** Description Build and send an L2CAP "configuration response" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_config_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ BT_HDR *p_buf;
+ UINT16 cfg_len = 0;
+ UINT8 *p;
+
+ /* Create an identifier for this packet */
+ if (p_cfg->mtu_present) {
+ cfg_len += L2CAP_CFG_MTU_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ if (p_cfg->flush_to_present) {
+ cfg_len += L2CAP_CFG_FLUSH_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ if (p_cfg->qos_present) {
+ cfg_len += L2CAP_CFG_QOS_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ if (p_cfg->fcr_present) {
+ cfg_len += L2CAP_CFG_FCR_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ if (p_cfg->ext_flow_spec_present) {
+ cfg_len += L2CAP_CFG_EXT_FLOW_OPTION_LEN + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+
+ if ((p_buf = l2cu_build_header (p_ccb->p_lcb, (UINT16)(L2CAP_CONFIG_RSP_LEN + cfg_len),
+ L2CAP_CMD_CONFIG_RSP, p_ccb->remote_id)) == NULL ) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for conn_req");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ UINT16_TO_STREAM (p, p_cfg->flags); /* Flags (continuation) Must match request */
+ UINT16_TO_STREAM (p, p_cfg->result);
+
+ /* Now, put the options */
+ if (p_cfg->mtu_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_MTU);
+ UINT8_TO_STREAM (p, L2CAP_CFG_MTU_OPTION_LEN);
+ UINT16_TO_STREAM (p, p_cfg->mtu);
+ }
+ if (p_cfg->flush_to_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FLUSH_TOUT);
+ UINT8_TO_STREAM (p, L2CAP_CFG_FLUSH_OPTION_LEN);
+ UINT16_TO_STREAM (p, p_cfg->flush_to);
+ }
+ if (p_cfg->qos_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_QOS);
+ UINT8_TO_STREAM (p, L2CAP_CFG_QOS_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->qos.qos_flags);
+ UINT8_TO_STREAM (p, p_cfg->qos.service_type);
+ UINT32_TO_STREAM (p, p_cfg->qos.token_rate);
+ UINT32_TO_STREAM (p, p_cfg->qos.token_bucket_size);
+ UINT32_TO_STREAM (p, p_cfg->qos.peak_bandwidth);
+ UINT32_TO_STREAM (p, p_cfg->qos.latency);
+ UINT32_TO_STREAM (p, p_cfg->qos.delay_variation);
+ }
+ if (p_cfg->fcr_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_FCR);
+ UINT8_TO_STREAM (p, L2CAP_CFG_FCR_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->fcr.mode);
+ UINT8_TO_STREAM (p, p_cfg->fcr.tx_win_sz);
+ UINT8_TO_STREAM (p, p_cfg->fcr.max_transmit);
+ UINT16_TO_STREAM (p, p_ccb->our_cfg.fcr.rtrans_tout);
+ UINT16_TO_STREAM (p, p_ccb->our_cfg.fcr.mon_tout);
+ UINT16_TO_STREAM (p, p_cfg->fcr.mps);
+ }
+
+ if (p_cfg->ext_flow_spec_present) {
+ UINT8_TO_STREAM (p, L2CAP_CFG_TYPE_EXT_FLOW);
+ UINT8_TO_STREAM (p, L2CAP_CFG_EXT_FLOW_OPTION_LEN);
+ UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.id);
+ UINT8_TO_STREAM (p, p_cfg->ext_flow_spec.stype);
+ UINT16_TO_STREAM (p, p_cfg->ext_flow_spec.max_sdu_size);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.sdu_inter_time);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.access_latency);
+ UINT32_TO_STREAM (p, p_cfg->ext_flow_spec.flush_timeout);
+ }
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_config_rej
+**
+** Description Build and send an L2CAP "configuration reject" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_config_rej (tL2C_CCB *p_ccb, UINT8 *p_data, UINT16 data_len, UINT16 rej_len)
+{
+ BT_HDR *p_buf;
+ UINT16 len, cfg_len, buf_space, len1;
+ UINT8 *p, *p_hci_len, *p_data_end;
+ UINT8 cfg_code;
+
+ L2CAP_TRACE_DEBUG("l2cu_send_peer_config_rej: data_len=%d, rej_len=%d", data_len, rej_len);
+
+
+ len = BT_HDR_SIZE + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD + L2CAP_CONFIG_RSP_LEN;
+ len1 = 0xFFFF - len;
+ if (rej_len > len1) {
+ L2CAP_TRACE_ERROR ("L2CAP - cfg_rej pkt size exceeds buffer design max limit.");
+ return;
+ }
+
+ p_buf = (BT_HDR *)osi_malloc (len + rej_len);
+
+ if (!p_buf) {
+ L2CAP_TRACE_ERROR ("L2CAP - no buffer for cfg_rej");
+ return;
+ }
+
+ p_buf->offset = L2CAP_SEND_CMD_OFFSET;
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET;
+
+ /* Put in HCI header - handle + pkt boundary */
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ if (HCI_NON_FLUSHABLE_PB_SUPPORTED(BTM_ReadLocalFeatures ())) {
+ UINT16_TO_STREAM (p, (p_ccb->p_lcb->handle | (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT)));
+ } else
+#endif
+ {
+ UINT16_TO_STREAM (p, (p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT)));
+ }
+
+ /* Remember the HCI header length position, and save space for it */
+ p_hci_len = p;
+ p += 2;
+
+ /* Put in L2CAP packet header */
+ UINT16_TO_STREAM (p, L2CAP_CMD_OVERHEAD + L2CAP_CONFIG_RSP_LEN + rej_len);
+ UINT16_TO_STREAM (p, L2CAP_SIGNALLING_CID);
+
+ /* Put in L2CAP command header */
+ UINT8_TO_STREAM (p, L2CAP_CMD_CONFIG_RSP);
+ UINT8_TO_STREAM (p, p_ccb->remote_id);
+
+ UINT16_TO_STREAM (p, L2CAP_CONFIG_RSP_LEN + rej_len);
+
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ UINT16_TO_STREAM (p, 0); /* Flags = 0 (no continuation) */
+ UINT16_TO_STREAM (p, L2CAP_CFG_UNKNOWN_OPTIONS);
+
+ buf_space = rej_len;
+
+ /* Now, put the rejected options */
+ p_data_end = p_data + data_len;
+ while (p_data < p_data_end) {
+ cfg_code = *p_data;
+ cfg_len = *(p_data + 1);
+
+ switch (cfg_code & 0x7F) {
+ /* skip known options */
+ case L2CAP_CFG_TYPE_MTU:
+ case L2CAP_CFG_TYPE_FLUSH_TOUT:
+ case L2CAP_CFG_TYPE_QOS:
+ p_data += cfg_len + L2CAP_CFG_OPTION_OVERHEAD;
+ break;
+
+ /* unknown options; copy into rsp if not hints */
+ default:
+ /* sanity check option length */
+ if ((cfg_len + L2CAP_CFG_OPTION_OVERHEAD) <= data_len) {
+ if ((cfg_code & 0x80) == 0) {
+ if (buf_space >= (cfg_len + L2CAP_CFG_OPTION_OVERHEAD)) {
+ memcpy(p, p_data, cfg_len + L2CAP_CFG_OPTION_OVERHEAD);
+ p += cfg_len + L2CAP_CFG_OPTION_OVERHEAD;
+ buf_space -= (cfg_len + L2CAP_CFG_OPTION_OVERHEAD);
+ } else {
+ L2CAP_TRACE_WARNING("L2CAP - cfg_rej exceeds allocated buffer");
+ p_data = p_data_end; /* force loop exit */
+ break;
+ }
+ }
+ p_data += cfg_len + L2CAP_CFG_OPTION_OVERHEAD;
+ }
+ /* bad length; force loop exit */
+ else {
+ p_data = p_data_end;
+ }
+ break;
+ }
+ }
+
+ len = (UINT16) (p - p_hci_len - 2);
+ UINT16_TO_STREAM (p_hci_len, len);
+
+ p_buf->len = len + 4;
+
+ L2CAP_TRACE_DEBUG ("L2CAP - cfg_rej pkt hci_len=%d, l2cap_len=%d",
+ len, (L2CAP_CMD_OVERHEAD + L2CAP_CONFIG_RSP_LEN + rej_len));
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_disc_req
+**
+** Description Build and send an L2CAP "disconnect request" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_disc_req (tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf, *p_buf2;
+ UINT8 *p;
+
+ /* Create an identifier for this packet */
+ p_ccb->p_lcb->id++;
+ l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID);
+
+ p_ccb->local_id = p_ccb->p_lcb->id;
+
+ if ((p_buf = l2cu_build_header(p_ccb->p_lcb, L2CAP_DISC_REQ_LEN, L2CAP_CMD_DISC_REQ, p_ccb->local_id)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for disc_req");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ UINT16_TO_STREAM (p, p_ccb->local_cid);
+
+ /* Move all queued data packets to the LCB. In FCR mode, assume the higher
+ layer checks that all buffers are sent before disconnecting.
+ */
+ if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE) {
+ while ((p_buf2 = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0)) != NULL) {
+ l2cu_set_acl_hci_header (p_buf2, p_ccb);
+ l2c_link_check_send_pkts (p_ccb->p_lcb, p_ccb, p_buf2);
+ }
+ }
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_disc_rsp
+**
+** Description Build and send an L2CAP "disconnect response" message
+** to the peer.
+**
+** This function is passed the parameters for the disconnect
+** response instead of the CCB address, as it may be called
+** to send a disconnect response when there is no CCB.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_disc_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 local_cid,
+ UINT16 remote_cid)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if (!p_lcb) {
+ L2CAP_TRACE_WARNING("lcb already released\n");
+ return;
+ }
+
+ if ((p_buf = l2cu_build_header(p_lcb, L2CAP_DISC_RSP_LEN, L2CAP_CMD_DISC_RSP, remote_id)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for disc_rsp");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, local_cid);
+ UINT16_TO_STREAM (p, remote_cid);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_echo_req
+**
+** Description Build and send an L2CAP "echo request" message
+** to the peer. Note that we do not currently allow
+** data in the echo request.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_echo_req (tL2C_LCB *p_lcb, UINT8 *p_data, UINT16 data_len)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ p_lcb->id++;
+ l2cu_adj_id(p_lcb, L2CAP_ADJ_ZERO_ID); /* check for wrap to '0' */
+
+ if ((p_buf = l2cu_build_header(p_lcb, (UINT16) (L2CAP_ECHO_REQ_LEN + data_len), L2CAP_CMD_ECHO_REQ, p_lcb->id)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for echo_req");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ if (data_len) {
+ ARRAY_TO_STREAM (p, p_data, data_len);
+ }
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_echo_rsp
+**
+** Description Build and send an L2CAP "echo response" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_echo_rsp (tL2C_LCB *p_lcb, UINT8 id, UINT8 *p_data, UINT16 data_len)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+ UINT16 maxlen;
+ /* Filter out duplicate IDs or if available buffers are low (intruder checking) */
+ if (!id || id == p_lcb->cur_echo_id) {
+ /* Dump this request since it is illegal */
+ L2CAP_TRACE_WARNING ("L2CAP ignoring duplicate echo request (%d)", id);
+ return;
+ } else {
+ p_lcb->cur_echo_id = id;
+ }
+
+ uint16_t acl_data_size = controller_get_interface()->get_acl_data_size_classic();
+ uint16_t acl_packet_size = controller_get_interface()->get_acl_packet_size_classic();
+ /* Don't return data if it does not fit in ACL and L2CAP MTU */
+ maxlen = (L2CAP_CMD_BUF_SIZE > acl_packet_size) ?
+ acl_data_size : (UINT16)L2CAP_CMD_BUF_SIZE;
+ maxlen -= (UINT16)(BT_HDR_SIZE + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD +
+ L2CAP_CMD_OVERHEAD + L2CAP_ECHO_RSP_LEN);
+
+ if (data_len > maxlen) {
+ data_len = 0;
+ }
+
+ if ((p_buf = l2cu_build_header (p_lcb, (UINT16)(L2CAP_ECHO_RSP_LEN + data_len), L2CAP_CMD_ECHO_RSP, id)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for echo_rsp");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ if (data_len) {
+ ARRAY_TO_STREAM (p, p_data, data_len);
+ }
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_info_req
+**
+** Description Build and send an L2CAP "info request" message
+** to the peer.
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_info_req (tL2C_LCB *p_lcb, UINT16 info_type)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ /* check for wrap and/or BRCM ID */
+ p_lcb->id++;
+ l2cu_adj_id(p_lcb, L2CAP_ADJ_ID);
+
+ if ((p_buf = l2cu_build_header(p_lcb, 2, L2CAP_CMD_INFO_REQ, p_lcb->id)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for info_req");
+ return;
+ }
+
+ L2CAP_TRACE_EVENT ("l2cu_send_peer_info_req: type 0x%04x", info_type);
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, info_type);
+
+ p_lcb->w4_info_rsp = TRUE;
+ btu_start_timer (&p_lcb->info_timer_entry, BTU_TTYPE_L2CAP_INFO, L2CAP_WAIT_INFO_RSP_TOUT);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_info_rsp
+**
+** Description Build and send an L2CAP "info response" message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_info_rsp (tL2C_LCB *p_lcb, UINT8 remote_id, UINT16 info_type)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+ UINT16 len = L2CAP_INFO_RSP_LEN;
+
+#if (L2CAP_CONFORMANCE_TESTING == TRUE)
+ if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE)
+ && (l2cb.test_info_resp & (L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE |
+ L2CAP_EXTFEA_NO_CRC | L2CAP_EXTFEA_EXT_FLOW_SPEC |
+ L2CAP_EXTFEA_FIXED_CHNLS | L2CAP_EXTFEA_EXT_WINDOW |
+ L2CAP_EXTFEA_UCD_RECEPTION )) )
+#else
+ if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE)
+ && (L2CAP_EXTFEA_SUPPORTED_MASK & (L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE |
+ L2CAP_EXTFEA_NO_CRC | L2CAP_EXTFEA_FIXED_CHNLS |
+ L2CAP_EXTFEA_UCD_RECEPTION )) )
+#endif
+ {
+ len += L2CAP_EXTENDED_FEATURES_ARRAY_SIZE;
+ } else if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE) {
+ len += L2CAP_FIXED_CHNL_ARRAY_SIZE;
+ } else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE) {
+ len += L2CAP_CONNLESS_MTU_INFO_SIZE;
+ }
+
+ if ((p_buf = l2cu_build_header(p_lcb, len, L2CAP_CMD_INFO_RSP, remote_id)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no buffer for info_rsp");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, info_type);
+
+#if (L2CAP_CONFORMANCE_TESTING == TRUE)
+ if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE)
+ && (l2cb.test_info_resp & ( L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE
+ | L2CAP_EXTFEA_UCD_RECEPTION )) )
+#else
+ if ((info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE)
+ && (L2CAP_EXTFEA_SUPPORTED_MASK & ( L2CAP_EXTFEA_ENH_RETRANS | L2CAP_EXTFEA_STREAM_MODE
+ | L2CAP_EXTFEA_UCD_RECEPTION )) )
+#endif
+ {
+ UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS);
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ /* optional data are not added for now */
+ UINT32_TO_STREAM (p, L2CAP_BLE_EXTFEA_MASK);
+ } else
+#endif
+ {
+#if L2CAP_CONFORMANCE_TESTING == TRUE
+ UINT32_TO_STREAM (p, l2cb.test_info_resp);
+#else
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ UINT32_TO_STREAM (p, L2CAP_EXTFEA_SUPPORTED_MASK | L2CAP_EXTFEA_FIXED_CHNLS);
+#else
+ UINT32_TO_STREAM (p, L2CAP_EXTFEA_SUPPORTED_MASK);
+#endif
+#endif
+ }
+ } else if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE) {
+ UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS);
+ memset (p, 0, L2CAP_FIXED_CHNL_ARRAY_SIZE);
+
+ p[0] = L2CAP_FIXED_CHNL_SIG_BIT;
+
+ if ( L2CAP_EXTFEA_SUPPORTED_MASK & L2CAP_EXTFEA_UCD_RECEPTION ) {
+ p[0] |= L2CAP_FIXED_CHNL_CNCTLESS_BIT;
+ }
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ {
+ int xx;
+
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
+ if (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) {
+ p[(xx + L2CAP_FIRST_FIXED_CHNL) / 8] |= 1 << ((xx + L2CAP_FIRST_FIXED_CHNL) % 8);
+ }
+ }
+#endif
+ } else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE) {
+ UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_SUCCESS);
+ UINT16_TO_STREAM (p, L2CAP_UCD_MTU);
+ } else {
+ UINT16_TO_STREAM (p, L2CAP_INFO_RESP_RESULT_NOT_SUPPORTED); /* 'not supported' */
+ }
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/******************************************************************************
+**
+** Function l2cu_enqueue_ccb
+**
+** Description queue CCB by priority. The first CCB is highest priority and
+** is served at first. The CCB is queued to an LLCB or an LCB.
+**
+** Returns None
+**
+*******************************************************************************/
+void l2cu_enqueue_ccb (tL2C_CCB *p_ccb)
+{
+ tL2C_CCB *p_ccb1;
+ tL2C_CCB_Q *p_q = NULL;
+
+ /* Find out which queue the channel is on
+ */
+ if (p_ccb->p_lcb != NULL) {
+ p_q = &p_ccb->p_lcb->ccb_queue;
+ }
+
+ if ( (!p_ccb->in_use) || (p_q == NULL) ) {
+ L2CAP_TRACE_ERROR ("l2cu_enqueue_ccb CID: 0x%04x ERROR in_use: %u p_lcb: %p",
+ p_ccb->local_cid, p_ccb->in_use, p_ccb->p_lcb);
+ return;
+ }
+
+ L2CAP_TRACE_DEBUG ("l2cu_enqueue_ccb CID: 0x%04x priority: %d",
+ p_ccb->local_cid, p_ccb->ccb_priority);
+
+ /* If the queue is empty, we go at the front */
+ if (!p_q->p_first_ccb) {
+ p_q->p_first_ccb = p_q->p_last_ccb = p_ccb;
+ p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL;
+ } else {
+ p_ccb1 = p_q->p_first_ccb;
+
+ while (p_ccb1 != NULL) {
+ /* Insert new ccb at the end of the same priority. Lower number, higher priority */
+ if (p_ccb->ccb_priority < p_ccb1->ccb_priority) {
+ /* Are we at the head of the queue ? */
+ if (p_ccb1 == p_q->p_first_ccb) {
+ p_q->p_first_ccb = p_ccb;
+ } else {
+ p_ccb1->p_prev_ccb->p_next_ccb = p_ccb;
+ }
+
+ p_ccb->p_next_ccb = p_ccb1;
+ p_ccb->p_prev_ccb = p_ccb1->p_prev_ccb;
+ p_ccb1->p_prev_ccb = p_ccb;
+ break;
+ }
+
+ p_ccb1 = p_ccb1->p_next_ccb;
+ }
+
+ /* If we are lower then anyone in the list, we go at the end */
+ if (!p_ccb1) {
+ /* add new ccb at the end of the list */
+ p_q->p_last_ccb->p_next_ccb = p_ccb;
+
+ p_ccb->p_next_ccb = NULL;
+ p_ccb->p_prev_ccb = p_q->p_last_ccb;
+ p_q->p_last_ccb = p_ccb;
+ }
+ }
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ /* Adding CCB into round robin service table of its LCB */
+ if (p_ccb->p_lcb != NULL) {
+ /* if this is the first channel in this priority group */
+ if (p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb == 0 ) {
+ /* Set the first channel to this CCB */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb;
+ /* Set the next serving channel in this group to this CCB */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = p_ccb;
+ /* Initialize quota of this priority group based on its priority */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota = L2CAP_GET_PRIORITY_QUOTA(p_ccb->ccb_priority);
+ }
+ /* increase number of channels in this group */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb++;
+ }
+#endif
+
+}
+
+/******************************************************************************
+**
+** Function l2cu_dequeue_ccb
+**
+** Description dequeue CCB from a queue
+**
+** Returns -
+**
+*******************************************************************************/
+void l2cu_dequeue_ccb (tL2C_CCB *p_ccb)
+{
+ tL2C_CCB_Q *p_q = NULL;
+
+ L2CAP_TRACE_DEBUG ("l2cu_dequeue_ccb CID: 0x%04x", p_ccb->local_cid);
+
+ /* Find out which queue the channel is on
+ */
+ if (p_ccb->p_lcb != NULL) {
+ p_q = &p_ccb->p_lcb->ccb_queue;
+ }
+
+ if ( (!p_ccb->in_use) || (p_q == NULL) || (p_q->p_first_ccb == NULL) ) {
+ L2CAP_TRACE_ERROR ("l2cu_dequeue_ccb CID: 0x%04x ERROR in_use: %u p_lcb: %p p_q: %p p_q->p_first_ccb: %p",
+ p_ccb->local_cid, p_ccb->in_use, p_ccb->p_lcb, p_q, p_q ? p_q->p_first_ccb : 0);
+ return;
+ }
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ /* Removing CCB from round robin service table of its LCB */
+ if (p_ccb->p_lcb != NULL) {
+ /* decrease number of channels in this priority group */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb--;
+
+ /* if it was the last channel in the priority group */
+ if (p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb == 0 ) {
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = NULL;
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = NULL;
+ } else {
+ /* if it is the first channel of this group */
+ if ( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb == p_ccb ) {
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb->p_next_ccb;
+ }
+ /* if it is the next serving channel of this group */
+ if ( p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb == p_ccb ) {
+ /* simply, start serving from the first channel */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb
+ = p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb;
+ }
+ }
+ }
+#endif
+
+ if (p_ccb == p_q->p_first_ccb) {
+ /* We are removing the first in a queue */
+ p_q->p_first_ccb = p_ccb->p_next_ccb;
+
+ if (p_q->p_first_ccb) {
+ p_q->p_first_ccb->p_prev_ccb = NULL;
+ } else {
+ p_q->p_last_ccb = NULL;
+ }
+ } else if (p_ccb == p_q->p_last_ccb) {
+ /* We are removing the last in a queue */
+ p_q->p_last_ccb = p_ccb->p_prev_ccb;
+ p_q->p_last_ccb->p_next_ccb = NULL;
+ } else {
+ /* In the middle of a chain. */
+ p_ccb->p_prev_ccb->p_next_ccb = p_ccb->p_next_ccb;
+ p_ccb->p_next_ccb->p_prev_ccb = p_ccb->p_prev_ccb;
+ }
+
+ p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL;
+}
+
+/******************************************************************************
+**
+** Function l2cu_change_pri_ccb
+**
+** Description
+**
+** Returns -
+**
+*******************************************************************************/
+void l2cu_change_pri_ccb (tL2C_CCB *p_ccb, tL2CAP_CHNL_PRIORITY priority)
+{
+ if (p_ccb->ccb_priority != priority) {
+ /* If CCB is not the only guy on the queue */
+ if ( (p_ccb->p_next_ccb != NULL) || (p_ccb->p_prev_ccb != NULL) ) {
+ L2CAP_TRACE_DEBUG ("Update CCB list in logical link");
+
+ /* Remove CCB from queue and re-queue it at new priority */
+ l2cu_dequeue_ccb (p_ccb);
+
+ p_ccb->ccb_priority = priority;
+ l2cu_enqueue_ccb (p_ccb);
+ }
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ else {
+ /* If CCB is the only guy on the queue, no need to re-enqueue */
+ /* update only round robin service data */
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb = 0;
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = NULL;
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = NULL;
+
+ p_ccb->ccb_priority = priority;
+
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_first_ccb = p_ccb;
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].p_serve_ccb = p_ccb;
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].quota = L2CAP_GET_PRIORITY_QUOTA(p_ccb->ccb_priority);
+ p_ccb->p_lcb->rr_serv[p_ccb->ccb_priority].num_ccb = 1;
+ }
+#endif
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_allocate_ccb
+**
+** Description This function allocates a Channel Control Block and
+** attaches it to a link control block. The local CID
+** is also assigned.
+**
+** Returns pointer to CCB, or NULL if none
+**
+*******************************************************************************/
+bool l2cu_find_ccb_in_list(void *p_ccb_node, void *p_local_cid);
+tL2C_CCB *l2cu_allocate_ccb (tL2C_LCB *p_lcb, UINT16 cid)
+{
+ tL2C_CCB *p_ccb = NULL;
+ uint16_t tmp_cid = L2CAP_BASE_APPL_CID;
+ L2CAP_TRACE_DEBUG ("l2cu_allocate_ccb: cid 0x%04x", cid);
+
+ p_ccb = l2cu_find_free_ccb ();
+ if(p_ccb == NULL) {
+ if (list_length(l2cb.p_ccb_pool) < MAX_L2CAP_CHANNELS) {
+ p_ccb = (tL2C_CCB *)osi_malloc(sizeof(tL2C_CCB));
+
+ if (p_ccb) {
+ memset (p_ccb, 0, sizeof(tL2C_CCB));
+ list_append(l2cb.p_ccb_pool, p_ccb);
+ }
+ }
+ }
+ if (p_ccb == NULL) {
+ return (NULL);
+ }
+
+ p_ccb->p_next_ccb = p_ccb->p_prev_ccb = NULL;
+
+ p_ccb->in_use = TRUE;
+
+ /* Get a CID for the connection */
+ for (tmp_cid = L2CAP_BASE_APPL_CID; tmp_cid < MAX_L2CAP_CHANNELS + L2CAP_BASE_APPL_CID; tmp_cid++) {
+ if (list_foreach(l2cb.p_ccb_pool, l2cu_find_ccb_in_list, &tmp_cid) == NULL) {
+ break;
+ }
+ }
+ assert(tmp_cid != MAX_L2CAP_CHANNELS + L2CAP_BASE_APPL_CID);
+ p_ccb->local_cid = tmp_cid;
+ p_ccb->p_lcb = p_lcb;
+ p_ccb->p_rcb = NULL;
+ p_ccb->should_free_rcb = false;
+
+ /* Set priority then insert ccb into LCB queue (if we have an LCB) */
+ p_ccb->ccb_priority = L2CAP_CHNL_PRIORITY_LOW;
+
+ if (p_lcb) {
+ l2cu_enqueue_ccb (p_ccb);
+ }
+
+ /* clear what peer wants to configure */
+ p_ccb->peer_cfg_bits = 0;
+
+ /* Put in default values for configuration */
+ memset (&p_ccb->our_cfg, 0, sizeof(tL2CAP_CFG_INFO));
+ memset (&p_ccb->peer_cfg, 0, sizeof(tL2CAP_CFG_INFO));
+
+ /* Put in default values for local/peer configurations */
+ p_ccb->our_cfg.flush_to = p_ccb->peer_cfg.flush_to = L2CAP_DEFAULT_FLUSH_TO;
+ p_ccb->our_cfg.mtu = p_ccb->peer_cfg.mtu = L2CAP_DEFAULT_MTU;
+ p_ccb->our_cfg.qos.service_type = p_ccb->peer_cfg.qos.service_type = L2CAP_DEFAULT_SERV_TYPE;
+ p_ccb->our_cfg.qos.token_rate = p_ccb->peer_cfg.qos.token_rate = L2CAP_DEFAULT_TOKEN_RATE;
+ p_ccb->our_cfg.qos.token_bucket_size = p_ccb->peer_cfg.qos.token_bucket_size = L2CAP_DEFAULT_BUCKET_SIZE;
+ p_ccb->our_cfg.qos.peak_bandwidth = p_ccb->peer_cfg.qos.peak_bandwidth = L2CAP_DEFAULT_PEAK_BANDWIDTH;
+ p_ccb->our_cfg.qos.latency = p_ccb->peer_cfg.qos.latency = L2CAP_DEFAULT_LATENCY;
+ p_ccb->our_cfg.qos.delay_variation = p_ccb->peer_cfg.qos.delay_variation = L2CAP_DEFAULT_DELAY;
+
+ p_ccb->bypass_fcs = 0;
+ memset (&p_ccb->ertm_info, 0, sizeof(tL2CAP_ERTM_INFO));
+ p_ccb->peer_cfg_already_rejected = FALSE;
+ p_ccb->fcr_cfg_tries = L2CAP_MAX_FCR_CFG_TRIES;
+
+ /* stop and release timers */
+ btu_free_quick_timer(&p_ccb->fcrb.ack_timer);
+ memset(&p_ccb->fcrb.ack_timer, 0, sizeof(TIMER_LIST_ENT));
+ p_ccb->fcrb.ack_timer.param = (TIMER_PARAM_TYPE)p_ccb;
+
+ btu_free_quick_timer(&p_ccb->fcrb.mon_retrans_timer);
+ memset(&p_ccb->fcrb.mon_retrans_timer, 0, sizeof(TIMER_LIST_ENT));
+ p_ccb->fcrb.mon_retrans_timer.param = (TIMER_PARAM_TYPE)p_ccb;
+
+// btla-specific ++
+ /* CSP408639 Fix: When L2CAP send amp move channel request or receive
+ * L2CEVT_AMP_MOVE_REQ do following sequence. Send channel move
+ * request -> Stop retrans/monitor timer -> Change channel state to CST_AMP_MOVING. */
+// btla-specific --
+
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ l2c_fcr_free_timer (p_ccb);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+#if BT_SDP_BQB_INCLUDED
+ if (l2cap_bqb_ertm_mode_included_flag) {
+ p_ccb->ertm_info.preferred_mode = L2CAP_FCR_ERTM_MODE;
+ p_ccb->ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
+ } else
+#endif /* BT_SDP_BQB_INCLUDED */
+ {
+ p_ccb->ertm_info.preferred_mode = L2CAP_FCR_BASIC_MODE; /* Default mode for channel is basic mode */
+ p_ccb->ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_BASIC|L2CAP_FCR_CHAN_OPT_ERTM;
+ }
+ p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_FCR_RX_BUF_SIZE;
+ p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_FCR_TX_BUF_SIZE;
+ p_ccb->ertm_info.user_rx_buf_size = L2CAP_USER_RX_BUF_SIZE;
+ p_ccb->ertm_info.user_tx_buf_size = L2CAP_USER_TX_BUF_SIZE;
+ p_ccb->max_rx_mtu = L2CAP_MTU_SIZE;
+ p_ccb->tx_mps = L2CAP_FCR_TX_BUF_SIZE - 32;
+
+ p_ccb->xmit_hold_q = fixed_queue_new(QUEUE_SIZE_MAX);
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ p_ccb->fcrb.srej_rcv_hold_q = fixed_queue_new(QUEUE_SIZE_MAX);
+ p_ccb->fcrb.retrans_q = fixed_queue_new(QUEUE_SIZE_MAX);
+ p_ccb->fcrb.waiting_for_ack_q = fixed_queue_new(QUEUE_SIZE_MAX);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+ p_ccb->cong_sent = FALSE;
+ p_ccb->buff_quota = 2; /* This gets set after config */
+
+ /* If CCB was reserved Config_Done can already have some value */
+ if (cid == 0) {
+ p_ccb->config_done = 0;
+ } else {
+ L2CAP_TRACE_DEBUG ("l2cu_allocate_ccb: cid 0x%04x config_done:0x%x", cid, p_ccb->config_done);
+ }
+
+ p_ccb->chnl_state = CST_CLOSED;
+ p_ccb->flags = 0;
+ p_ccb->tx_data_rate = L2CAP_CHNL_DATA_RATE_LOW;
+ p_ccb->rx_data_rate = L2CAP_CHNL_DATA_RATE_LOW;
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ p_ccb->is_flushable = FALSE;
+#endif
+
+ btu_free_timer(&p_ccb->timer_entry);
+ memset(&p_ccb->timer_entry, 0, sizeof(TIMER_LIST_ENT));
+ p_ccb->timer_entry.param = (TIMER_PARAM_TYPE)p_ccb;
+ p_ccb->timer_entry.in_use = 0;
+
+ l2c_link_adjust_chnl_allocation ();
+
+ return (p_ccb);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_start_post_bond_timer
+**
+** Description This function starts the ACL Link inactivity timer after
+** dedicated bonding
+** This timer can be longer than the normal link inactivity
+** timer for some platforms.
+**
+** Returns BOOLEAN - TRUE if idle timer started or disconnect initiated
+** FALSE if there's one or more pending CCB's exist
+**
+*******************************************************************************/
+BOOLEAN l2cu_start_post_bond_timer (UINT16 handle)
+{
+ UINT16 timeout;
+ tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(handle);
+
+ if (!p_lcb) {
+ return (TRUE);
+ }
+
+ p_lcb->is_bonding = FALSE;
+
+ /* Only start timer if no control blocks allocated */
+ if (p_lcb->ccb_queue.p_first_ccb != NULL) {
+ return (FALSE);
+ }
+
+ /* If no channels on the connection, start idle timeout */
+ if ( (p_lcb->link_state == LST_CONNECTED) || (p_lcb->link_state == LST_CONNECTING) || (p_lcb->link_state == LST_DISCONNECTING) ) {
+ if (p_lcb->idle_timeout == 0) {
+ if (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)) {
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ } else {
+ timeout = BT_1SEC_TIMEOUT;
+ }
+ } else {
+ timeout = L2CAP_BONDING_TIMEOUT;
+ }
+
+ if (timeout != 0xFFFF) {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout);
+ }
+
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_release_ccb
+**
+** Description This function releases a Channel Control Block. The timer
+** is stopped, any attached buffers freed, and the CCB is removed
+** from the link control block.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_release_ccb (tL2C_CCB *p_ccb)
+{
+ tL2C_LCB *p_lcb = p_ccb->p_lcb;
+ tL2C_RCB *p_rcb = p_ccb->p_rcb;
+ L2CAP_TRACE_DEBUG ("l2cu_release_ccb: cid 0x%04x in_use: %u", p_ccb->local_cid, p_ccb->in_use);
+
+ /* If already released, could be race condition */
+ if (!p_ccb->in_use) {
+ return;
+ }
+#if BLE_INCLUDED == TRUE
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ /* Take samephore to avoid race condition */
+ l2ble_update_att_acl_pkt_num(L2CA_BUFF_FREE, NULL);
+ }
+#endif
+#if (SDP_INCLUDED == TRUE)
+ if (p_rcb && (p_rcb->psm != p_rcb->real_psm)) {
+ btm_sec_clr_service_by_psm(p_rcb->psm);
+ }
+#endif ///SMP_INCLUDED == TRUE
+ if (p_ccb->should_free_rcb) {
+ osi_free(p_rcb);
+ p_ccb->p_rcb = NULL;
+ p_ccb->should_free_rcb = false;
+ }
+ if (p_lcb) {
+ btm_sec_clr_temp_auth_service (p_lcb->remote_bd_addr);
+ }
+
+ /* Stop and free the timer */
+ btu_free_timer (&p_ccb->timer_entry);
+
+ fixed_queue_free(p_ccb->xmit_hold_q, osi_free_func);
+ p_ccb->xmit_hold_q = NULL;
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ fixed_queue_free(p_ccb->fcrb.srej_rcv_hold_q, osi_free_func);
+ fixed_queue_free(p_ccb->fcrb.retrans_q, osi_free_func);
+ fixed_queue_free(p_ccb->fcrb.waiting_for_ack_q, osi_free_func);
+ p_ccb->fcrb.srej_rcv_hold_q = NULL;
+ p_ccb->fcrb.retrans_q = NULL;
+ p_ccb->fcrb.waiting_for_ack_q = NULL;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ l2c_fcr_cleanup (p_ccb);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ /* Channel may not be assigned to any LCB if it was just pre-reserved */
+ if ( (p_lcb) &&
+ ( (p_ccb->local_cid >= L2CAP_BASE_APPL_CID)
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ || (p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID)
+#endif
+ )
+ ) {
+ l2cu_dequeue_ccb (p_ccb);
+
+ /* Delink the CCB from the LCB */
+ p_ccb->p_lcb = NULL;
+ }
+
+
+ /* Flag as not in use */
+ p_ccb->in_use = FALSE;
+
+ /* If no channels on the connection, start idle timeout */
+ if ((p_lcb) && p_lcb->in_use && (p_lcb->link_state == LST_CONNECTED)) {
+ if (!p_lcb->ccb_queue.p_first_ccb) {
+ l2cu_no_dynamic_ccbs (p_lcb);
+ } else {
+ /* Link is still active, adjust channel quotas. */
+ l2c_link_adjust_chnl_allocation ();
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_find_ccb_by_remote_cid
+**
+** Description Look through all active CCBs on a link for a match based
+** on the remote CID.
+**
+** Returns pointer to matched CCB, or NULL if no match
+**
+*******************************************************************************/
+tL2C_CCB *l2cu_find_ccb_by_remote_cid (tL2C_LCB *p_lcb, UINT16 remote_cid)
+{
+ tL2C_CCB *p_ccb;
+
+ /* If LCB is NULL, look through all active links */
+ if (!p_lcb) {
+ return NULL;
+ } else {
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb)
+ if ((p_ccb->in_use) && (p_ccb->remote_cid == remote_cid)) {
+ return (p_ccb);
+ }
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_allocate_rcb
+**
+** Description Look through the Registration Control Blocks for a free
+** one.
+**
+** Returns Pointer to the RCB or NULL if not found
+**
+*******************************************************************************/
+tL2C_RCB *l2cu_allocate_rcb (UINT16 psm)
+{
+ tL2C_RCB *p_rcb = &l2cb.rcb_pool[0];
+ UINT16 xx;
+
+ for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) {
+ if (!p_rcb->in_use) {
+ p_rcb->in_use = TRUE;
+ p_rcb->psm = psm;
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ p_rcb->ucd.state = L2C_UCD_STATE_UNUSED;
+#endif
+ return (p_rcb);
+ }
+ }
+
+ /* If here, no free RCB found */
+ return (NULL);
+}
+
+#if (BLE_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function l2cu_allocate_ble_rcb
+**
+** Description Look through the BLE Registration Control Blocks for a free
+** one.
+**
+** Returns Pointer to the BLE RCB or NULL if not found
+**
+*******************************************************************************/
+tL2C_RCB *l2cu_allocate_ble_rcb (UINT16 psm)
+{
+ tL2C_RCB *p_rcb = &l2cb.ble_rcb_pool[0];
+ UINT16 xx;
+
+ for (xx = 0; xx < BLE_MAX_L2CAP_CLIENTS; xx++, p_rcb++)
+ {
+ if (!p_rcb->in_use)
+ {
+ p_rcb->in_use = TRUE;
+ p_rcb->psm = psm;
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ p_rcb->ucd.state = L2C_UCD_STATE_UNUSED;
+#endif
+ return (p_rcb);
+ }
+ }
+
+ /* If here, no free RCB found */
+ return (NULL);
+}
+#endif ///BLE_INCLUDED == TRUE
+
+/*******************************************************************************
+**
+** Function l2cu_release_rcb
+**
+** Description Mark an RCB as no longet in use
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_release_rcb (tL2C_RCB *p_rcb)
+{
+ p_rcb->in_use = FALSE;
+ p_rcb->psm = 0;
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_disconnect_chnl
+**
+** Description Disconnect a channel. Typically, this is due to either
+** receiving a bad configuration, bad packet or max_retries expiring.
+**
+*******************************************************************************/
+void l2cu_disconnect_chnl (tL2C_CCB *p_ccb)
+{
+ UINT16 local_cid = p_ccb->local_cid;
+
+ if (local_cid >= L2CAP_BASE_APPL_CID) {
+ tL2CA_DISCONNECT_IND_CB *p_disc_cb = p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb;
+
+ L2CAP_TRACE_WARNING ("L2CAP - disconnect_chnl CID: 0x%04x", local_cid);
+
+ l2cu_send_peer_disc_req (p_ccb);
+
+ l2cu_release_ccb (p_ccb);
+
+ (*p_disc_cb)(local_cid, FALSE);
+ } else {
+ /* failure on the AMP channel, probably need to disconnect ACL */
+ L2CAP_TRACE_ERROR ("L2CAP - disconnect_chnl CID: 0x%04x Ignored", local_cid);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_find_rcb_by_psm
+**
+** Description Look through the Registration Control Blocks to see if
+** anyone registered to handle the PSM in question
+**
+** Returns Pointer to the RCB or NULL if not found
+**
+*******************************************************************************/
+tL2C_RCB *l2cu_find_rcb_by_psm (UINT16 psm)
+{
+ tL2C_RCB *p_rcb = &l2cb.rcb_pool[0];
+ UINT16 xx;
+
+ for (xx = 0; xx < MAX_L2CAP_CLIENTS; xx++, p_rcb++) {
+ if ((p_rcb->in_use) && (p_rcb->psm == psm)) {
+ return (p_rcb);
+ }
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+#if (BLE_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function l2cu_find_ble_rcb_by_psm
+**
+** Description Look through the BLE Registration Control Blocks to see if
+** anyone registered to handle the PSM in question
+**
+** Returns Pointer to the BLE RCB or NULL if not found
+**
+*******************************************************************************/
+tL2C_RCB *l2cu_find_ble_rcb_by_psm (UINT16 psm)
+{
+ tL2C_RCB *p_rcb = &l2cb.ble_rcb_pool[0];
+ UINT16 xx;
+
+ for (xx = 0; xx < BLE_MAX_L2CAP_CLIENTS; xx++, p_rcb++)
+ {
+ if ((p_rcb->in_use) && (p_rcb->psm == psm)) {
+ return (p_rcb);
+ }
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+#endif ///BLE_INCLUDED == TRUE
+
+#if (L2CAP_COC_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function l2cu_process_peer_cfg_req
+**
+** Description This function is called when the peer sends us a "config request"
+** message. It extracts the configuration of interest and saves
+** it in the CCB.
+**
+** Note: Negotiation of the FCR channel type is handled internally,
+** all others are passed to the upper layer.
+**
+** Returns UINT8 - L2CAP_PEER_CFG_OK if passed to upper layer,
+** L2CAP_PEER_CFG_UNACCEPTABLE if automatically responded to
+** because parameters are unnacceptable from a specification
+** point of view.
+** L2CAP_PEER_CFG_DISCONNECT if no compatible channel modes
+** between the two devices, and shall be closed.
+**
+*******************************************************************************/
+UINT8 l2cu_process_peer_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ BOOLEAN mtu_ok = TRUE;
+ BOOLEAN qos_type_ok = TRUE;
+ BOOLEAN flush_to_ok = TRUE;
+ BOOLEAN fcr_ok = TRUE;
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ UINT8 fcr_status;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ /* Ignore FCR parameters for basic mode */
+ if (!p_cfg->fcr_present) {
+ p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE;
+ }
+
+ /* Save the MTU that our peer can receive */
+ if (p_cfg->mtu_present) {
+ /* Make sure MTU is at least the minimum */
+ if (p_cfg->mtu >= L2CAP_MIN_MTU) {
+ /* In basic mode, limit the MTU to our buffer size */
+ if ( (p_cfg->fcr_present == FALSE) && (p_cfg->mtu > L2CAP_MTU_SIZE) ) {
+ p_cfg->mtu = L2CAP_MTU_SIZE;
+ }
+
+ /* Save the accepted value in case of renegotiation */
+ p_ccb->peer_cfg.mtu = p_cfg->mtu;
+ p_ccb->peer_cfg.mtu_present = TRUE;
+ p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_MTU;
+ } else { /* Illegal MTU value */
+ p_cfg->mtu = L2CAP_MIN_MTU;
+ mtu_ok = FALSE;
+ }
+ }
+ /* Reload mtu from a previously accepted config request */
+ else if (p_ccb->peer_cfg.mtu_present) {
+ p_cfg->mtu_present = TRUE;
+ p_cfg->mtu = p_ccb->peer_cfg.mtu;
+ }
+
+ /* Verify that the flush timeout is a valid value (0 is illegal) */
+ if (p_cfg->flush_to_present) {
+ if (!p_cfg->flush_to) {
+ p_cfg->flush_to = 0xFFFF; /* Infinite retransmissions (spec default) */
+ flush_to_ok = FALSE;
+ } else { /* Save the accepted value in case of renegotiation */
+ p_ccb->peer_cfg.flush_to_present = TRUE;
+ p_ccb->peer_cfg.flush_to = p_cfg->flush_to;
+ p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_FLUSH_TO;
+ }
+ }
+ /* Reload flush_to from a previously accepted config request */
+ else if (p_ccb->peer_cfg.flush_to_present) {
+ p_cfg->flush_to_present = TRUE;
+ p_cfg->flush_to = p_ccb->peer_cfg.flush_to;
+ }
+
+ /* Save the QOS settings the the peer is using */
+ if (p_cfg->qos_present) {
+ /* Make sure service type is not a reserved value; otherwise let upper
+ layer decide if acceptable
+ */
+ if (p_cfg->qos.service_type <= GUARANTEED) {
+ p_ccb->peer_cfg.qos = p_cfg->qos;
+ p_ccb->peer_cfg.qos_present = TRUE;
+ p_ccb->peer_cfg_bits |= L2CAP_CH_CFG_MASK_QOS;
+ } else { /* Illegal service type value */
+ p_cfg->qos.service_type = BEST_EFFORT;
+ qos_type_ok = FALSE;
+ }
+ }
+ /* Reload QOS from a previously accepted config request */
+ else if (p_ccb->peer_cfg.qos_present) {
+ p_cfg->qos_present = TRUE;
+ p_cfg->qos = p_ccb->peer_cfg.qos;
+ }
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ if ((fcr_status = l2c_fcr_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_DISCONNECT) {
+ /* Notify caller to disconnect the channel (incompatible modes) */
+ p_cfg->result = L2CAP_CFG_FAILED_NO_REASON;
+ p_cfg->mtu_present = p_cfg->qos_present = p_cfg->flush_to_present = 0;
+
+ return (L2CAP_PEER_CFG_DISCONNECT);
+ }
+
+ fcr_ok = (fcr_status == L2CAP_PEER_CFG_OK);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+ /* Return any unacceptable parameters */
+ if (mtu_ok && flush_to_ok && qos_type_ok && fcr_ok) {
+ l2cu_adjust_out_mps (p_ccb);
+ return (L2CAP_PEER_CFG_OK);
+ } else {
+ p_cfg->result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
+
+ if (mtu_ok) {
+ p_cfg->mtu_present = FALSE;
+ }
+ if (flush_to_ok) {
+ p_cfg->flush_to_present = FALSE;
+ }
+ if (qos_type_ok) {
+ p_cfg->qos_present = FALSE;
+ }
+ if (fcr_ok) {
+ p_cfg->fcr_present = FALSE;
+ }
+
+ return (L2CAP_PEER_CFG_UNACCEPTABLE);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_process_peer_cfg_rsp
+**
+** Description This function is called when the peer sends us a "config response"
+** message. It extracts the configuration of interest and saves
+** it in the CCB.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_process_peer_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ /* If we wanted QoS and the peer sends us a positive response with QoS, use his values */
+ if ( (p_cfg->qos_present) && (p_ccb->our_cfg.qos_present) ) {
+ p_ccb->our_cfg.qos = p_cfg->qos;
+ }
+
+ if (p_cfg->fcr_present) {
+ /* Save the retransmission and monitor timeout values */
+ if (p_cfg->fcr.mode == L2CAP_FCR_ERTM_MODE) {
+ p_ccb->peer_cfg.fcr.rtrans_tout = p_cfg->fcr.rtrans_tout;
+ p_ccb->peer_cfg.fcr.mon_tout = p_cfg->fcr.mon_tout;
+ }
+
+ /* Calculate the max number of packets for which we can delay sending an ack */
+ if (p_cfg->fcr.tx_win_sz < p_ccb->our_cfg.fcr.tx_win_sz) {
+ p_ccb->fcrb.max_held_acks = p_cfg->fcr.tx_win_sz / 3;
+ } else {
+ p_ccb->fcrb.max_held_acks = p_ccb->our_cfg.fcr.tx_win_sz / 3;
+ }
+
+ L2CAP_TRACE_DEBUG ("l2cu_process_peer_cfg_rsp(): peer tx_win_sz: %d, our tx_win_sz: %d, max_held_acks: %d",
+ p_cfg->fcr.tx_win_sz, p_ccb->our_cfg.fcr.tx_win_sz, p_ccb->fcrb.max_held_acks);
+ }
+}
+
+/*******************************************************************************
+**
+** Function l2cu_process_our_cfg_req
+**
+** Description This function is called when we send a "config request"
+** message. It extracts the configuration of interest and saves
+** it in the CCB.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_process_our_cfg_req (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ tL2C_LCB *p_lcb;
+ UINT16 hci_flush_to;
+
+ /* Save the QOS settings we are using for transmit */
+ if (p_cfg->qos_present) {
+ p_ccb->our_cfg.qos_present = TRUE;
+ p_ccb->our_cfg.qos = p_cfg->qos;
+ }
+
+ if (p_cfg->fcr_present) {
+ /* Override FCR options if attempting streaming or basic */
+ if (p_cfg->fcr.mode == L2CAP_FCR_BASIC_MODE) {
+ memset(&p_cfg->fcr, 0, sizeof(tL2CAP_FCR_OPTS));
+ } else {
+ /* On BR/EDR, timer values are zero in config request */
+ /* On class 2 AMP, timer value in config request shall be non-0 processing time */
+ /* timer value in config response shall be greater than received processing time */
+ p_cfg->fcr.mon_tout = p_cfg->fcr.rtrans_tout = 0;
+
+ if (p_cfg->fcr.mode == L2CAP_FCR_STREAM_MODE) {
+ p_cfg->fcr.max_transmit = p_cfg->fcr.tx_win_sz = 0;
+ }
+ }
+
+ /* Set the threshold to send acks (may be updated in the cfg response) */
+ p_ccb->fcrb.max_held_acks = p_cfg->fcr.tx_win_sz / 3;
+
+ /* Include FCS option only if peer can handle it */
+ if (p_ccb->p_lcb->peer_ext_fea & L2CAP_EXTFEA_NO_CRC) {
+ /* FCS check can be bypassed if peer also desires to bypass */
+ if (p_cfg->fcs_present && p_cfg->fcs == L2CAP_CFG_FCS_BYPASS) {
+ p_ccb->bypass_fcs |= L2CAP_CFG_FCS_OUR;
+ }
+ } else {
+ p_cfg->fcs_present = FALSE;
+ }
+ } else {
+ p_cfg->fcr.mode = L2CAP_FCR_BASIC_MODE;
+ }
+
+ p_ccb->our_cfg.fcr.mode = p_cfg->fcr.mode;
+ p_ccb->our_cfg.fcr_present = p_cfg->fcr_present;
+
+ /* Check the flush timeout. If it is lower than the current one used */
+ /* then we need to adjust the flush timeout sent to the controller */
+ if (p_cfg->flush_to_present) {
+ if ((p_cfg->flush_to == 0) || (p_cfg->flush_to == L2CAP_NO_AUTOMATIC_FLUSH)) {
+ /* don't send invalid flush timeout */
+ /* SPEC: The sender of the Request shall specify its flush timeout value */
+ /* if it differs from the default value of 0xFFFF */
+ p_cfg->flush_to_present = FALSE;
+ } else {
+ p_ccb->our_cfg.flush_to = p_cfg->flush_to;
+ p_lcb = p_ccb->p_lcb;
+
+ if (p_cfg->flush_to < p_lcb->link_flush_tout) {
+ p_lcb->link_flush_tout = p_cfg->flush_to;
+
+ /* If the timeout is within range of HCI, set the flush timeout */
+ if (p_cfg->flush_to <= ((HCI_MAX_AUTO_FLUSH_TOUT * 5) / 8)) {
+ /* Convert flush timeout to 0.625 ms units, with round */
+ hci_flush_to = ((p_cfg->flush_to * 8) + 3) / 5;
+ btsnd_hcic_write_auto_flush_tout (p_lcb->handle, hci_flush_to);
+ }
+ }
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_process_our_cfg_rsp
+**
+** Description This function is called when we send the peer a "config response"
+** message. It extracts the configuration of interest and saves
+** it in the CCB.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_process_our_cfg_rsp (tL2C_CCB *p_ccb, tL2CAP_CFG_INFO *p_cfg)
+{
+ /* If peer wants QoS, we are allowed to change the values in a positive response */
+ if ( (p_cfg->qos_present) && (p_ccb->peer_cfg.qos_present) ) {
+ p_ccb->peer_cfg.qos = p_cfg->qos;
+ } else {
+ p_cfg->qos_present = FALSE;
+ }
+
+ l2c_fcr_adj_our_rsp_options (p_ccb, p_cfg);
+}
+#endif // (L2CAP_COC_INCLUDED == TRUE)
+
+
+/*******************************************************************************
+**
+** Function l2cu_device_reset
+**
+** Description This function is called when reset of the device is
+** completed. For all active connection simulate HCI_DISC
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_device_reset (void)
+{
+ list_node_t *p_node = NULL;
+ tL2C_LCB *p_lcb = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if ((p_lcb->in_use) && (p_lcb->handle != HCI_INVALID_HANDLE)) {
+ l2c_link_hci_disc_comp (p_lcb->handle, (UINT8) - 1);
+ }
+ }
+#if (BLE_INCLUDED == TRUE)
+ l2cb.is_ble_connecting = FALSE;
+#endif
+}
+
+/*******************************************************************************
+**
+** Function l2cu_create_conn
+**
+** Description This function initiates an acl connection via HCI
+**
+** Returns TRUE if successful, FALSE if gki get buffer fails.
+**
+*******************************************************************************/
+BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb, tBT_TRANSPORT transport)
+{
+#if BTM_SCO_INCLUDED == TRUE
+ BOOLEAN is_sco_active;
+#endif
+ list_node_t *p_node = NULL;
+ tL2C_LCB *p_lcb_cur = NULL;
+
+#if (BLE_INCLUDED == TRUE)
+ tBT_DEVICE_TYPE dev_type;
+ tBLE_ADDR_TYPE addr_type = p_lcb->open_addr_type;
+ if(addr_type == BLE_ADDR_UNKNOWN_TYPE) {
+ BTM_ReadDevInfo(p_lcb->remote_bd_addr, &dev_type, &addr_type);
+ }
+
+ if (transport == BT_TRANSPORT_LE) {
+ if (!controller_get_interface()->supports_ble()) {
+ return FALSE;
+ }
+ if(addr_type > BLE_ADDR_TYPE_MAX) {
+ addr_type = BLE_ADDR_PUBLIC;
+ }
+ p_lcb->ble_addr_type = addr_type;
+ p_lcb->transport = BT_TRANSPORT_LE;
+
+ return (l2cble_create_conn(p_lcb));
+ }
+#endif
+
+ /* If there is a connection where we perform as a slave, try to switch roles
+ for this connection */
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb_cur = list_node(p_node);
+ if (p_lcb_cur == p_lcb) {
+ continue;
+ }
+
+ if ((p_lcb_cur->in_use) && (p_lcb_cur->link_role == HCI_ROLE_SLAVE)) {
+
+#if BTM_SCO_INCLUDED == TRUE
+ /* The LMP_switch_req shall be sent only if the ACL logical transport
+ is in active mode, when encryption is disabled, and all synchronous
+ logical transports on the same physical link are disabled." */
+
+ /* Check if there is any SCO Active on this BD Address */
+ is_sco_active = btm_is_sco_active_by_bdaddr(p_lcb_cur->remote_bd_addr);
+
+ L2CAP_TRACE_API ("l2cu_create_conn - btm_is_sco_active_by_bdaddr() is_sco_active = %s", \
+ (is_sco_active == TRUE) ? "TRUE" : "FALSE");
+
+ if (is_sco_active == TRUE) {
+ continue; /* No Master Slave switch not allowed when SCO Active */
+ }
+#endif
+ /*4_1_TODO check if btm_cb.devcb.local_features to be used instead */
+ if (HCI_SWITCH_SUPPORTED(BTM_ReadLocalFeatures())) {
+ /* mark this lcb waiting for switch to be completed and
+ start switch on the other one */
+ p_lcb->link_state = LST_CONNECTING_WAIT_SWITCH;
+ p_lcb->link_role = HCI_ROLE_MASTER;
+
+ if (BTM_SwitchRole (p_lcb_cur->remote_bd_addr, HCI_ROLE_MASTER, NULL) == BTM_CMD_STARTED) {
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, L2CAP_LINK_ROLE_SWITCH_TOUT);
+ return (TRUE);
+ }
+ }
+ }
+ }
+
+ p_lcb->link_state = LST_CONNECTING;
+
+ return (l2cu_create_conn_after_switch (p_lcb));
+}
+
+/*******************************************************************************
+**
+** Function l2cu_get_num_hi_priority
+**
+** Description Gets the number of high priority channels.
+**
+** Returns
+**
+*******************************************************************************/
+UINT8 l2cu_get_num_hi_priority (void)
+{
+ UINT8 no_hi = 0;
+ list_node_t *p_node = NULL;
+ tL2C_LCB *p_lcb = NULL;
+
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if ((p_lcb->in_use) && (p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)) {
+ no_hi++;
+ }
+ }
+ return no_hi;
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_create_conn_after_switch
+**
+** Description This function initiates an acl connection via HCI
+** If switch required to create connection it is already done.
+**
+** Returns TRUE if successful, FALSE if osi get buffer fails.
+**
+*******************************************************************************/
+
+BOOLEAN l2cu_create_conn_after_switch (tL2C_LCB *p_lcb)
+{
+ UINT8 allow_switch = HCI_CR_CONN_ALLOW_SWITCH;
+ tBTM_INQ_INFO *p_inq_info;
+ UINT8 page_scan_rep_mode;
+ UINT8 page_scan_mode;
+ UINT16 clock_offset;
+ UINT8 *p_features;
+ UINT16 num_acl = BTM_GetNumAclLinks();
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (p_lcb->remote_bd_addr);
+ UINT8 no_hi_prio_chs = l2cu_get_num_hi_priority();
+
+ p_features = BTM_ReadLocalFeatures();
+
+ L2CAP_TRACE_DEBUG ("l2cu_create_conn_after_switch :%d num_acl:%d no_hi: %d is_bonding:%d",
+ l2cb.disallow_switch, num_acl, no_hi_prio_chs, p_lcb->is_bonding);
+ /* FW team says that we can participant in 4 piconets
+ * typically 3 piconet + 1 for scanning.
+ * We can enhance the code to count the number of piconets later. */
+ if ( ((!l2cb.disallow_switch && (num_acl < 3)) || (p_lcb->is_bonding && (no_hi_prio_chs == 0)))
+ && HCI_SWITCH_SUPPORTED(p_features)) {
+ allow_switch = HCI_CR_CONN_ALLOW_SWITCH;
+ } else {
+ allow_switch = HCI_CR_CONN_NOT_ALLOW_SWITCH;
+ }
+
+ p_lcb->link_state = LST_CONNECTING;
+
+ /* Check with the BT manager if details about remote device are known */
+ if ((p_inq_info = BTM_InqDbRead(p_lcb->remote_bd_addr)) != NULL) {
+ page_scan_rep_mode = p_inq_info->results.page_scan_rep_mode;
+ page_scan_mode = p_inq_info->results.page_scan_mode;
+ clock_offset = (UINT16)(p_inq_info->results.clock_offset);
+ } else {
+ /* No info known. Use default settings */
+ page_scan_rep_mode = HCI_PAGE_SCAN_REP_MODE_R2;
+ page_scan_mode = HCI_MANDATARY_PAGE_SCAN_MODE;
+
+ clock_offset = (p_dev_rec) ? p_dev_rec->clock_offset : 0;
+ }
+
+ if (!btsnd_hcic_create_conn (p_lcb->remote_bd_addr,
+ ( HCI_PKT_TYPES_MASK_DM1 | HCI_PKT_TYPES_MASK_DH1
+ | HCI_PKT_TYPES_MASK_DM3 | HCI_PKT_TYPES_MASK_DH3
+ | HCI_PKT_TYPES_MASK_DM5 | HCI_PKT_TYPES_MASK_DH5 ),
+ page_scan_rep_mode,
+ page_scan_mode,
+ clock_offset,
+ allow_switch))
+
+ {
+ L2CAP_TRACE_ERROR ("L2CAP - no buffer for l2cu_create_conn");
+ l2cu_release_lcb (p_lcb);
+ return (FALSE);
+ }
+
+ btm_acl_update_busy_level (BTM_BLI_PAGE_EVT);
+
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK,
+ L2CAP_LINK_CONNECT_TOUT);
+
+ return (TRUE);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_find_lcb_by_state
+**
+** Description Look through all active LCBs for a match based on the
+** LCB state.
+**
+** Returns pointer to first matched LCB, or NULL if no match
+**
+*******************************************************************************/
+tL2C_LCB *l2cu_find_lcb_by_state (tL2C_LINK_STATE state)
+{
+ list_node_t *p_node = NULL;
+ tL2C_LCB *p_lcb = NULL;
+
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if ((p_lcb->in_use) && (p_lcb->link_state == state)) {
+ return (p_lcb);
+ }
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_lcb_disconnecting
+**
+** Description On each active lcb, check if the lcb is in disconnecting
+** state, or if there are no ccb's on the lcb (implying
+ idle timeout is running), or if last ccb on the link
+ is in disconnecting state.
+**
+** Returns TRUE if any of above conditions met, FALSE otherwise
+**
+*******************************************************************************/
+BOOLEAN l2cu_lcb_disconnecting (void)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ BOOLEAN status = FALSE;
+ list_node_t *p_node = NULL;
+
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (p_lcb->in_use) {
+ /* no ccbs on lcb, or lcb is in disconnecting state */
+ if ((!p_lcb->ccb_queue.p_first_ccb) || (p_lcb->link_state == LST_DISCONNECTING)) {
+ status = TRUE;
+ break;
+ }
+ /* only one ccb left on lcb */
+ else if (p_lcb->ccb_queue.p_first_ccb == p_lcb->ccb_queue.p_last_ccb) {
+ p_ccb = p_lcb->ccb_queue.p_first_ccb;
+
+ if ((p_ccb->in_use) &&
+ ((p_ccb->chnl_state == CST_W4_L2CAP_DISCONNECT_RSP) ||
+ (p_ccb->chnl_state == CST_W4_L2CA_DISCONNECT_RSP))) {
+ status = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ return status;
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_set_acl_priority
+**
+** Description Sets the transmission priority for a channel.
+** (For initial implementation only two values are valid.
+** L2CAP_PRIORITY_NORMAL and L2CAP_PRIORITY_HIGH).
+**
+** Returns TRUE if a valid channel, else FALSE
+**
+*******************************************************************************/
+
+BOOLEAN l2cu_set_acl_priority (BD_ADDR bd_addr, UINT8 priority, BOOLEAN reset_after_rs)
+{
+ tL2C_LCB *p_lcb;
+ UINT8 *pp;
+ UINT8 command[HCI_BRCM_ACL_PRIORITY_PARAM_SIZE];
+ UINT8 vs_param;
+
+ //APPL_TRACE_EVENT("SET ACL PRIORITY %d", priority);
+
+ /* Find the link control block for the acl channel */
+ if ((p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_BR_EDR)) == NULL) {
+ L2CAP_TRACE_WARNING ("L2CAP - no LCB for L2CA_SetAclPriority");
+ return (FALSE);
+ }
+
+ if (BTM_IS_BRCM_CONTROLLER()) {
+ /* Called from above L2CAP through API; send VSC if changed */
+ if ((!reset_after_rs && (priority != p_lcb->acl_priority)) ||
+ /* Called because of a master/slave role switch; if high resend VSC */
+ ( reset_after_rs && p_lcb->acl_priority == L2CAP_PRIORITY_HIGH)) {
+ pp = command;
+
+ vs_param = (priority == L2CAP_PRIORITY_HIGH) ? HCI_BRCM_ACL_PRIORITY_HIGH : HCI_BRCM_ACL_PRIORITY_LOW;
+
+ UINT16_TO_STREAM (pp, p_lcb->handle);
+ UINT8_TO_STREAM (pp, vs_param);
+
+ BTM_VendorSpecificCommand (HCI_BRCM_SET_ACL_PRIORITY, HCI_BRCM_ACL_PRIORITY_PARAM_SIZE, command, NULL);
+
+ /* Adjust lmp buffer allocation for this channel if priority changed */
+ if (p_lcb->acl_priority != priority) {
+ p_lcb->acl_priority = priority;
+ l2c_link_adjust_allocation();
+ }
+ }
+ }
+ return (TRUE);
+}
+
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+/******************************************************************************
+**
+** Function l2cu_set_non_flushable_pbf
+**
+** Description set L2CAP_PKT_START_NON_FLUSHABLE if controller supoorts
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_set_non_flushable_pbf (BOOLEAN is_supported)
+{
+ if (is_supported) {
+ l2cb.non_flushable_pbf = (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT);
+ } else {
+ l2cb.non_flushable_pbf = (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT);
+ }
+}
+#endif
+
+/*******************************************************************************
+**
+** Function l2cu_resubmit_pending_sec_req
+**
+** Description This function is called when required security procedures
+** are completed and any pending requests can be re-submitted.
+**
+** Returns void
+**
+*******************************************************************************/
+#if (CLASSIC_BT_INCLUDED == TRUE)
+void l2cu_resubmit_pending_sec_req (BD_ADDR p_bda)
+{
+ tL2C_LCB *p_lcb;
+ tL2C_CCB *p_ccb;
+ tL2C_CCB *p_next_ccb;
+ L2CAP_TRACE_DEBUG ("l2cu_resubmit_pending_sec_req p_bda: %p", p_bda);
+ list_node_t *p_node = NULL;
+
+ /* If we are called with a BDA, only resubmit for that BDA */
+ if (p_bda) {
+ p_lcb = l2cu_find_lcb_by_bd_addr (p_bda, BT_TRANSPORT_BR_EDR);
+ /* If we don't have one, this is an error */
+ if (p_lcb) {
+ /* For all channels, send the event through their FSMs */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb) {
+ p_next_ccb = p_ccb->p_next_ccb;
+ l2c_csm_execute (p_ccb, L2CEVT_SEC_RE_SEND_CMD, NULL);
+ }
+ } else {
+ L2CAP_TRACE_WARNING ("l2cu_resubmit_pending_sec_req - unknown BD_ADDR");
+ }
+ } else {
+ /* No BDA pasesed in, so check all links */
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if (p_lcb->in_use) {
+ /* For all channels, send the event through their FSMs */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_next_ccb) {
+ p_next_ccb = p_ccb->p_next_ccb;
+ l2c_csm_execute (p_ccb, L2CEVT_SEC_RE_SEND_CMD, NULL);
+ }
+ }
+ }
+ }
+}
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+#if L2CAP_CONFORMANCE_TESTING == TRUE
+/*******************************************************************************
+**
+** Function l2cu_set_info_rsp_mask
+**
+** Description This function allows the script wrapper to change the
+** info resp mask for conformance testing.
+**
+** Returns pointer to CCB, or NULL if none
+**
+*******************************************************************************/
+void l2cu_set_info_rsp_mask (UINT32 mask)
+{
+ l2cb.test_info_resp = mask;
+}
+#endif /* L2CAP_CONFORMANCE_TESTING */
+
+/*******************************************************************************
+**
+** Function l2cu_adjust_out_mps
+**
+** Description Sets our MPS based on current controller capabilities
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_adjust_out_mps (tL2C_CCB *p_ccb)
+{
+ UINT16 packet_size;
+
+ /* on the tx side MTU is selected based on packet size of the controller */
+ packet_size = btm_get_max_packet_size (p_ccb->p_lcb->remote_bd_addr);
+
+ if (packet_size <= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN)) {
+ /* something is very wrong */
+ L2CAP_TRACE_DEBUG ("l2cu_adjust_out_mps bad packet size: %u will use MPS: %u", packet_size, p_ccb->peer_cfg.fcr.mps);
+ p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps;
+ } else {
+ packet_size -= (L2CAP_PKT_OVERHEAD + L2CAP_FCR_OVERHEAD + L2CAP_SDU_LEN_OVERHEAD + L2CAP_FCS_LEN);
+
+ /* We try to negotiate MTU that each packet can be split into whole
+ number of max packets. For example if link is 1.2 max packet size is 339 bytes.
+ At first calculate how many whole packets it is. MAX L2CAP is 1691 + 4 overhead.
+ 1695, that will be 5 Dh5 packets. Now maximum L2CAP packet is
+ 5 * 339 = 1695. Minus 4 bytes L2CAP header 1691.
+
+ For EDR 2.0 packet size is 1027. So we better send RFCOMM packet as 1 3DH5 packet
+ 1 * 1027 = 1027. Minus 4 bytes L2CAP header 1023. */
+ if (p_ccb->peer_cfg.fcr.mps >= packet_size) {
+ p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps / packet_size * packet_size;
+ } else {
+ p_ccb->tx_mps = p_ccb->peer_cfg.fcr.mps;
+ }
+
+ L2CAP_TRACE_DEBUG ("l2cu_adjust_out_mps use %d Based on peer_cfg.fcr.mps: %u packet_size: %u",
+ p_ccb->tx_mps, p_ccb->peer_cfg.fcr.mps, packet_size);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function l2cu_initialize_fixed_ccb
+**
+** Description Initialize a fixed channel's CCB
+**
+** Returns TRUE or FALSE
+**
+*******************************************************************************/
+BOOLEAN l2cu_initialize_fixed_ccb (tL2C_LCB *p_lcb, UINT16 fixed_cid, tL2CAP_FCR_OPTS *p_fcr)
+{
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ tL2C_CCB *p_ccb;
+ /* If we already have a CCB, then simply return */
+ if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] != NULL) {
+ return (TRUE);
+ }
+
+ if ((p_ccb = l2cu_allocate_ccb (NULL, 0)) == NULL) {
+ return (FALSE);
+ }
+
+ btu_stop_timer(&p_lcb->timer_entry);
+
+ /* Set CID for the connection */
+ p_ccb->local_cid = fixed_cid;
+ p_ccb->remote_cid = fixed_cid;
+
+ p_ccb->is_flushable = FALSE;
+
+ p_ccb->timer_entry.param = (TIMER_PARAM_TYPE)p_ccb;
+
+
+ if (p_fcr) {
+ /* Set the FCR parameters. For now, we will use default pools */
+ p_ccb->our_cfg.fcr = p_ccb->peer_cfg.fcr = *p_fcr;
+
+ p_ccb->ertm_info.fcr_rx_buf_size = L2CAP_FCR_RX_BUF_SIZE;
+ p_ccb->ertm_info.fcr_tx_buf_size = L2CAP_FCR_TX_BUF_SIZE;
+ p_ccb->ertm_info.user_rx_buf_size = L2CAP_USER_RX_BUF_SIZE;
+ p_ccb->ertm_info.user_tx_buf_size = L2CAP_USER_TX_BUF_SIZE;
+
+ p_ccb->fcrb.max_held_acks = p_fcr->tx_win_sz / 3;
+ }
+
+ /* Link ccb to lcb and lcb to ccb */
+ p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = p_ccb;
+ p_ccb->p_lcb = p_lcb;
+
+ /* There is no configuration, so if the link is up, the channel is up */
+ if (p_lcb->link_state == LST_CONNECTED) {
+ p_ccb->chnl_state = CST_OPEN;
+ }
+
+ /* Set the default idle timeout value to use */
+ p_ccb->fixed_chnl_idle_tout = l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].default_idle_tout;
+#endif
+ return (TRUE);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_no_dynamic_ccbs
+**
+** Description Handles the case when there are no more dynamic CCBs. If there
+** are any fixed CCBs, start the longest of the fixed CCB timeouts,
+** otherwise start the default link idle timeout or disconnect.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb)
+{
+#if (SMP_INCLUDED == TRUE)
+ tBTM_STATUS rc;
+#endif ///SMP_INCLUDED == TRUE
+ UINT16 timeout = p_lcb->idle_timeout;
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ int xx;
+
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) {
+ if ( (p_lcb->p_fixed_ccbs[xx] != NULL) && (p_lcb->p_fixed_ccbs[xx]->fixed_chnl_idle_tout > timeout) ) {
+ timeout = p_lcb->p_fixed_ccbs[xx]->fixed_chnl_idle_tout;
+ }
+ }
+#endif
+
+ /* If the link is pairing, do not mess with the timeouts */
+ if (p_lcb->is_bonding) {
+ return;
+ }
+
+ if (timeout == 0) {
+ L2CAP_TRACE_DEBUG ("l2cu_no_dynamic_ccbs() IDLE timer 0, disconnecting link");
+#if (SMP_INCLUDED == TRUE)
+ rc = btm_sec_disconnect (p_lcb->handle, HCI_ERR_PEER_USER);
+ if (rc == BTM_CMD_STARTED) {
+ l2cu_process_fixed_disc_cback(p_lcb);
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ } else if (rc == BTM_SUCCESS) {
+ l2cu_process_fixed_disc_cback(p_lcb);
+ /* BTM SEC will make sure that link is release (probably after pairing is done) */
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = 0xFFFF;
+ } else if ( (p_lcb->is_bonding)
+ && (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)) ) {
+ l2cu_process_fixed_disc_cback(p_lcb);
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ } else {
+ /* probably no buffer to send disconnect */
+ timeout = BT_1SEC_TIMEOUT;
+ }
+#else
+ if (btsnd_hcic_disconnect (p_lcb->handle, HCI_ERR_PEER_USER)) {
+ l2cu_process_fixed_disc_cback(p_lcb);
+ p_lcb->link_state = LST_DISCONNECTING;
+ timeout = L2CAP_LINK_DISCONNECT_TOUT;
+ } else {
+ timeout = BT_1SEC_TIMEOUT;
+ }
+#endif ///SMP_INCLUDED == TRUE
+
+ }
+
+ if (timeout != 0xFFFF) {
+ L2CAP_TRACE_DEBUG ("l2cu_no_dynamic_ccbs() starting IDLE timeout: %d", timeout);
+ btu_start_timer (&p_lcb->timer_entry, BTU_TTYPE_L2CAP_LINK, timeout);
+ } else {
+ btu_stop_timer(&p_lcb->timer_entry);
+ }
+}
+
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+/*******************************************************************************
+**
+** Function l2cu_process_fixed_chnl_resp
+**
+** Description handle a fixed channel response (or lack thereof)
+** if the link failed, or a fixed channel response was
+** not received, the bitfield is all zeros.
+**
+*******************************************************************************/
+void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb)
+{
+ L2CAP_TRACE_DEBUG("%s",__func__);
+#if (BLE_INCLUDED == TRUE)
+ if (p_lcb->transport == BT_TRANSPORT_BR_EDR) {
+ /* ignore all not assigned BR/EDR channels */
+ p_lcb->peer_chnl_mask[0] &= (L2CAP_FIXED_CHNL_SIG_BIT | \
+ L2CAP_FIXED_CHNL_CNCTLESS_BIT | \
+ L2CAP_FIXED_CHNL_SMP_BR_BIT);
+ } else {
+ p_lcb->peer_chnl_mask[0] = l2cb.l2c_ble_fixed_chnls_mask;
+ }
+#endif
+
+ /* Tell all registered fixed channels about the connection */
+ for (int xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) {
+#if BLE_INCLUDED == TRUE
+ /* skip sending LE fix channel callbacks on BR/EDR links */
+ if (p_lcb->transport == BT_TRANSPORT_BR_EDR &&
+ xx + L2CAP_FIRST_FIXED_CHNL >= L2CAP_ATT_CID &&
+ xx + L2CAP_FIRST_FIXED_CHNL <= L2CAP_SMP_CID) {
+ continue;
+ }
+#endif
+ if (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) {
+ if (p_lcb->peer_chnl_mask[(xx + L2CAP_FIRST_FIXED_CHNL) / 8]
+ & (1 << ((xx + L2CAP_FIRST_FIXED_CHNL) % 8))) {
+ if (p_lcb->p_fixed_ccbs[xx]) {
+ p_lcb->p_fixed_ccbs[xx]->chnl_state = CST_OPEN;
+ }
+#if BLE_INCLUDED == TRUE
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, TRUE, 0, p_lcb->transport);
+#else
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, TRUE, 0, BT_TRANSPORT_BR_EDR);
+#endif
+ } else {
+#if BLE_INCLUDED == TRUE
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
+#else
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
+#endif
+
+ if (p_lcb->p_fixed_ccbs[xx]) {
+ l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]);
+ p_lcb->p_fixed_ccbs[xx] = NULL;
+ }
+ }
+ }
+ }
+}
+#endif
+
+
+/*******************************************************************************
+**
+** Function l2cu_process_fixed_disc_cback
+**
+** Description send l2cap fixed channel disconnection callback to application
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_process_fixed_disc_cback (tL2C_LCB *p_lcb)
+{
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+
+ /* Select peer channels mask to use depending on transport */
+ UINT8 peer_channel_mask = p_lcb->peer_chnl_mask[0];
+
+ // For LE, reset the stored peer channel mask
+ if (p_lcb->transport == BT_TRANSPORT_LE) {
+ p_lcb->peer_chnl_mask[0] = 0;
+ }
+
+ for (int xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) {
+ if (p_lcb->p_fixed_ccbs[xx]) {
+ if (p_lcb->p_fixed_ccbs[xx] != p_lcb->p_pending_ccb) {
+ tL2C_CCB *p_l2c_chnl_ctrl_block;
+ p_l2c_chnl_ctrl_block = p_lcb->p_fixed_ccbs[xx];
+ p_lcb->p_fixed_ccbs[xx] = NULL;
+ l2cu_release_ccb(p_l2c_chnl_ctrl_block);
+#if BLE_INCLUDED == TRUE
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
+#else
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
+#endif
+ }
+ } else if ( (peer_channel_mask & (1 << (xx + L2CAP_FIRST_FIXED_CHNL)))
+ && (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) ) {
+#if BLE_INCLUDED == TRUE
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
+#else
+ (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
+ p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, BT_TRANSPORT_BR_EDR);
+#endif
+ }
+ }
+#endif
+}
+
+#if (BLE_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function l2cu_send_peer_ble_par_req
+**
+** Description Build and send a BLE parameter update request message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_ble_par_req (tL2C_LCB *p_lcb, UINT16 min_int, UINT16 max_int,
+ UINT16 latency, UINT16 timeout)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ /* Create an identifier for this packet */
+ p_lcb->id++;
+ l2cu_adj_id (p_lcb, L2CAP_ADJ_ID);
+
+ if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_UPD_REQ_LEN,
+ L2CAP_CMD_BLE_UPDATE_REQ, p_lcb->id)) == NULL ) {
+ L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_par_req - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, min_int);
+ UINT16_TO_STREAM (p, max_int);
+ UINT16_TO_STREAM (p, latency);
+ UINT16_TO_STREAM (p, timeout);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_ble_par_rsp
+**
+** Description Build and send a BLE parameter update response message
+** to the peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_ble_par_rsp (tL2C_LCB *p_lcb, UINT16 reason, UINT8 rem_id)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_UPD_RSP_LEN,
+ L2CAP_CMD_BLE_UPDATE_RSP, rem_id)) == NULL ) {
+ L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_par_rsp - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, reason);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_ble_credit_based_conn_req
+**
+** Description Build and send a BLE packet to establish LE connection oriented
+** L2CAP channel.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_ble_credit_based_conn_req (tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+ tL2C_LCB *p_lcb = NULL;
+ UINT16 mtu;
+ UINT16 mps;
+ UINT16 initial_credit;
+
+ if (!p_ccb) {
+ return;
+ }
+ p_lcb = p_ccb->p_lcb;
+
+ /* Create an identifier for this packet */
+ p_ccb->p_lcb->id++;
+ l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID);
+
+ p_ccb->local_id = p_ccb->p_lcb->id;
+
+ if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ_LEN,
+ L2CAP_CMD_BLE_CREDIT_BASED_CONN_REQ, p_lcb->id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_credit_based_conn_req - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ mtu = p_ccb->local_conn_cfg.mtu;
+ mps = p_ccb->local_conn_cfg.mps;
+ initial_credit = p_ccb->local_conn_cfg.credits;
+
+ L2CAP_TRACE_DEBUG ("l2cu_send_peer_ble_credit_based_conn_req PSM:0x%04x local_cid:%d\
+ mtu:%d mps:%d initial_credit:%d", p_ccb->p_rcb->real_psm,\
+ p_ccb->local_cid, mtu, mps, initial_credit);
+
+ UINT16_TO_STREAM (p, p_ccb->p_rcb->real_psm);
+ UINT16_TO_STREAM (p, p_ccb->local_cid);
+ UINT16_TO_STREAM (p, mtu);
+ UINT16_TO_STREAM (p, mps);
+ UINT16_TO_STREAM (p, initial_credit);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_reject_ble_connection
+**
+** Description Build and send an L2CAP "Credit based connection res" message
+** to the peer. This function is called for non-success cases.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_reject_ble_connection (tL2C_LCB *p_lcb, UINT8 rem_id, UINT16 result)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ if ((p_buf = l2cu_build_header(p_lcb, L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES_LEN,
+ L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES, rem_id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING ("l2cu_reject_ble_connection - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, 0); /* Local CID of 0 */
+ UINT16_TO_STREAM (p, 0); /* MTU */
+ UINT16_TO_STREAM (p, 0); /* MPS */
+ UINT16_TO_STREAM (p, 0); /* initial credit */
+ UINT16_TO_STREAM (p, result);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_ble_credit_based_conn_res
+**
+** Description Build and send an L2CAP "Credit based connection res" message
+** to the peer. This function is called in case of success.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_ble_credit_based_conn_res (tL2C_CCB *p_ccb, UINT16 result)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+
+ L2CAP_TRACE_DEBUG ("l2cu_send_peer_ble_credit_based_conn_res");
+ if ((p_buf = l2cu_build_header(p_ccb->p_lcb, L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES_LEN,
+ L2CAP_CMD_BLE_CREDIT_BASED_CONN_RES, p_ccb->remote_id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_credit_based_conn_res - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->local_cid); /* Local CID */
+ UINT16_TO_STREAM (p, p_ccb->local_conn_cfg.mtu); /* MTU */
+ UINT16_TO_STREAM (p, p_ccb->local_conn_cfg.mps); /* MPS */
+ UINT16_TO_STREAM (p, p_ccb->local_conn_cfg.credits); /* initial credit */
+ UINT16_TO_STREAM (p, result);
+
+ l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_ble_flow_control_credit
+**
+** Description Build and send a BLE packet to give credits to peer device
+** for LE connection oriented L2CAP channel.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_ble_flow_control_credit(tL2C_CCB *p_ccb, UINT16 credit_value)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+ tL2C_LCB *p_lcb = NULL;
+
+ if (!p_ccb) {
+ return;
+ }
+ p_lcb = p_ccb->p_lcb;
+
+ /* Create an identifier for this packet */
+ p_ccb->p_lcb->id++;
+ l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID);
+
+ p_ccb->local_id = p_ccb->p_lcb->id;
+
+ if ((p_buf = l2cu_build_header (p_lcb, L2CAP_CMD_BLE_FLOW_CTRL_CREDIT_LEN,
+ L2CAP_CMD_BLE_FLOW_CTRL_CREDIT, p_lcb->id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_credit_based_conn_req - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->local_cid);
+ UINT16_TO_STREAM (p, credit_value);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_send_peer_ble_credit_based_conn_req
+**
+** Description Build and send a BLE packet to disconnect LE connection oriented
+** L2CAP channel.
+**
+** Returns void
+**
+*******************************************************************************/
+void l2cu_send_peer_ble_credit_based_disconn_req(tL2C_CCB *p_ccb)
+{
+ BT_HDR *p_buf;
+ UINT8 *p;
+ tL2C_LCB *p_lcb = NULL;
+ L2CAP_TRACE_DEBUG ("%s",__func__);
+
+ if (!p_ccb) {
+ return;
+ }
+ p_lcb = p_ccb->p_lcb;
+
+ /* Create an identifier for this packet */
+ p_ccb->p_lcb->id++;
+ l2cu_adj_id(p_ccb->p_lcb, L2CAP_ADJ_ID);
+
+ p_ccb->local_id = p_ccb->p_lcb->id;
+ if ((p_buf = l2cu_build_header (p_lcb, L2CAP_DISC_REQ_LEN,
+ L2CAP_CMD_DISC_REQ, p_lcb->id)) == NULL )
+ {
+ L2CAP_TRACE_WARNING ("l2cu_send_peer_ble_credit_based_disconn_req - no buffer");
+ return;
+ }
+
+ p = (UINT8 *)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE +
+ L2CAP_PKT_OVERHEAD + L2CAP_CMD_OVERHEAD;
+
+ UINT16_TO_STREAM (p, p_ccb->remote_cid);
+ UINT16_TO_STREAM (p,p_ccb->local_cid);
+
+ l2c_link_check_send_pkts (p_lcb, NULL, p_buf);
+}
+
+#endif /* BLE_INCLUDED == TRUE */
+
+/*******************************************************************************
+** Functions used by both Full and Light Stack
+********************************************************************************/
+
+/*******************************************************************************
+**
+** Function l2cu_find_lcb_by_handle
+**
+** Description Look through all active LCBs for a match based on the
+** HCI handle.
+**
+** Returns pointer to matched LCB, or NULL if no match
+**
+*******************************************************************************/
+tL2C_LCB *l2cu_find_lcb_by_handle (UINT16 handle)
+{
+ list_node_t *p_node = NULL;
+ tL2C_LCB *p_lcb = NULL;
+ for (p_node = list_begin(l2cb.p_lcb_pool); p_node; p_node = list_next(p_node)) {
+ p_lcb = list_node(p_node);
+ if ((p_lcb->in_use) && (p_lcb->handle == handle)) {
+ return (p_lcb);
+ }
+ }
+
+ /* If here, no match found */
+ return (NULL);
+}
+
+/*******************************************************************************
+**
+** Function l2cu_find_ccb_by_cid
+**
+** Description Look through all active CCBs on a link for a match based
+** on the local CID. If passed the link pointer is NULL, all
+** active links are searched.
+**
+** Returns pointer to matched CCB, or NULL if no match
+**
+*******************************************************************************/
+bool l2cu_find_ccb_in_list(void *p_ccb_node, void *p_local_cid)
+{
+ tL2C_CCB *p_ccb = (tL2C_CCB *)p_ccb_node;
+ uint8_t local_cid = *((uint8_t *)p_local_cid);
+
+ if (p_ccb->local_cid == local_cid && p_ccb->in_use) {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid)
+{
+ tL2C_CCB *p_ccb = NULL;
+#if (L2CAP_UCD_INCLUDED == FALSE)
+ if (local_cid < L2CAP_BASE_APPL_CID) {
+ return NULL;
+ }
+#endif //(L2CAP_UCD_INCLUDED == FALSE)
+ list_node_t *p_node = NULL;
+
+ p_node = (list_foreach(l2cb.p_ccb_pool, l2cu_find_ccb_in_list, &local_cid));
+ if (p_node) {
+ p_ccb = (tL2C_CCB *)list_node(p_node);
+
+ if (p_lcb && p_lcb != p_ccb->p_lcb) {
+ p_ccb = NULL;
+ }
+ }
+
+ return (p_ccb);
+}
+
+tL2C_CCB *l2cu_find_free_ccb (void)
+{
+ tL2C_CCB *p_ccb = NULL;
+
+ list_node_t *p_node = NULL;
+
+ for (p_node = list_begin(l2cb.p_ccb_pool); p_node; p_node = list_next(p_node))
+ {
+ p_ccb = list_node(p_node);
+ if(p_ccb && !p_ccb->in_use ) {
+ return p_ccb;
+ }
+ }
+
+ return (NULL);
+}
+
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE && CLASSIC_BT_INCLUDED == TRUE)
+
+/******************************************************************************
+**
+** Function l2cu_get_next_channel_in_rr
+**
+** Description get the next channel to send on a link. It also adjusts the
+** CCB queue to do a basic priority and round-robin scheduling.
+**
+** Returns pointer to CCB or NULL
+**
+*******************************************************************************/
+static tL2C_CCB *l2cu_get_next_channel_in_rr(tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_serve_ccb = NULL;
+ tL2C_CCB *p_ccb;
+
+ int i, j;
+
+ /* scan all of priority until finding a channel to serve */
+ for ( i = 0; (i < L2CAP_NUM_CHNL_PRIORITY) && (!p_serve_ccb); i++ ) {
+ /* scan all channel within serving priority group until finding a channel to serve */
+ for ( j = 0; (j < p_lcb->rr_serv[p_lcb->rr_pri].num_ccb) && (!p_serve_ccb); j++) {
+ /* scaning from next serving channel */
+ p_ccb = p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb;
+
+ if (!p_ccb) {
+ L2CAP_TRACE_ERROR("p_serve_ccb is NULL, rr_pri=%d", p_lcb->rr_pri);
+ return NULL;
+ }
+
+ L2CAP_TRACE_DEBUG("RR scan pri=%d, lcid=0x%04x, q_cout=%d",
+ p_ccb->ccb_priority, p_ccb->local_cid,
+ fixed_queue_length(p_ccb->xmit_hold_q));
+
+ /* store the next serving channel */
+ /* this channel is the last channel of its priority group */
+ if (( p_ccb->p_next_ccb == NULL )
+ || ( p_ccb->p_next_ccb->ccb_priority != p_ccb->ccb_priority )) {
+ /* next serving channel is set to the first channel in the group */
+ p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb = p_lcb->rr_serv[p_lcb->rr_pri].p_first_ccb;
+ } else {
+ /* next serving channel is set to the next channel in the group */
+ p_lcb->rr_serv[p_lcb->rr_pri].p_serve_ccb = p_ccb->p_next_ccb;
+ }
+
+ if (p_ccb->chnl_state != CST_OPEN) {
+ continue;
+ }
+
+ /* eL2CAP option in use */
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) {
+ if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy) {
+ continue;
+ }
+
+ if (fixed_queue_is_empty(p_ccb->fcrb.retrans_q)) {
+ if (fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
+ continue;
+ }
+
+
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ /* If in eRTM mode, check for window closure */
+ if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) ) {
+ continue;
+ }
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+ }
+ } else {
+ if (fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
+ continue;
+ }
+ }
+
+ /* found a channel to serve */
+ p_serve_ccb = p_ccb;
+ /* decrease quota of its priority group */
+ p_lcb->rr_serv[p_lcb->rr_pri].quota--;
+ }
+
+ /* if there is no more quota of the priority group or no channel to have data to send */
+ if ((p_lcb->rr_serv[p_lcb->rr_pri].quota == 0) || (!p_serve_ccb)) {
+ /* serve next priority group */
+ p_lcb->rr_pri = (p_lcb->rr_pri + 1) % L2CAP_NUM_CHNL_PRIORITY;
+ /* initialize its quota */
+ p_lcb->rr_serv[p_lcb->rr_pri].quota = L2CAP_GET_PRIORITY_QUOTA(p_lcb->rr_pri);
+ }
+ }
+
+ if (p_serve_ccb) {
+ L2CAP_TRACE_DEBUG("RR service pri=%d, quota=%d, lcid=0x%04x",
+ p_serve_ccb->ccb_priority,
+ p_lcb->rr_serv[p_serve_ccb->ccb_priority].quota,
+ p_serve_ccb->local_cid );
+ }
+
+ return p_serve_ccb;
+}
+
+#else /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */
+
+/******************************************************************************
+**
+** Function l2cu_get_next_channel
+**
+** Description get the next channel to send on a link bassed on priority
+** scheduling.
+**
+** Returns pointer to CCB or NULL
+**
+*******************************************************************************/
+#if (CLASSIC_BT_INCLUDED == TRUE)
+static tL2C_CCB *l2cu_get_next_channel(tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_ccb;
+
+ /* Get the first CCB with data to send.
+ */
+ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) {
+ if (p_ccb->chnl_state != CST_OPEN) {
+ continue;
+ }
+
+ if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy) {
+ continue;
+ }
+
+ if (!fixed_queue_is_empty(p_ccb->fcrb.retrans_q))
+ return p_ccb;
+ }
+
+ if (fixed_queue_is_empty(p_ccb->xmit_hold_q))
+ continue;
+ }
+
+ /* If in eRTM mode, check for window closure */
+ if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) ) {
+ continue;
+ }
+
+ /* If here, we found someone */
+ return p_ccb;
+ }
+
+ return NULL;
+}
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+#endif /* (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE) */
+
+/******************************************************************************
+**
+** Function l2cu_get_next_buffer_to_send
+**
+** Description get the next buffer to send on a link. It also adjusts the
+** CCB queue to do a basic priority and round-robin scheduling.
+**
+** Returns pointer to buffer or NULL
+**
+*******************************************************************************/
+BT_HDR *l2cu_get_next_buffer_to_send (tL2C_LCB *p_lcb)
+{
+ tL2C_CCB *p_ccb;
+ BT_HDR *p_buf = NULL;
+
+ /* Highest priority are fixed channels */
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ int xx;
+
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) {
+ if ((p_ccb = p_lcb->p_fixed_ccbs[xx]) == NULL) {
+ continue;
+ }
+
+ /* eL2CAP option in use */
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) {
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ if (p_ccb->fcrb.wait_ack || p_ccb->fcrb.remote_busy) {
+ continue;
+ }
+
+ /* No more checks needed if sending from the reatransmit queue */
+ if (fixed_queue_is_empty(p_ccb->fcrb.retrans_q))
+ {
+ if (fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
+ continue;
+ }
+ /* If in eRTM mode, check for window closure */
+ if ( (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE) && (l2c_fcr_is_flow_controlled (p_ccb)) ) {
+ continue;
+ }
+ }
+ if ((p_buf = l2c_fcr_get_next_xmit_sdu_seg(p_ccb, 0)) != NULL) {
+ l2cu_check_channel_congestion (p_ccb);
+ l2cu_set_acl_hci_header (p_buf, p_ccb);
+ return (p_buf);
+ }
+#else
+ continue;
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+ } else {
+ if (!fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
+ p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0);
+ if (NULL == p_buf) {
+ L2CAP_TRACE_ERROR("l2cu_get_buffer_to_send: No data to be sent");
+ return (NULL);
+ }
+ l2cu_check_channel_congestion (p_ccb);
+ l2cu_set_acl_hci_header (p_buf, p_ccb);
+ /* send tx complete */
+ if (l2cb.fixed_reg[xx].pL2CA_FixedTxComplete_Cb) {
+ (*l2cb.fixed_reg[xx].pL2CA_FixedTxComplete_Cb)(p_ccb->local_cid, 1);
+ }
+ return (p_buf);
+ }
+ }
+ }
+#endif
+#if (CLASSIC_BT_INCLUDED == TRUE)
+#if (L2CAP_ROUND_ROBIN_CHANNEL_SERVICE == TRUE)
+ /* get next serving channel in round-robin */
+ p_ccb = l2cu_get_next_channel_in_rr( p_lcb );
+#else
+ p_ccb = l2cu_get_next_channel( p_lcb );
+#endif
+
+ /* Return if no buffer */
+ if (p_ccb == NULL) {
+ return (NULL);
+ }
+
+ if (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE) {
+
+ if ((p_buf = l2c_fcr_get_next_xmit_sdu_seg(p_ccb, 0)) == NULL) {
+ return (NULL);
+ }
+
+ } else {
+ p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_hold_q, 0);
+ if (NULL == p_buf) {
+ L2CAP_TRACE_ERROR("l2cu_get_buffer_to_send() #2: No data to be sent");
+ return (NULL);
+ }
+ }
+
+ if ( p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_TxComplete_Cb && (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_ERTM_MODE) ) {
+ (*p_ccb->p_rcb->api.pL2CA_TxComplete_Cb)(p_ccb->local_cid, 1);
+ }
+
+
+ l2cu_check_channel_congestion (p_ccb);
+
+ l2cu_set_acl_hci_header (p_buf, p_ccb);
+#endif ///CLASSIC_BT_INCLUDED == TRUE
+
+ return (p_buf);
+}
+
+/******************************************************************************
+**
+** Function l2cu_set_acl_hci_header
+**
+** Description Set HCI handle for ACL packet
+**
+** Returns None
+**
+*******************************************************************************/
+void l2cu_set_acl_hci_header (BT_HDR *p_buf, tL2C_CCB *p_ccb)
+{
+ UINT8 *p;
+
+ /* Set the pointer to the beginning of the data minus 4 bytes for the packet header */
+ p = (UINT8 *)(p_buf + 1) + p_buf->offset - HCI_DATA_PREAMBLE_SIZE;
+
+#if (BLE_INCLUDED == TRUE)
+ if (p_ccb->p_lcb->transport == BT_TRANSPORT_LE) {
+ UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | (L2CAP_PKT_START_NON_FLUSHABLE << L2CAP_PKT_TYPE_SHIFT));
+
+ uint16_t acl_data_size = controller_get_interface()->get_acl_data_size_ble();
+ /* The HCI transport will segment the buffers. */
+ if (p_buf->len > acl_data_size) {
+ UINT16_TO_STREAM (p, acl_data_size);
+ } else {
+ UINT16_TO_STREAM (p, p_buf->len);
+ }
+ } /* (BLE_INCLUDED == TRUE) */
+ else
+#endif
+ {
+#if (L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE)
+ if ( (((p_buf->layer_specific & L2CAP_FLUSHABLE_MASK) == L2CAP_FLUSHABLE_CH_BASED) && (p_ccb->is_flushable))
+ || ((p_buf->layer_specific & L2CAP_FLUSHABLE_MASK) == L2CAP_FLUSHABLE_PKT) ) {
+ UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT));
+ } else {
+ UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | l2cb.non_flushable_pbf);
+ }
+#else
+ UINT16_TO_STREAM (p, p_ccb->p_lcb->handle | (L2CAP_PKT_START << L2CAP_PKT_TYPE_SHIFT));
+#endif
+
+ uint16_t acl_data_size = controller_get_interface()->get_acl_data_size_classic();
+ /* The HCI transport will segment the buffers. */
+ if (p_buf->len > acl_data_size) {
+ UINT16_TO_STREAM (p, acl_data_size);
+ } else {
+ UINT16_TO_STREAM (p, p_buf->len);
+ }
+ }
+ p_buf->offset -= HCI_DATA_PREAMBLE_SIZE;
+ p_buf->len += HCI_DATA_PREAMBLE_SIZE;
+}
+
+/******************************************************************************
+**
+** Function l2cu_check_channel_congestion
+**
+** Description check if any change in congestion status
+**
+** Returns None
+**
+*******************************************************************************/
+void l2cu_check_channel_congestion (tL2C_CCB *p_ccb)
+{
+ size_t q_count = fixed_queue_length(p_ccb->xmit_hold_q);
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ size_t q_waiting_ack_count = fixed_queue_length(p_ccb->fcrb.waiting_for_ack_q);
+#endif
+
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ if ( p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) {
+ q_count += fixed_queue_length(p_ccb->p_lcb->ucd_out_sec_pending_q);
+ }
+#endif
+ /* If the CCB queue limit is subject to a quota, check for congestion */
+ /* if this channel has outgoing traffic */
+ if (p_ccb->buff_quota != 0) {
+ /* If this channel was congested */
+ if ( p_ccb->cong_sent ) {
+ /* If the channel is not congested now, tell the app */
+ if (q_count <= (p_ccb->buff_quota / 2)
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ && (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_BASIC_MODE || q_waiting_ack_count < p_ccb->our_cfg.fcr.tx_win_sz)
+#endif
+ ) {
+ p_ccb->cong_sent = FALSE;
+ if (p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb) {
+ L2CAP_TRACE_DEBUG ("L2CAP - Calling CongestionStatus_Cb (FALSE), CID: 0x%04x xmit_hold_q.count: %u buff_quota: %u",
+ p_ccb->local_cid, q_count, p_ccb->buff_quota);
+
+ /* Prevent recursive calling */
+ l2cb.is_cong_cback_context = TRUE;
+ (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, FALSE);
+ l2cb.is_cong_cback_context = FALSE;
+ }
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ else if ( p_ccb->p_rcb && p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) {
+ if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) {
+ L2CAP_TRACE_DEBUG ("L2CAP - Calling UCD CongestionStatus_Cb (FALSE), SecPendingQ:%u,XmitQ:%u,Quota:%u",
+ fixed_queue_length(p_ccb->p_lcb->ucd_out_sec_pending_q),
+ fixed_queue_length(p_ccb->xmit_hold_q),
+ p_ccb->buff_quota);
+ p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, FALSE );
+ }
+ }
+#endif
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ else {
+ UINT8 xx;
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx ++) {
+ if (p_ccb->p_lcb->p_fixed_ccbs[xx] == p_ccb) {
+ if (l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb != NULL) {
+ (* l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb)(p_ccb->p_lcb->remote_bd_addr, FALSE);
+ }
+ break;
+ }
+ }
+ }
+#endif
+ }
+ } else {
+ tL2C_LCB *p_lcb = p_ccb->p_lcb;
+ /* If this channel was not congested but it is congested now, tell the app */
+ if (q_count > p_ccb->buff_quota || (p_lcb && (p_lcb->link_xmit_data_q) && (list_length(p_lcb->link_xmit_data_q) + q_count) > p_ccb->buff_quota)
+#if (CLASSIC_BT_INCLUDED == TRUE)
+ || (p_ccb->peer_cfg.fcr.mode != L2CAP_FCR_BASIC_MODE && q_waiting_ack_count >= p_ccb->our_cfg.fcr.tx_win_sz)
+#endif
+ ) {
+ p_ccb->cong_sent = TRUE;
+ if (p_ccb->p_rcb && p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb) {
+ L2CAP_TRACE_DEBUG ("L2CAP - Calling CongestionStatus_Cb (TRUE),CID:0x%04x,XmitQ:%u,Quota:%u",
+ p_ccb->local_cid, q_count, p_ccb->buff_quota);
+
+ (*p_ccb->p_rcb->api.pL2CA_CongestionStatus_Cb)(p_ccb->local_cid, TRUE);
+ }
+#if (L2CAP_UCD_INCLUDED == TRUE)
+ else if ( p_ccb->p_rcb && p_ccb->local_cid == L2CAP_CONNECTIONLESS_CID ) {
+ if ( p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb ) {
+ L2CAP_TRACE_DEBUG ("L2CAP - Calling UCD CongestionStatus_Cb (TRUE), SecPendingQ:%u,XmitQ:%u,Quota:%u",
+ fixed_queue_length(p_ccb->p_lcb->ucd_out_sec_pending_q),
+ fixed_queue_length(p_ccb->xmit_hold_q),
+ p_ccb->buff_quota);
+ p_ccb->p_rcb->ucd.cb_info.pL2CA_UCD_Congestion_Status_Cb( p_ccb->p_lcb->remote_bd_addr, TRUE );
+ }
+ }
+#endif
+#if (L2CAP_NUM_FIXED_CHNLS > 0)
+ else {
+ UINT8 xx;
+ for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx ++) {
+ if (p_ccb->p_lcb->p_fixed_ccbs[xx] == p_ccb) {
+ if (l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb != NULL) {
+ (* l2cb.fixed_reg[xx].pL2CA_FixedCong_Cb)(p_ccb->p_lcb->remote_bd_addr, TRUE);
+ }
+ break;
+ }
+ }
+ }
+#endif
+ }
+ }
+ }
+}
diff --git a/lib/bt/host/bluedroid/stack/l2cap/l2cap_client.c b/lib/bt/host/bluedroid/stack/l2cap/l2cap_client.c
new file mode 100644
index 00000000..c7314b62
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/l2cap/l2cap_client.c
@@ -0,0 +1,462 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#if (defined(L2CAP_CLIENT_INCLUDED) && L2CAP_CLIENT_INCLUDED == TRUE)
+#include <string.h>
+#include "common/bt_trace.h"
+#include "common/bt_defs.h"
+#include "device/bdaddr.h"
+#include "osi/allocator.h"
+#include "osi/buffer.h"
+#include "osi/list.h"
+#include "osi/osi.h"
+#include "stack/l2cap_client.h"
+#include "stack/l2c_api.h"
+
+struct l2cap_client_t {
+ l2cap_client_callbacks_t callbacks;
+ void *context;
+
+ uint16_t local_channel_id;
+ uint16_t remote_mtu;
+ bool configured_self;
+ bool configured_peer;
+ bool is_congested;
+ list_t *outbound_fragments;
+};
+
+static void connect_completed_cb(uint16_t local_channel_id, uint16_t error_code);
+static void config_request_cb(uint16_t local_channel_id, tL2CAP_CFG_INFO *requested_parameters);
+static void config_completed_cb(uint16_t local_channel_id, tL2CAP_CFG_INFO *negotiated_parameters);
+static void disconnect_request_cb(uint16_t local_channel_id, bool ack_required);
+static void disconnect_completed_cb(uint16_t local_channel_id, uint16_t error_code);
+static void congestion_cb(uint16_t local_channel_id, bool is_congested);
+static void read_ready_cb(uint16_t local_channel_id, BT_HDR *packet);
+static void write_completed_cb(uint16_t local_channel_id, uint16_t packets_completed);
+
+static void fragment_packet(l2cap_client_t *client, buffer_t *packet);
+static void dispatch_fragments(l2cap_client_t *client);
+static l2cap_client_t *find(uint16_t local_channel_id);
+
+// From the Bluetooth Core specification.
+static const uint16_t L2CAP_MTU_DEFAULT = 672;
+static const uint16_t L2CAP_MTU_MINIMUM = 48;
+
+static const tL2CAP_APPL_INFO l2cap_callbacks = {
+ .pL2CA_ConnectCfm_Cb = connect_completed_cb,
+ .pL2CA_ConfigInd_Cb = config_request_cb,
+ .pL2CA_ConfigCfm_Cb = config_completed_cb,
+ .pL2CA_DisconnectInd_Cb = disconnect_request_cb,
+ .pL2CA_DisconnectCfm_Cb = disconnect_completed_cb,
+ .pL2CA_CongestionStatus_Cb = congestion_cb,
+ .pL2CA_DataInd_Cb = read_ready_cb,
+ .pL2CA_TxComplete_Cb = write_completed_cb,
+};
+
+static list_t *l2cap_clients; // A list of l2cap_client_t. Container does not own objects.
+
+buffer_t *l2cap_buffer_new(size_t size)
+{
+ buffer_t *buf = buffer_new(size + L2CAP_MIN_OFFSET);
+ buffer_t *slice = NULL;
+ if (buf) {
+ slice = buffer_new_slice(buf, size);
+ }
+ buffer_free(buf);
+ return slice;
+}
+
+l2cap_client_t *l2cap_client_new(const l2cap_client_callbacks_t *callbacks, void *context)
+{
+ assert(callbacks != NULL);
+ assert(callbacks->connected != NULL);
+ assert(callbacks->disconnected != NULL);
+ assert(callbacks->read_ready != NULL);
+ assert(callbacks->write_ready != NULL);
+
+ if (!l2cap_clients) {
+ l2cap_clients = list_new(NULL);
+ if (!l2cap_clients) {
+ L2CAP_TRACE_ERROR("%s unable to allocate space for L2CAP client list.", __func__);
+ return NULL;
+ }
+ }
+
+ l2cap_client_t *ret = (l2cap_client_t *)osi_calloc(sizeof(l2cap_client_t));
+ if (!ret) {
+ L2CAP_TRACE_ERROR("%s unable to allocate L2CAP client.", __func__);
+ goto error;
+ }
+
+ ret->callbacks = *callbacks;
+ ret->context = context;
+
+ ret->remote_mtu = L2CAP_MTU_DEFAULT;
+ ret->outbound_fragments = list_new(NULL);
+ if (!ret) {
+ L2CAP_TRACE_ERROR("%s unable to allocate outbound L2CAP fragment list.", __func__);
+ goto error;
+ }
+
+ list_append(l2cap_clients, ret);
+
+ return ret;
+
+error:;
+ osi_free(ret);
+ return NULL;
+}
+
+void l2cap_client_free(l2cap_client_t *client)
+{
+ if (!client) {
+ return;
+ }
+
+ list_remove(l2cap_clients, client);
+ l2cap_client_disconnect(client);
+ list_free(client->outbound_fragments);
+ osi_free(client);
+}
+
+bool l2cap_client_connect(l2cap_client_t *client, const bt_bdaddr_t *remote_bdaddr, uint16_t psm)
+{
+ assert(client != NULL);
+ assert(remote_bdaddr != NULL);
+ assert(psm != 0);
+ assert(!bdaddr_is_empty(remote_bdaddr));
+ assert(client->local_channel_id == 0);
+ assert(!client->configured_self);
+ assert(!client->configured_peer);
+ assert(!L2C_INVALID_PSM(psm));
+
+ client->local_channel_id = L2CA_ConnectReq(psm, (uint8_t *)remote_bdaddr);
+ if (!client->local_channel_id) {
+ L2CAP_TRACE_ERROR("%s unable to create L2CAP connection.", __func__);
+ return false;
+ }
+
+ L2CA_SetConnectionCallbacks(client->local_channel_id, &l2cap_callbacks);
+ return true;
+}
+
+void l2cap_client_disconnect(l2cap_client_t *client)
+{
+ assert(client != NULL);
+
+ if (client->local_channel_id && !L2CA_DisconnectReq(client->local_channel_id)) {
+ L2CAP_TRACE_ERROR("%s unable to send disconnect message for LCID 0x%04x.", __func__, client->local_channel_id);
+ }
+
+ client->local_channel_id = 0;
+ client->remote_mtu = L2CAP_MTU_DEFAULT;
+ client->configured_self = false;
+ client->configured_peer = false;
+ client->is_congested = false;
+
+ for (const list_node_t *node = list_begin(client->outbound_fragments); node != list_end(client->outbound_fragments); node = list_next(node)) {
+ osi_free(list_node(node));
+ }
+
+ list_clear(client->outbound_fragments);
+}
+
+bool l2cap_client_is_connected(const l2cap_client_t *client)
+{
+ assert(client != NULL);
+
+ return client->local_channel_id != 0 && client->configured_self && client->configured_peer;
+}
+
+bool l2cap_client_write(l2cap_client_t *client, buffer_t *packet)
+{
+ assert(client != NULL);
+ assert(packet != NULL);
+ assert(l2cap_client_is_connected(client));
+
+ if (client->is_congested) {
+ return false;
+ }
+
+ fragment_packet(client, packet);
+ dispatch_fragments(client);
+ return true;
+}
+
+static void connect_completed_cb(uint16_t local_channel_id, uint16_t error_code)
+{
+ assert(local_channel_id != 0);
+
+ l2cap_client_t *client = find(local_channel_id);
+ if (!client) {
+ L2CAP_TRACE_ERROR("%s unable to find L2CAP client for LCID 0x%04x.", __func__, local_channel_id);
+ return;
+ }
+
+ if (error_code != L2CAP_CONN_OK) {
+ L2CAP_TRACE_ERROR("%s error connecting L2CAP channel: %d.", __func__, error_code);
+ client->callbacks.disconnected(client, client->context);
+ return;
+ }
+
+ // Use default L2CAP parameters.
+ tL2CAP_CFG_INFO desired_parameters = { 0 };
+ if (!L2CA_ConfigReq(local_channel_id, &desired_parameters)) {
+ L2CAP_TRACE_ERROR("%s error sending L2CAP config parameters.", __func__);
+ client->callbacks.disconnected(client, client->context);
+ }
+}
+
+static void config_request_cb(uint16_t local_channel_id, tL2CAP_CFG_INFO *requested_parameters)
+{
+ tL2CAP_CFG_INFO response = { 0 };
+ l2cap_client_t *client = find(local_channel_id);
+
+ if (!client) {
+ L2CAP_TRACE_ERROR("%s unable to find L2CAP client matching LCID 0x%04x.", __func__, local_channel_id);
+ return;
+ }
+
+ response.result = L2CAP_CFG_OK;
+
+ if (requested_parameters->mtu_present) {
+ // Make sure the peer chose an MTU at least as large as the minimum L2CAP MTU defined
+ // by the Bluetooth Core spec.
+ if (requested_parameters->mtu < L2CAP_MTU_MINIMUM) {
+ response.mtu = L2CAP_MTU_MINIMUM;
+ response.mtu_present = true;
+ response.result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
+ } else {
+ client->remote_mtu = requested_parameters->mtu;
+ }
+ }
+
+ if (requested_parameters->fcr_present) {
+ if (requested_parameters->fcr.mode != L2CAP_FCR_BASIC_MODE) {
+ response.fcr_present = true;
+ response.fcr = requested_parameters->fcr;
+ response.fcr.mode = L2CAP_FCR_BASIC_MODE;
+ response.result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
+ }
+ }
+
+ if (!L2CA_ConfigRsp(local_channel_id, &response)) {
+ L2CAP_TRACE_ERROR("%s unable to send config response for LCID 0x%04x.", __func__, local_channel_id);
+ l2cap_client_disconnect(client);
+ return;
+ }
+
+ // If we've configured both endpoints, let the listener know we've connected.
+ client->configured_peer = true;
+ if (l2cap_client_is_connected(client)) {
+ client->callbacks.connected(client, client->context);
+ }
+}
+
+static void config_completed_cb(uint16_t local_channel_id, tL2CAP_CFG_INFO *negotiated_parameters)
+{
+ l2cap_client_t *client = find(local_channel_id);
+
+ if (!client) {
+ L2CAP_TRACE_ERROR("%s unable to find L2CAP client matching LCID 0x%04x.", __func__, local_channel_id);
+ return;
+ }
+
+ switch (negotiated_parameters->result) {
+ // We'll get another configuration response later.
+ case L2CAP_CFG_PENDING:
+ break;
+
+ case L2CAP_CFG_UNACCEPTABLE_PARAMS:
+ // TODO: see if we can renegotiate parameters instead of dropping the connection.
+ L2CAP_TRACE_WARNING("%s dropping L2CAP connection due to unacceptable config parameters.\n", __func__);
+ l2cap_client_disconnect(client);
+ break;
+
+ case L2CAP_CFG_OK:
+ // If we've configured both endpoints, let the listener know we've connected.
+ client->configured_self = true;
+ if (l2cap_client_is_connected(client)) {
+ client->callbacks.connected(client, client->context);
+ }
+ break;
+
+ // Failure, no further parameter negotiation possible.
+ default:
+ L2CAP_TRACE_WARNING("%s L2CAP parameter negotiation failed with error code %d.\n", __func__, negotiated_parameters->result);
+ l2cap_client_disconnect(client);
+ break;
+ }
+}
+
+static void disconnect_request_cb(uint16_t local_channel_id, bool ack_required)
+{
+ l2cap_client_t *client = find(local_channel_id);
+ if (!client) {
+ L2CAP_TRACE_ERROR("%s unable to find L2CAP client with LCID 0x%04x.\n", __func__, local_channel_id);
+ return;
+ }
+
+ if (ack_required) {
+ L2CA_DisconnectRsp(local_channel_id);
+ }
+
+ // We already sent a disconnect response so this LCID is now invalid.
+ client->local_channel_id = 0;
+ l2cap_client_disconnect(client);
+
+ client->callbacks.disconnected(client, client->context);
+}
+
+static void disconnect_completed_cb(uint16_t local_channel_id, UNUSED_ATTR uint16_t error_code)
+{
+ assert(local_channel_id != 0);
+
+ l2cap_client_t *client = find(local_channel_id);
+ if (!client) {
+ L2CAP_TRACE_ERROR("%s unable to find L2CAP client with LCID 0x%04x.\n", __func__, local_channel_id);
+ return;
+ }
+
+ client->local_channel_id = 0;
+ l2cap_client_disconnect(client);
+
+ client->callbacks.disconnected(client, client->context);
+}
+
+static void congestion_cb(uint16_t local_channel_id, bool is_congested)
+{
+ assert(local_channel_id != 0);
+
+ l2cap_client_t *client = find(local_channel_id);
+ if (!client) {
+ L2CAP_TRACE_ERROR("%s unable to find L2CAP client matching LCID 0x%04x.\n", __func__, local_channel_id);
+ return;
+ }
+
+ client->is_congested = is_congested;
+
+ if (!is_congested) {
+ // If we just decongested, dispatch whatever we have left over in our queue.
+ // Once that's done, if we're still decongested, notify the listener so it
+ // can start writing again.
+ dispatch_fragments(client);
+ if (!client->is_congested) {
+ client->callbacks.write_ready(client, client->context);
+ }
+ }
+}
+
+static void read_ready_cb(uint16_t local_channel_id, BT_HDR *packet)
+{
+ assert(local_channel_id != 0);
+
+ l2cap_client_t *client = find(local_channel_id);
+ if (!client) {
+ L2CAP_TRACE_ERROR("%s unable to find L2CAP client matching LCID 0x%04x.\n", __func__, local_channel_id);
+ return;
+ }
+
+ // TODO(sharvil): eliminate copy from BT_HDR.
+ buffer_t *buffer = buffer_new(packet->len);
+ memcpy(buffer_ptr(buffer), packet->data + packet->offset, packet->len);
+ osi_free(packet);
+
+ client->callbacks.read_ready(client, buffer, client->context);
+ buffer_free(buffer);
+}
+
+static void write_completed_cb(UNUSED_ATTR uint16_t local_channel_id, UNUSED_ATTR uint16_t packets_completed)
+{
+ // Do nothing. We update congestion state based on the congestion callback
+ // and we've already removed items from outbound_fragments list so we don't
+ // really care how many packets were successfully dispatched.
+}
+
+static void fragment_packet(l2cap_client_t *client, buffer_t *packet)
+{
+ assert(client != NULL);
+ assert(packet != NULL);
+
+ // TODO(sharvil): eliminate copy into BT_HDR.
+ BT_HDR *bt_packet = osi_malloc(buffer_length(packet) + L2CAP_MIN_OFFSET);
+ bt_packet->offset = L2CAP_MIN_OFFSET;
+ bt_packet->len = buffer_length(packet);
+ memcpy(bt_packet->data + bt_packet->offset, buffer_ptr(packet), buffer_length(packet));
+
+ for (;;) {
+ if (bt_packet->len <= client->remote_mtu) {
+ if (bt_packet->len > 0) {
+ list_append(client->outbound_fragments, bt_packet);
+ } else {
+ osi_free(bt_packet);
+ }
+ break;
+ }
+
+ BT_HDR *fragment = osi_malloc(client->remote_mtu + L2CAP_MIN_OFFSET);
+ fragment->offset = L2CAP_MIN_OFFSET;
+ fragment->len = client->remote_mtu;
+ memcpy(fragment->data + fragment->offset, bt_packet->data + bt_packet->offset, client->remote_mtu);
+
+ list_append(client->outbound_fragments, fragment);
+
+ bt_packet->offset += client->remote_mtu;
+ bt_packet->len -= client->remote_mtu;
+ }
+}
+
+static void dispatch_fragments(l2cap_client_t *client)
+{
+ assert(client != NULL);
+ assert(!client->is_congested);
+
+ while (!list_is_empty(client->outbound_fragments)) {
+ BT_HDR *packet = (BT_HDR *)list_front(client->outbound_fragments);
+ list_remove(client->outbound_fragments, packet);
+
+ switch (L2CA_DataWrite(client->local_channel_id, packet)) {
+ case L2CAP_DW_CONGESTED:
+ client->is_congested = true;
+ return;
+
+ case L2CAP_DW_FAILED:
+ L2CAP_TRACE_ERROR("%s error writing data to L2CAP connection LCID 0x%04x; disconnecting.", __func__, client->local_channel_id);
+ l2cap_client_disconnect(client);
+ return;
+
+ case L2CAP_DW_SUCCESS:
+ break;
+ }
+ }
+}
+
+static l2cap_client_t *find(uint16_t local_channel_id)
+{
+ assert(local_channel_id != 0);
+
+ for (const list_node_t *node = list_begin(l2cap_clients); node != list_end(l2cap_clients); node = list_next(node)) {
+ l2cap_client_t *client = (l2cap_client_t *)list_node(node);
+ if (client->local_channel_id == local_channel_id) {
+ return client;
+ }
+ }
+
+ return NULL;
+}
+
+#endif /*L2CAP_CLIENT_INCLUDED*/