LE Peripheral Privacy
ble_peripheral_privacy 示例工程用于展示 Privacy 管理模块的使用。 ble_peripheral_privacy 示例工程基于 ble_peripheral 示例工程实现一个 Privacy 管理模块。
更多关于 Peripheral role 的信息可参考 LE Peripheral。
更多关于 Privacy 管理模块的信息可参考 代码介绍 中的 Privacy 管理模块。
环境需求
该示例支持以下开发套件:
Hardware Platforms |
Board Name |
---|---|
RTL8752H HDK |
RTL8752H EVB |
为快速搭建起开发环境,可参考 快速入门 中提供的详细指导。
硬件连线
请参考 快速入门 中的 RTL8752H EVB 接口和模块。
该示例工程需要支持用户命令接口。连线请参考 用户命令接口 中的 Data UART 连接。
配置
配置选项
该示例工程全部配置选项在
src\sample\ble_peripheral_privacy\app_flags.h
中,
开发者可以根据实际需求配置。
/** @brief Configure APP LE link number */
#define APP_MAX_LINKS 1
/** @brief User command: 0-Close user command, 1-Open user command */
#define USER_CMD_EN 1
/** @brief Configure Privacy1.2 feature: 0-Closed, 1-Open */
#define APP_PRIVACY_EN 1
#if APP_PRIVACY_EN
/** @brief Configure the authentication requirement of simple_ble_service.c */
#define SIMP_SRV_AUTHEN_EN 1
#endif
Privacy 配置
所有 privacy 相关的代码用宏
APP_PRIVACY_EN
隔开。Service 的 Security Requirement
所有 service security 相关的代码用宏
SIMP_SRV_AUTHEN_EN
隔开。如果开发者想使用 privacy,就要打开 security。更多信息请参考 代码介绍 中的 Service 的 Security 要求。
生成系统配置文件
开发者可以通过 MP Tool 配置以下项目:
Configurable Item |
Value |
---|---|
LE Slave Link Num |
≥ APP_MAX_LINKS |
更多有关于 MP Tool 配置的信息请参考 快速入门 中的 生成 System Config File。
编译和下载
该示例可以在 SDK 文件夹中找到:
Project file: board\evb\ble_peripheral_privacy\mdk
Project file: board\evb\ble_peripheral_privacy\gcc
编译和运行该示例请遵循以下步骤:
打开项目文件。
要编译目标文件,请参考 快速入门 的 编译 APP Image 中列出的步骤。
编译成功后,目录
board\evb\ble_peripheral_privacy\mdk\bin
下会生成 app bin 文件app_MP_sdk_xxx.bin
。在 EVB 上按下 reset 键,程序将开始运行。
测试验证
将示例工程烧录到 EVB 后,开发者可以使用一个装有 LightBlue
之类 LE 应用的手机与之对测。
准备开发板
准备步骤
使用 MP Tool 将 DUT 地址设为[00:11:22:33:44:80],然后编译 ble_peripheral_privacy 示例工程,并下载 images 到 DUT。 开发者可以在电脑的串口辅助工具端输入用户命令,请参考 用户命令接口 中的 如何使用命令。
更多关于如何修改 蓝牙地址 的细节请查阅 快速入门 中的 生成 System Config File。
测试步骤
按 DUT 上的
reset
键,开发者可以在电脑的串口辅助工具端输入用户命令。在 iOS 设备上打开
LightBlue
,搜索并连接 DUT,如图所示:与 iOS 设备测试
开发者可以在手机端与 peripheral 设备进行扫描,连接,配对,查服务,删除绑定信息,断线等交互。
DUT 也可以使用以下用户命令主动断开连线及清除绑定信息:
DUT User Command
Description
DUT Log
disc 0
DUT 断开连线
Serial port assistant tool 显示:
Disconnect conn_id 0bondclear
清除 DUT 端的绑定信息
Debug Analyzer 显示:
[GAP] !**le_clear_all_keys
代码介绍
本章将按照以下几个部分进行介绍:
源码路径 中介绍项目目录和源代码文件。
Bluetooth Host 介绍 中介绍了 Bluetooth Host 相关信息。
Privacy 管理模块 中介绍了 Privacy 管理模块。
Privacy 使用流程 中介绍了 Privacy 管理模块的使用流程。
Service 的 Security 要求 中介绍了服务的 Security 要求。
源码路径
工程目录:
board\evb\ble_peripheral_privacy
。源码目录:
src\sample\ble_peripheral_privacy
。
ble_peripheral_privacy 示例工程中的源文件当前被分为几个组,如下所示。
└── Project: peripheral_privacy
└── peripheral_privacy
└── include
├── lib Includes all binary symbol files that user application is built on.
├── ROM.lib
├── gap_utils.lib
├── lowerstack.lib
└── rtl8752h_sdk.lib
├── cmsis Includes startup code.
├── peripheral Includes all peripheral drivers and module code used by the application.
├── profile Includes LE profiles or services used by the sample application.
└── app Includes the ble_peripheral_privacy user application implementation.
├── app_task.c
├── main.c
├── peripheral_privacy_app.c
├── privacy_mgnt.c
├── overlay_mgr.c
├── user_cmd.c
├── data_uart.c
└── user_cmd_parse.c
示例工程使用与 upperstack_0_0
匹配的默认 GAP LIB,请参阅文档 Host Image 中的 GAP LIB 的使用方法 以获取更多信息。
Bluetooth Host 介绍
示例工程默认使用 Bluetooth Host image 版本 upperstack_0_0
,更多信息可参阅
Host Image。
关于 Bluetooth Host 所支持的蓝牙功能的详细信息,请参阅文件 bin\upperstack_img\upperstack_0_0\upperstack_config.h
。
初始化
当 EVB 启动并且芯片复位时,main()
函数将被调用,它执行以下初始化函数:
int main(void)
{
board_init();
le_gap_init(APP_MAX_LINKS);
gap_lib_init();
app_le_gap_init();
app_le_profile_init();
pwr_mgr_init();
task_init();
os_sched_start();
return 0;
}
le_gap_init()
用于初始化 GAP 并配置链接个数。app_le_profile_init()
用于初始化 profile。app_le_gap_init()
用于初始化 GAP 参数,开发者可以自行配置以下参数:Device name 和 device appearance,Advertising 参数,GAP Bond Manager 参数(请参考 LE Host 中的 Device Name 和 Device Appearance 的配置, Advertising 参数的配置, Pairing 参数的配置 , 其它参数的配置)。
void app_le_gap_init(void)
{
/* Device name and device appearance */
......
/* Advertising parameters */
......
/* GAP Bond Manager parameters */
......
/* Set device name and device appearance */
le_set_gap_param(GAP_PARAM_DEVICE_NAME, GAP_DEVICE_NAME_LEN, device_name);
le_set_gap_param(GAP_PARAM_APPEARANCE, sizeof(appearance), &appearance);
le_set_gap_param(GAP_PARAM_SLAVE_INIT_GATT_MTU_REQ, sizeof(slave_init_mtu_req),
&slave_init_mtu_req);
/* Set advertising parameters */
le_adv_set_param(GAP_PARAM_ADV_EVENT_TYPE, sizeof(adv_evt_type), &adv_evt_type);
le_adv_set_param(GAP_PARAM_ADV_DIRECT_ADDR_TYPE, sizeof(adv_direct_type), &adv_direct_type);
le_adv_set_param(GAP_PARAM_ADV_DIRECT_ADDR, sizeof(adv_direct_addr), adv_direct_addr);
le_adv_set_param(GAP_PARAM_ADV_CHANNEL_MAP, sizeof(adv_chann_map), &adv_chann_map);
le_adv_set_param(GAP_PARAM_ADV_FILTER_POLICY, sizeof(adv_filter_policy), &adv_filter_policy);
le_adv_set_param(GAP_PARAM_ADV_INTERVAL_MIN, sizeof(adv_int_min), &adv_int_min);
le_adv_set_param(GAP_PARAM_ADV_INTERVAL_MAX, sizeof(adv_int_max), &adv_int_max);
le_adv_set_param(GAP_PARAM_ADV_DATA, sizeof(adv_data), (void *)adv_data);
le_adv_set_param(GAP_PARAM_SCAN_RSP_DATA, sizeof(scan_rsp_data), (void *)scan_rsp_data);
/* Setup the GAP Bond Manager */
gap_set_param(GAP_PARAM_BOND_PAIRING_MODE, sizeof(auth_pair_mode), &auth_pair_mode);
gap_set_param(GAP_PARAM_BOND_AUTHEN_REQUIREMENTS_FLAGS, sizeof(auth_flags), &auth_flags);
gap_set_param(GAP_PARAM_BOND_IO_CAPABILITIES, sizeof(auth_io_cap), &auth_io_cap);
gap_set_param(GAP_PARAM_BOND_OOB_ENABLED, sizeof(auth_oob), &auth_oob);
le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY, sizeof(auth_fix_passkey), &auth_fix_passkey);
le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY_ENABLE, sizeof(auth_use_fix_passkey),
&auth_use_fix_passkey);
le_bond_set_param(GAP_PARAM_BOND_SEC_REQ_REQUIREMENT, sizeof(auth_sec_req_flags),
&auth_sec_req_flags);
/* register gap message callback */
le_register_app_cb(app_gap_callback);
#if APP_PRIVACY_EN
privacy_init(app_privacy_callback, true);
#endif
}
更多关于 LE GAP 初始化和启动流程的信息可以查阅 LE Host 中的 GAP 参数的初始化。
Privacy 管理模块
Privacy 管理模块的开发是基于头文件 gap_privacy.h
的。
若开发者需要使用 Privacy 管理模块,可以使用头文件 privacy_mgnt.h
用于开发 privacy 相关应用。
对于 RTL8752H,Privacy 管理模块的文件路径如下:
源文件:
src\ble\privacy\privacy_mgnt.c
头文件:
src\ble\privacy\privacy_mgnt.h
开发者需要在工程的 app group 中添加 privacy_mgnt.c
文件,在 Include Paths 中添加头文件路径。
接口
Privacy 管理模块的接口都在头文件 privacy_mgnt.h
中定义。
/* privacy_mgnt.h APIs */
typedef void(*P_FUN_PRIVACY_STATE_CB)(T_PRIVACY_CB_TYPE type, T_PRIVACY_CB_DATA cb_data);
void privacy_init(P_FUN_PRIVACY_STATE_CB p_fun, bool whitelist);
T_PRIVACY_STATE privacy_handle_resolv_list(void);
void privacy_handle_bond_modify_msg(T_LE_BOND_MODIFY_TYPE type, T_LE_KEY_ENTRY *p_entry,
bool handle_add);
bool privacy_add_device(T_LE_KEY_ENTRY *p_entry);
T_GAP_CAUSE privacy_set_addr_resolution(bool enable);
T_GAP_CAUSE privacy_read_peer_resolv_addr(T_GAP_REMOTE_ADDR_TYPE peer_address_type,
uint8_t *peer_address);
T_GAP_CAUSE privacy_read_local_resolv_addr(T_GAP_REMOTE_ADDR_TYPE peer_address_type,
uint8_t *peer_address);
Privacy 管理模块的使用方法
Privacy 管理模块初始化
APP 需要调用 privacy_init()
,以初始化 Privacy 管理模块并注册回调函数。
void app_le_gap_init(void)
{
/* register gap message callback */
le_register_app_cb(app_gap_callback);
#if APP_PRIVACY_EN
privacy_init(app_privacy_callback, true);
#endif
}
privacy_init()
的参数 whitelist
用于配置对 white list 的管理。若 whitelist 参数为 true,当 Privacy 管理模块修改 resolving list 时,Privacy 管理模块会管理 white list。若 whitelist 参数为 false,APP 需要管理 white list。
回调函数用于处理 Privacy 管理模块发送的消息。
void app_privacy_callback(T_PRIVACY_CB_TYPE type, T_PRIVACY_CB_DATA cb_data)
{
APP_PRINT_INFO1("app_privacy_callback: type %d", type);
switch (type)
{
case PRIVACY_STATE_MSGTYPE:
app_privacy_state = cb_data.privacy_state;
APP_PRINT_INFO1("PRIVACY_STATE_MSGTYPE: status %d", app_privacy_state);
break;
case PRIVACY_RESOLUTION_STATUS_MSGTYPE:
app_privacy_resolution_state = cb_data.resolution_state;
APP_PRINT_INFO1("PRIVACY_RESOLUTION_STATUS_MSGTYPE: status %d", app_privacy_resolution_state);
break;
default:
break;
}
}
Resolving List 的管理
Privacy 管理模块的关键功能是当绑定信息变化时,用于管理 resolving list 和 white list。white list 的管理是可选特性, privacy_init()
的 whitelist
参数用于配置对 white list 的管理。
当 APP 处理 GAP_MSG_LE_BOND_MODIFY_INFO
消息时,APP 必须调用函数 privacy_handle_bond_modify_msg()
。
Privacy 管理模块将根据绑定信息的修改类型管理 resolving list,流程如下:
LE_BOND_DELETE:在 resolving list 删除该设备。
LE_BOND_ADD:在 resolving list 中加入该设备。
LE_BOND_CLEAR:清除 resolving list。
T_APP_RESULT app_gap_callback(uint8_t cb_type, void *p_cb_data)
{
T_APP_RESULT result = APP_RESULT_SUCCESS;
T_LE_CB_DATA *p_data = (T_LE_CB_DATA *)p_cb_data;
switch (cb_type)
{
#if APP_PRIVACY_EN
case GAP_MSG_LE_BOND_MODIFY_INFO:
APP_PRINT_INFO1("GAP_MSG_LE_BOND_MODIFY_INFO: type 0x%x",
p_data->p_le_bond_modify_info->type);
privacy_handle_bond_modify_msg(p_data->p_le_bond_modify_info->type,
p_data->p_le_bond_modify_info->p_entry, true);
break;
#endif
......
}
当 address resolution 已启用,在以下场景,不能执行对 resolving list 的修改流程:
已启用 advertising。
已启用 scanning。
正在创建连线
当 address resolution 未启用,resolving list 的修改流程可以在任何时候执行。
当 GAP 设备状态为 idle 状态时,APP 必须调用函数 privacy_handle_resolv_list()
。 privacy_handle_resolv_list()
用于处理挂起的 resolving list 修改流程。
void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause)
{
APP_PRINT_INFO3("app_handle_dev_state_evt: init state %d, adv state %d, cause 0x%x",
new_state.gap_init_state, new_state.gap_adv_state, cause);
#if APP_PRIVACY_EN
if ((new_state.gap_init_state == GAP_INIT_STATE_STACK_READY)
&& (new_state.gap_adv_state == GAP_ADV_STATE_IDLE)
&& (new_state.gap_conn_state == GAP_CONN_DEV_STATE_IDLE))
{
privacy_handle_resolv_list();
}
#endif
......
}
回调类型 PRIVACY_STATE_MSGTYPE
用于通知 resolving list 修改流程的状态。回调数据为 T_PRIVACY_STATE
。
typedef enum
{
PRIVACY_STATE_INIT, //!< Privacy management module is not initialized.
PRIVACY_STATE_IDLE, //!< Idle. No pending resolving list modification procedure.
PRIVACY_STATE_BUSY //!< Busy. Resolving list modification procedure is not completed.
} T_PRIVACY_STATE;
Address Resolution
若对端设备使用 resolvable private address,APP 希望使用 white list 过滤该对端设备。APP 需要调用函数 privacy_set_addr_resolution()
以启用 address resolution。
若 APP 希望与新的设备配对,APP 需要调用函数 privacy_set_addr_resolution()
以禁用 address resolution。当 address resolution 已启用,本地设备不能与不属于 resolving list 的设备建立连线。示例代码如下:
void app_adv_start(void)
{
uint8_t adv_evt_type = GAP_ADTYPE_ADV_IND;
uint8_t adv_filter_policy = GAP_ADV_FILTER_ANY;
#if APP_PRIVACY_EN
T_LE_KEY_ENTRY *p_entry;
p_entry = le_get_high_priority_bond();
if (p_entry == NULL)
{
/* No bonded device, send connectable undirected advertising event without using whitelist*/
app_work_mode = APP_PAIRABLE_MODE;
adv_filter_policy = GAP_ADV_FILTER_ANY;
if (app_privacy_resolution_state == PRIVACY_ADDR_RESOLUTION_ENABLED)
{
privacy_set_addr_resolution(false);
}
}
else
{
app_work_mode = APP_RECONNECTION_MODE;
adv_filter_policy = GAP_ADV_FILTER_WHITE_LIST_ALL;
if (app_privacy_resolution_state == PRIVACY_ADDR_RESOLUTION_DISABLED)
{
privacy_set_addr_resolution(true);
}
}
#endif
le_adv_set_param(GAP_PARAM_ADV_EVENT_TYPE, sizeof(adv_evt_type), &adv_evt_type);
le_adv_set_param(GAP_PARAM_ADV_FILTER_POLICY, sizeof(adv_filter_policy), &adv_filter_policy);
le_adv_start();
}
函数 privacy_set_addr_resolution()
用于启用 Controller 中对 resolvable private address 的解析功能。这将使得 Controller 收到本地或对端的 resolvable private address 时,Controller 将使用 resolving list。
除了以下场景,其它时间均可启用调用该函数:
已启用 advertising。
已启用 scanning。
正在创建连线。
回调类型 PRIVACY_RESOLUTION_STATUS_MSGTYPE
用于通知 address resolution 的状态。回调数据为 T_PRIVACY_ADDR_RESOLUTION_STATE
。
/** @brief Define the privacy address resolution state */
typedef enum
{
PRIVACY_ADDR_RESOLUTION_DISABLED,
PRIVACY_ADDR_RESOLUTION_DISABLING,
PRIVACY_ADDR_RESOLUTION_ENABLING,
PRIVACY_ADDR_RESOLUTION_ENABLED
} T_PRIVACY_ADDR_RESOLUTION_STATE;
Privacy 使用流程
在 APP 中有两种模式:配对模式和回连模式。处于配对模式的设备可以与任何设备建立连线,必须禁用 address resolution 和 white list filter policy;处于回连模式的设备只能与在 resolving list 和 white list 中的设备建立连线。若 APP 希望过滤使用 resolvable private address 的设备,则必须调用函数 privacy_set_addr_resolution()
以启用 address resolution。
peripheral privacy 应用的流程如图所示:

Peripheral Privacy APP 流程图
Service 的 Security 要求
所有 service security 相关的代码通过宏定义 SIMP_SRV_AUTHEN_EN
分隔。
若 APP 希望使用 privacy,那么 APP 需要启用 security。
更多信息参见 LE Host 中的 Service 的 Security 要求。
/* client characteristic configuration */
{
ATTRIB_FLAG_VALUE_INCL | ATTRIB_FLAG_CCCD_APPL, /* flags */
{ /* type_value */
LO_WORD(GATT_UUID_CHAR_CLIENT_CONFIG),
HI_WORD(GATT_UUID_CHAR_CLIENT_CONFIG),
/* NOTE: this value has an instantiation for each client, a write to */
/* this attribute does not modify this default value: */
LO_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT), /* client char. config. bit field */
HI_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT)
},
2, /* bValueLen */
NULL,
#if SIMP_SRV_AUTHEN_EN
(GATT_PERM_READ_AUTHEN_REQ | GATT_PERM_WRITE_AUTHEN_REQ) /* permissions */
#else
(GATT_PERM_READ | GATT_PERM_WRITE) /* permissions */
#endif
},