OSDN Git Service

Avoid duplicate disconnection callbacks for fixed channel connections
authorSatya Calloji <satyac@broadcom.com>
Tue, 17 Mar 2015 20:12:01 +0000 (13:12 -0700)
committerAndre Eisenbach <eisenbach@google.com>
Thu, 9 Apr 2015 00:16:57 +0000 (17:16 -0700)
Fixed channel connections are getting a disconnect callback when the
HCI command is issued and when the HCI disconnect completes. This
causes problems if the upper layer trying to reconnect at link
disconnecting state.
Triggering the disconnect callback only once solves this problem.

Original author: Chaojing Sun <cjsun@broadcom.com>

Bug: 19816438
Change-Id: Ib661c968e586975a7fc7244e2d0745f71d52e3e9

stack/l2cap/l2c_api.c
stack/l2cap/l2c_int.h
stack/l2cap/l2c_link.c
stack/l2cap/l2c_main.c [changed mode: 0755->0644]
stack/l2cap/l2c_utils.c

index f717b1e..959f4d2 100644 (file)
@@ -1378,25 +1378,24 @@ BOOLEAN  L2CA_RegisterFixedChannel (UINT16 fixed_cid, tL2CAP_FIXED_CHNL_REG *p_f
 *******************************************************************************/
 BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda)
 {
-    tL2C_LCB        *p_lcb;
-    tBT_TRANSPORT   transport = BT_TRANSPORT_BR_EDR;
-    UINT16          reason;
+    tL2C_LCB *p_lcb;
+    tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
 
-    L2CAP_TRACE_API  ("L2CA_ConnectFixedChnl()  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]);
+    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 */
+    // 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_ConnectFixedChnl()  Invalid CID: 0x%04x", fixed_cid);
+        L2CAP_TRACE_ERROR ("%s() Invalid CID: 0x%04x", __func__, fixed_cid);
         return (FALSE);
     }
 
-    /* Fail if BT is not yet up */
+    // Fail if BT is not yet up
     if (!BTM_IsDeviceUp())
     {
-        L2CAP_TRACE_WARNING ("L2CA_ConnectFixedChnl(0x%04x) - BTU not ready", fixed_cid);
+        L2CAP_TRACE_WARNING ("%s(0x%04x) - BTU not ready", __func__, fixed_cid);
         return (FALSE);
     }
 
@@ -1405,31 +1404,43 @@ BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda)
         transport = BT_TRANSPORT_LE;
 #endif
 
-    /* If we already have a link to the remote, check if it supports that CID */
+    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)
     {
-        if (!(p_lcb->peer_chnl_mask[0] & (1 << fixed_cid)))
+        // Fixed channels are mandatory on LE transports so ignore the received
+        // channel mask and use the locally cached LE channel mask.
+
+        if (transport == BT_TRANSPORT_LE)
+            peer_channel_mask = l2cb.l2c_ble_fixed_chnls_mask;
+        else
+            peer_channel_mask = p_lcb->peer_chnl_mask[0];
+
+        // Check for supported channel
+        if (!(peer_channel_mask & (1 << fixed_cid)))
         {
-            L2CAP_TRACE_EVENT  ("L2CA_ConnectFixedChnl() CID:0x%04x  BDA: %08x%04x not supported",
+            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);
+            return FALSE;
         }
-        /* Get a CCB and link the lcb to it */
+
+        // 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 ("L2CA_ConnectFixedChnl(0x%04x) - LCB but no CCB", fixed_cid);
-            return (FALSE);
+            L2CAP_TRACE_WARNING ("%s(0x%04x) - LCB but no CCB", __func__, fixed_cid);
+            return FALSE;
         }
 
-        /* racing with disconnecting, queue the connection request */
+        // racing with disconnecting, queue the connection request
         if (p_lcb->link_state == LST_DISCONNECTING)
         {
-            L2CAP_TRACE_DEBUG ("L2CAP API - link disconnecting: RETRY LATER");
+            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);
+            return TRUE;
         }
 
 #if BLE_INCLUDED == TRUE
@@ -1439,33 +1450,33 @@ BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda)
         (*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)
         (p_lcb->remote_bd_addr, TRUE, 0, BT_TRANSPORT_BR_EDR);
 #endif
-        return (TRUE);
+        return TRUE;
     }
 
-    /* No link. Get an LCB and start link establishment */
+    // No link. Get an LCB and start link establishment
     if ((p_lcb = l2cu_allocate_lcb (rem_bda, FALSE, transport)) == NULL)
     {
-        L2CAP_TRACE_WARNING ("L2CA_ConnectFixedChnl(0x%04x) - no LCB", fixed_cid);
-        return (FALSE);
+        L2CAP_TRACE_WARNING ("%s(0x%04x) - no LCB", __func__, fixed_cid);
+        return FALSE;
     }
 
-    /* Get a CCB and link the lcb to it */
+    // 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 ("L2CA_ConnectFixedChnl(0x%04x) - no CCB", fixed_cid);
+        L2CAP_TRACE_WARNING ("%s(0x%04x) - no CCB", __func__, fixed_cid);
         l2cu_release_lcb (p_lcb);
-        return (FALSE);
+        return FALSE;
     }
 
     if (!l2cu_create_conn(p_lcb, transport))
     {
-        L2CAP_TRACE_WARNING ("L2CA_ConnectFixedChnl create_conn failed");
+        L2CAP_TRACE_WARNING ("%s() - create_conn failed", __func__);
         l2cu_release_lcb (p_lcb);
-        return (FALSE);
+        return FALSE;
     }
-    return (TRUE);
+    return TRUE;
 }
 
 /*******************************************************************************
@@ -1495,7 +1506,7 @@ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf)
         transport = BT_TRANSPORT_LE;
 #endif
 
-    /* Check CID is valid and registered */
+    // 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) )
     {
@@ -1504,7 +1515,7 @@ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf)
         return (L2CAP_DW_FAILED);
     }
 
-    /* Fail if BT is not yet up */
+    // Fail if BT is not yet up
     if (!BTM_IsDeviceUp())
     {
         L2CAP_TRACE_WARNING ("L2CA_SendFixedChnlData(0x%04x) - BTU not ready", fixed_cid);
@@ -1512,7 +1523,7 @@ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf)
         return (L2CAP_DW_FAILED);
     }
 
-    /* We need to have a link up */
+    // 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)
@@ -1522,7 +1533,15 @@ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf)
         return (L2CAP_DW_FAILED);
     }
 
-    if ((p_lcb->peer_chnl_mask[0] & (1 << fixed_cid)) == 0)
+    tL2C_BLE_FIXED_CHNLS_MASK peer_channel_mask;
+
+    // Select peer channels mask to use depending on transport
+    if (transport == BT_TRANSPORT_LE)
+        peer_channel_mask = l2cb.l2c_ble_fixed_chnls_mask;
+    else
+        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);
         GKI_freebuf (p_buf);
@@ -1542,7 +1561,7 @@ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *p_buf)
         }
     }
 
-    /* If already congested, do not accept any more packets */
+    // If already congested, do not accept any more packets
     if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent)
     {
         L2CAP_TRACE_ERROR ("L2CAP - CID: 0x%04x cannot send, already congested \
@@ -1557,7 +1576,7 @@ UINT16 L2CA_SendFixedChnlData (UINT16 fixed_cid, BD_ADDR rem_bda, BT_HDR *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 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);
index 9cf79a9..08c4ea1 100644 (file)
@@ -155,6 +155,8 @@ typedef enum
 
 #define L2CAP_MAX_FCR_CFG_TRIES         2       /* Config attempts before disconnecting */
 
+typedef uint8_t tL2C_BLE_FIXED_CHNLS_MASK;
+
 typedef struct
 {
     UINT8       next_tx_seq;                /* Next sequence number to be Tx'ed         */
@@ -474,6 +476,7 @@ typedef struct
     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              */
index cfa3498..615e9a7 100644 (file)
@@ -423,8 +423,11 @@ BOOLEAN l2c_link_hci_disc_comp (UINT16 handle, UINT8 reason)
             /* for LE link, always drop and re-open to ensure to get LE remote feature */
             if (p_lcb->transport == BT_TRANSPORT_LE)
             {
+                BD_ADDR bd_addr;
+                memcpy(bd_addr, p_lcb->remote_bd_addr, BD_ADDR_LEN);
                 l2cu_release_lcb (p_lcb);
-                p_lcb->in_use = TRUE;
+                // make sure Tx credit allocation is redistributed in between links by calling l2cu_allocate_lcb
+                p_lcb = l2cu_allocate_lcb (bd_addr, FALSE, BT_TRANSPORT_LE);
                 transport = BT_TRANSPORT_LE;
             }
             else
old mode 100755 (executable)
new mode 100644 (file)
index 02f12a7..5f541b5
@@ -892,6 +892,9 @@ void l2c_init (void)
     l2cb.high_pri_min_xmit_quota = L2CAP_HIGH_PRI_MIN_XMIT_QUOTA;
 #endif
 
+    l2cb.l2c_ble_fixed_chnls_mask =
+         L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT;
+
     l2cb.rcv_pending_q = list_new(NULL);
     if (l2cb.rcv_pending_q == NULL)
         LOG_ERROR("%s unable to allocate memory for link layer control block", __func__);
index 2c992d2..ef155ff 100644 (file)
@@ -183,22 +183,7 @@ void l2cu_release_lcb (tL2C_LCB *p_lcb)
 #endif
 
 #if (L2CAP_NUM_FIXED_CHNLS > 0)
-    {
-        int         xx;
-
-        for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
-        {
-            if (p_lcb->p_fixed_ccbs[xx])
-            {
-                l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]);
-                p_lcb->p_fixed_ccbs[xx] = NULL;
-                (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
-            }
-            else if ( (p_lcb->peer_chnl_mask[0] & (1 << (xx + L2CAP_FIRST_FIXED_CHNL)))
-                   && (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) )
-                    (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
-        }
-    }
+    l2cu_process_fixed_disc_cback(p_lcb);
 #endif
 
     /* Ensure no CCBs left on this LCB */
@@ -2829,17 +2814,18 @@ void l2cu_no_dynamic_ccbs (tL2C_LCB *p_lcb)
 *******************************************************************************/
 void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb)
 {
-    int     xx;
 #if (BLE_INCLUDED == TRUE)
     /* always exclude LE fixed channel on BR/EDR fix channel capability */
     if (p_lcb->transport == BT_TRANSPORT_BR_EDR)
         p_lcb->peer_chnl_mask[0] &= ~(L2CAP_FIXED_CHNL_ATT_BIT| \
                                       L2CAP_FIXED_CHNL_BLE_SIG_BIT| \
                                       L2CAP_FIXED_CHNL_SMP_BIT);
+    else
+        p_lcb->peer_chnl_mask[0] = l2cb.l2c_ble_fixed_chnls_mask;
 #endif
 
     /* Tell all registered fixed channels about the connection */
-    for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
+    for (int xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
     {
 #if BLE_INCLUDED == TRUE
         /* skip sending LE fix channel callbacks on BR/EDR links */
@@ -2893,16 +2879,24 @@ void l2cu_process_fixed_chnl_resp (tL2C_LCB *p_lcb)
 void l2cu_process_fixed_disc_cback (tL2C_LCB *p_lcb)
 {
 #if (L2CAP_NUM_FIXED_CHNLS > 0)
-    int         xx;
 
-    for (xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++)
+    /* 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)
             {
-                l2cu_release_ccb (p_lcb->p_fixed_ccbs[xx]);
+                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)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);
 #else
@@ -2910,7 +2904,7 @@ void l2cu_process_fixed_disc_cback (tL2C_LCB *p_lcb)
 #endif
            }
         }
-        else if ( (p_lcb->peer_chnl_mask[0] & (1 << (xx + L2CAP_FIRST_FIXED_CHNL)))
+        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)(p_lcb->remote_bd_addr, FALSE, p_lcb->disc_reason, p_lcb->transport);