| /* |
| * Copyright (c) 2020 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <stdlib.h> |
| #include <zephyr/sys/slist.h> |
| #include <zephyr/sys/byteorder.h> |
| #include <zephyr/bluetooth/hci.h> |
| #include <zephyr/bluetooth/crypto.h> |
| #include <zephyr/bluetooth/mesh/rpr_srv.h> |
| #include <common/bt_str.h> |
| #include <zephyr/bluetooth/mesh/sar_cfg.h> |
| #include <zephyr/bluetooth/mesh/keys.h> |
| #include "access.h" |
| #include "prov.h" |
| #include "crypto.h" |
| #include "rpr.h" |
| #include "net.h" |
| #include "mesh.h" |
| |
| #define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(bt_mesh_rpr_srv); |
| |
| #define LINK_OPEN_TIMEOUT_DEFAULT 10 |
| |
| #define LINK_CTX(_cli, _send_rel) \ |
| { \ |
| .net_idx = (_cli)->net_idx, .app_idx = BT_MESH_KEY_DEV_LOCAL, \ |
| .addr = (_cli)->addr, .send_ttl = (_cli)->ttl, \ |
| .send_rel = (_send_rel) \ |
| } |
| |
| enum { |
| SCANNING, |
| SCAN_REPORT_PENDING, |
| SCAN_EXT_HAS_ADDR, |
| NODE_REFRESH, |
| URI_MATCHED, |
| URI_REQUESTED, |
| |
| RPR_SRV_NUM_FLAGS, |
| }; |
| |
| /** Remote provisioning server instance. */ |
| static struct { |
| const struct bt_mesh_model *mod; |
| |
| ATOMIC_DEFINE(flags, RPR_SRV_NUM_FLAGS); |
| |
| struct { |
| struct bt_mesh_rpr_unprov |
| devs[CONFIG_BT_MESH_RPR_SRV_SCANNED_ITEMS_MAX]; |
| uint8_t max_devs; |
| enum bt_mesh_rpr_scan state; |
| struct k_work_delayable report; |
| struct k_work_delayable timeout; |
| /* Extended scanning */ |
| bt_addr_le_t addr; |
| uint8_t ad[CONFIG_BT_MESH_RPR_AD_TYPES_MAX]; |
| uint8_t ad_count; |
| /* Time to do regular scanning after extended scanning ends: */ |
| uint32_t additional_time; |
| struct net_buf_simple *adv_data; |
| struct bt_mesh_rpr_node cli; |
| struct bt_mesh_rpr_unprov *dev; |
| } scan; |
| struct { |
| struct k_work report; |
| enum bt_mesh_rpr_link_state state; |
| enum bt_mesh_rpr_status status; |
| uint8_t close_reason; |
| uint8_t tx_pdu; |
| uint8_t rx_pdu; |
| struct bt_mesh_rpr_node cli; |
| struct bt_mesh_rpr_unprov *dev; |
| } link; |
| struct { |
| const struct prov_bearer_cb *cb; |
| enum bt_mesh_rpr_node_refresh procedure; |
| void *cb_data; |
| struct { |
| prov_bearer_send_complete_t cb; |
| void *cb_data; |
| } tx; |
| } refresh; |
| } srv = { |
| .scan = { |
| .adv_data = NET_BUF_SIMPLE(CONFIG_BT_MESH_RPR_SRV_AD_DATA_MAX) |
| } |
| }; |
| |
| enum bt_mesh_rpr_node_refresh bt_mesh_node_refresh_get(void) |
| { |
| return srv.refresh.procedure; |
| } |
| |
| static struct bt_mesh_rpr_unprov *unprov_get(const uint8_t uuid[16]) |
| { |
| int i; |
| |
| for (i = 0; i < srv.scan.max_devs; ++i) { |
| if (uuid) { |
| if ((srv.scan.devs[i].flags & BT_MESH_RPR_UNPROV_ACTIVE) && |
| !memcmp(srv.scan.devs[i].uuid, uuid, 16)) { |
| return &srv.scan.devs[i]; |
| } |
| } else if (!(srv.scan.devs[i].flags & BT_MESH_RPR_UNPROV_ACTIVE)) { |
| return &srv.scan.devs[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static uint8_t *get_ad_type(uint8_t *list, size_t count, uint8_t ad) |
| { |
| int i; |
| |
| for (i = 0; i < count; ++i) { |
| if (ad == list[i] || (ad == BT_DATA_NAME_SHORTENED && |
| list[i] == BT_DATA_NAME_COMPLETE)) { |
| return &list[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static void cli_scan_clear(void) |
| { |
| srv.scan.cli.addr = BT_MESH_ADDR_UNASSIGNED; |
| srv.scan.cli.net_idx = BT_MESH_KEY_UNUSED; |
| } |
| |
| static void cli_link_clear(void) |
| { |
| srv.link.cli.addr = BT_MESH_ADDR_UNASSIGNED; |
| srv.link.cli.net_idx = BT_MESH_KEY_UNUSED; |
| } |
| |
| static void scan_status_send(struct bt_mesh_msg_ctx *ctx, |
| enum bt_mesh_rpr_status status) |
| { |
| uint8_t timeout = 0; |
| |
| if (atomic_test_bit(srv.flags, SCANNING)) { |
| timeout = k_ticks_to_ms_floor32( |
| k_work_delayable_remaining_get(&srv.scan.timeout)) / |
| MSEC_PER_SEC; |
| } |
| |
| BT_MESH_MODEL_BUF_DEFINE(rsp, RPR_OP_SCAN_STATUS, 4); |
| bt_mesh_model_msg_init(&rsp, RPR_OP_SCAN_STATUS); |
| net_buf_simple_add_u8(&rsp, status); |
| net_buf_simple_add_u8(&rsp, srv.scan.state); |
| net_buf_simple_add_u8(&rsp, srv.scan.max_devs); |
| net_buf_simple_add_u8(&rsp, timeout); |
| |
| bt_mesh_model_send(srv.mod, ctx, &rsp, NULL, NULL); |
| } |
| |
| static void link_status_send(struct bt_mesh_msg_ctx *ctx, |
| enum bt_mesh_rpr_status status) |
| { |
| BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_LINK_STATUS, 2); |
| bt_mesh_model_msg_init(&buf, RPR_OP_LINK_STATUS); |
| net_buf_simple_add_u8(&buf, status); |
| net_buf_simple_add_u8(&buf, srv.link.state); |
| |
| bt_mesh_model_send(srv.mod, ctx, &buf, NULL, NULL); |
| } |
| |
| static void link_report_send(void) |
| { |
| struct bt_mesh_msg_ctx ctx = LINK_CTX(&srv.link.cli, true); |
| |
| BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_LINK_REPORT, 3); |
| bt_mesh_model_msg_init(&buf, RPR_OP_LINK_REPORT); |
| net_buf_simple_add_u8(&buf, srv.link.status); |
| net_buf_simple_add_u8(&buf, srv.link.state); |
| if (srv.link.status == BT_MESH_RPR_ERR_LINK_CLOSED_BY_SERVER || |
| srv.link.status == BT_MESH_RPR_ERR_LINK_CLOSED_BY_DEVICE) { |
| net_buf_simple_add_u8(&buf, srv.link.close_reason); |
| } |
| |
| LOG_DBG("%u %u", srv.link.status, srv.link.state); |
| |
| bt_mesh_model_send(srv.mod, &ctx, &buf, NULL, NULL); |
| } |
| |
| static void scan_report_schedule(void) |
| { |
| uint32_t delay = 0; |
| |
| if (k_work_delayable_remaining_get(&srv.scan.report) || |
| atomic_test_bit(srv.flags, SCAN_REPORT_PENDING)) { |
| return; |
| } |
| |
| (void)bt_rand(&delay, sizeof(uint32_t)); |
| delay = (delay % 480) + 20; |
| |
| k_work_reschedule(&srv.scan.report, K_MSEC(delay)); |
| } |
| |
| static void scan_report_sent(int err, void *cb_data) |
| { |
| atomic_clear_bit(srv.flags, SCAN_REPORT_PENDING); |
| k_work_reschedule(&srv.scan.report, K_NO_WAIT); |
| } |
| |
| static const struct bt_mesh_send_cb report_cb = { |
| .end = scan_report_sent, |
| }; |
| |
| static void scan_report_send(void) |
| { |
| struct bt_mesh_msg_ctx ctx = LINK_CTX(&srv.scan.cli, true); |
| int i, err; |
| |
| if (atomic_test_bit(srv.flags, SCAN_REPORT_PENDING)) { |
| return; |
| } |
| |
| for (i = 0; i < srv.scan.max_devs; ++i) { |
| struct bt_mesh_rpr_unprov *dev = &srv.scan.devs[i]; |
| |
| if (!(dev->flags & BT_MESH_RPR_UNPROV_FOUND) || |
| (dev->flags & BT_MESH_RPR_UNPROV_REPORTED)) { |
| continue; |
| } |
| |
| BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_SCAN_REPORT, 23); |
| bt_mesh_model_msg_init(&buf, RPR_OP_SCAN_REPORT); |
| net_buf_simple_add_u8(&buf, dev->rssi); |
| net_buf_simple_add_mem(&buf, dev->uuid, 16); |
| net_buf_simple_add_le16(&buf, dev->oob); |
| if (dev->flags & BT_MESH_RPR_UNPROV_HASH) { |
| net_buf_simple_add_mem(&buf, &dev->hash, 4); |
| } |
| |
| atomic_set_bit(srv.flags, SCAN_REPORT_PENDING); |
| |
| err = bt_mesh_model_send(srv.mod, &ctx, &buf, &report_cb, NULL); |
| if (err) { |
| atomic_clear_bit(srv.flags, SCAN_REPORT_PENDING); |
| LOG_DBG("tx failed: %d", err); |
| break; |
| } |
| |
| LOG_DBG("Reported unprov #%u", i); |
| dev->flags |= BT_MESH_RPR_UNPROV_REPORTED; |
| break; |
| } |
| } |
| |
| static void scan_ext_report_send(void) |
| { |
| struct bt_mesh_msg_ctx ctx = LINK_CTX(&srv.scan.cli, true); |
| int err; |
| |
| BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_EXTENDED_SCAN_REPORT, |
| 19 + CONFIG_BT_MESH_RPR_SRV_AD_DATA_MAX); |
| bt_mesh_model_msg_init(&buf, RPR_OP_EXTENDED_SCAN_REPORT); |
| net_buf_simple_add_u8(&buf, BT_MESH_RPR_SUCCESS); |
| net_buf_simple_add_mem(&buf, srv.scan.dev->uuid, 16); |
| |
| if (srv.scan.dev->flags & BT_MESH_RPR_UNPROV_FOUND) { |
| net_buf_simple_add_le16(&buf, srv.scan.dev->oob); |
| } else { |
| LOG_DBG("not found"); |
| goto send; |
| } |
| |
| if (srv.scan.dev->flags & BT_MESH_RPR_UNPROV_EXT_ADV_RXD) { |
| net_buf_simple_add_mem(&buf, srv.scan.adv_data->data, |
| srv.scan.adv_data->len); |
| LOG_DBG("adv data: %s", |
| bt_hex(srv.scan.adv_data->data, srv.scan.adv_data->len)); |
| } |
| |
| srv.scan.dev->flags &= ~BT_MESH_RPR_UNPROV_EXT_ADV_RXD; |
| send: |
| err = bt_mesh_model_send(srv.mod, &ctx, &buf, NULL, NULL); |
| if (!err) { |
| srv.scan.dev->flags |= BT_MESH_RPR_UNPROV_REPORTED; |
| } |
| } |
| |
| static void scan_stop(void) |
| { |
| LOG_DBG(""); |
| |
| k_work_cancel_delayable(&srv.scan.report); |
| k_work_cancel_delayable(&srv.scan.timeout); |
| srv.scan.state = BT_MESH_RPR_SCAN_IDLE; |
| cli_scan_clear(); |
| atomic_clear_bit(srv.flags, SCANNING); |
| } |
| |
| static void scan_report_timeout(struct k_work *work) |
| { |
| scan_report_send(); |
| } |
| |
| static void scan_ext_stop(uint32_t remaining_time) |
| { |
| atomic_clear_bit(srv.flags, URI_MATCHED); |
| atomic_clear_bit(srv.flags, URI_REQUESTED); |
| |
| if ((remaining_time + srv.scan.additional_time) && |
| srv.scan.state != BT_MESH_RPR_SCAN_IDLE) { |
| k_work_reschedule( |
| &srv.scan.timeout, |
| K_MSEC(remaining_time + srv.scan.additional_time)); |
| } else if (srv.scan.state == BT_MESH_RPR_SCAN_MULTI) { |
| /* Extended scan might have finished early */ |
| scan_ext_report_send(); |
| } else if (srv.scan.state != BT_MESH_RPR_SCAN_IDLE) { |
| scan_report_send(); |
| scan_stop(); |
| } else { |
| atomic_clear_bit(srv.flags, SCANNING); |
| } |
| |
| if (!(srv.scan.dev->flags & BT_MESH_RPR_UNPROV_REPORTED)) { |
| scan_ext_report_send(); |
| } |
| |
| bt_mesh_scan_active_set(false); |
| srv.scan.dev = NULL; |
| } |
| |
| static void adv_handle_ext_scan(const struct bt_le_scan_recv_info *info, |
| struct net_buf_simple *buf); |
| |
| static void scan_timeout(struct k_work *work) |
| { |
| LOG_DBG("%s", (srv.scan.dev ? "Extended scanning" : "Normal scanning")); |
| |
| if (srv.scan.dev) { |
| scan_ext_stop(0); |
| } else { |
| scan_report_send(); |
| scan_stop(); |
| } |
| } |
| |
| static void link_close(enum bt_mesh_rpr_status status, |
| enum prov_bearer_link_status reason) |
| { |
| srv.link.status = status; |
| srv.link.close_reason = reason; |
| srv.link.state = BT_MESH_RPR_LINK_CLOSING; |
| |
| LOG_DBG("status: %u reason: %u", status, reason); |
| |
| if (atomic_test_and_clear_bit(srv.flags, NODE_REFRESH)) { |
| /* Link closing is an atomic operation: */ |
| srv.link.state = BT_MESH_RPR_LINK_IDLE; |
| link_report_send(); |
| srv.refresh.cb->link_closed(&pb_remote_srv, srv.refresh.cb_data, |
| srv.link.close_reason); |
| |
| cli_link_clear(); |
| } else { |
| bt_mesh_pb_adv.link_close(reason); |
| } |
| } |
| |
| static void outbound_pdu_report_send(void) |
| { |
| struct bt_mesh_msg_ctx ctx = LINK_CTX(&srv.link.cli, true); |
| |
| BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_PDU_OUTBOUND_REPORT, 1); |
| bt_mesh_model_msg_init(&buf, RPR_OP_PDU_OUTBOUND_REPORT); |
| net_buf_simple_add_u8(&buf, srv.link.tx_pdu); |
| |
| LOG_DBG("%u", srv.link.tx_pdu); |
| |
| bt_mesh_model_send(srv.mod, &ctx, &buf, NULL, NULL); |
| } |
| |
| static void pdu_send_complete(int err, void *cb_data) |
| { |
| if (err) { |
| link_close(BT_MESH_RPR_ERR_LINK_CLOSED_AS_CANNOT_SEND_PDU, |
| PROV_BEARER_LINK_STATUS_FAIL); |
| } else if (srv.link.state == BT_MESH_RPR_LINK_SENDING) { |
| srv.link.state = BT_MESH_RPR_LINK_ACTIVE; |
| srv.link.tx_pdu++; |
| outbound_pdu_report_send(); |
| } |
| } |
| |
| static int inbound_pdu_send(struct net_buf_simple *buf, |
| const struct bt_mesh_send_cb *cb) |
| { |
| struct bt_mesh_msg_ctx ctx = LINK_CTX(&srv.link.cli, true); |
| |
| BT_MESH_MODEL_BUF_DEFINE(msg, RPR_OP_PDU_REPORT, 66); |
| bt_mesh_model_msg_init(&msg, RPR_OP_PDU_REPORT); |
| net_buf_simple_add_u8(&msg, srv.link.rx_pdu); |
| net_buf_simple_add_mem(&msg, buf->data, buf->len); |
| |
| return bt_mesh_model_send(srv.mod, &ctx, &msg, cb, NULL); |
| } |
| |
| static void subnet_evt_handler(struct bt_mesh_subnet *subnet, |
| enum bt_mesh_key_evt evt) |
| { |
| if (!srv.mod || evt != BT_MESH_KEY_DELETED) { |
| return; |
| } |
| |
| LOG_DBG("Subnet deleted"); |
| |
| if (srv.link.state != BT_MESH_RPR_LINK_IDLE && |
| subnet->net_idx == srv.link.cli.net_idx) { |
| link_close(BT_MESH_RPR_ERR_LINK_CLOSED_BY_SERVER, |
| PROV_BEARER_LINK_STATUS_FAIL); |
| /* Skip the link closing stage, as specified in the Bluetooth |
| * MshPRTv1.1: 4.4.5.4. |
| */ |
| srv.link.state = BT_MESH_RPR_LINK_IDLE; |
| } else if (atomic_test_bit(srv.flags, SCANNING) && |
| subnet->net_idx == srv.scan.cli.net_idx) { |
| scan_stop(); |
| } |
| } |
| |
| BT_MESH_SUBNET_CB_DEFINE(rpr_srv) = { |
| .evt_handler = subnet_evt_handler |
| }; |
| |
| /******************************************************************************* |
| * Prov bearer interface |
| ******************************************************************************/ |
| static void pb_link_opened(const struct prov_bearer *bearer, void *cb_data) |
| { |
| LOG_DBG(""); |
| |
| srv.link.state = BT_MESH_RPR_LINK_ACTIVE; |
| srv.link.status = BT_MESH_RPR_SUCCESS; |
| link_report_send(); |
| } |
| |
| static void link_report_send_and_clear(struct k_work *work) |
| { |
| link_report_send(); |
| cli_link_clear(); |
| } |
| |
| static void pb_link_closed(const struct prov_bearer *bearer, void *cb_data, |
| enum prov_bearer_link_status reason) |
| { |
| if (srv.link.state == BT_MESH_RPR_LINK_IDLE) { |
| return; |
| } |
| |
| LOG_DBG("%u", reason); |
| |
| if (srv.link.state == BT_MESH_RPR_LINK_OPENING) { |
| srv.link.status = BT_MESH_RPR_ERR_LINK_OPEN_FAILED; |
| } else if (reason == PROV_BEARER_LINK_STATUS_TIMEOUT) { |
| if (srv.link.state == BT_MESH_RPR_LINK_SENDING) { |
| srv.link.status = |
| BT_MESH_RPR_ERR_LINK_CLOSED_AS_CANNOT_SEND_PDU; |
| } else { |
| srv.link.status = BT_MESH_RPR_ERR_LINK_CLOSED_BY_SERVER; |
| } |
| } else if (reason == PROV_BEARER_LINK_STATUS_FAIL && |
| srv.link.status != BT_MESH_RPR_ERR_LINK_CLOSED_BY_CLIENT && |
| srv.link.status != BT_MESH_RPR_ERR_LINK_CLOSED_BY_SERVER) { |
| srv.link.status = BT_MESH_RPR_ERR_LINK_CLOSED_BY_DEVICE; |
| } |
| |
| if (reason == PROV_BEARER_LINK_STATUS_SUCCESS) { |
| srv.link.close_reason = PROV_BEARER_LINK_STATUS_SUCCESS; |
| } else { |
| srv.link.close_reason = PROV_BEARER_LINK_STATUS_FAIL; |
| } |
| |
| srv.link.state = BT_MESH_RPR_LINK_IDLE; |
| k_work_submit(&srv.link.report); |
| } |
| |
| static void pb_error(const struct prov_bearer *bearer, void *cb_data, |
| uint8_t err) |
| { |
| if (srv.link.state == BT_MESH_RPR_LINK_IDLE) { |
| return; |
| } |
| |
| LOG_DBG("%d", err); |
| srv.link.close_reason = err; |
| srv.link.state = BT_MESH_RPR_LINK_IDLE; |
| srv.link.status = BT_MESH_RPR_ERR_LINK_CLOSED_AS_CANNOT_RECEIVE_PDU; |
| link_report_send(); |
| cli_link_clear(); |
| } |
| |
| static void pb_rx(const struct prov_bearer *bearer, void *cb_data, |
| struct net_buf_simple *buf) |
| { |
| int err; |
| |
| if (srv.link.state != BT_MESH_RPR_LINK_ACTIVE && |
| srv.link.state != BT_MESH_RPR_LINK_SENDING) { |
| return; |
| } |
| |
| srv.link.rx_pdu++; |
| LOG_DBG(""); |
| |
| err = inbound_pdu_send(buf, NULL); |
| if (err) { |
| LOG_ERR("PDU send fail: %d", err); |
| link_close(BT_MESH_RPR_ERR_LINK_CLOSED_AS_CANNOT_SEND_PDU, |
| PROV_BEARER_LINK_STATUS_FAIL); |
| bt_mesh_pb_adv.link_close(PROV_ERR_RESOURCES); |
| } |
| } |
| |
| static const struct prov_bearer_cb prov_bearer_cb = { |
| .link_opened = pb_link_opened, |
| .link_closed = pb_link_closed, |
| .error = pb_error, |
| .recv = pb_rx, |
| }; |
| |
| /******************************************************************************* |
| * Message handlers |
| ******************************************************************************/ |
| |
| static int handle_scan_caps_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| BT_MESH_MODEL_BUF_DEFINE(rsp, RPR_OP_SCAN_CAPS_STATUS, 2); |
| bt_mesh_model_msg_init(&rsp, RPR_OP_SCAN_CAPS_STATUS); |
| net_buf_simple_add_u8(&rsp, CONFIG_BT_MESH_RPR_SRV_SCANNED_ITEMS_MAX); |
| net_buf_simple_add_u8(&rsp, true); |
| |
| bt_mesh_model_send(srv.mod, ctx, &rsp, NULL, NULL); |
| |
| return 0; |
| } |
| |
| static int handle_scan_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| scan_status_send(ctx, BT_MESH_RPR_SUCCESS); |
| |
| return 0; |
| } |
| |
| static int handle_scan_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| struct bt_mesh_rpr_node cli = RPR_NODE(ctx); |
| enum bt_mesh_rpr_status status; |
| const uint8_t *uuid = NULL; |
| uint8_t max_devs; |
| uint8_t timeout; |
| int i; |
| |
| max_devs = net_buf_simple_pull_u8(buf); |
| timeout = net_buf_simple_pull_u8(buf); |
| if (!timeout) { |
| return -EINVAL; |
| } |
| |
| if (buf->len == 16) { |
| uuid = net_buf_simple_pull_mem(buf, 16); |
| } else if (buf->len) { |
| return -EINVAL; |
| } |
| |
| LOG_DBG("max %u devs, %u s %s", max_devs, timeout, |
| uuid ? bt_hex(uuid, 16) : ""); |
| |
| if (max_devs > CONFIG_BT_MESH_RPR_SRV_SCANNED_ITEMS_MAX) { |
| status = BT_MESH_RPR_ERR_SCANNING_CANNOT_START; |
| goto rsp; |
| } |
| |
| if (srv.scan.state != BT_MESH_RPR_SCAN_IDLE && |
| !rpr_node_equal(&cli, &srv.scan.cli)) { |
| status = BT_MESH_RPR_ERR_INVALID_STATE; |
| goto rsp; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(srv.scan.devs); ++i) { |
| srv.scan.devs[i].flags = 0; |
| } |
| |
| if (uuid) { |
| srv.scan.state = BT_MESH_RPR_SCAN_SINGLE; |
| |
| srv.scan.devs[0].flags = BT_MESH_RPR_UNPROV_ACTIVE; |
| memcpy(srv.scan.devs[0].uuid, uuid, 16); |
| } else { |
| srv.scan.state = BT_MESH_RPR_SCAN_MULTI; |
| } |
| |
| srv.scan.max_devs = |
| (max_devs ? max_devs : |
| CONFIG_BT_MESH_RPR_SRV_SCANNED_ITEMS_MAX); |
| srv.scan.cli = cli; |
| status = BT_MESH_RPR_SUCCESS; |
| |
| atomic_set_bit(srv.flags, SCANNING); |
| k_work_reschedule(&srv.scan.timeout, K_SECONDS(timeout)); |
| |
| rsp: |
| scan_status_send(ctx, status); |
| |
| return 0; |
| } |
| |
| static int handle_extended_scan_start(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| BT_MESH_MODEL_BUF_DEFINE(rsp, RPR_OP_EXTENDED_SCAN_REPORT, |
| 19 + CONFIG_BT_MESH_RPR_SRV_AD_DATA_MAX); |
| struct bt_mesh_rpr_node cli = RPR_NODE(ctx); |
| enum bt_mesh_rpr_status status; |
| const uint8_t *uuid; |
| uint8_t *ad = NULL; |
| uint8_t ad_count; |
| uint8_t timeout; |
| int i; |
| |
| /* According to MshPRTv1.1: 4.4.5.5.1.7, scan reports shall be |
| * sent as segmented messages. |
| */ |
| ctx->send_rel = true; |
| |
| ad_count = net_buf_simple_pull_u8(buf); |
| if (buf->len < ad_count || ad_count == 0 || ad_count > 0x10) { |
| /* Prohibited */ |
| return -EINVAL; |
| } |
| |
| ad = net_buf_simple_pull_mem(buf, ad_count); |
| for (i = 0; i < ad_count; ++i) { |
| if (ad[i] == BT_DATA_NAME_SHORTENED || |
| ad[i] == BT_DATA_UUID16_SOME || |
| ad[i] == BT_DATA_UUID32_SOME || |
| ad[i] == BT_DATA_UUID128_SOME) { |
| return -EINVAL; |
| } |
| |
| for (int j = 0; j < i; j++) { |
| if (ad[i] == ad[j]) { |
| /* Duplicate entry */ |
| return -EINVAL; |
| } |
| } |
| } |
| |
| ad_count = MIN(ad_count, CONFIG_BT_MESH_RPR_AD_TYPES_MAX); |
| |
| if (!buf->len) { |
| const struct bt_mesh_prov *prov = bt_mesh_prov_get(); |
| |
| LOG_DBG("Self scan"); |
| |
| /* Want our local info. Could also include additional adv data, |
| * but there's no functionality for this in the mesh stack at |
| * the moment, so we'll only include the URI (if requested) |
| */ |
| bt_mesh_model_msg_init(&rsp, RPR_OP_EXTENDED_SCAN_REPORT); |
| |
| net_buf_simple_add_u8(&rsp, BT_MESH_RPR_SUCCESS); |
| net_buf_simple_add_mem(&rsp, prov->uuid, 16); |
| net_buf_simple_add_le16(&rsp, prov->oob_info); |
| |
| if (prov->uri && get_ad_type(ad, ad_count, BT_DATA_URI)) { |
| uint8_t uri_len = strlen(prov->uri); |
| |
| if (uri_len < CONFIG_BT_MESH_RPR_SRV_AD_DATA_MAX - 2) { |
| net_buf_simple_add_u8(&rsp, uri_len + 1); |
| net_buf_simple_add_u8(&rsp, BT_DATA_URI); |
| net_buf_simple_add_mem(&rsp, prov->uri, |
| uri_len); |
| LOG_DBG("URI added: %s", prov->uri); |
| } else { |
| LOG_WRN("URI data won't fit in scan report"); |
| } |
| } |
| |
| bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); |
| return 0; |
| } |
| |
| if (buf->len != 17) { |
| return -EINVAL; |
| } |
| |
| uuid = net_buf_simple_pull_mem(buf, 16); |
| timeout = net_buf_simple_pull_u8(buf); |
| |
| if (IS_ENABLED(CONFIG_BT_MESH_MODEL_LOG_LEVEL_DBG)) { |
| struct bt_uuid_128 uuid_repr = { .uuid = { BT_UUID_TYPE_128 } }; |
| |
| memcpy(uuid_repr.val, uuid, 16); |
| LOG_DBG("%s AD types: %s", bt_uuid_str(&uuid_repr.uuid), |
| bt_hex(ad, ad_count)); |
| } |
| |
| if (timeout < BT_MESH_RPR_EXT_SCAN_TIME_MIN || |
| timeout > BT_MESH_RPR_EXT_SCAN_TIME_MAX) { |
| LOG_ERR("Invalid extended scan timeout %u", timeout); |
| return -EINVAL; |
| } |
| |
| if (srv.link.state != BT_MESH_RPR_LINK_IDLE) { |
| status = BT_MESH_RPR_ERR_LIMITED_RESOURCES; |
| goto rsp; |
| } |
| |
| if (srv.scan.dev && (memcmp(srv.scan.dev->uuid, uuid, 16) || |
| !rpr_node_equal(&srv.scan.cli, &cli))) { |
| LOG_WRN("Extended scan fail: Busy"); |
| status = BT_MESH_RPR_ERR_LIMITED_RESOURCES; |
| goto rsp; |
| } |
| |
| if (srv.scan.state == BT_MESH_RPR_SCAN_IDLE) { |
| srv.scan.max_devs = 1; |
| srv.scan.devs[0].flags = 0; |
| } |
| |
| srv.scan.dev = unprov_get(uuid); |
| if (!srv.scan.dev) { |
| srv.scan.dev = unprov_get(NULL); |
| if (!srv.scan.dev) { |
| LOG_WRN("Extended scan fail: No memory"); |
| status = BT_MESH_RPR_ERR_LIMITED_RESOURCES; |
| goto rsp; |
| } |
| |
| memcpy(srv.scan.dev->uuid, uuid, 16); |
| srv.scan.dev->oob = 0; |
| srv.scan.dev->flags = 0; |
| } |
| |
| memcpy(srv.scan.ad, ad, ad_count); |
| srv.scan.ad_count = ad_count; |
| net_buf_simple_reset(srv.scan.adv_data); |
| |
| atomic_set_bit(srv.flags, SCANNING); |
| atomic_clear_bit(srv.flags, SCAN_EXT_HAS_ADDR); |
| srv.scan.dev->flags &= ~BT_MESH_RPR_UNPROV_REPORTED; |
| srv.scan.dev->flags |= BT_MESH_RPR_UNPROV_ACTIVE | BT_MESH_RPR_UNPROV_EXT; |
| |
| if (srv.scan.state == BT_MESH_RPR_SCAN_IDLE) { |
| srv.scan.additional_time = 0; |
| srv.scan.cli = cli; |
| } else if (k_ticks_to_ms_floor32( |
| k_work_delayable_remaining_get(&srv.scan.timeout)) < |
| (timeout * MSEC_PER_SEC)) { |
| srv.scan.additional_time = 0; |
| } else { |
| srv.scan.additional_time = |
| k_ticks_to_ms_floor32(k_work_delayable_remaining_get(&srv.scan.timeout)) - |
| (timeout * MSEC_PER_SEC); |
| } |
| |
| bt_mesh_scan_active_set(true); |
| k_work_reschedule(&srv.scan.timeout, K_SECONDS(timeout)); |
| return 0; |
| |
| rsp: |
| bt_mesh_model_msg_init(&rsp, RPR_OP_EXTENDED_SCAN_REPORT); |
| net_buf_simple_add_u8(&rsp, status); |
| net_buf_simple_add_mem(&rsp, uuid, 16); |
| bt_mesh_model_send(mod, ctx, &rsp, NULL, NULL); |
| |
| return 0; |
| } |
| |
| static int handle_scan_stop(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| if (atomic_test_bit(srv.flags, SCANNING)) { |
| scan_report_send(); |
| scan_stop(); |
| } |
| |
| scan_status_send(ctx, BT_MESH_RPR_SUCCESS); |
| |
| return 0; |
| } |
| |
| static int handle_link_get(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| LOG_DBG(""); |
| |
| link_status_send(ctx, BT_MESH_RPR_SUCCESS); |
| |
| return 0; |
| } |
| |
| static int handle_link_open(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| bool is_refresh_procedure = (buf->len == 1); |
| struct bt_mesh_rpr_node cli = RPR_NODE(ctx); |
| int8_t timeout = LINK_OPEN_TIMEOUT_DEFAULT; |
| enum bt_mesh_rpr_status status; |
| const uint8_t *uuid; |
| uint8_t refresh; |
| int err; |
| |
| if (buf->len != 1 && buf->len != 16 && buf->len != 17) { |
| return -EINVAL; |
| } |
| |
| if (srv.link.state == BT_MESH_RPR_LINK_CLOSING || |
| srv.link.state == BT_MESH_RPR_LINK_SENDING) { |
| status = BT_MESH_RPR_ERR_INVALID_STATE; |
| LOG_ERR("Invalid state: %u", srv.link.state); |
| goto rsp; |
| } |
| |
| if (srv.link.state == BT_MESH_RPR_LINK_OPENING || |
| srv.link.state == BT_MESH_RPR_LINK_ACTIVE) { |
| |
| if (!rpr_node_equal(&cli, &srv.link.cli)) { |
| status = BT_MESH_RPR_ERR_LINK_CANNOT_OPEN; |
| goto rsp; |
| } |
| |
| if (is_refresh_procedure) { |
| refresh = net_buf_simple_pull_u8(buf); |
| if (!atomic_test_bit(srv.flags, NODE_REFRESH) || |
| srv.refresh.procedure != refresh) { |
| status = BT_MESH_RPR_ERR_LINK_CANNOT_OPEN; |
| } else { |
| status = BT_MESH_RPR_SUCCESS; |
| } |
| |
| goto rsp; |
| } |
| |
| if (atomic_test_bit(srv.flags, NODE_REFRESH)) { |
| status = BT_MESH_RPR_ERR_LINK_CANNOT_OPEN; |
| goto rsp; |
| } |
| |
| uuid = net_buf_simple_pull_mem(buf, 16); |
| |
| if (memcmp(uuid, srv.link.dev->uuid, 16)) { |
| status = BT_MESH_RPR_ERR_LINK_CANNOT_OPEN; |
| } else { |
| status = BT_MESH_RPR_SUCCESS; |
| } |
| |
| goto rsp; |
| } |
| |
| /* Link state is IDLE */ |
| |
| if (is_refresh_procedure) { |
| refresh = net_buf_simple_pull_u8(buf); |
| if (refresh > BT_MESH_RPR_NODE_REFRESH_COMPOSITION) { |
| LOG_ERR("Invalid refresh: %u", refresh); |
| return -EINVAL; |
| } |
| |
| if (refresh == BT_MESH_RPR_NODE_REFRESH_COMPOSITION && |
| !atomic_test_bit(bt_mesh.flags, BT_MESH_COMP_DIRTY)) { |
| LOG_WRN("Composition data page 128 is equal to page 0"); |
| status = BT_MESH_RPR_ERR_LINK_CANNOT_OPEN; |
| goto rsp; |
| } |
| |
| LOG_DBG("Node Refresh: %u", refresh); |
| |
| atomic_set_bit(srv.flags, NODE_REFRESH); |
| srv.refresh.procedure = refresh; |
| srv.link.cli = cli; |
| srv.link.rx_pdu = 0; |
| srv.link.tx_pdu = 0; |
| srv.link.state = BT_MESH_RPR_LINK_ACTIVE; |
| srv.link.status = BT_MESH_RPR_SUCCESS; |
| srv.refresh.cb->link_opened(&pb_remote_srv, &srv); |
| status = BT_MESH_RPR_SUCCESS; |
| link_report_send(); |
| goto rsp; |
| } |
| |
| uuid = net_buf_simple_pull_mem(buf, 16); |
| if (buf->len) { |
| timeout = net_buf_simple_pull_u8(buf); |
| if (!timeout || timeout > 0x3c) { |
| LOG_ERR("Invalid timeout: %u", timeout); |
| return -EINVAL; |
| } |
| } |
| |
| LOG_DBG("0x%04x: %s", cli.addr, bt_hex(uuid, 16)); |
| |
| /* Attempt to reuse the scanned unprovisioned device, to preserve as |
| * much information as possible, but fall back to hijacking the first |
| * slot if none was found. |
| */ |
| srv.link.dev = unprov_get(uuid); |
| if (!srv.link.dev) { |
| srv.link.dev = &srv.scan.devs[0]; |
| memcpy(srv.link.dev->uuid, uuid, 16); |
| srv.link.dev->flags = 0; |
| } |
| |
| err = bt_mesh_pb_adv.link_open(uuid, timeout, &prov_bearer_cb, &srv); |
| if (err) { |
| status = BT_MESH_RPR_ERR_LINK_CANNOT_OPEN; |
| goto rsp; |
| } |
| |
| srv.link.cli = cli; |
| srv.link.rx_pdu = 0; |
| srv.link.tx_pdu = 0; |
| srv.link.state = BT_MESH_RPR_LINK_OPENING; |
| srv.link.status = BT_MESH_RPR_SUCCESS; |
| srv.link.dev->flags |= BT_MESH_RPR_UNPROV_HAS_LINK; |
| status = BT_MESH_RPR_SUCCESS; |
| |
| rsp: |
| link_status_send(ctx, status); |
| |
| return 0; |
| } |
| |
| static int handle_link_close(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| struct bt_mesh_rpr_node cli = RPR_NODE(ctx); |
| enum prov_bearer_link_status reason; |
| |
| reason = net_buf_simple_pull_u8(buf); |
| if (reason != PROV_BEARER_LINK_STATUS_SUCCESS && |
| reason != PROV_BEARER_LINK_STATUS_FAIL) { |
| return -EINVAL; |
| } |
| |
| LOG_DBG(""); |
| |
| if (srv.link.state == BT_MESH_RPR_LINK_IDLE || |
| srv.link.state == BT_MESH_RPR_LINK_CLOSING) { |
| link_status_send(ctx, BT_MESH_RPR_SUCCESS); |
| return 0; |
| } |
| |
| if (!rpr_node_equal(&cli, &srv.link.cli)) { |
| link_status_send(ctx, BT_MESH_RPR_ERR_INVALID_STATE); |
| return 0; |
| } |
| |
| srv.link.state = BT_MESH_RPR_LINK_CLOSING; |
| |
| /* Note: The response status isn't the same as the link status state, |
| * which will be used in the link report when the link is fully closed. |
| */ |
| |
| /* Disable randomization for the Remote Provisioning Link Status message to avoid reordering |
| * of it with the Remote Provisioning Link Report message that shall be sent in a sequence |
| * when closing an active link (see section 4.4.5.5.3.3 of MshPRTv1.1). |
| */ |
| ctx->rnd_delay = false; |
| |
| link_status_send(ctx, BT_MESH_RPR_SUCCESS); |
| link_close(BT_MESH_RPR_ERR_LINK_CLOSED_BY_CLIENT, reason); |
| |
| return 0; |
| } |
| |
| static int handle_pdu_send(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx, |
| struct net_buf_simple *buf) |
| { |
| struct bt_mesh_rpr_node cli = RPR_NODE(ctx); |
| uint8_t pdu_num; |
| int err; |
| |
| pdu_num = net_buf_simple_pull_u8(buf); |
| |
| if (srv.link.state != BT_MESH_RPR_LINK_ACTIVE) { |
| LOG_WRN("Sending PDU while busy (state %u)", srv.link.state); |
| return 0; |
| } |
| |
| if (!rpr_node_equal(&cli, &srv.link.cli)) { |
| LOG_WRN("Unknown client 0x%04x", cli.addr); |
| return 0; |
| } |
| |
| if (pdu_num != srv.link.tx_pdu + 1) { |
| LOG_WRN("Invalid pdu number: %u, expected %u", pdu_num, |
| srv.link.tx_pdu + 1); |
| outbound_pdu_report_send(); |
| return 0; |
| } |
| |
| LOG_DBG("0x%02x", buf->data[0]); |
| |
| if (atomic_test_bit(srv.flags, NODE_REFRESH)) { |
| srv.link.tx_pdu++; |
| outbound_pdu_report_send(); |
| srv.refresh.cb->recv(&pb_remote_srv, srv.refresh.cb_data, buf); |
| } else { |
| srv.link.state = BT_MESH_RPR_LINK_SENDING; |
| err = bt_mesh_pb_adv.send(buf, pdu_send_complete, &srv); |
| if (err) { |
| link_close( |
| BT_MESH_RPR_ERR_LINK_CLOSED_AS_CANNOT_SEND_PDU, |
| PROV_BEARER_LINK_STATUS_FAIL); |
| } |
| } |
| |
| return 0; |
| } |
| |
| const struct bt_mesh_model_op _bt_mesh_rpr_srv_op[] = { |
| { RPR_OP_SCAN_CAPS_GET, BT_MESH_LEN_EXACT(0), handle_scan_caps_get }, |
| { RPR_OP_SCAN_GET, BT_MESH_LEN_EXACT(0), handle_scan_get }, |
| { RPR_OP_SCAN_START, BT_MESH_LEN_MIN(2), handle_scan_start }, |
| { RPR_OP_EXTENDED_SCAN_START, BT_MESH_LEN_MIN(1), handle_extended_scan_start }, |
| { RPR_OP_SCAN_STOP, BT_MESH_LEN_EXACT(0), handle_scan_stop }, |
| { RPR_OP_LINK_GET, BT_MESH_LEN_EXACT(0), handle_link_get }, |
| { RPR_OP_LINK_OPEN, BT_MESH_LEN_MIN(1), handle_link_open }, |
| { RPR_OP_LINK_CLOSE, BT_MESH_LEN_EXACT(1), handle_link_close }, |
| { RPR_OP_PDU_SEND, BT_MESH_LEN_MIN(1), handle_pdu_send }, |
| BT_MESH_MODEL_OP_END, |
| }; |
| |
| static struct bt_mesh_rpr_unprov * |
| adv_handle_beacon(const struct bt_le_scan_recv_info *info, |
| struct bt_data *ad) |
| { |
| struct bt_uuid_128 uuid_repr = { .uuid = { BT_UUID_TYPE_128 } }; |
| struct bt_mesh_rpr_unprov *dev = NULL; |
| const uint8_t *uuid; |
| |
| if (ad->data[0] != 0x00 || (ad->data_len != 19 && ad->data_len != 23)) { |
| return NULL; |
| } |
| |
| uuid = &ad->data[1]; |
| |
| dev = unprov_get(uuid); |
| if (!dev) { |
| if (srv.scan.state != BT_MESH_RPR_SCAN_MULTI) { |
| return NULL; |
| } |
| |
| dev = unprov_get(NULL); |
| if (!dev) { |
| return NULL; |
| } |
| |
| memcpy(dev->uuid, uuid, 16); |
| dev->flags = BT_MESH_RPR_UNPROV_ACTIVE; |
| } else if (dev->flags & BT_MESH_RPR_UNPROV_FOUND) { |
| return dev; |
| } |
| |
| dev->oob = sys_get_be16(&ad->data[17]); |
| dev->rssi = info->rssi; |
| |
| if (ad->data_len == 23) { |
| memcpy(&dev->hash, &ad->data[19], 4); |
| dev->flags |= BT_MESH_RPR_UNPROV_HASH; |
| } |
| |
| dev->flags |= BT_MESH_RPR_UNPROV_FOUND; |
| memcpy(uuid_repr.val, uuid, 16); |
| |
| LOG_DBG("Unprov #%u: %s OOB: 0x%04x %s", dev - &srv.scan.devs[0], |
| bt_uuid_str(&uuid_repr.uuid), dev->oob, |
| (dev->flags & BT_MESH_RPR_UNPROV_HASH) ? bt_hex(&dev->hash, 4) : |
| "(no hash)"); |
| |
| if (dev != srv.scan.dev && !(dev->flags & BT_MESH_RPR_UNPROV_REPORTED)) { |
| scan_report_schedule(); |
| } |
| |
| return dev; |
| } |
| |
| static bool pull_ad_data(struct net_buf_simple *buf, struct bt_data *ad) |
| { |
| uint8_t len; |
| |
| if (!buf->len) { |
| return false; |
| } |
| |
| len = net_buf_simple_pull_u8(buf); |
| if (!len || len > buf->len) { |
| return false; |
| } |
| |
| ad->type = net_buf_simple_pull_u8(buf); |
| ad->data_len = len - sizeof(ad->type); |
| ad->data = net_buf_simple_pull_mem(buf, ad->data_len); |
| return true; |
| } |
| |
| static void adv_handle_ext_scan(const struct bt_le_scan_recv_info *info, |
| struct net_buf_simple *buf) |
| { |
| struct bt_mesh_rpr_unprov *dev = NULL; |
| struct net_buf_simple_state initial; |
| struct bt_data ad; |
| bool uri_match = false; |
| bool uri_present = false; |
| bool is_beacon = false; |
| |
| if (atomic_test_bit(srv.flags, SCAN_EXT_HAS_ADDR) && |
| !bt_addr_le_cmp(&srv.scan.addr, info->addr)) { |
| dev = srv.scan.dev; |
| } |
| |
| /* Do AD data walk in two rounds: First to figure out which |
| * unprovisioned device this is (if any), and the second to copy out |
| * relevant AD data to the extended scan report. |
| */ |
| |
| net_buf_simple_save(buf, &initial); |
| while (pull_ad_data(buf, &ad)) { |
| if (ad.type == BT_DATA_URI) { |
| uri_present = true; |
| } |
| |
| if (ad.type == BT_DATA_MESH_BEACON && !dev) { |
| dev = adv_handle_beacon(info, &ad); |
| is_beacon = true; |
| } else if (ad.type == BT_DATA_URI && |
| (srv.scan.dev->flags & BT_MESH_RPR_UNPROV_HASH)) { |
| uint8_t hash[16]; |
| |
| if (bt_mesh_s1(ad.data, ad.data_len, hash) || |
| memcmp(hash, &srv.scan.dev->hash, 4)) { |
| continue; |
| } |
| |
| LOG_DBG("Found matching URI"); |
| uri_match = true; |
| dev = srv.scan.dev; |
| srv.scan.dev->flags |= BT_MESH_RPR_UNPROV_EXT_ADV_RXD; |
| } |
| } |
| |
| if (uri_match) { |
| atomic_set_bit(srv.flags, URI_MATCHED); |
| } |
| |
| if (!dev) { |
| return; |
| } |
| |
| /* Do not process advertisement if it was not identified by URI hash from beacon */ |
| if (!(dev->flags & BT_MESH_RPR_UNPROV_EXT_ADV_RXD)) { |
| return; |
| } |
| |
| srv.scan.addr = *info->addr; |
| atomic_set_bit(srv.flags, SCAN_EXT_HAS_ADDR); |
| |
| if (IS_ENABLED(CONFIG_BT_MESH_MODEL_LOG_LEVEL_DBG)) { |
| struct bt_uuid_128 uuid_repr = { .uuid = { BT_UUID_TYPE_128 } }; |
| |
| memcpy(uuid_repr.val, dev->uuid, 16); |
| LOG_DBG("Is %s", bt_uuid_str(&uuid_repr.uuid)); |
| } |
| |
| net_buf_simple_restore(buf, &initial); |
| |
| /* The ADTypeFilter field of the Remote Provisioning Extended Scan Start message |
| * contains only the URI AD Type, and the URI Hash is not available for the device |
| * with the Device UUID that was requested in the Remote Provisioning Extended Scan |
| * Start message. |
| */ |
| if (srv.scan.ad_count == 1 && |
| get_ad_type(srv.scan.ad, 1, BT_DATA_URI) && |
| !uri_match) { |
| goto complete; |
| } |
| |
| while (srv.scan.ad_count && pull_ad_data(buf, &ad)) { |
| uint8_t *ad_entry; |
| |
| ad_entry = get_ad_type(srv.scan.ad, srv.scan.ad_count, ad.type); |
| if (!ad_entry || (ad.type == BT_DATA_URI && !uri_match)) { |
| continue; |
| } |
| |
| LOG_DBG("AD type 0x%02x", ad.type); |
| |
| if (ad.type == BT_DATA_URI) { |
| atomic_set_bit(srv.flags, URI_REQUESTED); |
| } |
| |
| if (ad.data_len + 2 > |
| net_buf_simple_tailroom(srv.scan.adv_data)) { |
| LOG_WRN("Can't fit AD 0x%02x in scan report", ad.type); |
| continue; |
| } |
| |
| net_buf_simple_add_u8(srv.scan.adv_data, ad.data_len + 1); |
| net_buf_simple_add_u8(srv.scan.adv_data, ad.type); |
| net_buf_simple_add_mem(srv.scan.adv_data, ad.data, ad.data_len); |
| |
| *ad_entry = srv.scan.ad[--srv.scan.ad_count]; |
| } |
| |
| /* The Remote Provisioning Server collects AD structures corresponding to all |
| * AD Types specified in the ADTypeFilter field of the Remote Provisioning Extended |
| * Scan Start message. The timeout specified in the Timeout field of the Remote |
| * Provisioning Extended Scan Start message expires. |
| * OR |
| * The ADTypeFilter field of the Remote Provisioning Extended Scan Start message |
| * contains only the URI AD Type, and the Remote Provisioning Server has received |
| * an advertising report or scan response with the URI corresponding to the URI Hash |
| * of the device with the Device UUID that was requested in the Remote Provisioning |
| * Extended Scan Start message. |
| */ |
| if (!srv.scan.ad_count) { |
| goto complete; |
| } |
| |
| /* The ADTypeFilter field of the Remote Provisioning Extended Scan Start message does |
| * not contain the URI AD Type, and the Remote Provisioning Server receives and processes |
| * the scan response data from the device with Device UUID requested in the Remote |
| * Provisioning Extended Scan Start message. |
| */ |
| if (!is_beacon && !uri_present && |
| info->adv_type == BT_GAP_ADV_TYPE_SCAN_RSP) { |
| goto complete; |
| } |
| |
| /* The ADTypeFilter field of the Remote Provisioning Extended Scan Start message contains |
| * the URI AD Type and at least one different AD Type in the ADTypeFilter field, and the |
| * Remote Provisioning Server has received an advertising report or scan response with the |
| * URI corresponding to the URI Hash of the device with the Device UUID that was requested |
| * in the Remote Provisioning Extended Scan Start message, and the Remote Provisioning |
| * Server received the scan response from the same device. |
| * OR |
| * The ADTypeFilter field of the Remote Provisioning Extended Scan Start message contains |
| * the URI AD Type and at least one different AD Type in the ADTypeFilter field, and the |
| * URI Hash is not available for the device with the Device UUID that was requested in the |
| * Remote Provisioning Extended Scan Start message, and the Remote Provisioning Server |
| * received the scan response from the same device. |
| */ |
| if (atomic_get(srv.flags) & URI_REQUESTED && |
| (atomic_get(srv.flags) & URI_MATCHED || |
| (dev->flags & ~BT_MESH_RPR_UNPROV_HASH)) && |
| info->adv_type == BT_GAP_ADV_TYPE_SCAN_RSP) { |
| goto complete; |
| } |
| |
| return; |
| complete: |
| srv.scan.additional_time = 0; |
| if (srv.scan.state != BT_MESH_RPR_SCAN_MULTI) { |
| k_work_cancel_delayable(&srv.scan.timeout); |
| } |
| scan_ext_stop(0); |
| } |
| |
| static void adv_handle_scan(const struct bt_le_scan_recv_info *info, |
| struct net_buf_simple *buf) |
| { |
| struct bt_data ad; |
| |
| if (info->adv_type != BT_HCI_ADV_NONCONN_IND) { |
| return; |
| } |
| |
| while (pull_ad_data(buf, &ad)) { |
| if (ad.type == BT_DATA_MESH_BEACON) { |
| adv_handle_beacon(info, &ad); |
| return; |
| } |
| } |
| } |
| |
| static void scan_packet_recv(const struct bt_le_scan_recv_info *info, |
| struct net_buf_simple *buf) |
| { |
| if (!atomic_test_bit(srv.flags, SCANNING)) { |
| return; |
| } |
| |
| if (srv.scan.dev) { |
| adv_handle_ext_scan(info, buf); |
| } else { |
| adv_handle_scan(info, buf); |
| } |
| } |
| |
| static struct bt_le_scan_cb scan_cb = { |
| .recv = scan_packet_recv, |
| }; |
| |
| static int rpr_srv_init(const struct bt_mesh_model *mod) |
| { |
| if (mod->rt->elem_idx || srv.mod) { |
| LOG_ERR("Remote provisioning server must be initialized " |
| "on first element"); |
| return -EINVAL; |
| } |
| |
| srv.mod = mod; |
| |
| net_buf_simple_init(srv.scan.adv_data, 0); |
| |
| k_work_init_delayable(&srv.scan.timeout, scan_timeout); |
| k_work_init_delayable(&srv.scan.report, scan_report_timeout); |
| k_work_init(&srv.link.report, link_report_send_and_clear); |
| bt_le_scan_cb_register(&scan_cb); |
| mod->keys[0] = BT_MESH_KEY_DEV_LOCAL; |
| mod->rt->flags |= BT_MESH_MOD_DEVKEY_ONLY; |
| |
| return 0; |
| } |
| |
| static void rpr_srv_reset(const struct bt_mesh_model *mod) |
| { |
| cli_link_clear(); |
| cli_scan_clear(); |
| srv.scan.state = BT_MESH_RPR_SCAN_IDLE; |
| srv.link.state = BT_MESH_RPR_LINK_IDLE; |
| k_work_cancel_delayable(&srv.scan.timeout); |
| k_work_cancel_delayable(&srv.scan.report); |
| net_buf_simple_init(srv.scan.adv_data, 0); |
| atomic_clear(srv.flags); |
| srv.link.dev = NULL; |
| srv.scan.dev = NULL; |
| } |
| |
| const struct bt_mesh_model_cb _bt_mesh_rpr_srv_cb = { |
| .init = rpr_srv_init, |
| .reset = rpr_srv_reset, |
| }; |
| |
| static int node_refresh_link_accept(const struct prov_bearer_cb *cb, |
| void *cb_data) |
| { |
| srv.refresh.cb = cb; |
| srv.refresh.cb_data = cb_data; |
| |
| return 0; |
| } |
| |
| static void node_refresh_tx_complete(int err, void *cb_data) |
| { |
| if (err) { |
| link_close(BT_MESH_RPR_ERR_LINK_CLOSED_AS_CANNOT_SEND_PDU, |
| PROV_BEARER_LINK_STATUS_FAIL); |
| return; |
| } |
| |
| if (srv.refresh.tx.cb) { |
| srv.refresh.tx.cb(err, srv.refresh.tx.cb_data); |
| } |
| } |
| |
| static int node_refresh_buf_send(struct net_buf_simple *buf, |
| prov_bearer_send_complete_t cb, void *cb_data) |
| { |
| static const struct bt_mesh_send_cb send_cb = { |
| .end = node_refresh_tx_complete, |
| }; |
| int err; |
| |
| if (!atomic_test_bit(srv.flags, NODE_REFRESH)) { |
| return -EBUSY; |
| } |
| |
| srv.refresh.tx.cb = cb; |
| srv.refresh.tx.cb_data = cb_data; |
| srv.link.rx_pdu++; |
| |
| LOG_DBG("%u", srv.link.rx_pdu); |
| |
| err = inbound_pdu_send(buf, &send_cb); |
| if (err) { |
| link_close(BT_MESH_RPR_ERR_LINK_CLOSED_BY_SERVER, |
| PROV_BEARER_LINK_STATUS_FAIL); |
| } |
| |
| return err; |
| } |
| |
| static void node_refresh_clear_tx(void) |
| { |
| /* Nothing can be done */ |
| } |
| |
| const struct prov_bearer pb_remote_srv = { |
| .type = BT_MESH_PROV_REMOTE, |
| |
| .link_accept = node_refresh_link_accept, |
| .send = node_refresh_buf_send, |
| .clear_tx = node_refresh_clear_tx, |
| }; |