diff --git a/components/bt/Kconfig b/components/bt/Kconfig index d71e1fe85f..dd0639e27b 100644 --- a/components/bt/Kconfig +++ b/components/bt/Kconfig @@ -1755,6 +1755,17 @@ if BLE_MESH This option specifies how many application keys the Provisioner can have. Indeed, this value decides the number of the application keys which can be added by a Provisioner. + config BLE_MESH_PROVISIONER_HB_FILTER_SIZE + int "Maximum number of filter entries for receiving heartbeat by Provisioner" + default 3 + range 1 1000 + help + This option specifies how many heartbeat filter entries Provisioner supports. + The entries of heartbeat filter (whitelist or blacklist) are used to store a + list of addresses which can be used to decide which heartbeat will be processed + and notified to the application layer by Provisioner. And in default, the filter + is blacklist and empty. + endif # BLE_MESH_PROVISIONER # Virtual option enabled whenever Generic Provisioning layer is needed diff --git a/components/bt/esp_ble_mesh/api/core/esp_ble_mesh_networking_api.c b/components/bt/esp_ble_mesh/api/core/esp_ble_mesh_networking_api.c index 67f4c352d5..b5f47eb373 100644 --- a/components/bt/esp_ble_mesh/api/core/esp_ble_mesh_networking_api.c +++ b/components/bt/esp_ble_mesh/api/core/esp_ble_mesh_networking_api.c @@ -719,6 +719,69 @@ uint8_t esp_ble_mesh_provisioner_get_free_settings_user_id_count(void) } #endif /* CONFIG_BLE_MESH_USE_MULTIPLE_NAMESPACE */ +esp_err_t esp_ble_mesh_provisioner_start_recv_heartbeat(void) +{ + btc_msg_t msg = {0}; + + ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_START_RECV_HEARTBEAT; + + return (btc_transfer_context(&msg, NULL, 0, NULL) == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_set_heartbeat_filter_type(uint8_t filter_type) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (filter_type > ESP_BLE_MESH_PROVISIONER_HB_FILTER_BLACKLIST) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE; + + arg.set_heartbeat_filter_type.filter_type = filter_type; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} + +esp_err_t esp_ble_mesh_provisioner_set_heartbeat_filter_info(uint8_t op_flag, esp_ble_mesh_provisioner_hb_filter_info_t *info) +{ + btc_ble_mesh_prov_args_t arg = {0}; + btc_msg_t msg = {0}; + + if (op_flag > ESP_BLE_MESH_PROVISIONER_HB_FILTER_CLEAN || info == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (!ESP_BLE_MESH_ADDR_IS_UNICAST(info->hb_src) && + !ESP_BLE_MESH_ADDR_IS_UNICAST(info->hb_dst) && + !ESP_BLE_MESH_ADDR_IS_GROUP(info->hb_dst)) { + return ESP_ERR_INVALID_ARG; + } + + ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_PROV; + msg.act = BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_INFO; + + arg.set_heartbeat_filter_info.op_flag = op_flag; + arg.set_heartbeat_filter_info.hb_src = info->hb_src; + arg.set_heartbeat_filter_info.hb_dst = info->hb_dst; + arg.set_heartbeat_filter_info.expiry = info->expiry; + + return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_prov_args_t), NULL) + == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); +} #endif /* CONFIG_BLE_MESH_PROVISIONER */ #if (CONFIG_BLE_MESH_FAST_PROV) diff --git a/components/bt/esp_ble_mesh/api/core/include/esp_ble_mesh_networking_api.h b/components/bt/esp_ble_mesh/api/core/include/esp_ble_mesh_networking_api.h index 8a572bf812..d72cf258d7 100644 --- a/components/bt/esp_ble_mesh/api/core/include/esp_ble_mesh_networking_api.h +++ b/components/bt/esp_ble_mesh/api/core/include/esp_ble_mesh_networking_api.h @@ -601,6 +601,81 @@ uint8_t esp_ble_mesh_provisioner_get_settings_index(const char *user_id); */ uint8_t esp_ble_mesh_provisioner_get_free_settings_user_id_count(void); +/** + * @brief This function is called by Provisioner to start receiving and processing + * heartbeat messages. + * + * @note If starting receiving heartbeat message successfully, the filter will be + * an empty blacklist, which means all the heartbeat messages received by + * Provisioner will be processed and reported to the application layer. + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_start_recv_heartbeat(void); + +/** + * @brief This function is called by Provisioner to set the heartbeat filter type. + * + * @note 1. If the filter type is not the same with the current value, then all the + * addresses in the filter will be cleared. + * 2. If the previous type is blacklist, and changed to whitelist, then the + * filter will be an empty whitelist, which means no heartbeat messages will + * be reported. And users need to add source addresses into the filter, then + * heartbeat messages from these addresses will be reported. + * + * @param[in] filter_type: Heartbeat filter type (whitelist or blacklist). + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_set_heartbeat_filter_type(uint8_t filter_type); + +/** + * @brief This function is called by Provisioner to add, remove or clean the corresponding + * information from the heartbeat filter. + * + * @note 1. If the op_flag is "add", the hb_src can be set to the source address (can only + * be a unicast address) of heartbeat messages, and the hb_dst can be set to the + * destination address (unicast address or group address), at least one of them needs + * to be set. + * If only one of them is set, the filter entry will only use the configured source + * address or destination address to filter heartbeat messages. If both of them are + * set, then the source address and destination address will both be used to decide + * if a heartbeat message can be reported. + * And when the filter is whitelist, users can use the expiry (in seconds) to decide + * how long the filter entry can be used to filter heartbeat messages. If the expiry + * is set to 0, then the corresponding filter entry will be valid indefinitely. Only + * when the filter entry is removed, cleaned or the filter_type is changed (whitelist + * to blacklist), then the filter entry will be invalid. + * If part of the filter information already exists, then the corresponding filter + * entry will be updated. For example, if the source address already exists, and users + * try to add a filter entry with the same source address. In this situation, the + * existed filter entry will be updated. The same for the destination address. + * And if the source address and destination address are both set, then all the filter + * entries which contain any of the two addresses will be cleaned. After this, a new + * filter entry will be allocated to store the filter information. + * 2. If the op_flag is "remove", the hb_src can be set to the source address (can only + * be a unicast address) of heartbeat messages, and the hb_dst can be set to the + * destination address (unicast address or group address), at least one of them needs + * to be set. If only one of the two addresses is set, then the filter entry with the + * same source address or destination address will be removed. And if both of them are + * set, then only the filter entry with the same source address and destination address + * will be removed. User don't need to set the expiry parameter. + * 3. If the op_flag is "clean", then Provisioner will remove all the information from + * each heartbeat filter entry, users don't need to set the parameter info. + * + * @param[in] op_flag: Add, remove or clean + * @param[in] info: Pointer to heartbeat filter entry information, the information includes: + hb_src - Heartbeat source address; + hb_dst - Heartbeat destination address; + expiry - Period (in seconds) for receiving heartbeat messages + * + * @return ESP_OK on success or error code otherwise. + * + */ +esp_err_t esp_ble_mesh_provisioner_set_heartbeat_filter_info(uint8_t op_flag, esp_ble_mesh_provisioner_hb_filter_info_t *info); + /** * @brief This function is called to get fast provisioning application key. * diff --git a/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h b/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h index 2ac81a6c82..6841a2e6bf 100644 --- a/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h +++ b/components/bt/esp_ble_mesh/api/esp_ble_mesh_defs.h @@ -702,6 +702,7 @@ typedef struct { char name[ESP_BLE_MESH_NODE_NAME_MAX_LEN]; /*!< Node name */ uint16_t comp_length; /*!< Length of Composition Data */ uint8_t *comp_data; /*!< Value of Composition Data */ + uint32_t last_hb; /*!< Time (in seconds) when the last heartbeat is received */ } __attribute__((packed)) esp_ble_mesh_node_t; /** Context of fast provisioning which need to be set. */ @@ -731,6 +732,22 @@ typedef enum { PROXY_FILTER_BLACKLIST, } esp_ble_mesh_proxy_filter_type_t; +/*!< Provisioner heartbeat filter type */ +#define ESP_BLE_MESH_PROVISIONER_HB_FILTER_WHITELIST 0x0 +#define ESP_BLE_MESH_PROVISIONER_HB_FILTER_BLACKLIST 0x1 + +/*!< Provisioner heartbeat filter operation */ +#define ESP_BLE_MESH_PROVISIONER_HB_FILTER_ADD 0x0 +#define ESP_BLE_MESH_PROVISIONER_HB_FILTER_REMOVE 0x1 +#define ESP_BLE_MESH_PROVISIONER_HB_FILTER_CLEAN 0x2 + +/** Context of Provisioner heartbeat filter information to be set */ +typedef struct { + uint16_t hb_src; /*!< Heartbeat source address (unicast address) */ + uint16_t hb_dst; /*!< Heartbeat destination address (unicast address or group address) */ + uint32_t expiry; /*!< Expiry (in seconds) for the whitelist filter entry */ +} esp_ble_mesh_provisioner_hb_filter_info_t; + /*!< This enum value is the event of node/provisioner/fast provisioning */ typedef enum { ESP_BLE_MESH_PROV_REGISTER_COMP_EVT, /*!< Initialize BLE Mesh provisioning capabilities and internal data information completion event */ @@ -789,6 +806,10 @@ typedef enum { ESP_BLE_MESH_PROVISIONER_RELEASE_SETTINGS_WITH_USER_ID_COMP_EVT, /*!< Provisioner release settings with user_id completion event */ ESP_BLE_MESH_PROVISIONER_DELETE_SETTINGS_WITH_INDEX_COMP_EVT, /*!< Provisioner delete settings with index completion event */ ESP_BLE_MESH_PROVISIONER_DELETE_SETTINGS_WITH_USER_ID_COMP_EVT, /*!< Provisioner delete settings with user_id completion event */ + ESP_BLE_MESH_PROVISIONER_START_RECV_HEARTBEAT_COMP_EVT, /*!< Provisioner start to receive Heartbeat message completion event */ + ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE_COMP_EVT, /*!< Provisioner set the filter type of receiving heartbeat message completion event */ + ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_INFO_COMP_EVT, /*!< Provisioner set the filter information of receiving heartbeat message completion event */ + ESP_BLE_MESH_PROVISIONER_RECV_HEARTBEAT_MESSAGE_EVT, /*!< Provisioner receives heartbeat message event */ ESP_BLE_MESH_SET_FAST_PROV_INFO_COMP_EVT, /*!< Set fast provisioning information (e.g. unicast address range, net_idx, etc.) completion event */ ESP_BLE_MESH_SET_FAST_PROV_ACTION_COMP_EVT, /*!< Set fast provisioning action completion event */ ESP_BLE_MESH_HEARTBEAT_MESSAGE_RECV_EVT, /*!< Receive Heartbeat message event */ @@ -1195,6 +1216,41 @@ typedef union { int err_code; /*!< Indicate the result of deleting settings with user_id by the Provisioner */ uint8_t index; /*!< Index of Provisioner settings */ } provisioner_delete_settings_with_user_id_comp; /*!< Event parameters of ESP_BLE_MESH_PROVISIONER_DELETE_SETTINGS_WITH_USER_ID_COMP_EVT */ + /** + * @brief ESP_BLE_MESH_PROVISIONER_START_RECV_HEARTBEAT_COMP_EVT + */ + struct ble_mesh_provisioner_start_recv_heartbeat_comp_param { + int err_code; /*!< Indicate the result of starting to receive heartbeat messages by the Provisioner */ + } provisioner_start_recv_heartbeat_comp; /*!< Event parameters of ESP_BLE_MESH_PROVISIONER_START_RECV_HEARTBEAT_COMP_EVT */ + /** + * @brief ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE_COMP_EVT + */ + struct ble_mesh_provisioner_set_heartbeat_filter_type_comp_param { + int err_code; /*!< Indicate the result of setting the heartbeat filter type by the Provisioner */ + uint8_t filter_type; /*!< Type of the filter used for receiving heartbeat messages */ + } provisioner_set_heartbeat_filter_type_comp; /*!< Event parameters of ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE_COMP_EVT */ + /** + * @brief ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_INFO_COMP_EVT + */ + struct ble_mesh_provisioner_set_heartbeat_filter_info_comp_param { + int err_code; /*!< Indicate the result of setting the heartbeat filter address by the Provisioner */ + uint8_t op_flag; /*!< Operation (add, remove, clean) */ + uint16_t hb_src; /*!< Heartbeat source address */ + uint16_t hb_dst; /*!< Heartbeat destination address */ + uint32_t expiry; /*!< Expiry of the source address */ + } provisioner_set_heartbeat_filter_info_comp; /*!< Event parameters of ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_INFO_COMP_EVT */ + /** + * @brief ESP_BLE_MESH_PROVISIONER_RECV_HEARTBEAT_MESSAGE_EVT + */ + struct ble_mesh_provisioner_recv_heartbeat_msg_param { + uint16_t hb_src; /*!< Heartbeat source address */ + uint16_t hb_dst; /*!< Heartbeat destination address */ + uint8_t init_ttl; /*!< Heartbeat InitTTL */ + uint8_t rx_ttl; /*!< Heartbeat RxTTL */ + uint8_t hops; /*!< Heartbeat hops (InitTTL - RxTTL + 1) */ + uint16_t feature; /*!< Bit field of currently active features of the node */ + uint32_t count; /*!< Number of received heartbeat messages */ + } provisioner_recv_heartbeat_msg; /*!< Event parameters of ESP_BLE_MESH_PROVISIONER_RECV_HEARTBEAT_MESSAGE_EVT */ /** * @brief ESP_BLE_MESH_SET_FAST_PROV_INFO_COMP_EVT */ diff --git a/components/bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c b/components/bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c index b9c9448748..ffa29f7b01 100644 --- a/components/bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c +++ b/components/bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c @@ -804,9 +804,28 @@ uint8_t btc_ble_mesh_provisioner_get_free_settings_user_id_count(void) return bt_mesh_provisioner_get_free_settings_user_id_count(); } #endif /* CONFIG_BLE_MESH_USE_MULTIPLE_NAMESPACE */ + +static void btc_ble_mesh_provisioner_recv_heartbeat_cb(u16_t hb_src, u16_t hb_dst, u8_t init_ttl, + u8_t rx_ttl, u8_t hops, u16_t feat, u32_t count) +{ + esp_ble_mesh_prov_cb_param_t mesh_param = {0}; + + BT_DBG("%s", __func__); + + mesh_param.provisioner_recv_heartbeat_msg.hb_src = hb_src; + mesh_param.provisioner_recv_heartbeat_msg.hb_dst = hb_dst; + mesh_param.provisioner_recv_heartbeat_msg.init_ttl = init_ttl; + mesh_param.provisioner_recv_heartbeat_msg.rx_ttl = rx_ttl; + mesh_param.provisioner_recv_heartbeat_msg.hops = hops; + mesh_param.provisioner_recv_heartbeat_msg.feature = feat; + mesh_param.provisioner_recv_heartbeat_msg.count = count; + + btc_ble_mesh_prov_callback(&mesh_param, ESP_BLE_MESH_PROVISIONER_RECV_HEARTBEAT_MESSAGE_EVT); + return; +} #endif /* CONFIG_BLE_MESH_PROVISIONER */ -static void btc_ble_mesh_heartbeat_msg_recv_cb(u8_t hops, u16_t feature) +static void btc_ble_mesh_node_recv_heartbeat_cb(u8_t hops, u16_t feature) { esp_ble_mesh_prov_cb_param_t mesh_param = {0}; @@ -1106,7 +1125,7 @@ static void btc_ble_mesh_model_op_add(esp_ble_mesh_model_t *model) model->op = (esp_ble_mesh_model_op_t *)bt_mesh_cfg_srv_op; struct bt_mesh_cfg_srv *srv = (struct bt_mesh_cfg_srv *)model->user_data; if (srv) { - srv->hb_sub.func = btc_ble_mesh_heartbeat_msg_recv_cb; + srv->hb_sub.func = btc_ble_mesh_node_recv_heartbeat_cb; } break; } @@ -1871,6 +1890,28 @@ void btc_ble_mesh_prov_call_handler(btc_msg_t *msg) ¶m.provisioner_delete_settings_with_user_id_comp.index); break; #endif /* CONFIG_BLE_MESH_USE_MULTIPLE_NAMESPACE */ + case BTC_BLE_MESH_ACT_PROVISIONER_START_RECV_HEARTBEAT: + act = ESP_BLE_MESH_PROVISIONER_START_RECV_HEARTBEAT_COMP_EVT; + param.provisioner_start_recv_heartbeat_comp.err_code = + bt_mesh_provisioner_start_recv_heartbeat(btc_ble_mesh_provisioner_recv_heartbeat_cb); + break; + case BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE: + act = ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE_COMP_EVT; + param.provisioner_set_heartbeat_filter_type_comp.filter_type = arg->set_heartbeat_filter_type.filter_type; + param.provisioner_set_heartbeat_filter_type_comp.err_code = + bt_mesh_provisioner_set_heartbeat_filter_type(arg->set_heartbeat_filter_type.filter_type); + break; + case BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_INFO: + act = ESP_BLE_MESH_PROVISIONER_SET_HEARTBEAT_FILTER_INFO_COMP_EVT; + param.provisioner_set_heartbeat_filter_info_comp.op_flag = arg->set_heartbeat_filter_info.op_flag; + param.provisioner_set_heartbeat_filter_info_comp.hb_src = arg->set_heartbeat_filter_info.hb_src; + param.provisioner_set_heartbeat_filter_info_comp.hb_dst = arg->set_heartbeat_filter_info.hb_dst; + param.provisioner_set_heartbeat_filter_info_comp.expiry = arg->set_heartbeat_filter_info.expiry; + param.provisioner_set_heartbeat_filter_info_comp.err_code = + bt_mesh_provisioner_set_heartbeat_filter_info(arg->set_heartbeat_filter_info.op_flag, + arg->set_heartbeat_filter_info.hb_src, arg->set_heartbeat_filter_info.hb_dst, + arg->set_heartbeat_filter_info.expiry); + break; #endif /* CONFIG_BLE_MESH_PROVISIONER */ #if CONFIG_BLE_MESH_FAST_PROV case BTC_BLE_MESH_ACT_SET_FAST_PROV_INFO: diff --git a/components/bt/esp_ble_mesh/btc/include/btc_ble_mesh_prov.h b/components/bt/esp_ble_mesh/btc/include/btc_ble_mesh_prov.h index bef060305e..f5f9abed0e 100644 --- a/components/bt/esp_ble_mesh/btc/include/btc_ble_mesh_prov.h +++ b/components/bt/esp_ble_mesh/btc/include/btc_ble_mesh_prov.h @@ -63,6 +63,9 @@ typedef enum { BTC_BLE_MESH_ACT_PROVISIONER_RELEASE_SETTINGS_WITH_USER_ID, BTC_BLE_MESH_ACT_PROVISIONER_DELETE_SETTINGS_WITH_INDEX, BTC_BLE_MESH_ACT_PROVISIONER_DELETE_SETTINGS_WITH_USER_ID, + BTC_BLE_MESH_ACT_PROVISIONER_START_RECV_HEARTBEAT, + BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_TYPE, + BTC_BLE_MESH_ACT_PROVISIONER_SET_HEARTBEAT_FILTER_INFO, BTC_BLE_MESH_ACT_SET_FAST_PROV_INFO, BTC_BLE_MESH_ACT_SET_FAST_PROV_ACTION, BTC_BLE_MESH_ACT_LPN_ENABLE, @@ -229,6 +232,15 @@ typedef union { struct ble_mesh_provisioner_delete_settings_with_user_id_args { char user_id[ESP_BLE_MESH_SETTINGS_USER_ID_SIZE + 1]; } delete_settings_with_user_id; + struct ble_mesh_provisioner_set_heartbeat_filter_type_args { + uint8_t filter_type; + } set_heartbeat_filter_type; + struct ble_mesh_provisioner_set_heartbeat_filter_info_args { + uint8_t op_flag; + uint16_t hb_src; + uint16_t hb_dst; + uint32_t expiry; + } set_heartbeat_filter_info; struct ble_mesh_set_fast_prov_info_args { uint16_t unicast_min; uint16_t unicast_max; diff --git a/components/bt/esp_ble_mesh/mesh_core/provisioner_main.c b/components/bt/esp_ble_mesh/mesh_core/provisioner_main.c index a0c3b344b4..f981025eaa 100644 --- a/components/bt/esp_ble_mesh/mesh_core/provisioner_main.c +++ b/components/bt/esp_ble_mesh/mesh_core/provisioner_main.c @@ -34,6 +34,34 @@ static bt_mesh_mutex_t provisioner_lock; static u16_t all_node_count; static u16_t prov_node_count; +#define PROVISIONER_HB_FILTER_WHITELIST 0x0 +#define PROVISIONER_HB_FILTER_BLACKLIST 0x1 + +#define PROVISIONER_HB_FILTER_ADD 0x0 +#define PROVISIONER_HB_FILTER_REMOVE 0x1 +#define PROVISIONER_HB_FILTER_CLEAN 0x2 + +#define PROVISIONER_HB_FILTER_WITH_SRC BIT(0) +#define PROVISIONER_HB_FILTER_WITH_DST BIT(1) +#define PROVISIONER_HB_FILTER_WITH_BOTH (BIT(1) | BIT(0)) + +#define PROVISIONER_HB_RECV_MAX_COUNT 0xFFFFFFFF + +static struct bt_mesh_provisioner_hb { + struct provisioner_hb_rx { + struct hb_rx_filter { + u16_t src; /* Heartbeat source address, only unicast address */ + u16_t dst; /* Heartbeat destination address, unicast address or group address */ + s64_t expiry; /* In milliseconds, 0 means no expiry will be used */ + u32_t count; /* Number of received heartbeat messages when using whitelist */ + } filter[CONFIG_BLE_MESH_PROVISIONER_HB_FILTER_SIZE]; + u8_t filter_type; /* Heartbeat rx filter type */ + u32_t count; /* Number of received heartbeat messages when using blacklist */ + void (*func)(u16_t hb_src, u16_t hb_dst, u8_t init_ttl, + u8_t rx_ttl, u8_t hops, u16_t feat, u32_t count); + } rx; +} provisioner_hb; + static int provisioner_remove_node(u16_t index, bool erase); static void bt_mesh_provisioner_mutex_new(void) @@ -224,6 +252,7 @@ int bt_mesh_provisioner_deinit(bool erase) bt_mesh_provisioner_release_netkey(erase); bt_mesh_provisioner_release_appkey(erase); bt_mesh_provisioner_release_node(erase); + memset(&provisioner_hb.rx, 0, sizeof(provisioner_hb.rx)); bt_mesh_provisioner_mutex_free(); return 0; } @@ -338,6 +367,7 @@ static int provisioner_store_node(struct bt_mesh_node *node, bool prov, bool sto } memcpy(mesh_nodes[i], node, sizeof(struct bt_mesh_node)); + node->last_hb = k_uptime_get_32() / 1000; provisioner_node_count_inc(prov); if (index) { *index = i; @@ -1537,6 +1567,314 @@ int bt_mesh_provisioner_bind_local_model_app_idx(u16_t elem_addr, u16_t mod_id, return -ENOMEM; } +int bt_mesh_provisioner_start_recv_heartbeat(void (*cb)(u16_t hb_src, u16_t hb_dst, u8_t init_ttl, + u8_t rx_ttl, u8_t hops, u16_t feat, u32_t count) + ) +{ + if (cb == NULL) { + BT_ERR("%s, Invalid heartbeat callback", __func__); + return -EINVAL; + } + + memset(&provisioner_hb.rx, 0, sizeof(provisioner_hb.rx)); + + /* Start with an empty blacklist, which means all heartbeat messages will be reported */ + provisioner_hb.rx.filter_type = PROVISIONER_HB_FILTER_BLACKLIST; + provisioner_hb.rx.func = cb; + + return 0; +} + +int bt_mesh_provisioner_set_heartbeat_filter_type(u8_t filter_type) +{ + if (filter_type > PROVISIONER_HB_FILTER_BLACKLIST) { + BT_ERR("%s, Invalid filter type 0x%02x", __func__, filter_type); + return -EINVAL; + } + + if (provisioner_hb.rx.filter_type != filter_type) { + memset(&provisioner_hb.rx, 0, offsetof(struct provisioner_hb_rx, func)); + } + + provisioner_hb.rx.filter_type = filter_type; + return 0; +} + +static u8_t hb_filter_addr_type(u16_t src, u16_t dst) +{ + if (BLE_MESH_ADDR_IS_UNICAST(src)) { + if (BLE_MESH_ADDR_IS_UNICAST(dst) || BLE_MESH_ADDR_IS_GROUP(dst)) { + return PROVISIONER_HB_FILTER_WITH_BOTH; + } else { + return PROVISIONER_HB_FILTER_WITH_SRC; + } + } else { + return PROVISIONER_HB_FILTER_WITH_DST; + } +} + +static int hb_filter_alloc(u16_t src, u16_t dst, u32_t expiry) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(provisioner_hb.rx.filter); i++) { + struct hb_rx_filter *filter = &provisioner_hb.rx.filter[i]; + if (filter->src == BLE_MESH_ADDR_UNASSIGNED && + filter->dst == BLE_MESH_ADDR_UNASSIGNED) { + filter->src = src; + filter->dst = dst; + filter->expiry = expiry ? (k_uptime_get() + K_SECONDS(expiry)) : expiry; + filter->count = 0U; + return 0; + } + } + + BT_ERR("%s, Heartbeat filter is full, src 0x%04x, dst 0x%04x", __func__, src, dst); + return -ENOMEM; +} + +static void hb_filter_free(struct hb_rx_filter *filter) +{ + filter->src = BLE_MESH_ADDR_UNASSIGNED; + filter->dst = BLE_MESH_ADDR_UNASSIGNED; + filter->expiry = 0; + filter->count = 0U; +} + +static int hb_filter_add(u16_t src, u16_t dst, u32_t expiry) +{ + struct hb_rx_filter *filter = NULL; + u8_t type = 0U; + int i; + + if (!BLE_MESH_ADDR_IS_UNICAST(src) && + !BLE_MESH_ADDR_IS_UNICAST(dst) && !BLE_MESH_ADDR_IS_GROUP(dst)) { + BT_ERR("%s, Invalid filter address, src 0x%04x, dst 0x%04x", __func__, src, dst); + return -EINVAL; + } + + type = hb_filter_addr_type(src, dst); + + switch (type) { + case PROVISIONER_HB_FILTER_WITH_SRC: { + for (i = 0; i < ARRAY_SIZE(provisioner_hb.rx.filter); i++) { + filter = &provisioner_hb.rx.filter[i]; + if (src == filter->src) { + filter->dst = dst; + filter->expiry = expiry ? (k_uptime_get() + K_SECONDS(expiry)) : expiry; + filter->count = 0U; + return 0; + } + } + return hb_filter_alloc(src, dst, expiry); + } + case PROVISIONER_HB_FILTER_WITH_DST: { + for (i = 0; i < ARRAY_SIZE(provisioner_hb.rx.filter); i++) { + filter = &provisioner_hb.rx.filter[i]; + if (dst == filter->dst) { + filter->src = src; + filter->expiry = expiry ? (k_uptime_get() + K_SECONDS(expiry)) : expiry; + filter->count = 0U; + return 0; + } + } + return hb_filter_alloc(src, dst, expiry); + } + case PROVISIONER_HB_FILTER_WITH_BOTH: { + for (i = 0; i < ARRAY_SIZE(provisioner_hb.rx.filter); i++) { + filter = &provisioner_hb.rx.filter[i]; + /* Clear already existed src and dst in the filter */ + if (src == filter->src || dst == filter->dst) { + filter->src = BLE_MESH_ADDR_UNASSIGNED; + filter->dst = BLE_MESH_ADDR_UNASSIGNED; + filter->expiry = 0; + filter->count = 0U; + } + } + return hb_filter_alloc(src, dst, expiry); + } + default: + BT_ERR("%s, Unknown filter addr type 0x%02x", __func__, type); + return -EINVAL; + } +} + +static int hb_filter_remove(u16_t src, u16_t dst) +{ + struct hb_rx_filter *filter = NULL; + u8_t type = 0U; + int i; + + if (!BLE_MESH_ADDR_IS_UNICAST(src) && + !BLE_MESH_ADDR_IS_UNICAST(dst) && !BLE_MESH_ADDR_IS_GROUP(dst)) { + BT_ERR("%s, Invalid filter address, src 0x%04x, dst 0x%04x", __func__, src, dst); + return -EINVAL; + } + + type = hb_filter_addr_type(src, dst); + + switch (type) { + case PROVISIONER_HB_FILTER_WITH_SRC: { + for (i = 0; i < ARRAY_SIZE(provisioner_hb.rx.filter); i++) { + filter = &provisioner_hb.rx.filter[i]; + if (src == filter->src && dst == BLE_MESH_ADDR_UNASSIGNED) { + hb_filter_free(filter); + } + } + return 0; + } + case PROVISIONER_HB_FILTER_WITH_DST: { + for (i = 0; i < ARRAY_SIZE(provisioner_hb.rx.filter); i++) { + filter = &provisioner_hb.rx.filter[i]; + if (src == BLE_MESH_ADDR_UNASSIGNED && dst == filter->dst) { + hb_filter_free(filter); + } + } + return 0; + } + case PROVISIONER_HB_FILTER_WITH_BOTH: { + for (i = 0; i < ARRAY_SIZE(provisioner_hb.rx.filter); i++) { + filter = &provisioner_hb.rx.filter[i]; + if (src == filter->src && dst == filter->dst) { + hb_filter_free(filter); + } + } + return 0; + } + default: + BT_ERR("%s, Unknown filter addr type 0x%02x", __func__, type); + return -EINVAL; + } +} + +int bt_mesh_provisioner_set_heartbeat_filter_info(u8_t op_flag, u16_t src, u16_t dst, u32_t expiry) +{ + switch (op_flag) { + case PROVISIONER_HB_FILTER_ADD: + return hb_filter_add(src, dst, expiry); + case PROVISIONER_HB_FILTER_REMOVE: + return hb_filter_remove(src, dst); + case PROVISIONER_HB_FILTER_CLEAN: + memset(&provisioner_hb.rx, 0, offsetof(struct provisioner_hb_rx, filter_type)); + return 0; + default: + BT_ERR("%s, Invalid operation flag 0x%02x", __func__, op_flag); + return -EINVAL; + } +} + +void bt_mesh_provisioner_heartbeat(u16_t src, u16_t dst, u8_t init_ttl, + u8_t rx_ttl, u8_t hops, u16_t feat) +{ + struct hb_rx_filter *filter = NULL; + struct bt_mesh_node *node = NULL; + bool ignore = true; + u32_t count = 0U; + u8_t type = 0U; + int i; + + if (provisioner_hb.rx.func == NULL) { + BT_DBG("Receiving heartbeat is not enabled"); + return; + } + + if (provisioner_hb.rx.filter_type == PROVISIONER_HB_FILTER_BLACKLIST) { + for (i = 0; i < ARRAY_SIZE(provisioner_hb.rx.filter); i++) { + filter = &provisioner_hb.rx.filter[i]; + if (src == filter->src || dst == filter->dst) { + type = hb_filter_addr_type(filter->src, filter->dst); + switch (type) { + case PROVISIONER_HB_FILTER_WITH_SRC: + if (src == filter->src) { + BT_INFO("Heartbeat filtered by backlist with src 0x%04x", src); + return; + } + break; + case PROVISIONER_HB_FILTER_WITH_DST: + if (dst == filter->dst) { + BT_INFO("Heartbeat filtered by backlist with dst 0x%04x", dst); + return; + } + break; + case PROVISIONER_HB_FILTER_WITH_BOTH: + if (src == filter->src && dst == filter->dst) { + BT_INFO("Heartbeat filtered by backlist with src 0x%04x, dst 0x%04x", src, dst); + return; + } + break; + default: + BT_ERR("%s, Unknown filter addr type 0x%02x", __func__, type); + return; + } + } + } + + if (provisioner_hb.rx.count < PROVISIONER_HB_RECV_MAX_COUNT) { + provisioner_hb.rx.count++; + } + + count = provisioner_hb.rx.count; + } else { + for (i = 0; i < ARRAY_SIZE(provisioner_hb.rx.filter); i++) { + filter = &provisioner_hb.rx.filter[i]; + if (src == filter->src || dst == filter->dst) { + type = hb_filter_addr_type(filter->src, filter->dst); + switch (type) { + case PROVISIONER_HB_FILTER_WITH_SRC: + if (src == filter->src) { + ignore = false; + } + break; + case PROVISIONER_HB_FILTER_WITH_DST: + if (dst == filter->dst) { + ignore = false; + } + break; + case PROVISIONER_HB_FILTER_WITH_BOTH: + if (src == filter->src && dst == filter->dst) { + ignore = false; + } + break; + default: + BT_ERR("%s, Unknown filter addr type 0x%02x", __func__, type); + return; + } + if (ignore == false) { + break; + } + } + } + + if (ignore == true) { + BT_INFO("Heartbeat filtered by whitelist, src 0x%04x, dst 0x%04x", src, dst); + return; + } + + if (filter->expiry && k_uptime_get() > filter->expiry) { + BT_INFO("Period for heartbeat from 0x%04x expired", src); + return; + } + + if (filter->count < PROVISIONER_HB_RECV_MAX_COUNT) { + filter->count++; + } + + count = filter->count; + } + + node = bt_mesh_provisioner_get_node_with_addr(src); + if (node) { + node->last_hb = k_uptime_get_32() / 1000; + } + + BT_INFO("src 0x%04x, dst 0x%04x, init_ttl %u, rx_ttl %u, hops %u, feat 0x%04x, count %u", + src, dst, init_ttl, rx_ttl, hops, feat, count); + + if (provisioner_hb.rx.func) { + provisioner_hb.rx.func(src, dst, init_ttl, rx_ttl, hops, feat, count); + } +} + int bt_mesh_print_local_composition_data(void) { const struct bt_mesh_comp *comp = NULL; diff --git a/components/bt/esp_ble_mesh/mesh_core/provisioner_main.h b/components/bt/esp_ble_mesh/mesh_core/provisioner_main.h index 302d022dc3..21a941b4ae 100644 --- a/components/bt/esp_ble_mesh/mesh_core/provisioner_main.h +++ b/components/bt/esp_ble_mesh/mesh_core/provisioner_main.h @@ -41,6 +41,7 @@ struct bt_mesh_node { char name[BLE_MESH_NODE_NAME_SIZE]; /* Node name */ u16_t comp_length; /* Length of Composition Data */ u8_t *comp_data; /* Value of Composition Data */ + u32_t last_hb; /* Time (in seconds) when the last heartbeat is received */ } __packed; int bt_mesh_provisioner_init(void); @@ -122,6 +123,18 @@ int bt_mesh_provisioner_local_net_key_delete(u16_t net_idx); int bt_mesh_provisioner_bind_local_model_app_idx(u16_t elem_addr, u16_t mod_id, u16_t cid, u16_t app_idx); +int bt_mesh_provisioner_start_recv_heartbeat(void (*cb)(u16_t src, u16_t dst, u8_t init_ttl, + u8_t rx_ttl, u8_t hops, u16_t feat, + u32_t count) + ); + +int bt_mesh_provisioner_set_heartbeat_filter_type(u8_t filter_type); + +int bt_mesh_provisioner_set_heartbeat_filter_info(u8_t op_flag, u16_t src, u16_t dst, u32_t expiry); + +void bt_mesh_provisioner_heartbeat(u16_t src, u16_t dst, u8_t init_ttl, + u8_t rx_ttl, u8_t hops, u16_t feat); + /* Provisioner print own element information */ int bt_mesh_print_local_composition_data(void); diff --git a/components/bt/esp_ble_mesh/mesh_core/transport.c b/components/bt/esp_ble_mesh/mesh_core/transport.c index 85a648e315..cc75cff0de 100644 --- a/components/bt/esp_ble_mesh/mesh_core/transport.c +++ b/components/bt/esp_ble_mesh/mesh_core/transport.c @@ -885,7 +885,8 @@ static int trans_heartbeat(struct bt_mesh_net_rx *rx, return -EINVAL; } - if (rx->ctx.recv_dst != hb_sub_dst) { + if (IS_ENABLED(CONFIG_BLE_MESH_NODE) && bt_mesh_is_provisioned() && + rx->ctx.recv_dst != hb_sub_dst) { BT_WARN("Ignoring heartbeat to non-subscribed destination"); return 0; } @@ -899,7 +900,13 @@ static int trans_heartbeat(struct bt_mesh_net_rx *rx, rx->ctx.addr, rx->ctx.recv_ttl, init_ttl, hops, (hops == 1U) ? "" : "s", feat); - bt_mesh_heartbeat(rx->ctx.addr, rx->ctx.recv_dst, hops, feat); + if (IS_ENABLED(CONFIG_BLE_MESH_PROVISIONER) && + bt_mesh_is_provisioner_en() && !bt_mesh_is_provisioned()) { + bt_mesh_provisioner_heartbeat( + rx->ctx.addr, rx->ctx.recv_dst, init_ttl, rx->ctx.recv_ttl, hops, feat); + } else { + bt_mesh_heartbeat(rx->ctx.addr, rx->ctx.recv_dst, hops, feat); + } return 0; }