zoukankan      html  css  js  c++  java
  • 蓝牙 link timeout分析

    蓝牙主机和蓝牙设备建立连接之后,会在l2cap 层面上建立相应的channel,这些channel 基本上是用于各种不同的profile 或者protocol 进行通信用的。

    当相应的profile或者protocol 不再被使用的时候,这些建立的channel 都要被清除掉。当一条link上面没有了 相应的channel之后,那么经过一段时间之后,它就会断开,这个时间就是link idle timeout。

    这里分析一下LE 设备的link idle timeout

    这段逻辑其实是在 建立channel 的过程中完成的,当前Android8.0 的bluedroid 是在link 建立完成,进行完remote feature的交互之后就会设置link idle timeout,这里分析的情况是Android6.0的bluedroid。

    其实在BTA_GATTC_OPEN 中已经描述了channel open的过程,但是没有讲到 link idle timeout 相关,我们这里从gatt_connect 来分析:

    /*******************************************************************************
    **
    ** Function         gatt_connect
    **
    ** Description      This function is called to initiate a connection to a peer device.
    **
    ** Parameter        rem_bda: remote device address to connect to.
    **
    ** Returns          TRUE if connection is started, otherwise return FALSE.
    **
    *******************************************************************************/
    BOOLEAN gatt_connect (BD_ADDR rem_bda, tGATT_TCB *p_tcb, tBT_TRANSPORT transport)
    {
        BOOLEAN             gatt_ret = FALSE;
    
        if (gatt_get_ch_state(p_tcb) != GATT_CH_OPEN)
            gatt_set_ch_state(p_tcb, GATT_CH_CONN);
    
        if (transport == BT_TRANSPORT_LE)
        {
            p_tcb->att_lcid = L2CAP_ATT_CID;
            gatt_ret = L2CA_ConnectFixedChnl (L2CAP_ATT_CID, rem_bda);//创建固定的channel
        }
        else
        {
            if ((p_tcb->att_lcid = L2CA_ConnectReq(BT_PSM_ATT, rem_bda)) != 0)
                gatt_ret = TRUE;
        }
    
        return gatt_ret;
    }

    LE设备 使用的固定的channel 都是L2CAP_ATT_CID :这里注意,执行到open channel的时候,一般都已经完成link的建立:

    /*******************************************************************************
    **
    **  Function        L2CA_ConnectFixedChnl
    **
    **  Description     Connect an fixed signalling channel to a remote device.
    **
    **  Parameters:     Fixed CID
    **                  BD Address of remote
    **
    **  Return value:   TRUE if connection started
    **
    *******************************************************************************/
    BOOLEAN L2CA_ConnectFixedChnl (UINT16 fixed_cid, BD_ADDR rem_bda)
    {
        tL2C_LCB *p_lcb;
        tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
    ...
        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;
            }
    ...
    #if BLE_INCLUDED == TRUE
            (*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)
            (fixed_cid,p_lcb->remote_bd_addr, TRUE, 0, p_lcb->transport);//回调,这里是重点
    #else
            (*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)
            (fixed_cid, p_lcb->remote_bd_addr, TRUE, 0, BT_TRANSPORT_BR_EDR);
    #endif
            return TRUE;
        }
    
        // No link. Get an LCB and start link establishment
        ...
        return TRUE;
    }

    那这个l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb 是在哪里注册的呢?

    在gatt_init里面:

    /*******************************************************************************
    **
    ** Function         gatt_init
    **
    ** Description      This function is enable the GATT profile on the device.
    **                  It clears out the control blocks, and registers with L2CAP.
    **
    ** Returns          void
    **
    *******************************************************************************/
    void gatt_init (void)
    {
        tL2CAP_FIXED_CHNL_REG  fixed_reg;
        memset (&gatt_cb, 0, sizeof(tGATT_CB));
        memset (&fixed_reg, 0, sizeof(tL2CAP_FIXED_CHNL_REG));
    
    #if defined(GATT_INITIAL_TRACE_LEVEL)
        gatt_cb.trace_level = GATT_INITIAL_TRACE_LEVEL;
    #else
        gatt_cb.trace_level = BT_TRACE_LEVEL_NONE;    /* No traces */
    #endif
        gatt_cb.def_mtu_size = GATT_DEF_BLE_MTU_SIZE;
        GKI_init_q (&gatt_cb.sign_op_queue);
        GKI_init_q (&gatt_cb.srv_chg_clt_q);
        GKI_init_q (&gatt_cb.pending_new_srv_start_q);
        /* First, register fixed L2CAP channel for ATT over BLE */
        fixed_reg.fixed_chnl_opts.mode         = L2CAP_FCR_BASIC_MODE;
        fixed_reg.fixed_chnl_opts.max_transmit = 0xFF;
        fixed_reg.fixed_chnl_opts.rtrans_tout  = 2000;
        fixed_reg.fixed_chnl_opts.mon_tout     = 12000;
        fixed_reg.fixed_chnl_opts.mps          = 670;
        fixed_reg.fixed_chnl_opts.tx_win_sz    = 1;
    
        fixed_reg.pL2CA_FixedConn_Cb = gatt_le_connect_cback;
        fixed_reg.pL2CA_FixedData_Cb = gatt_le_data_ind;
        fixed_reg.pL2CA_FixedCong_Cb = gatt_le_cong_cback;      /* congestion callback */
        fixed_reg.default_idle_tout  = 0xffff;                  /* 0xffff default idle timeout */
    
        L2CA_RegisterFixedChannel (L2CAP_ATT_CID, &fixed_reg);//把ATT相关的参数和回调 注册到l2cap
    ...
        gatt_cb.hdl_cfg.gatt_start_hdl = GATT_GATT_START_HANDLE;
        gatt_cb.hdl_cfg.gap_start_hdl  = GATT_GAP_START_HANDLE;
        gatt_cb.hdl_cfg.app_start_hdl  = GATT_APP_START_HANDLE;
        gatt_profile_db_init();
    
    }

     注册的过程很简单就是 将注册结构 放置到l2cb 结构下:

    BOOLEAN  L2CA_RegisterFixedChannel (UINT16 fixed_cid, tL2CAP_FIXED_CHNL_REG *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);
    }

    我们下面重点 看一下 刚刚的回调:

    fixed_reg.pL2CA_FixedConn_Cb = gatt_le_connect_cback;

    看看这个回调的功能,看注册其是 当fix channel 建立完成之后才会调用的:

    /*******************************************************************************
    **
    ** Function         gatt_le_connect_cback
    **
    ** Description      This callback function is called by L2CAP to indicate that
    **                  the ATT fixed channel for LE is
    **                      connected (conn = TRUE)/disconnected (conn = FALSE).
    **
    *******************************************************************************/
    static void gatt_le_connect_cback (UINT16 chan, BD_ADDR bd_addr, BOOLEAN connected,
                                       UINT16 reason, tBT_TRANSPORT transport)
    {
    
        tGATT_TCB       *p_tcb = gatt_find_tcb_by_addr(bd_addr, transport);
        BOOLEAN                 check_srv_chg = FALSE;
        tGATTS_SRV_CHG          *p_srv_chg_clt=NULL;
    
        /* ignore all fixed channel connect/disconnect on BR/EDR link for GATT */
        if (transport == BT_TRANSPORT_BR_EDR)
            return;
        if ((p_srv_chg_clt = gatt_is_bda_in_the_srv_chg_clt_list(bd_addr)) != NULL)
        {
            check_srv_chg = TRUE;
        }
        else
        {
            if (btm_sec_is_a_bonded_dev(bd_addr))
                gatt_add_a_bonded_dev_for_srv_chg(bd_addr);
        }
    
        if (connected)
        {
            /* do we have a channel initiating a connection? */
            if (p_tcb)
            {
                /* we are initiating connection */
                if ( gatt_get_ch_state(p_tcb) == GATT_CH_CONN)
                {
                    /* send callback */
                    gatt_set_ch_state(p_tcb, GATT_CH_OPEN);
                    p_tcb->payload_size = GATT_DEF_BLE_MTU_SIZE;
    
                    gatt_send_conn_cback(p_tcb);//看这里的回调
                }
                if (check_srv_chg)
                    gatt_chk_srv_chg (p_srv_chg_clt);
            }
            /* this is incoming connection or background connection callback */
          ...
    }

    这里我们关注重点,就是 如何设置 link timeout 的:

    /*******************************************************************************
    **
    ** Function         gatt_send_conn_cback
    **
    ** Description      Callback used to notify layer above about a connection.
    **
    **
    ** Returns          void
    **
    *******************************************************************************/
    static void gatt_send_conn_cback(tGATT_TCB *p_tcb)
    {
        UINT8               i;
        tGATT_REG           *p_reg;
        tGATT_BG_CONN_DEV   *p_bg_dev=NULL;
        UINT16              conn_id;
    
        p_bg_dev = gatt_find_bg_dev(p_tcb->peer_bda);
    ...
    
        if (gatt_num_apps_hold_link(p_tcb) &&  p_tcb->att_lcid == L2CAP_ATT_CID )
        {
            /* disable idle timeout if one or more clients are holding the link disable the idle timer */
            GATT_SetIdleTimeout(p_tcb->peer_bda, GATT_LINK_NO_IDLE_TIMEOUT, p_tcb->transport);
        }
    }

    我们看到了GATT_SetIdleTimeout ,这个函数从名字上面 看就是设置了GATT所在link的 timeout的时间。

    void GATT_SetIdleTimeout (BD_ADDR bd_addr, UINT16 idle_tout, tBT_TRANSPORT transport)
    {
        tGATT_TCB       *p_tcb;
        BOOLEAN         status = FALSE;
    
        if ((p_tcb = gatt_find_tcb_by_addr (bd_addr, transport)) != NULL)
        {
            if (p_tcb->att_lcid == L2CAP_ATT_CID)
            {
                status = L2CA_SetFixedChannelTout (bd_addr, L2CAP_ATT_CID, idle_tout);
    
                if (idle_tout == GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP)
                    L2CA_SetIdleTimeoutByBdAddr(p_tcb->peer_bda,
                                                GATT_LINK_IDLE_TIMEOUT_WHEN_NO_APP, BT_TRANSPORT_LE);
            }
            else
            {
                status = L2CA_SetIdleTimeout (p_tcb->att_lcid, idle_tout, FALSE);
            }
        }
    
    
    }

    下面函数的注释写的非常好,我就不多加解释了:

    /*******************************************************************************
    **
    ** 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
    
        /* Is a fixed channel connected to the remote BDA ?*/
        p_lcb = l2cu_find_lcb_by_bd_addr (rem_bda, transport);
    ...
    
        p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->fixed_chnl_idle_tout = idle_tout;//设置timeout 时间
    
        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;
    }

    在l2cu_no_dynamic_ccbs里面进行 idle timer 的设置。关于link timeout 的设置暂时就讲到这里。我们能够发现,这个link timeout与link 本身无关,而是和跑在link 上面的应用有关。


  • 相关阅读:
    【原创】阿里云 ECS 腾讯云CVM(云服务器)网站搭建教程 + PHP+MYSQL环境搭建教程
    50+慕课收费资源下载
    王雨的jquery练习01---显示隐藏列表
    王雨的JavaScript练习06---js实现动画效果(2)
    王雨的JavaScript练习05---js实现动画效果(1)
    王雨的JavaScript练习04---DOM操作CSS
    王雨的JavaScript练习03---Ajax初体验
    王雨的JavaScript练习02---复习DOM,充实文档内容
    王雨的JavaScript练习01---js幻灯(纯手打)
    目前最高清C值解析接口!还有各类VIP接口![9.09]
  • 原文地址:https://www.cnblogs.com/libs-liu/p/9379337.html
Copyright © 2011-2022 走看看