blob: 5d5aeb9ee601d7f02b28dfdf240ee6e1dd2dfa6f [file] [log] [blame]
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/bluetooth/mesh.h>
#include <zephyr/bluetooth/conn.h>
#include <common/bt_str.h>
#include "access.h"
#include "prov.h"
#include "rpr.h"
#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_rpr_cli);
#define LINK_TIMEOUT_SECONDS_DEFAULT 10
BUILD_ASSERT(BT_MESH_MODEL_OP_LEN(RPR_OP_PDU_SEND) == 2, "Assumes PDU send is a 2 byte opcode");
#define LINK_CTX(_srv, _send_rel) \
{ \
.net_idx = (_srv)->net_idx, .app_idx = BT_MESH_KEY_DEV_REMOTE, \
.addr = (_srv)->addr, .send_ttl = (_srv)->ttl, \
.send_rel = (_send_rel), \
}
enum {
BEARER_LINK_IDLE,
BEARER_LINK_OPENING,
BEARER_LINK_OPENED,
};
static struct {
int link;
const struct prov_bearer_cb *cb;
struct bt_mesh_rpr_cli *cli;
struct {
prov_bearer_send_complete_t cb;
} tx;
} bearer;
static int32_t tx_timeout = (2 * MSEC_PER_SEC);
static void link_reset(struct bt_mesh_rpr_cli *cli);
static void link_closed(struct bt_mesh_rpr_cli *cli,
enum bt_mesh_rpr_status status);
static void link_report(struct bt_mesh_rpr_cli *cli,
struct bt_mesh_rpr_node *srv,
struct bt_mesh_rpr_link *link)
{
struct pb_remote_ctx ctx = { cli, srv };
if (link->state == BT_MESH_RPR_LINK_ACTIVE &&
bearer.link == BEARER_LINK_OPENING) {
bearer.link = BEARER_LINK_OPENED;
LOG_DBG("Opened");
bearer.cb->link_opened(&pb_remote_cli, &ctx);
/* PB-Remote Open Link procedure timeout is configurable, but the provisioning
* protocol timeout is not. Use default provisioning protocol timeout.
*/
cli->link.time = PROTOCOL_TIMEOUT_SEC;
return;
}
if (link->state == BT_MESH_RPR_LINK_IDLE &&
bearer.link != BEARER_LINK_IDLE) {
bearer.link = BEARER_LINK_IDLE;
LOG_DBG("Closed (%u)", link->status);
bearer.cb->link_closed(&pb_remote_cli, &ctx,
((link->status == BT_MESH_RPR_SUCCESS) ?
PROV_BEARER_LINK_STATUS_SUCCESS :
PROV_BEARER_LINK_STATUS_FAIL));
}
}
static void tx_complete(struct bt_mesh_rpr_cli *cli, int err, void *cb_data)
{
LOG_DBG("%d", err);
cli->link.tx_pdu++;
bt_mesh_msg_ack_ctx_clear(&cli->prov_ack_ctx);
if (bearer.tx.cb) {
bearer.tx.cb(err, cb_data);
}
}
static int handle_extended_scan_report(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_rpr_node srv = RPR_NODE(ctx);
struct bt_mesh_rpr_cli *cli = mod->rt->user_data;
struct bt_mesh_rpr_unprov dev = { 0 };
enum bt_mesh_rpr_status status;
bool found_dev = false;
status = net_buf_simple_pull_u8(buf);
if (status != BT_MESH_RPR_SUCCESS) {
LOG_WRN("scan report fail (%u)", status);
return 0;
}
memcpy(dev.uuid, net_buf_simple_pull_mem(buf, 16), 16);
if (buf->len >= 2) {
dev.oob = net_buf_simple_pull_le16(buf);
found_dev = true;
LOG_DBG("0x%04x: %s oob: 0x%04x adv data: %s", srv.addr,
bt_hex(dev.uuid, 16), dev.oob,
bt_hex(buf->data, buf->len));
} else {
LOG_DBG("0x%04x: %s not found.", srv.addr, bt_hex(dev.uuid, 16));
}
if (cli->scan_report && found_dev) {
cli->scan_report(cli, &srv, &dev, buf);
}
return 0;
}
static int handle_link_report(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_rpr_node srv = RPR_NODE(ctx);
struct bt_mesh_rpr_cli *cli = mod->rt->user_data;
struct bt_mesh_rpr_link link;
uint8_t reason = PROV_BEARER_LINK_STATUS_SUCCESS;
link.status = net_buf_simple_pull_u8(buf);
link.state = net_buf_simple_pull_u8(buf);
if (buf->len == 1) {
reason = net_buf_simple_pull_u8(buf);
} else if (buf->len) {
LOG_WRN("Invalid link report len");
return -EINVAL;
}
if (cli->link.srv.addr != srv.addr) {
LOG_DBG("Link report from unknown server 0x%04x", srv.addr);
return 0;
}
k_work_reschedule(&cli->link.timeout, K_SECONDS(cli->link.time));
cli->link.state = link.state;
LOG_DBG("0x%04x: status: %u state: %u reason: %u", srv.addr, link.status, link.state,
reason);
if (link.state == BT_MESH_RPR_LINK_IDLE) {
link_reset(cli);
}
link_report(cli, &cli->link.srv, &link);
return 0;
}
static int handle_link_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_rpr_cli *cli = mod->rt->user_data;
struct bt_mesh_rpr_node srv = RPR_NODE(ctx);
struct bt_mesh_rpr_link *rsp;
struct bt_mesh_rpr_link link;
link.status = net_buf_simple_pull_u8(buf);
link.state = net_buf_simple_pull_u8(buf);
LOG_DBG("0x%04x: status: %u state: %u", srv.addr, link.status,
link.state);
if (bt_mesh_msg_ack_ctx_match(&cli->prov_ack_ctx, RPR_OP_LINK_STATUS,
srv.addr, (void **)&rsp)) {
*rsp = link;
bt_mesh_msg_ack_ctx_rx(&cli->prov_ack_ctx);
}
if (cli->link.srv.addr == srv.addr) {
k_work_reschedule(&cli->link.timeout, K_SECONDS(cli->link.time));
cli->link.state = link.state;
if (link.state == BT_MESH_RPR_LINK_IDLE) {
cli->link.srv.addr = BT_MESH_ADDR_UNASSIGNED;
}
link_report(cli, &cli->link.srv, &link);
}
return 0;
}
static int handle_pdu_outbound_report(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_rpr_cli *cli = mod->rt->user_data;
struct bt_mesh_rpr_node srv = RPR_NODE(ctx);
void *cb_data;
uint8_t num;
if (srv.addr != cli->link.srv.addr) {
LOG_WRN("Outbound report from unknown server 0x%04x", srv.addr);
return 0;
}
num = net_buf_simple_pull_u8(buf);
LOG_DBG("0x%04x: %u", srv.addr, num);
k_work_reschedule(&cli->link.timeout, K_SECONDS(cli->link.time));
if (!bt_mesh_msg_ack_ctx_match(&cli->prov_ack_ctx, RPR_OP_PDU_OUTBOUND_REPORT,
srv.addr, &cb_data) ||
num != cli->link.tx_pdu) {
LOG_WRN("Non-matching PDU report (%u)", num);
return 0;
}
tx_complete(cli, 0, cb_data);
return 0;
}
static int handle_pdu_report(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_rpr_cli *cli = mod->rt->user_data;
struct bt_mesh_rpr_node srv = RPR_NODE(ctx);
struct pb_remote_ctx cb_ctx = {
cli,
&cli->link.srv,
};
uint8_t pdu;
if (cli->link.srv.addr != srv.addr) {
LOG_WRN("PDU report from unknown server 0x%04x", srv.addr);
return 0;
}
k_work_reschedule(&cli->link.timeout, K_SECONDS(cli->link.time));
pdu = net_buf_simple_pull_u8(buf);
if (pdu <= cli->link.rx_pdu) {
LOG_WRN("Duplicate rx %u", pdu);
return 0;
}
LOG_DBG("0x%04x: %u (%u bytes)", srv.addr, pdu, buf->len);
bearer.cb->recv(&pb_remote_cli, &cb_ctx, buf);
return 0;
}
static int handle_scan_caps_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_rpr_cli *cli = mod->rt->user_data;
struct bt_mesh_rpr_node srv = RPR_NODE(ctx);
struct bt_mesh_rpr_caps *caps;
if (!bt_mesh_msg_ack_ctx_match(&cli->scan_ack_ctx, RPR_OP_SCAN_CAPS_STATUS,
srv.addr, (void **)&caps)) {
LOG_WRN("Unexpected scan caps rsp from 0x%04x", srv.addr);
return 0;
}
caps->max_devs = net_buf_simple_pull_u8(buf);
caps->active_scan = net_buf_simple_pull_u8(buf);
LOG_DBG("max devs: %u active scan: %u", caps->max_devs,
caps->active_scan);
bt_mesh_msg_ack_ctx_rx(&cli->scan_ack_ctx);
return 0;
}
static int handle_scan_report(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_rpr_cli *cli = mod->rt->user_data;
struct bt_mesh_rpr_node srv = RPR_NODE(ctx);
struct bt_mesh_rpr_unprov dev = { 0 };
dev.rssi = net_buf_simple_pull_u8(buf);
memcpy(dev.uuid, net_buf_simple_pull_mem(buf, 16), 16);
dev.oob = net_buf_simple_pull_le16(buf);
if (buf->len == 4) {
memcpy(&dev.hash, net_buf_simple_pull_mem(buf, 4), 4);
dev.flags = BT_MESH_RPR_UNPROV_HASH;
} else if (buf->len) {
return -EINVAL;
}
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("0x%04x: %s oob: 0x%04x %ddBm", srv.addr,
bt_uuid_str(&uuid_repr.uuid), dev.oob, dev.rssi);
}
if (cli->scan_report) {
cli->scan_report(cli, &srv, &dev, NULL);
}
return 0;
}
static int handle_scan_status(const struct bt_mesh_model *mod, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_rpr_cli *cli = mod->rt->user_data;
struct bt_mesh_rpr_scan_status *status;
struct bt_mesh_rpr_node srv = RPR_NODE(ctx);
if (!bt_mesh_msg_ack_ctx_match(&cli->scan_ack_ctx, RPR_OP_SCAN_STATUS,
srv.addr, (void **)&status)) {
LOG_WRN("Unexpected scan status from 0x%04x", srv.addr);
return 0;
}
status->status = net_buf_simple_pull_u8(buf);
status->scan = net_buf_simple_pull_u8(buf);
status->max_devs = net_buf_simple_pull_u8(buf);
status->timeout = net_buf_simple_pull_u8(buf);
LOG_DBG("status: %u state: %u max devs: %u timeout: %u seconds",
status->status, status->scan, status->max_devs, status->timeout);
bt_mesh_msg_ack_ctx_rx(&cli->scan_ack_ctx);
return 0;
}
const struct bt_mesh_model_op _bt_mesh_rpr_cli_op[] = {
{ RPR_OP_EXTENDED_SCAN_REPORT, BT_MESH_LEN_MIN(17), handle_extended_scan_report },
{ RPR_OP_LINK_REPORT, BT_MESH_LEN_MIN(2), handle_link_report },
{ RPR_OP_LINK_STATUS, BT_MESH_LEN_EXACT(2), handle_link_status },
{ RPR_OP_PDU_OUTBOUND_REPORT, BT_MESH_LEN_EXACT(1), handle_pdu_outbound_report },
{ RPR_OP_PDU_REPORT, BT_MESH_LEN_MIN(2), handle_pdu_report },
{ RPR_OP_SCAN_CAPS_STATUS, BT_MESH_LEN_EXACT(2), handle_scan_caps_status },
{ RPR_OP_SCAN_REPORT, BT_MESH_LEN_MIN(19), handle_scan_report },
{ RPR_OP_SCAN_STATUS, BT_MESH_LEN_EXACT(4), handle_scan_status },
BT_MESH_MODEL_OP_END,
};
static void link_timeout(struct k_work *work)
{
struct bt_mesh_rpr_cli *cli = CONTAINER_OF(k_work_delayable_from_work(work),
struct bt_mesh_rpr_cli, link.timeout);
if (bearer.link != BEARER_LINK_IDLE) {
LOG_DBG("");
link_closed(cli, BT_MESH_RPR_ERR_LINK_CLOSED_BY_CLIENT);
}
}
static int rpr_cli_init(const struct bt_mesh_model *mod)
{
if (mod->rt->elem_idx) {
LOG_ERR("Remote provisioning client must be initialized "
"on first element");
return -EINVAL;
}
struct bt_mesh_rpr_cli *cli = mod->rt->user_data;
cli->mod = mod;
cli->link.time = LINK_TIMEOUT_SECONDS_DEFAULT;
bt_mesh_msg_ack_ctx_init(&cli->scan_ack_ctx);
bt_mesh_msg_ack_ctx_init(&cli->prov_ack_ctx);
k_work_init_delayable(&cli->link.timeout, link_timeout);
mod->keys[0] = BT_MESH_KEY_DEV_ANY;
mod->rt->flags |= BT_MESH_MOD_DEVKEY_ONLY;
return 0;
}
const struct bt_mesh_model_cb _bt_mesh_rpr_cli_cb = {
.init = rpr_cli_init,
};
static void pdu_send_start(uint16_t duration, int err, void *cb_data)
{
struct bt_mesh_rpr_cli *cli = cb_data;
if (err) {
LOG_ERR("PDU Send failed: %d", err);
link_closed(cli,
BT_MESH_RPR_ERR_LINK_CLOSED_AS_CANNOT_SEND_PDU);
}
}
static void pdu_send_end(int err, void *cb_data)
{
struct bt_mesh_rpr_cli *cli = cb_data;
if (err) {
LOG_ERR("PDU Send failed: %d", err);
link_closed(cli,
BT_MESH_RPR_ERR_LINK_CLOSED_AS_CANNOT_SEND_PDU);
return;
}
k_work_reschedule(&cli->link.timeout, K_SECONDS(cli->link.time));
}
static const struct bt_mesh_send_cb pdu_send_cb = {
.start = pdu_send_start,
.end = pdu_send_end,
};
static int tx_wait(struct bt_mesh_rpr_cli *cli,
struct bt_mesh_msg_ack_ctx *ack_ctx, const struct bt_mesh_rpr_node *srv,
struct net_buf_simple *buf, uint32_t rsp, void *rsp_ctx)
{
struct bt_mesh_msg_ctx ctx = LINK_CTX(srv, false);
int err;
err = bt_mesh_msg_ack_ctx_prepare(ack_ctx, rsp, srv->addr, rsp_ctx);
if (err) {
return err;
}
err = bt_mesh_model_send(cli->mod, &ctx, buf, NULL, NULL);
if (err) {
bt_mesh_msg_ack_ctx_clear(ack_ctx);
LOG_WRN("TX fail");
return err;
}
err = bt_mesh_msg_ack_ctx_wait(ack_ctx, K_MSEC(tx_timeout));
bt_mesh_msg_ack_ctx_clear(ack_ctx);
return err;
}
static void link_init(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv)
{
cli->link.srv = *srv;
cli->link.state = BT_MESH_RPR_LINK_IDLE;
cli->link.rx_pdu = 0;
cli->link.tx_pdu = 1;
k_work_reschedule(&cli->link.timeout, K_SECONDS(cli->link.time));
}
static void link_reset(struct bt_mesh_rpr_cli *cli)
{
k_work_cancel_delayable(&cli->link.timeout);
cli->link.srv.addr = BT_MESH_ADDR_UNASSIGNED;
cli->link.state = BT_MESH_RPR_LINK_IDLE;
bt_mesh_msg_ack_ctx_clear(&cli->prov_ack_ctx);
}
static void link_closed(struct bt_mesh_rpr_cli *cli,
enum bt_mesh_rpr_status status)
{
struct bt_mesh_rpr_node srv = cli->link.srv;
struct bt_mesh_rpr_link link = {
.status = status,
.state = BT_MESH_RPR_LINK_IDLE,
};
LOG_DBG("0x%04x: status: %u state: %u rx: %u tx: %u", srv.addr, link.status,
cli->link.state, cli->link.rx_pdu, cli->link.tx_pdu);
link_reset(cli);
link_report(cli, &srv, &link);
}
int bt_mesh_rpr_scan_caps_get(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv,
struct bt_mesh_rpr_caps *caps)
{
BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_SCAN_CAPS_GET, 0);
bt_mesh_model_msg_init(&buf, RPR_OP_SCAN_CAPS_GET);
return tx_wait(cli, &cli->scan_ack_ctx, srv, &buf, RPR_OP_SCAN_CAPS_STATUS, caps);
}
int bt_mesh_rpr_scan_get(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv,
struct bt_mesh_rpr_scan_status *status)
{
BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_SCAN_GET, 0);
bt_mesh_model_msg_init(&buf, RPR_OP_SCAN_GET);
return tx_wait(cli, &cli->scan_ack_ctx, srv, &buf, RPR_OP_SCAN_STATUS, status);
}
int bt_mesh_rpr_scan_start(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv,
const uint8_t uuid[16], uint8_t timeout,
uint8_t max_devs,
struct bt_mesh_rpr_scan_status *status)
{
if (!timeout) {
return -EINVAL;
}
BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_SCAN_START, 18);
bt_mesh_model_msg_init(&buf, RPR_OP_SCAN_START);
net_buf_simple_add_u8(&buf, max_devs);
net_buf_simple_add_u8(&buf, timeout);
if (uuid) {
net_buf_simple_add_mem(&buf, uuid, 16);
}
return tx_wait(cli, &cli->scan_ack_ctx, srv, &buf, RPR_OP_SCAN_STATUS, status);
}
int bt_mesh_rpr_scan_start_ext(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv,
const uint8_t uuid[16], uint8_t timeout,
const uint8_t *ad_types, size_t ad_count)
{
struct bt_mesh_msg_ctx ctx = LINK_CTX(srv, false);
if ((uuid && (timeout < BT_MESH_RPR_EXT_SCAN_TIME_MIN ||
timeout > BT_MESH_RPR_EXT_SCAN_TIME_MAX)) ||
!ad_count || ad_count > CONFIG_BT_MESH_RPR_AD_TYPES_MAX) {
return -EINVAL;
}
BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_EXTENDED_SCAN_START,
18 + CONFIG_BT_MESH_RPR_AD_TYPES_MAX);
bt_mesh_model_msg_init(&buf, RPR_OP_EXTENDED_SCAN_START);
net_buf_simple_add_u8(&buf, ad_count);
net_buf_simple_add_mem(&buf, ad_types, ad_count);
if (uuid) {
net_buf_simple_add_mem(&buf, uuid, 16);
net_buf_simple_add_u8(&buf, timeout);
}
return bt_mesh_model_send(cli->mod, &ctx, &buf, NULL, NULL);
}
int bt_mesh_rpr_scan_stop(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv,
struct bt_mesh_rpr_scan_status *status)
{
BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_SCAN_STOP, 0);
bt_mesh_model_msg_init(&buf, RPR_OP_SCAN_STOP);
return tx_wait(cli, &cli->scan_ack_ctx, srv, &buf, RPR_OP_SCAN_STATUS, status);
}
int bt_mesh_rpr_link_get(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv,
struct bt_mesh_rpr_link *rsp)
{
BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_LINK_GET, 0);
bt_mesh_model_msg_init(&buf, RPR_OP_LINK_GET);
return tx_wait(cli, &cli->prov_ack_ctx, srv, &buf, RPR_OP_LINK_STATUS, rsp);
}
int bt_mesh_rpr_link_close(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv,
struct bt_mesh_rpr_link *rsp)
{
BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_LINK_CLOSE, 1);
bt_mesh_model_msg_init(&buf, RPR_OP_LINK_CLOSE);
net_buf_simple_add_u8(&buf, PROV_BEARER_LINK_STATUS_FAIL);
return tx_wait(cli, &cli->prov_ack_ctx, srv, &buf, RPR_OP_LINK_STATUS, rsp);
}
static int link_open_prov(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv, const uint8_t uuid[16],
uint8_t timeout)
{
struct bt_mesh_msg_ctx ctx = LINK_CTX(srv, false);
BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_LINK_OPEN, 17);
bt_mesh_model_msg_init(&buf, RPR_OP_LINK_OPEN);
net_buf_simple_add_mem(&buf, uuid, 16);
if (cli->link.time != LINK_TIMEOUT_SECONDS_DEFAULT) {
net_buf_simple_add_u8(&buf, cli->link.time);
}
return bt_mesh_model_send(cli->mod, &ctx, &buf, NULL, NULL);
}
static int link_open_node(struct bt_mesh_rpr_cli *cli,
const struct bt_mesh_rpr_node *srv,
enum bt_mesh_rpr_node_refresh type)
{
struct bt_mesh_msg_ctx ctx = LINK_CTX(srv, false);
BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_LINK_OPEN, 1);
bt_mesh_model_msg_init(&buf, RPR_OP_LINK_OPEN);
net_buf_simple_add_u8(&buf, type);
return bt_mesh_model_send(cli->mod, &ctx, &buf, NULL, NULL);
}
static int link_close(struct bt_mesh_rpr_cli *cli,
enum prov_bearer_link_status status)
{
struct bt_mesh_msg_ctx ctx = LINK_CTX(&cli->link.srv, false);
int err;
if (cli->link.srv.addr == BT_MESH_ADDR_UNASSIGNED) {
return -EALREADY;
}
BT_MESH_MODEL_BUF_DEFINE(buf, RPR_OP_LINK_CLOSE, 1);
bt_mesh_model_msg_init(&buf, RPR_OP_LINK_CLOSE);
net_buf_simple_add_u8(&buf, status);
err = bt_mesh_model_send(cli->mod, &ctx, &buf, NULL, NULL);
if (err) {
link_reset(cli);
}
k_work_reschedule(&cli->link.timeout, K_SECONDS(cli->link.time));
return err;
}
static int send(struct bt_mesh_rpr_cli *cli, struct net_buf_simple *buf,
void *cb_data)
{
struct bt_mesh_msg_ctx ctx = LINK_CTX(&cli->link.srv, true);
int err;
if (cli->link.srv.addr == BT_MESH_ADDR_UNASSIGNED) {
LOG_ERR("No server");
return -ESHUTDOWN;
}
if (net_buf_simple_headroom(buf) < 3) {
LOG_ERR("Invalid buffer");
return -EINVAL;
}
err = bt_mesh_msg_ack_ctx_prepare(&cli->prov_ack_ctx,
RPR_OP_PDU_OUTBOUND_REPORT,
cli->link.srv.addr, cb_data);
if (err) {
LOG_ERR("Busy");
return err;
}
LOG_DBG("0x%02x", buf->data[0]);
net_buf_simple_push_u8(buf, cli->link.tx_pdu);
/* Assumes opcode is 2 bytes. Build assert at top of file ensures this.
*/
net_buf_simple_push_be16(buf, RPR_OP_PDU_SEND);
err = bt_mesh_model_send(cli->mod, &ctx, buf, &pdu_send_cb, cli);
if (err) {
link_closed(cli,
BT_MESH_RPR_ERR_LINK_CLOSED_AS_CANNOT_SEND_PDU);
}
return err;
}
int32_t bt_mesh_rpr_cli_timeout_get(void)
{
return tx_timeout;
}
void bt_mesh_rpr_cli_timeout_set(int32_t timeout)
{
tx_timeout = timeout;
}
/*******************************************************************************
* Prov bearer interface
******************************************************************************/
static int pb_send(struct net_buf_simple *buf, prov_bearer_send_complete_t cb,
void *cb_data)
{
bearer.tx.cb = cb;
return send(bearer.cli, buf, cb_data);
}
static void pb_clear_tx(void)
{
/* Nothing can be done */
}
static int pb_link_open(const uint8_t uuid[16], uint8_t timeout,
const struct prov_bearer_cb *cb, void *cb_data)
{
struct pb_remote_ctx *ctx = cb_data;
struct bt_mesh_rpr_cli *cli = ctx->cli;
const struct bt_mesh_rpr_node *srv = ctx->srv;
int err;
if (cli->link.srv.addr != BT_MESH_ADDR_UNASSIGNED) {
return -EBUSY;
}
bearer.cli = ctx->cli;
bearer.cb = cb;
cli->link.time = timeout ? timeout : LINK_TIMEOUT_SECONDS_DEFAULT;
LOG_DBG("timeout: %d", cli->link.time);
link_init(cli, srv);
if (uuid) {
err = link_open_prov(cli, srv, uuid, timeout);
} else {
err = link_open_node(cli, srv, ctx->refresh);
}
if (err) {
link_reset(cli);
return err;
}
bearer.link = BEARER_LINK_OPENING;
return 0;
}
static void pb_link_close(enum prov_bearer_link_status status)
{
int err;
err = link_close(bearer.cli, status);
if (err) {
LOG_ERR("Link close failed (%d)", err);
}
}
const struct prov_bearer pb_remote_cli = {
.type = BT_MESH_PROV_REMOTE,
.send = pb_send,
.clear_tx = pb_clear_tx,
.link_open = pb_link_open,
.link_close = pb_link_close,
};