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 配置以下项目:

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

编译和运行该示例请遵循以下步骤:

  1. 打开项目文件。

  2. 要编译目标文件,请参考 快速入门编译 APP Image 中列出的步骤。

  3. 编译成功后,目录 board\evb\ble_peripheral_privacy\mdk\bin 下会生成 app bin 文件 app_MP_sdk_xxx.bin

  4. 要下载 app bin 到 EVB,请参考 快速入门MP Tool 中列出的步骤。

  5. 在 EVB 上按下 reset 键,程序将开始运行。

测试验证

将示例工程烧录到 EVB 后,开发者可以使用一个装有 LightBlue 之类 LE 应用的手机与之对测。

准备开发板

准备一块开发板命名为 DUT,开发者可以使用 Debug AnalyzerLog 验证

准备步骤

使用 MP Tool 将 DUT 地址设为[00:11:22:33:44:80],然后编译 ble_peripheral_privacy 示例工程,并下载 images 到 DUT。 开发者可以在电脑的串口辅助工具端输入用户命令,请参考 用户命令接口 中的 如何使用命令

更多关于如何修改 蓝牙地址 的细节请查阅 快速入门 中的 生成 System Config File

测试步骤

  1. DUT 上的 reset 键,开发者可以在电脑的串口辅助工具端输入用户命令。

  2. 在 iOS 设备上打开 LightBlue,搜索并连接 DUT,如图所示:

    ../../../../_images/test_with_ios.png

    与 iOS 设备测试

    开发者可以在手机端与 peripheral 设备进行扫描,连接,配对,查服务,删除绑定信息,断线等交互。

  3. DUT 也可以使用以下用户命令主动断开连线及清除绑定信息:

DUT User Command

Description

DUT Log

disc 0

DUT 断开连线

Serial port assistant tool 显示:

Disconnect conn_id 0

bondclear

清除 DUT 端的绑定信息

Debug Analyzer 显示:

[GAP] !**le_clear_all_keys

代码介绍

本章将按照以下几个部分进行介绍:

源码路径

  • 工程目录: 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;
 }
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 应用的流程如图所示:

../../../../_images/Peripheral_Privacy_APP_Flow_Chart.png

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
},