蓝牙主机和蓝牙设备建立连接之后,会在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)) != )
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[]; // Check for supported channel
if (!(peer_channel_mask & ( << fixed_cid)))
{
L2CAP_TRACE_EVENT ("%s() CID:0x%04x BDA: %08x%04x not supported", __func__,
fixed_cid,(rem_bda[]<<)+(rem_bda[]<<)+(rem_bda[]<<)+rem_bda[],
(rem_bda[]<<)+rem_bda[]);
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, , p_lcb->transport);//回调,这里是重点
#else
(*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)
(fixed_cid, p_lcb->remote_bd_addr, TRUE, , 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, , sizeof(tGATT_CB));
memset (&fixed_reg, , 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 = ;
fixed_reg.fixed_chnl_opts.mon_tout = ;
fixed_reg.fixed_chnl_opts.mps = ;
fixed_reg.fixed_chnl_opts.tx_win_sz = ; 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 上面的应用有关。


蓝牙 link timeout分析的更多相关文章

  1. Nginx 504 Gateway Time-out分析及解决方法

    一.场景还原php程序在执行抓取远程图片库并保存至本地服务器的时候,出现了“504 Gateway Time-out”错误提示. 问题定位:由于图片巨多,所以下载时间很长(10分钟以上),引起网关超时 ...

  2. Lock wait timeout分析

    ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction分析 1.4个用户连接数据库(A和D是本地回环登陆, ...

  3. 低功耗蓝牙4.0BLE编程-nrf51822开发(11)-蓝牙串口代码分析

    代码实例:点击打开链接 实现的功能是从uart口发送数据至另一个蓝牙串口,或是从蓝牙读取数据通过uart打印出数据. int main(void) { // Initialize leds_init( ...

  4. Android4.4 蓝牙源代码段分析

    最近GOOGLE发布时间Android4.4,我看了看源代码.4.4蓝牙打开过程或这部分的一些变化,判断蓝牙开关是从接口设置settings在里面switch开关,widget当然,它可以切换,也许启 ...

  5. 蓝牙spp协议分析

    基本概念 蓝牙串口是基于 SPP 协议(Serial Port Profile),能在蓝牙设备之间创建串口进行数据传输的一种设备. 蓝牙串口的目的是针对如何在两个不同设备(通信的两端)上的应用之间保证 ...

  6. 动态添加LInk的分析

    动态创建HyperLink超链接: 1.如果添加HyperLink的代码只写在Button中,则只会显示最后一次添加的内容.所以必须在Pageload中添加. 2.首次载入: PageLoad    ...

  7. 蓝牙speaker配对流程源码分析

    这篇文章简单分析一下 蓝牙音箱配对流程.现在的音箱基本都支持security simple pairing.所以这里的流程基本上就是ssp的代码流程. 源码参考的是 Android 6.0 上面的bl ...

  8. Android上成功实现了蓝牙的一些Profile

    前段时间做蓝牙方面的开发,Google的Android只实现了Handset/Handfree和A2DP/AVRCP等Profile,而其 它常用的Profile如HID/DUN/SPP/OPP/FT ...

  9. 蓝牙disable流程简述

    蓝牙关闭的流程比打开流程要简单,主要就是一些profile的断连以及协议栈相关结构的释放. 这里简单说一下其流程,就直接从协议栈的disable的接口说起了. static int disable(v ...

随机推荐

  1. 二. Redis 安全性

    由于Redis速度相当快,当一台服务器比较好的时候,一个外部用户可以在一秒钟内进行150K(15万)次的密码尝试,因此意味着你需要设置一个非常非常强大的密码来防止暴力破解. 1.设置密码 (1). 通 ...

  2. Apache与Tomcat有什么关系和区别

    Apache 和 Tomcat 都是web网络服务器,两者既有联系又有区别,在进行HTML.PHP.JSP.Perl等开发过程中,需要准确掌握其各自特点,选择最佳的服务器配置. Apache是web服 ...

  3. IP负载均衡

    推荐一篇关于LVS的好文: https://www.cnblogs.com/gaoxu387/p/7941381.html 一.原博主要内容: 1.概述 IP负载均衡:四层负载,是基于IP+端口的负载 ...

  4. C#检测U盘是否插入

    public partial class Form1 : Form { #region u盘属性 public const int WM_DEVICECHANGE = 0x219;//U盘插入后,OS ...

  5. win7 系统中的加密文件打不开了

    网友提问:我原来安装的时候win7 32位 旗舰版系统,因为想电脑达到最大运行速度,所以决定把系统重装为64位的win 7系统.[了解win 7 32位于64位的区别]因为重装前未解密bitlocke ...

  6. EBS-新增和更新价目表行

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/gh320/article/details/36666133  新增和更新价目表行 --目的:在已 ...

  7. HTML和CSS实现左侧固定宽度右侧内容可滚动

    在做移动端页面的时候,经常会碰到一个div中分左右两个div,左侧div固定宽度或百分比,右侧div中内容左右溢出,需要左右滑动才可以浏览到全部内容,为此写了一个demo. 处理这个问题的核心关键点是 ...

  8. UVa 11846 - Finding Seats Again

    链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...

  9. BZOJ3295:[CQOI2011]动态逆序对(CDQ分治)

    Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计 ...

  10. [SDOI2010]捉迷藏

    嘟嘟嘟 k-d tree板儿题. 建完树后对每一个点求一遍最小和最大曼哈顿距离,是曼哈顿,不是欧几里得. #include<cstdio> #include<iostream> ...