博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
蓝牙 link timeout分析
阅读量:5955 次
发布时间:2019-06-19

本文共 11369 字,大约阅读时间需要 37 分钟。

蓝牙主机和蓝牙设备建立连接之后,会在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。

其实在 中已经描述了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 上面的应用有关。


 

转载于:https://www.cnblogs.com/libs-liu/p/9379337.html

你可能感兴趣的文章
Spring学习总结(2)——Spring的常用注解
查看>>
关于IT行业人员吃的都是青春饭?[透彻讲解]
查看>>
钱到用时方恨少(随记)
查看>>
mybatis主键返回的实现
查看>>
org.openqa.selenium.StaleElementReferenceException
查看>>
数论之 莫比乌斯函数
查看>>
linux下查找某个文件位置的方法
查看>>
python之MySQL学习——数据操作
查看>>
Harmonic Number (II)
查看>>
长连接、短连接、长轮询和WebSocket
查看>>
day30 模拟ssh远程执行命令
查看>>
做错的题目——给Array附加属性
查看>>
Url.Action取消字符转义
查看>>
JQuery选择器大全
查看>>
Gamma阶段第三次scrum meeting
查看>>
python3之装饰器修复技术@wraps
查看>>
[考试]20150606
查看>>
Javascript_备忘录5
查看>>
Can’t create handler inside thread that has not called Looper.prepare()
查看>>
敏捷开发方法综述
查看>>