// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include "esp_ble_mesh_defs.h" #include "esp_ble_mesh_local_data_operation_api.h" #include "esp_ble_mesh_provisioning_api.h" #include "esp_ble_mesh_networking_api.h" #include "esp_ble_mesh_proxy_api.h" #include "esp_ble_mesh_config_model_api.h" #include "esp_fast_prov_operation.h" #include "esp_fast_prov_server_model.h" #define TAG "FAST_PROV_SERVER" /* Array used to store all node addresses */ static uint16_t all_node_addr[120]; static uint16_t all_node_addr_cnt; esp_err_t example_store_remote_node_address(uint16_t node_addr) { if (!ESP_BLE_MESH_ADDR_IS_UNICAST(node_addr)) { ESP_LOGE(TAG, "%s: Not a unicast address", __func__); return ESP_ERR_INVALID_ARG; } for (int i = 0; i < ARRAY_SIZE(all_node_addr); i++) { if (all_node_addr[i] == node_addr) { ESP_LOGW(TAG, "%s: Node address 0x%04x already exists", __func__, node_addr); return ESP_OK; } } for (int i = 0; i < ARRAY_SIZE(all_node_addr); i++) { if (all_node_addr[i] == ESP_BLE_MESH_ADDR_UNASSIGNED) { all_node_addr[i] = node_addr; all_node_addr_cnt++; return ESP_OK; } } ESP_LOGE(TAG, "%s: remote node address queue is full", __func__); return ESP_FAIL; } esp_ble_mesh_cfg_srv_t *get_cfg_srv_user_data(void) { esp_ble_mesh_model_t *model = NULL; model = example_find_model(esp_ble_mesh_get_primary_element_address(), ESP_BLE_MESH_MODEL_ID_CONFIG_SRV, CID_NVAL); if (!model) { ESP_LOGE(TAG, "%s: Failed to get config server model", __func__); return NULL; } return (esp_ble_mesh_cfg_srv_t *)(model->user_data); } /* Timeout handler for disable_fast_prov_timer */ static void disable_fast_prov_cb(struct k_work *work) { example_fast_prov_server_t *srv = NULL; srv = CONTAINER_OF(work, example_fast_prov_server_t, disable_fast_prov_timer.work); if (!srv) { ESP_LOGE(TAG, "%s: Failed to get fast prov server model user_data", __func__); return; } if (esp_ble_mesh_set_fast_prov_action(FAST_PROV_ACT_SUSPEND)) { ESP_LOGE(TAG, "%s: Failed to disable fast provisioning", __func__); return; } } /* Timeout handler for gatt_proxy_enable_timer */ static void enable_gatt_proxy_cb(struct k_work *work) { example_fast_prov_server_t *srv = NULL; srv = CONTAINER_OF(work, example_fast_prov_server_t, gatt_proxy_enable_timer.work); if (!srv) { ESP_LOGE(TAG, "%s: Failed to get fast prov server model user_data", __func__); return; } if (bt_mesh_atomic_test_and_clear_bit(srv->srv_flags, RELAY_PROXY_DISABLED)) { ESP_LOGI(TAG, "%s: Enable BLE Mesh Relay & GATT Proxy", __func__); /* For Primary Provisioner, Relay will not be enabled */ esp_ble_mesh_proxy_gatt_enable(); esp_ble_mesh_proxy_identity_enable(); } return; } static void example_free_set_info(example_fast_prov_server_t *srv) { if (srv && srv->set_info) { bt_mesh_free(srv->set_info); srv->set_info = NULL; } } esp_err_t example_fast_prov_server_recv_msg(esp_ble_mesh_model_t *model, esp_ble_mesh_msg_ctx_t *ctx, struct net_buf_simple *buf) { example_fast_prov_server_t *srv = NULL; struct net_buf_simple *msg = NULL; uint32_t opcode = 0; esp_err_t err; if (!model || !model->user_data || !ctx || !buf) { return ESP_ERR_INVALID_ARG; } srv = (example_fast_prov_server_t *)model->user_data; ESP_LOG_BUFFER_HEX("fast prov server recv", buf->data, buf->len); switch (ctx->recv_op) { case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_SET: { /* fast prov info status (maximum 9 octets): * status_bit_mask (2) + status_ctx_flag (1) + status_unicast (1) + status_net_idx (1) + * status_group (1) + status_pri_prov (1) + status_match (1) + status_action (1). */ uint8_t match_len = 0, match_val[16] = {0}; uint8_t status_unicast = 0; uint8_t flags = 0; msg = bt_mesh_alloc_buf(9); opcode = ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS; if (srv->set_info) { ESP_LOGW(TAG, "%s: Set fast prov info is already in progress", __func__); net_buf_simple_add_le16(msg, 0xFFFF); break; } /* If fast prov server state is pending, can not set fast prov info, * and send response message with all status set to 0x01 (i.e. fail). */ if (srv->state == STATE_PEND) { uint8_t val[7] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; net_buf_simple_add_le16(msg, 0x7f); net_buf_simple_add_mem(msg, val, sizeof(val)); break; } uint16_t ctx_flags = net_buf_simple_pull_le16(buf); if (ctx_flags == 0) { net_buf_simple_add_le16(msg, BIT(0)); net_buf_simple_add_u8(msg, 0x01); /* invalid ctx_flags */ break; } uint16_t node_addr_cnt = (ctx_flags & BIT(0)) ? net_buf_simple_pull_le16(buf) : 0x0; uint16_t unicast_min = (ctx_flags & BIT(1)) ? net_buf_simple_pull_le16(buf) : (esp_ble_mesh_get_primary_element_address() + esp_ble_mesh_get_element_count()); uint16_t unicast_max = (ctx_flags & BIT(2)) ? net_buf_simple_pull_le16(buf) : 0x7FFF; if (ctx_flags & BIT(3)) { flags = net_buf_simple_pull_u8(buf); } else { flags = (uint8_t)bt_mesh.sub[0].kr_flag; if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_IVU_IN_PROGRESS)) { flags |= BLE_MESH_NET_FLAG_IVU; } } uint32_t iv_index = (ctx_flags & BIT(4)) ? net_buf_simple_pull_le32(buf) : bt_mesh.iv_index; uint16_t net_idx = (ctx_flags & BIT(5)) ? net_buf_simple_pull_le16(buf) : srv->net_idx; uint16_t group_addr = (ctx_flags & BIT(6)) ? net_buf_simple_pull_le16(buf) : ESP_BLE_MESH_ADDR_UNASSIGNED; uint16_t pri_prov_addr = (ctx_flags & BIT(7)) ? net_buf_simple_pull_le16(buf) : ESP_BLE_MESH_ADDR_UNASSIGNED; if (ctx_flags & BIT(8)) { match_len = buf->len - ((ctx_flags & BIT(9)) ? 1 : 0); if (match_len > ESP_BLE_MESH_OCTET16_LEN) { net_buf_simple_add_le16(msg, BIT(5)); net_buf_simple_add_u8(msg, 0x01); /* too large match value length */ break; } memcpy(match_val, buf->data, match_len); net_buf_simple_pull(buf, match_len); } uint8_t action = (ctx_flags & BIT(9)) ? net_buf_simple_pull_u8(buf) : FAST_PROV_ACT_NONE; /* If fast prov server state is active, the device can only suspend or exit fast provisioning */ if (srv->state == STATE_ACTIVE) { net_buf_simple_add_le16(msg, BIT(6)); switch (action & BIT_MASK(2)) { case FAST_PROV_ACT_SUSPEND: case FAST_PROV_ACT_EXIT: srv->pend_act = action & BIT_MASK(2); if (!bt_mesh_atomic_test_and_set_bit(srv->srv_flags, DISABLE_FAST_PROV_START)) { k_delayed_work_submit(&srv->disable_fast_prov_timer, DISABLE_FAST_PROV_TIMEOUT); } net_buf_simple_add_u8(msg, 0x00); /* action succeed */ break; default: net_buf_simple_add_u8(msg, 0x04); /* action already in progress */ break; } break; } if ((ctx_flags & BIT(1)) || (ctx_flags & BIT(2))) { if (!ESP_BLE_MESH_ADDR_IS_UNICAST(unicast_min) || !ESP_BLE_MESH_ADDR_IS_UNICAST(unicast_max)) { status_unicast = 0x01; /* not a unicast address */ } else if (unicast_min > unicast_max) { status_unicast = 0x02; /* min bigger than max */ } else if (unicast_min < (esp_ble_mesh_get_primary_element_address() + esp_ble_mesh_get_element_count())) { status_unicast = 0x04; /* overlap with own element address */ } if (status_unicast) { net_buf_simple_add_le16(msg, BIT(1)); net_buf_simple_add_u8(msg, status_unicast); break; } } if (ctx_flags & BIT(6)) { if (!ESP_BLE_MESH_ADDR_IS_GROUP(group_addr)) { net_buf_simple_add_le16(msg, BIT(3)); net_buf_simple_add_u8(msg, 0x01); /* not a group address */ break; } err = example_add_fast_prov_group_address(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, group_addr); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to add group address 0x%04x", __func__, group_addr); net_buf_simple_add_le16(msg, BIT(3)); net_buf_simple_add_u8(msg, 0x02); /* add group address failed */ break; } srv->group_addr = group_addr; } if (ctx_flags & BIT(7)) { if (!ESP_BLE_MESH_ADDR_IS_UNICAST(pri_prov_addr)) { net_buf_simple_add_le16(msg, BIT(4)); net_buf_simple_add_u8(msg, 0x01); /* not a unicast address */ break; } } if (ctx_flags & BIT(9)) { if ((action & BIT_MASK(2)) != FAST_PROV_ACT_ENTER) { net_buf_simple_add_le16(msg, BIT(6)); net_buf_simple_add_u8(msg, 0x01); /* action failed */ break; } } else { net_buf_simple_add_le16(msg, BIT(6)); net_buf_simple_add_u8(msg, 0x03); /* none action */ break; } memcpy(&srv->ctx, ctx, sizeof(esp_ble_mesh_msg_ctx_t)); srv->set_info = bt_mesh_calloc(sizeof(struct fast_prov_info_set)); if (!srv->set_info) { ESP_LOGE(TAG, "%s: Failed to allocate memory", __func__); bt_mesh_free_buf(msg); return ESP_FAIL; } if (unicast_max < unicast_min + srv->max_node_num - 1) { srv->max_node_num = unicast_max - unicast_min + 1; } srv->set_info->set_succeed = false; srv->set_info->node_addr_cnt = node_addr_cnt; srv->set_info->unicast_min = unicast_min; srv->set_info->unicast_max = unicast_max; srv->set_info->flags = flags; srv->set_info->iv_index = iv_index; srv->set_info->net_idx = net_idx; srv->set_info->pri_prov_addr = pri_prov_addr; srv->set_info->match_len = match_len; memcpy(srv->set_info->match_val, match_val, match_len); srv->set_info->action = action; esp_ble_mesh_fast_prov_info_t info_set = { .unicast_min = unicast_min, .unicast_max = unicast_min + srv->max_node_num - 1, .flags = flags, .iv_index = iv_index, .net_idx = net_idx, .offset = 0x00, .match_len = match_len, }; memcpy(info_set.match_val, match_val, match_len); err = esp_ble_mesh_set_fast_prov_info(&info_set); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to set fast prov info", __func__); net_buf_simple_add_le16(msg, BIT(5) | BIT(2) | BIT(1)); net_buf_simple_add_u8(msg, 0x01); /* set unicast failed */ net_buf_simple_add_u8(msg, 0x01); /* set net_idx failed */ net_buf_simple_add_u8(msg, 0x01); /* set UUID match failed */ break; } /* If setting fast prov info successfully, wait for the event callback */ bt_mesh_free_buf(msg); return ESP_OK; } case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_ADD: { uint8_t status_net_key, status_action; msg = bt_mesh_alloc_buf(2); if (srv->state == STATE_PEND) { /* Add fast prov net_key, wait for event callback and call esp_ble_mesh_set_fast_prov_act() to set action */ } else { /* If state is not pending, can not add net_key */ status_net_key = 0x01; /* status_net_key: fail */ status_action = 0x01; /* status_action: fail */ } opcode = ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS; net_buf_simple_add_u8(msg, status_net_key); net_buf_simple_add_u8(msg, status_action); break; } case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR: { if (buf->len % 2) { ESP_LOGE(TAG, "%s: Invalid Fast Prov Node Addr message length", __func__); return ESP_FAIL; } if (bt_mesh_atomic_test_and_clear_bit(srv->srv_flags, GATT_PROXY_ENABLE_START)) { k_delayed_work_cancel(&srv->gatt_proxy_enable_timer); } for (; buf->len; ) { uint16_t node_addr = net_buf_simple_pull_le16(buf); ESP_LOGI(TAG, "Node address: 0x%04x", node_addr); err = example_store_remote_node_address(node_addr); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to store node address 0x%04x", __func__, node_addr); } } opcode = ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK; break; } case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_GET: { /* Top device (e.g. phone) tries to get all node addresses */ msg = bt_mesh_alloc_buf(all_node_addr_cnt * 2); if (!msg) { ESP_LOGE(TAG, "%s: Failed to allocate memory", __func__); return ESP_FAIL; } for (int i = 0; i < all_node_addr_cnt; i++) { net_buf_simple_add_le16(msg, all_node_addr[i]); } ESP_LOG_BUFFER_HEX("All node address", msg->data, msg->len); opcode = ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS; break; } case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_GROUP_ADD: { uint16_t own_addr = esp_ble_mesh_get_primary_element_address(); uint16_t group_addr = net_buf_simple_pull_le16(buf); ESP_LOGI(TAG, "%s, group address 0x%04x", __func__, group_addr); if (!ESP_BLE_MESH_ADDR_IS_GROUP(group_addr)) { return ESP_FAIL; } for (; buf->len; ) { uint16_t dst = net_buf_simple_pull_le16(buf); ESP_LOGI(TAG, "%s, dst 0x%04x, own address 0x%04x", __func__, dst, own_addr); if (dst == own_addr) { err = example_add_fast_prov_group_address(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, group_addr); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to add group address 0x%04x", __func__, group_addr); } return err; } } return ESP_OK; } case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_GROUP_DELETE: { uint16_t own_addr = esp_ble_mesh_get_primary_element_address(); uint16_t group_addr = net_buf_simple_pull_le16(buf); ESP_LOGI(TAG, "%s, group address 0x%04x", __func__, group_addr); if (!ESP_BLE_MESH_ADDR_IS_GROUP(group_addr)) { return ESP_FAIL; } for (; buf->len; ) { uint16_t dst = net_buf_simple_pull_le16(buf); ESP_LOGI(TAG, "%s, dst 0x%04x, own address 0x%04x", __func__, dst, own_addr); if (dst == own_addr) { err = example_delete_fast_prov_group_address(ESP_BLE_MESH_MODEL_ID_GEN_ONOFF_SRV, group_addr); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to delete group address 0x%04x", __func__, group_addr); } return err; } } return ESP_OK; } default: ESP_LOGW(TAG, "%s: Not a Fast Prov Client message opcode", __func__); return ESP_FAIL; } err = example_send_fast_prov_status_msg(model, ctx, opcode, msg); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to send Fast Prov Status message", __func__); example_free_set_info(srv); } bt_mesh_free_buf(msg); return err; } esp_err_t example_handle_fast_prov_info_set_comp_evt(esp_ble_mesh_model_t *model, uint8_t status_unicast, uint8_t status_net_idx, uint8_t status_match) { example_fast_prov_server_t *srv = NULL; struct net_buf_simple *msg = NULL; esp_err_t err; if (!model || !model->user_data) { return ESP_ERR_INVALID_ARG; } srv = (example_fast_prov_server_t *)model->user_data; if (!srv->set_info) { return ESP_FAIL; } msg = bt_mesh_alloc_buf(9); if (status_unicast || status_match) { net_buf_simple_add_le16(msg, BIT(5) | BIT(1)); net_buf_simple_add_u8(msg, status_unicast); net_buf_simple_add_u8(msg, status_match); goto send; } /* Update Fast Prov Server Model user_data */ srv->unicast_min = srv->set_info->unicast_min + srv->max_node_num; srv->unicast_max = srv->set_info->unicast_max; srv->unicast_cur = srv->set_info->unicast_min + srv->max_node_num; if (srv->unicast_max <= srv->unicast_min) { srv->unicast_step = 0; } else { srv->unicast_step = (srv->unicast_max - srv->unicast_min) / srv->max_node_num; } srv->flags = srv->set_info->flags; srv->iv_index = srv->set_info->iv_index; srv->net_idx = srv->set_info->net_idx; if (srv->set_info->action & BIT(7)) { srv->primary_role = true; srv->node_addr_cnt = srv->set_info->node_addr_cnt; srv->prim_prov_addr = esp_ble_mesh_get_primary_element_address(); srv->top_address = srv->ctx.addr; } else { srv->primary_role = false; srv->prim_prov_addr = srv->set_info->pri_prov_addr; } srv->match_len = srv->set_info->match_len; memcpy(srv->match_val, srv->set_info->match_val, srv->set_info->match_len); if (status_net_idx) { ESP_LOGW(TAG, "%s: Wait for fast prov netkey to be added", __func__); srv->pend_act = FAST_PROV_ACT_ENTER; srv->state = STATE_PEND; net_buf_simple_add_le16(msg, BIT(6) | BIT(2)); net_buf_simple_add_u8(msg, status_net_idx); /* wait for net_key */ net_buf_simple_add_u8(msg, 0x02); /* pending action */ goto send; } /* Sets fast prov action */ err = esp_ble_mesh_set_fast_prov_action(FAST_PROV_ACT_ENTER); if (err == ESP_OK) { bt_mesh_free_buf(msg); return ESP_OK; } ESP_LOGE(TAG, "%s: Failed to set fast prov action", __func__); net_buf_simple_add_le16(msg, BIT(6)); net_buf_simple_add_u8(msg, 0x01); /* action failed */ send: err = example_send_fast_prov_status_msg(model, &srv->ctx, ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS, msg); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to send Fast Prov Status message", __func__); example_free_set_info(srv); } bt_mesh_free_buf(msg); return err; } esp_err_t example_handle_fast_prov_action_set_comp_evt(esp_ble_mesh_model_t *model, uint8_t status_action) { example_fast_prov_server_t *srv = NULL; struct net_buf_simple *msg = NULL; uint32_t opcode; esp_err_t err; if (!model || !model->user_data) { return ESP_ERR_INVALID_ARG; } srv = (example_fast_prov_server_t *)model->user_data; msg = bt_mesh_alloc_buf(9); switch (srv->state) { case STATE_IDLE: { /* fast prov info set (enter) */ const uint8_t zero[6] = {0}; net_buf_simple_add_le16(msg, 0x7f); net_buf_simple_add_mem(msg, zero, 6); net_buf_simple_add_u8(msg, status_action); opcode = ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS; /** * Disable relay should not have a impact on Mesh Proxy PDU, and * we can also move "disabling relay" in the event of "disabling * gatt proxy" here. */ if (srv->node_addr_cnt == FAST_PROV_NODE_COUNT_MIN) { if (bt_mesh_atomic_test_and_clear_bit(srv->srv_flags, GATT_PROXY_ENABLE_START)) { k_delayed_work_cancel(&srv->gatt_proxy_enable_timer); } if (!bt_mesh_atomic_test_and_set_bit(srv->srv_flags, GATT_PROXY_ENABLE_START)) { k_delayed_work_submit(&srv->gatt_proxy_enable_timer, K_SECONDS(3)); } } break; } case STATE_ACTIVE: /* fast prov info set (suspend/exit) */ /* Currently we only support suspend/exit fast prov after Generic * OnOff Set/Set Unack is received. So no fast prov status message * will be sent. */ case STATE_PEND: /* fast prov net_key add */ /* In this case, we should send fast prov net_key status */ default: bt_mesh_free_buf(msg); return ESP_OK; } err = example_send_fast_prov_status_msg(model, &srv->ctx, opcode, msg); if (err != ESP_OK) { ESP_LOGE(TAG, "%s: Failed to send Fast Prov Status message", __func__); example_free_set_info(srv); bt_mesh_free_buf(msg); return ESP_FAIL; } if (status_action == 0x00) { if (srv->state == STATE_IDLE || srv->state == STATE_PEND) { srv->state = STATE_ACTIVE; } else if (srv->state == STATE_ACTIVE) { srv->state = STATE_IDLE; } if (srv->set_info) { srv->set_info->set_succeed = true; } } bt_mesh_free_buf(msg); return ESP_OK; } esp_err_t example_handle_fast_prov_status_send_comp_evt(int err_code, uint32_t opcode, esp_ble_mesh_model_t *model, esp_ble_mesh_msg_ctx_t *ctx) { example_fast_prov_server_t *srv = NULL; if (!model || !model->user_data) { return ESP_ERR_INVALID_ARG; } srv = (example_fast_prov_server_t *)model->user_data; ESP_LOGI(TAG, "%s: opcode 0x%06x", __func__, opcode); switch (opcode) { case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_INFO_STATUS: if (err_code == 0 && srv->set_info && srv->set_info->set_succeed == true) { if (!bt_mesh_atomic_test_and_set_bit(srv->srv_flags, RELAY_PROXY_DISABLED)) { /* For Primary Provisioner: disable Relay and GATT Proxy; * For other Provisioners: only disable GATT Proxy */ ESP_LOGW(TAG, "%s: Disable BLE Mesh Relay & GATT Proxy", __func__); esp_ble_mesh_proxy_gatt_disable(); } } example_free_set_info(srv); break; case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NET_KEY_STATUS: break; case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_ACK: if (!bt_mesh_atomic_test_and_set_bit(srv->srv_flags, GATT_PROXY_ENABLE_START)) { k_delayed_work_submit(&srv->gatt_proxy_enable_timer, GATT_PROXY_ENABLE_TIMEOUT); } break; case ESP_BLE_MESH_VND_MODEL_OP_FAST_PROV_NODE_ADDR_STATUS: break; default: break; } return ESP_OK; } esp_err_t example_fast_prov_server_init(esp_ble_mesh_model_t *model) { example_fast_prov_server_t *srv = NULL; if (!model || !model->user_data) { return ESP_ERR_INVALID_ARG; } srv = (example_fast_prov_server_t *)model->user_data; srv->model = model; k_delayed_work_init(&srv->disable_fast_prov_timer, disable_fast_prov_cb); k_delayed_work_init(&srv->gatt_proxy_enable_timer, enable_gatt_proxy_cb); return ESP_OK; }