The `BT_DEBUG_*` Kconfig symbols have been deprecated for more than 2 versions, remove them. Update code that was still using them. Remove the Bluetooth specific `Kconfig.template.log_config_bt` and use `Kconfig.template.log_config_inherit` from the logging subsystem instead, now that the legacy symbols can be removed. Signed-off-by: Théo Battrel <theo.battrel@nordicsemi.no>
1413 lines
37 KiB
C
1413 lines
37 KiB
C
/*
|
|
* 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,
|
|
};
|