前面几篇文章介绍了设备管理对上层的接口和bt profile层的实现,其中涉及到蓝牙开关(初始化)、设备扫描、服务发现、安全管理、功耗管理等等模块,本文将挑选几个模块介绍bt stack层的实现,其他模块(如安全管理)将在后续专门讲述。
使能蓝牙
协议栈中使能蓝牙的入口是BTM_DeviceReset
,即打开蓝牙时会复位controller,复位之后会获取controller中的信息,然后将状态上报到上层。
其中,controller模块和HCI的内容在HCI相关的文章中介绍。
从上至下完整梳理使能蓝牙的流程如下:
设备扫描
在上层介绍设备管理时包含了设备扫描和服务发现,本文介绍设备扫描,服务发现使用的SDP,在后续介绍。
在stack层中对上提供设备扫描相关的API如下:
// inquiry/inquiry scan/page/page scan设置
tBTM_STATUS BTM_SetInquiryMode(uint8_t mode);
tBTM_STATUS BTM_SetInquiryScanType(uint16_t scan_type);
tBTM_STATUS BTM_SetPageScanType(uint16_t scan_type);
extern void BTM_WritePageTimeout(uint16_t timeout);
// 设置/获取本机可被发现及参数(inquiry scan)
tBTM_STATUS BTM_SetDiscoverability(uint16_t inq_mode, uint16_t window, uint16_t interval);
uint16_t BTM_ReadDiscoverability(uint16_t* p_window, uint16_t* p_interval);
// 设置/获取本机可被连接及参数(page scan)
tBTM_STATUS BTM_SetConnectability(uint16_t page_mode, uint16_t window, uint16_t interval);
uint16_t BTM_ReadConnectability(uint16_t* p_window, uint16_t* p_interval);
// 扫描周围设备相关(inquiry)
tBTM_STATUS BTM_StartInquiry(tBTM_INQ_PARMS* p_inqparms, tBTM_INQ_RESULTS_CB* p_results_cb, tBTM_CMPL_CB* p_cmpl_cb);
uint16_t BTM_IsInquiryActive(void);
tBTM_STATUS BTM_CancelInquiry(void);
tBTM_STATUS BTM_CancelPeriodicInquiry(void);
// EIR相关
tBTM_STATUS BTM_WriteEIR(BT_HDR* p_buff);
bool BTM_HasEirService(const uint32_t* p_eir_uuid, uint16_t uuid16);
void BTM_AddEirService(uint32_t* p_eir_uuid, uint16_t uuid16);
void BTM_RemoveEirService(uint32_t* p_eir_uuid, uint16_t uuid16);
uint8_t BTM_GetEirSupportedServices(uint32_t* p_eir_uuid, uint8_t** p, uint8_t max_num_uuid16, uint8_t* p_num_uuid16);
uint8_t BTM_GetEirUuidList(uint8_t* p_eir, size_t eir_len, uint8_t uuid_size, uint8_t* p_num_uuid, uint8_t* p_uuid_list, uint8_t max_num_uuid);
inquiry和inquiry scan
蓝牙通过inquiry发现周围的设备,进入可[被]发现的设备会一直inquiry scan,它们在特殊的物理信道发起请求和回复响应。
以手机和蓝牙耳机为例:手机扫描周围设备时会一直特定信道发送inquiry,耳机进入配对模式时会一直在特定信道扫描(监听)inquiry,当扫描到inquiry之后会回复inquiry response,手机收到response后就知道扫描到了这台设备。inquiry类型和方式如下:
// 类型
#define BTM_INQ_RESULT_STANDARD 0 // 标准inquiry格式返回结果
#define BTM_INQ_RESULT_WITH_RSSI 1 // 带RSSI的格式
#define BTM_INQ_RESULT_EXTENDED 2 // 带RSSI的结果或扩展查询格式
// 方式
#define BTM_GENERAL_INQUIRY 0x01 //通用inquiry,即发现所有可发现的设备
#define BTM_LIMITED_INQUIRY 0x02 // 有限inquiry,即发现一定范围内设置了有限inquiry代码的设备
通用inquiry的交互如下:
有限inquiry的交互如下:
EIR
EIR是Extended Inquiry Response的缩写,EIR是基本inquiry response外还可以携带更多的信息,即可以在不需要更多通信的情况下获得更多信息,如设备名称、支持的服务等。EIR数据格式如下:
page和page scan
当获取到另一台蓝牙设备的蓝牙地址后可以发起连接,此时发起连接的设备将在特殊通道发生连接请求包(page),另一台设备处于可连接状态即可监听(page scan)到连接请求,进行后续的通信。
inquiry scan和page scan的类型如下:
#define BTM_SCAN_TYPE_STANDARD 0 //标准扫描,一直扫描窗口内一直处于扫描状态
#define BTM_SCAN_TYPE_INTERLACED 1 //隔行扫描,在扫描窗口内进行两次背靠背扫描,要求扫描间隔必须大于等于扫描窗口的两倍
在标准扫描的情况下,如果相关器在页面扫描期间超过触发阈值,设备应进入第 8.3.3.1 节所述的外设响应子状态。扫描设备也可使用通用隔行扫描。在这种情况下,如果相关器在第一次扫描时未超过触发阈值,则应使用 [CLKN 16-12 + interlace_offset] mod 32 所确定的序列中的相位进行第二次扫描。如果在第二次扫描中相关器超过触发阈值,则设备应进入外设响应子状态,在 Xprp 的计算中使用 [CLKN 16-12 + interlace_offset] mod 32 作为冻结 CLKN*(详见第 2.6.4.3 节)。如果相关器在正常模式下的一次扫描或隔行扫描模式下的第二次扫描中未超过触发阈值,则应返回待机或连接状态。
隔行扫描偏移值范围为 0 至 31。除非不可扫描的插槽模式意味着应使用不同的值,否则应使用 16 值。
状态转化如下: