diff options
-rw-r--r-- | system/stack/l2cap/l2c_link.cc | 168 |
1 files changed, 82 insertions, 86 deletions
diff --git a/system/stack/l2cap/l2c_link.cc b/system/stack/l2cap/l2c_link.cc index 426a12a2ba..082e055afe 100644 --- a/system/stack/l2cap/l2c_link.cc +++ b/system/stack/l2cap/l2c_link.cc @@ -284,113 +284,109 @@ static void l2c_link_iot_store_disc_reason(RawAddress& bda, uint8_t reason) { * ******************************************************************************/ bool l2c_link_hci_disc_comp(uint16_t handle, tHCI_REASON reason) { - tL2C_CCB* p_ccb; - bool status = true; + tL2C_LCB* p_lcb = l2cu_find_lcb_by_handle(handle); + if (p_lcb == nullptr) { + log::error("No LCB found for handle:0x{:04x}", handle); + + p_lcb = l2cu_find_lcb_by_state(LST_CONNECT_HOLDING); + if (p_lcb != nullptr) { + log::info("Resuming pending ACL request {}", p_lcb->remote_bd_addr); + l2cu_create_conn_br_edr(p_lcb); + } + return false; + } + bool lcb_is_free = true; + l2c_link_iot_store_disc_reason(p_lcb->remote_bd_addr, reason); + p_lcb->SetDisconnectReason(reason); - /* If we don't have one, maybe an SCO link. Send to MM */ - tL2C_LCB* p_lcb = l2cu_find_lcb_by_handle(handle); - if (!p_lcb) { - status = false; - } else { - l2c_link_iot_store_disc_reason(p_lcb->remote_bd_addr, reason); + /* Just in case app decides to try again in the callback context */ + p_lcb->link_state = LST_DISCONNECTING; - p_lcb->SetDisconnectReason(reason); + /* Check for BLE and handle that differently */ + if (p_lcb->transport == BT_TRANSPORT_LE) { + btm_ble_decrement_link_topology_mask(p_lcb->LinkRole()); + } - /* Just in case app decides to try again in the callback context */ - p_lcb->link_state = LST_DISCONNECTING; + /* Link is disconnected. For all channels, send the event through their FSMs. The CCBs should + * remove themselves from the LCB */ + for (tL2C_CCB* p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb;) { + tL2C_CCB* pn = p_ccb->p_next_ccb; - /* Check for BLE and handle that differently */ - if (p_lcb->transport == BT_TRANSPORT_LE) { - btm_ble_decrement_link_topology_mask(p_lcb->LinkRole()); + /* 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); } - /* 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; + p_ccb = pn; + } - /* 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; - } + if (p_lcb->transport == BT_TRANSPORT_BR_EDR) { + /* Tell SCO management to drop any SCOs on this ACL */ + btm_sco_acl_removed(&p_lcb->remote_bd_addr); + } - if (p_lcb->transport == BT_TRANSPORT_BR_EDR) { - /* Tell SCO management to drop any SCOs on this ACL */ - btm_sco_acl_removed(&p_lcb->remote_bd_addr); + /* 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 != nullptr || p_lcb->p_pending_ccb) { + log::debug("l2c_link_hci_disc_comp: Restarting pending ACL request"); + /* Release any held buffers */ + while (!list_is_empty(p_lcb->link_xmit_data_q)) { + BT_HDR* p_buf = static_cast<BT_HDR*>(list_front(p_lcb->link_xmit_data_q)); + list_remove(p_lcb->link_xmit_data_q, p_buf); + osi_free(p_buf); } - - /* 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 + /* for LE link, always drop and re-open to ensure to get LE remote feature */ - if (p_lcb->ccb_queue.p_first_ccb != NULL || p_lcb->p_pending_ccb) { - log::debug("l2c_link_hci_disc_comp: Restarting pending ACL request"); - /* Release any held buffers */ - while (!list_is_empty(p_lcb->link_xmit_data_q)) { - BT_HDR* p_buf = static_cast<BT_HDR*>(list_front(p_lcb->link_xmit_data_q)); - list_remove(p_lcb->link_xmit_data_q, p_buf); - osi_free(p_buf); - } - /* for LE link, always drop and re-open to ensure to get LE remote feature - */ - if (p_lcb->transport == BT_TRANSPORT_LE) { - btm_acl_removed(handle); - if (com::android::bluetooth::flags::invalidate_hci_handle_on_acl_removal()) { - p_lcb->InvalidateHandle(); - } - } else { - /* 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) { - l2cu_release_ccb(p_lcb->p_fixed_ccbs[xx]); - - p_lcb->p_fixed_ccbs[xx] = NULL; - (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, - p_lcb->remote_bd_addr, false, - p_lcb->DisconnectReason(), p_lcb->transport); - } - } - /* Cleanup connection state to avoid race conditions because - * l2cu_release_lcb won't be invoked to cleanup */ - btm_acl_removed(p_lcb->Handle()); + if (p_lcb->transport == BT_TRANSPORT_LE) { + btm_acl_removed(handle); + if (com::android::bluetooth::flags::invalidate_hci_handle_on_acl_removal()) { p_lcb->InvalidateHandle(); } - if (p_lcb->transport == BT_TRANSPORT_LE) { - if (l2cu_create_conn_le(p_lcb)) { - lcb_is_free = false; /* still using this lcb */ + } else { + /* 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) { + l2cu_release_ccb(p_lcb->p_fixed_ccbs[xx]); + p_lcb->p_fixed_ccbs[xx] = nullptr; + (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL, + p_lcb->remote_bd_addr, false, + p_lcb->DisconnectReason(), p_lcb->transport); } - } else { - l2cu_create_conn_br_edr(p_lcb); + } + /* Cleanup connection state to avoid race conditions because + * l2cu_release_lcb won't be invoked to cleanup */ + btm_acl_removed(p_lcb->Handle()); + p_lcb->InvalidateHandle(); + } + if (p_lcb->transport == BT_TRANSPORT_LE) { + if (l2cu_create_conn_le(p_lcb)) { lcb_is_free = false; /* still using this lcb */ } + } else { + l2cu_create_conn_br_edr(p_lcb); + lcb_is_free = false; /* still using this lcb */ } + } + p_lcb->p_pending_ccb = nullptr; - p_lcb->p_pending_ccb = NULL; + /* Release the LCB */ + if (lcb_is_free) { + l2cu_release_lcb(p_lcb); - /* 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 */ + p_lcb = l2cu_find_lcb_by_state(LST_CONNECT_HOLDING); + if (p_lcb != nullptr) { + log::info("Resuming pending ACL request {}", p_lcb->remote_bd_addr); + l2cu_create_conn_br_edr(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_br_edr(p_lcb); - } - - return status; + return true; } /******************************************************************************* |