kopia lustrzana https://github.com/espressif/esp-idf
698 wiersze
18 KiB
C
698 wiersze
18 KiB
C
/* Bluetooth Mesh */
|
|
|
|
/*
|
|
* SPDX-FileCopyrightText: 2017 Intel Corporation
|
|
* SPDX-FileContributor: 2018-2023 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include "adv.h"
|
|
#include "crypto.h"
|
|
#include "mesh/mutex.h"
|
|
#include "mesh/common.h"
|
|
#include "mesh/access.h"
|
|
#include "prov_common.h"
|
|
|
|
static const struct bt_mesh_prov *prov;
|
|
|
|
const struct bt_mesh_prov *bt_mesh_prov_get(void)
|
|
{
|
|
return prov;
|
|
}
|
|
|
|
int bt_mesh_prov_set(const struct bt_mesh_prov *val)
|
|
{
|
|
prov = val;
|
|
return 0;
|
|
}
|
|
|
|
void bt_mesh_prov_buf_init(struct net_buf_simple *buf, uint8_t type)
|
|
{
|
|
net_buf_simple_reserve(buf, PROV_BUF_HEADROOM);
|
|
net_buf_simple_add_u8(buf, type);
|
|
}
|
|
|
|
#define OUTPUT_OOB_BLINK 0x00
|
|
#define OUTPUT_OOB_BEEP 0x01
|
|
#define OUTPUT_OOB_VIBRATE 0x02
|
|
#define OUTPUT_OOB_NUMBER 0x03
|
|
#define OUTPUT_OOB_STRING 0x04
|
|
|
|
#define INPUT_OOB_PUSH 0x00
|
|
#define INPUT_OOB_TWIST 0x01
|
|
#define INPUT_OOB_NUMBER 0x02
|
|
#define INPUT_OOB_STRING 0x03
|
|
|
|
bt_mesh_output_action_t bt_mesh_prov_output_action(uint8_t action)
|
|
{
|
|
switch (action) {
|
|
case OUTPUT_OOB_BLINK:
|
|
return BLE_MESH_BLINK;
|
|
case OUTPUT_OOB_BEEP:
|
|
return BLE_MESH_BEEP;
|
|
case OUTPUT_OOB_VIBRATE:
|
|
return BLE_MESH_VIBRATE;
|
|
case OUTPUT_OOB_NUMBER:
|
|
return BLE_MESH_DISPLAY_NUMBER;
|
|
case OUTPUT_OOB_STRING:
|
|
return BLE_MESH_DISPLAY_STRING;
|
|
default:
|
|
return BLE_MESH_NO_OUTPUT;
|
|
}
|
|
}
|
|
|
|
bt_mesh_input_action_t bt_mesh_prov_input_action(uint8_t action)
|
|
{
|
|
switch (action) {
|
|
case INPUT_OOB_PUSH:
|
|
return BLE_MESH_PUSH;
|
|
case INPUT_OOB_TWIST:
|
|
return BLE_MESH_TWIST;
|
|
case INPUT_OOB_NUMBER:
|
|
return BLE_MESH_ENTER_NUMBER;
|
|
case INPUT_OOB_STRING:
|
|
return BLE_MESH_ENTER_STRING;
|
|
default:
|
|
return BLE_MESH_NO_INPUT;
|
|
}
|
|
}
|
|
|
|
static const struct {
|
|
uint16_t length;
|
|
} prov_pdu[] = {
|
|
{ 1 }, /* Provisioning Invite */
|
|
{ 11 }, /* Provisioning Capabilities */
|
|
{ 5 }, /* Provisioning Start */
|
|
{ 64 }, /* Provisioning Public Key */
|
|
{ 0 }, /* Provisioning Input Complete */
|
|
{ 16 }, /* Provisioning Confirmation */
|
|
{ 16 }, /* Provisioning Random */
|
|
{ 33 }, /* Provisioning Data */
|
|
{ 0 }, /* Provisioning Complete */
|
|
{ 1 }, /* Provisioning Failed */
|
|
{ 6 }, /* Provisioning Record Request */
|
|
{ 7 }, /* Provisioning Record Response */
|
|
{ 0 }, /* Provisioning Records Get */
|
|
{ 2 }, /* Provisioning Records List */
|
|
};
|
|
|
|
bool bt_mesh_prov_pdu_check(uint8_t type, uint16_t length, uint8_t *reason)
|
|
{
|
|
if (prov_pdu[type].length != length) {
|
|
#if CONFIG_BLE_MESH_CERT_BASED_PROV
|
|
if ((type == PROV_REC_LIST || type == PROV_REC_RSP) &&
|
|
length >= prov_pdu[type].length) {
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#if CONFIG_BLE_MESH_PROV_EPA
|
|
/* NOTE: PROV_CONFIRM and PROV_RANDOM PDU may have length 16 or 32 */
|
|
if ((type == PROV_CONFIRM || type == PROV_RANDOM) && length == 32) {
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
BT_ERR("Invalid length %u for type 0x%02x", length, type);
|
|
if (reason) {
|
|
*reason = PROV_ERR_NVAL_FMT;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#if CONFIG_BLE_MESH_PB_ADV
|
|
|
|
/* 3 transmissions, 20ms interval */
|
|
#define PROV_XMIT BLE_MESH_TRANSMIT(2, 20)
|
|
|
|
/* 3 transmissions, 20ms interval */
|
|
#define CLOSE_XMIT BLE_MESH_TRANSMIT(2, 20)
|
|
|
|
#define CLOSE_TIMEOUT K_MSEC(100)
|
|
|
|
#define BUF_TIMEOUT K_MSEC(400)
|
|
|
|
#define XACT_SEG_DATA(link, _seg) (&link->rx.buf->data[20 + (((_seg) - 1) * 23)])
|
|
#define XACT_SEG_RECV(link, _seg) (link->rx.seg &= ~(1 << (_seg)))
|
|
|
|
static uint8_t bt_mesh_prov_buf_type_get(struct net_buf_simple *buf)
|
|
{
|
|
return buf->data[PROV_BUF_HEADROOM];
|
|
}
|
|
|
|
uint8_t node_next_xact_id(struct bt_mesh_prov_link *link)
|
|
{
|
|
if (link->tx.id != 0 && link->tx.id != 0xFF) {
|
|
return ++link->tx.id;
|
|
}
|
|
|
|
link->tx.id = 0x80;
|
|
return link->tx.id;
|
|
}
|
|
|
|
uint8_t pvnr_next_xact_id(struct bt_mesh_prov_link *link)
|
|
{
|
|
if (link->tx.id > 0x7F) {
|
|
link->tx.id = 0;
|
|
}
|
|
return link->tx.id++;
|
|
}
|
|
|
|
bool bt_mesh_gen_prov_start(struct bt_mesh_prov_link *link,
|
|
struct net_buf_simple *buf,
|
|
struct prov_rx *rx, bool *close)
|
|
{
|
|
if (link->rx.seg) {
|
|
BT_INFO("Get Start while there are unreceived segments");
|
|
return false;
|
|
}
|
|
|
|
if (link->rx.prev_id == rx->xact_id) {
|
|
BT_INFO("Resending ack");
|
|
bt_mesh_gen_prov_ack_send(link, rx->xact_id);
|
|
return false;
|
|
}
|
|
|
|
link->rx.buf->len = net_buf_simple_pull_be16(buf);
|
|
link->rx.id = rx->xact_id;
|
|
link->rx.fcs = net_buf_simple_pull_u8(buf);
|
|
|
|
BT_DBG("len %u last_seg %u total_len %u fcs 0x%02x", buf->len,
|
|
START_LAST_SEG(rx->gpc), link->rx.buf->len, link->rx.fcs);
|
|
|
|
/* At least one-octet pdu type is needed */
|
|
if (link->rx.buf->len < 1) {
|
|
BT_ERR("Ignoring zero-length provisioning PDU");
|
|
if (close) {
|
|
*close = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (START_LAST_SEG(rx->gpc) > START_LAST_SEG_MAX) {
|
|
BT_ERR("Invalid SegN 0x%02x", START_LAST_SEG(rx->gpc));
|
|
if (close) {
|
|
*close = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (link->rx.buf->len > link->rx.buf->size) {
|
|
BT_ERR("Too large provisioning PDU (%u bytes)",
|
|
link->rx.buf->len);
|
|
if (close) {
|
|
*close = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (START_LAST_SEG(rx->gpc) > 0 && link->rx.buf->len <= 20) {
|
|
BT_ERR("Too small total length for multi-segment PDU");
|
|
if (close) {
|
|
*close = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
link->rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1;
|
|
link->rx.last_seg = START_LAST_SEG(rx->gpc);
|
|
memcpy(link->rx.buf->data, buf->data, buf->len);
|
|
XACT_SEG_RECV(link, 0);
|
|
|
|
/* Still have some segments to receive */
|
|
if (link->rx.seg) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool bt_mesh_gen_prov_cont(struct bt_mesh_prov_link *link,
|
|
struct net_buf_simple *buf,
|
|
struct prov_rx *rx, bool *close)
|
|
{
|
|
uint8_t seg = CONT_SEG_INDEX(rx->gpc);
|
|
|
|
BT_DBG("len %u, seg_index %u", buf->len, seg);
|
|
|
|
if (link->rx.seg == 0 && link->rx.prev_id == rx->xact_id) {
|
|
BT_INFO("Resending ack");
|
|
bt_mesh_gen_prov_ack_send(link, rx->xact_id);
|
|
return false;
|
|
}
|
|
|
|
if (rx->xact_id != link->rx.id) {
|
|
BT_WARN("Data for unknown transaction (%u != %u)",
|
|
rx->xact_id, link->rx.id);
|
|
return false;
|
|
}
|
|
|
|
if (seg > link->rx.last_seg) {
|
|
BT_ERR("Invalid segment index %u", seg);
|
|
if (close) {
|
|
*close = true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (seg == link->rx.last_seg) {
|
|
uint8_t expect_len = (link->rx.buf->len - 20 -
|
|
(23 * (link->rx.last_seg - 1)));
|
|
if (expect_len != buf->len) {
|
|
BT_ERR("Incorrect last seg len: %u != %u",
|
|
expect_len, buf->len);
|
|
if (close) {
|
|
*close = true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if ((link->rx.seg & BIT(seg)) == 0) {
|
|
BT_INFO("Ignore already received segment");
|
|
return false;
|
|
}
|
|
|
|
memcpy(XACT_SEG_DATA(link, seg), buf->data, buf->len);
|
|
XACT_SEG_RECV(link, seg);
|
|
|
|
/* Still have some segments to receive */
|
|
if (link->rx.seg) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static struct net_buf *adv_buf_create(void)
|
|
{
|
|
struct net_buf *buf = NULL;
|
|
|
|
buf = bt_mesh_adv_create(BLE_MESH_ADV_PROV, BUF_TIMEOUT);
|
|
if (!buf) {
|
|
BT_ERR("Out of provisioning buffers");
|
|
return NULL;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static void ack_complete(uint16_t duration, int err, void *user_data)
|
|
{
|
|
struct bt_mesh_prov_link *link = user_data;
|
|
|
|
BT_DBG("xact %u complete", link->pending_ack);
|
|
|
|
link->pending_ack = PROV_XACT_NVAL;
|
|
}
|
|
|
|
void bt_mesh_gen_prov_ack_send(struct bt_mesh_prov_link *link, uint8_t xact_id)
|
|
{
|
|
static const struct bt_mesh_send_cb cb = {
|
|
.start = ack_complete,
|
|
};
|
|
const struct bt_mesh_send_cb *complete = NULL;
|
|
struct net_buf *buf = NULL;
|
|
|
|
BT_DBG("xact_id %u", xact_id);
|
|
|
|
if (link->pending_ack == xact_id) {
|
|
BT_DBG("Not sending duplicate ack");
|
|
return;
|
|
}
|
|
|
|
buf = adv_buf_create();
|
|
if (!buf) {
|
|
return;
|
|
}
|
|
|
|
if (link->pending_ack == PROV_XACT_NVAL) {
|
|
link->pending_ack = xact_id;
|
|
complete = &cb;
|
|
} else {
|
|
complete = NULL;
|
|
}
|
|
|
|
net_buf_add_be32(buf, link->link_id);
|
|
net_buf_add_u8(buf, xact_id);
|
|
net_buf_add_u8(buf, GPC_ACK);
|
|
|
|
bt_mesh_adv_send(buf, PROV_XMIT, complete, link);
|
|
net_buf_unref(buf);
|
|
}
|
|
|
|
static void free_segments(struct bt_mesh_prov_link *link)
|
|
{
|
|
for (size_t i = 0; i < ARRAY_SIZE(link->tx.buf); i++) {
|
|
struct net_buf *buf = link->tx.buf[i];
|
|
|
|
if (!buf) {
|
|
break;
|
|
}
|
|
|
|
link->tx.buf[i] = NULL;
|
|
bt_mesh_adv_buf_ref_debug(__func__, buf, 3U, BLE_MESH_BUF_REF_SMALL);
|
|
/* Mark as canceled */
|
|
BLE_MESH_ADV(buf)->busy = 0U;
|
|
net_buf_unref(buf);
|
|
}
|
|
}
|
|
|
|
void bt_mesh_prov_clear_tx(struct bt_mesh_prov_link *link, bool cancel)
|
|
{
|
|
bt_mesh_mutex_lock(&link->buf_lock);
|
|
|
|
if (cancel) {
|
|
k_delayed_work_cancel(&link->tx.retransmit);
|
|
}
|
|
|
|
free_segments(link);
|
|
|
|
bt_mesh_mutex_unlock(&link->buf_lock);
|
|
}
|
|
|
|
static void buf_sent(int err, void *user_data)
|
|
{
|
|
struct bt_mesh_prov_link *link = user_data;
|
|
int32_t timeout = RETRANSMIT_TIMEOUT;
|
|
|
|
if (!link->tx.buf[0]) {
|
|
return;
|
|
}
|
|
|
|
/* The following may happen in TWO situations:
|
|
* 1. LINK_CLOSING flag is set, and Link Close is sent;
|
|
* 2. LINK_CLOSING flag is set, and any other provisioning
|
|
* PDU within the adv queue is sent.
|
|
* Regarding the second situation, since LINK_CLOSING flag
|
|
* is set, so once a pdu is sent, the link could be closed.
|
|
*/
|
|
if (bt_mesh_atomic_test_bit(link->flags, LINK_CLOSING)) {
|
|
timeout = CLOSE_TIMEOUT;
|
|
}
|
|
|
|
k_delayed_work_submit(&link->tx.retransmit, timeout);
|
|
}
|
|
|
|
static struct bt_mesh_send_cb buf_sent_cb = {
|
|
.end = buf_sent,
|
|
};
|
|
|
|
static void prov_retransmit(struct k_work *work)
|
|
{
|
|
struct bt_mesh_prov_link *link = work->user_data;
|
|
int64_t timeout = TRANSACTION_TIMEOUT;
|
|
|
|
if (!bt_mesh_atomic_test_bit(link->flags, LINK_ACTIVE) &&
|
|
!bt_mesh_atomic_test_bit(link->flags, LINK_CLOSING)) {
|
|
BT_WARN("Link not active");
|
|
return;
|
|
}
|
|
|
|
#if CONFIG_BLE_MESH_FAST_PROV
|
|
if (link->tx_pdu_type >= link->last_tx_pdu) {
|
|
timeout = K_SECONDS(30);
|
|
}
|
|
#endif /* CONFIG_BLE_MESH_FAST_PROV */
|
|
|
|
/* Use a timeout of 0 ~ 60s for PB-Remote Link Open Procedure. */
|
|
if (bt_mesh_atomic_test_bit(link->flags, PB_REMOTE) &&
|
|
bt_mesh_atomic_test_bit(link->flags, PBR_OPENING)) {
|
|
timeout = K_SECONDS(link->pb_remote_timeout);
|
|
}
|
|
|
|
if (k_uptime_get() - link->tx.start > timeout) {
|
|
BT_WARN("Timeout, giving up transaction");
|
|
if (bt_mesh_atomic_test_bit(link->flags, PB_REMOTE)) {
|
|
bt_mesh_prov_clear_tx(link, true);
|
|
link->pb_remote_cbd = false;
|
|
link->pb_remote_reset = false;
|
|
link->pb_remote_csp = true;
|
|
if (link->pb_remote_close) {
|
|
link->pb_remote_close(link, CLOSE_REASON_TIMEOUT);
|
|
}
|
|
} else {
|
|
/* Send Link Close if the device is Provisioner, or
|
|
* directly reset adv link if the device is Node.
|
|
*/
|
|
if (link->retrans_timeout) {
|
|
link->retrans_timeout(link, CLOSE_REASON_TIMEOUT);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (bt_mesh_atomic_test_bit(link->flags, LINK_CLOSING)) {
|
|
if (bt_mesh_atomic_test_bit(link->flags, PB_REMOTE)) {
|
|
bt_mesh_prov_clear_tx(link, true);
|
|
link->pb_remote_cbd = false;
|
|
/* In this case, no need to send Link Close */
|
|
link->pb_remote_reset = true;
|
|
if (link->pb_remote_close) {
|
|
link->pb_remote_close(link, link->reason);
|
|
}
|
|
} else {
|
|
if (link->reset_adv_link) {
|
|
link->reset_adv_link(link, link->reason);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
bt_mesh_mutex_lock(&link->buf_lock);
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(link->tx.buf); i++) {
|
|
struct net_buf *buf = link->tx.buf[i];
|
|
|
|
if (!buf) {
|
|
break;
|
|
}
|
|
|
|
if (BLE_MESH_ADV(buf)->busy) {
|
|
continue;
|
|
}
|
|
|
|
BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len));
|
|
|
|
if (i + 1 < ARRAY_SIZE(link->tx.buf) && link->tx.buf[i + 1]) {
|
|
bt_mesh_adv_send(buf, PROV_XMIT, NULL, NULL);
|
|
} else {
|
|
bt_mesh_adv_send(buf, PROV_XMIT, &buf_sent_cb, link);
|
|
}
|
|
}
|
|
|
|
bt_mesh_mutex_unlock(&link->buf_lock);
|
|
}
|
|
|
|
int bt_mesh_prov_retransmit_init(struct bt_mesh_prov_link *link)
|
|
{
|
|
link->tx.retransmit.work.user_data = link;
|
|
|
|
return k_delayed_work_init(&link->tx.retransmit, prov_retransmit);
|
|
}
|
|
|
|
static void send_reliable(struct bt_mesh_prov_link *link, uint8_t xmit)
|
|
{
|
|
link->tx.start = k_uptime_get();
|
|
|
|
for (size_t i = 0; i < ARRAY_SIZE(link->tx.buf); i++) {
|
|
struct net_buf *buf = link->tx.buf[i];
|
|
|
|
if (!buf) {
|
|
break;
|
|
}
|
|
|
|
if (i + 1 < ARRAY_SIZE(link->tx.buf) && link->tx.buf[i + 1]) {
|
|
bt_mesh_adv_send(buf, xmit, NULL, NULL);
|
|
} else {
|
|
bt_mesh_adv_send(buf, xmit, &buf_sent_cb, link);
|
|
}
|
|
}
|
|
}
|
|
|
|
int bt_mesh_prov_bearer_ctl_send(struct bt_mesh_prov_link *link, uint8_t op,
|
|
void *data, uint8_t data_len)
|
|
{
|
|
struct net_buf *buf = NULL;
|
|
uint8_t xmit = 0;
|
|
|
|
BT_DBG("op 0x%02x data_len %u", op, data_len);
|
|
|
|
bt_mesh_prov_clear_tx(link, true);
|
|
|
|
buf = adv_buf_create();
|
|
if (!buf) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
net_buf_add_be32(buf, link->link_id);
|
|
/* Transaction ID, always 0 for Bearer messages */
|
|
net_buf_add_u8(buf, 0x00);
|
|
net_buf_add_u8(buf, GPC_CTL(op));
|
|
net_buf_add_mem(buf, data, data_len);
|
|
|
|
link->tx.buf[0] = buf;
|
|
link->tx.id = 0; /* Set Transaction ID to 0 */
|
|
|
|
xmit = (op == LINK_CLOSE) ? CLOSE_XMIT : PROV_XMIT;
|
|
|
|
send_reliable(link, xmit);
|
|
|
|
if (op == LINK_CLOSE) {
|
|
bt_mesh_atomic_clear_bit(link->flags, LINK_ACTIVE);
|
|
bt_mesh_atomic_set_bit(link->flags, LINK_CLOSING);
|
|
link->reason = *((uint8_t *)data);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t last_seg(uint8_t len)
|
|
{
|
|
if (len <= START_PAYLOAD_MAX) {
|
|
return 0;
|
|
}
|
|
|
|
len -= START_PAYLOAD_MAX;
|
|
|
|
return 1 + (len / CONT_PAYLOAD_MAX);
|
|
}
|
|
|
|
int bt_mesh_prov_send_adv(struct bt_mesh_prov_link *link, struct net_buf_simple *msg)
|
|
{
|
|
struct net_buf *start = NULL, *buf = NULL;
|
|
int32_t timeout = PROTOCOL_TIMEOUT;
|
|
uint8_t seg_len = 0U, seg_id = 0U;
|
|
uint8_t xact_id = 0U;
|
|
|
|
BT_DBG("len %u: %s", msg->len, bt_hex(msg->data, msg->len));
|
|
|
|
if (link->next_xact_id == NULL) {
|
|
BT_ERR("Empty transaction id cb");
|
|
return -EIO;
|
|
}
|
|
|
|
bt_mesh_prov_clear_tx(link, true);
|
|
|
|
start = adv_buf_create();
|
|
if (!start) {
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
xact_id = link->next_xact_id(link);
|
|
net_buf_add_be32(start, link->link_id);
|
|
net_buf_add_u8(start, xact_id);
|
|
|
|
net_buf_add_u8(start, GPC_START(last_seg(msg->len)));
|
|
net_buf_add_be16(start, msg->len);
|
|
net_buf_add_u8(start, bt_mesh_fcs_calc(msg->data, msg->len));
|
|
|
|
link->tx.buf[0] = start;
|
|
|
|
#if CONFIG_BLE_MESH_FAST_PROV
|
|
link->tx_pdu_type = msg->data[0];
|
|
#endif /* CONFIG_BLE_MESH_FAST_PROV */
|
|
|
|
seg_len = MIN(msg->len, START_PAYLOAD_MAX);
|
|
BT_DBG("seg 0 len %u: %s", seg_len, bt_hex(msg->data, seg_len));
|
|
net_buf_add_mem(start, msg->data, seg_len);
|
|
net_buf_simple_pull(msg, seg_len);
|
|
|
|
for (seg_id = 1; msg->len > 0; seg_id++) {
|
|
if (seg_id >= ARRAY_SIZE(link->tx.buf)) {
|
|
BT_ERR("Too big message (seg_id %d)", seg_id);
|
|
bt_mesh_prov_clear_tx(link, false);
|
|
return -E2BIG;
|
|
}
|
|
|
|
buf = adv_buf_create();
|
|
if (!buf) {
|
|
bt_mesh_prov_clear_tx(link, false);
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
link->tx.buf[seg_id] = buf;
|
|
|
|
seg_len = MIN(msg->len, CONT_PAYLOAD_MAX);
|
|
|
|
BT_DBG("seg_id %u len %u: %s", seg_id, seg_len,
|
|
bt_hex(msg->data, seg_len));
|
|
|
|
net_buf_add_be32(buf, link->link_id);
|
|
net_buf_add_u8(buf, xact_id);
|
|
net_buf_add_u8(buf, GPC_CONT(seg_id));
|
|
net_buf_add_mem(buf, msg->data, seg_len);
|
|
net_buf_simple_pull(msg, seg_len);
|
|
}
|
|
|
|
send_reliable(link, PROV_XMIT);
|
|
|
|
#if CONFIG_BLE_MESH_FAST_PROV
|
|
if (link->tx_pdu_type >= link->last_tx_pdu) {
|
|
timeout = K_SECONDS(60);
|
|
}
|
|
#endif /* CONFIG_BLE_MESH_FAST_PROV */
|
|
|
|
k_delayed_work_submit(&link->prot_timer, timeout);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BLE_MESH_PB_ADV */
|
|
|
|
int bt_mesh_prov_send(struct bt_mesh_prov_link *link, struct net_buf_simple *buf)
|
|
{
|
|
#if CONFIG_BLE_MESH_RPR_SRV
|
|
if (bt_mesh_atomic_test_bit(link->flags, PB_NPPI)) {
|
|
if (link->pb_remote_send) {
|
|
BT_INFO("NPPI, send prov pdu 0x%02x", buf->data[0]);
|
|
return link->pb_remote_send(link, buf);
|
|
}
|
|
|
|
BT_ERR("No NPPI send callback provided");
|
|
return -EIO;
|
|
}
|
|
#endif /* CONFIG_BLE_MESH_RPR_SRV */
|
|
|
|
#if CONFIG_BLE_MESH_RPR_CLI || CONFIG_BLE_MESH_RPR_SRV
|
|
if (bt_mesh_atomic_test_bit(link->flags, PB_REMOTE)) {
|
|
if (link->pb_remote_send) {
|
|
BT_INFO("PB-Remote, send prov pdu 0x%02x", buf->data[0]);
|
|
return link->pb_remote_send(link, buf);
|
|
}
|
|
BT_ERR("No PB-Remote send callback provided");
|
|
return -EIO;
|
|
}
|
|
#endif /* CONFIG_BLE_MESH_RPR_CLI || CONFIG_BLE_MESH_RPR_SRV */
|
|
|
|
#if CONFIG_BLE_MESH_PB_GATT
|
|
if (link->conn) {
|
|
if (link->pb_gatt_send) {
|
|
return link->pb_gatt_send(link, buf);
|
|
}
|
|
|
|
BT_ERR("No PB-GATT send callback provided");
|
|
return -EIO;
|
|
}
|
|
#endif /* CONFIG_BLE_MESH_PB_GATT */
|
|
|
|
#if CONFIG_BLE_MESH_PB_ADV
|
|
if (bt_mesh_prov_buf_type_get(buf) == PROV_FAILED) {
|
|
/* For case MESH/NODE/PROV/BV-10-C, Node must send Transaction
|
|
* ACK before Provisioning Failed message is transmitted.
|
|
*/
|
|
bt_mesh_gen_prov_ack_send(link, link->rx.id);
|
|
}
|
|
|
|
return bt_mesh_prov_send_adv(link, buf);
|
|
#endif /* CONFIG_BLE_MESH_PB_ADV */
|
|
|
|
/* Shall not reach here. */
|
|
return 0;
|
|
}
|