|  | /*  Bluetooth Mesh */ | 
|  |  | 
|  | /* | 
|  | * Copyright (c) 2017 Intel Corporation | 
|  | * Copyright (c) 2020 Nordic Semiconductor ASA | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  | #include <stdint.h> | 
|  | #include <string.h> | 
|  | #include <bluetooth/conn.h> | 
|  | #include <bluetooth/mesh.h> | 
|  | #include <net/buf.h> | 
|  | #include "host/testing.h" | 
|  | #include "net.h" | 
|  | #include "adv.h" | 
|  | #include "crypto.h" | 
|  | #include "beacon.h" | 
|  | #include "prov.h" | 
|  |  | 
|  | #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_PROV) | 
|  | #define LOG_MODULE_NAME bt_mesh_pb_adv | 
|  | #include "common/log.h" | 
|  |  | 
|  | #define GPCF(gpc)           (gpc & 0x03) | 
|  | #define GPC_START(last_seg) (((last_seg) << 2) | 0x00) | 
|  | #define GPC_ACK             0x01 | 
|  | #define GPC_CONT(seg_id)    (((seg_id) << 2) | 0x02) | 
|  | #define GPC_CTL(op)         (((op) << 2) | 0x03) | 
|  |  | 
|  | #define START_PAYLOAD_MAX 20 | 
|  | #define CONT_PAYLOAD_MAX  23 | 
|  |  | 
|  | #define START_LAST_SEG(gpc) (gpc >> 2) | 
|  | #define CONT_SEG_INDEX(gpc) (gpc >> 2) | 
|  |  | 
|  | #define BEARER_CTL(gpc) (gpc >> 2) | 
|  | #define LINK_OPEN       0x00 | 
|  | #define LINK_ACK        0x01 | 
|  | #define LINK_CLOSE      0x02 | 
|  |  | 
|  | #define XACT_SEG_DATA(_seg) (&link.rx.buf->data[20 + ((_seg - 1) * 23)]) | 
|  | #define XACT_SEG_RECV(_seg) (link.rx.seg &= ~(1 << (_seg))) | 
|  |  | 
|  | #define XACT_ID_MAX  0x7f | 
|  | #define XACT_ID_NVAL 0xff | 
|  | #define SEG_NVAL     0xff | 
|  |  | 
|  | #define RETRANSMIT_TIMEOUT  K_MSEC(CONFIG_BT_MESH_PB_ADV_RETRANS_TIMEOUT) | 
|  | #define BUF_TIMEOUT         K_MSEC(400) | 
|  | #define CLOSING_TIMEOUT     (3 * MSEC_PER_SEC) | 
|  | #define TRANSACTION_TIMEOUT (30 * MSEC_PER_SEC) | 
|  |  | 
|  | /* Acked messages, will do retransmissions manually, taking acks into account: | 
|  | */ | 
|  | #define RETRANSMITS_RELIABLE   0 | 
|  | /* Unacked messages: */ | 
|  | #define RETRANSMITS_UNRELIABLE 2 | 
|  | /* PDU acks: */ | 
|  | #define RETRANSMITS_ACK        2 | 
|  |  | 
|  | enum { | 
|  | ADV_LINK_ACTIVE,    	/* Link has been opened */ | 
|  | ADV_LINK_ACK_RECVD, 	/* Ack for link has been received */ | 
|  | ADV_LINK_CLOSING,   	/* Link is closing down */ | 
|  | ADV_LINK_INVALID,   	/* Error occurred during provisioning */ | 
|  | ADV_ACK_PENDING,    	/* An acknowledgment is being sent */ | 
|  | ADV_PROVISIONER,    	/* The link was opened as provisioner */ | 
|  |  | 
|  | ADV_NUM_FLAGS, | 
|  | }; | 
|  |  | 
|  | struct pb_adv { | 
|  | uint32_t id; /* Link ID */ | 
|  |  | 
|  | ATOMIC_DEFINE(flags, ADV_NUM_FLAGS); | 
|  |  | 
|  | const struct prov_bearer_cb *cb; | 
|  | void *cb_data; | 
|  |  | 
|  | struct { | 
|  | uint8_t id;       /* Most recent transaction ID */ | 
|  | uint8_t seg;      /* Bit-field of unreceived segments */ | 
|  | uint8_t last_seg; /* Last segment (to check length) */ | 
|  | uint8_t fcs;      /* Expected FCS value */ | 
|  | struct net_buf_simple *buf; | 
|  | } rx; | 
|  |  | 
|  | struct { | 
|  | /* Start timestamp of the transaction */ | 
|  | int64_t start; | 
|  |  | 
|  | /* Transaction id */ | 
|  | uint8_t id; | 
|  |  | 
|  | /* Current ack id */ | 
|  | uint8_t pending_ack; | 
|  |  | 
|  | /* Pending outgoing buffer(s) */ | 
|  | struct net_buf *buf[3]; | 
|  |  | 
|  | prov_bearer_send_complete_t cb; | 
|  |  | 
|  | void *cb_data; | 
|  |  | 
|  | /* Retransmit timer */ | 
|  | struct k_delayed_work retransmit; | 
|  | } tx; | 
|  |  | 
|  | /* Protocol timeout */ | 
|  | struct k_delayed_work prot_timer; | 
|  | }; | 
|  |  | 
|  | struct prov_rx { | 
|  | uint32_t link_id; | 
|  | uint8_t xact_id; | 
|  | uint8_t gpc; | 
|  | }; | 
|  |  | 
|  | NET_BUF_SIMPLE_DEFINE_STATIC(rx_buf, 65); | 
|  |  | 
|  | static struct pb_adv link = { .rx = { .buf = &rx_buf } }; | 
|  |  | 
|  | static void gen_prov_ack_send(uint8_t xact_id); | 
|  | static void link_open(struct prov_rx *rx, struct net_buf_simple *buf); | 
|  | static void link_ack(struct prov_rx *rx, struct net_buf_simple *buf); | 
|  | static void link_close(struct prov_rx *rx, struct net_buf_simple *buf); | 
|  | static void prov_link_close(enum prov_bearer_link_status status); | 
|  |  | 
|  | static void buf_sent(int err, void *user_data) | 
|  | { | 
|  | if (!link.tx.buf[0]) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | k_delayed_work_submit(&link.tx.retransmit, RETRANSMIT_TIMEOUT); | 
|  | } | 
|  |  | 
|  | static struct bt_mesh_send_cb buf_sent_cb = { | 
|  | .end = buf_sent, | 
|  | }; | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | static void free_segments(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (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; | 
|  | /* Mark as canceled */ | 
|  | BT_MESH_ADV(buf)->busy = 0U; | 
|  | net_buf_unref(buf); | 
|  | } | 
|  | } | 
|  |  | 
|  | static uint8_t next_transaction_id(uint8_t id) | 
|  | { | 
|  | return (((id + 1) & XACT_ID_MAX) | (id & (XACT_ID_MAX+1))); | 
|  | } | 
|  |  | 
|  | static void prov_clear_tx(void) | 
|  | { | 
|  | BT_DBG(""); | 
|  |  | 
|  | k_delayed_work_cancel(&link.tx.retransmit); | 
|  |  | 
|  | free_segments(); | 
|  | } | 
|  |  | 
|  | static void reset_adv_link(void) | 
|  | { | 
|  | BT_DBG(""); | 
|  | prov_clear_tx(); | 
|  |  | 
|  | k_delayed_work_cancel(&link.prot_timer); | 
|  |  | 
|  | if (atomic_test_bit(link.flags, ADV_PROVISIONER)) { | 
|  | /* Clear everything except the retransmit and protocol timer | 
|  | * delayed work objects. | 
|  | */ | 
|  | (void)memset(&link, 0, offsetof(struct pb_adv, tx.retransmit)); | 
|  | link.rx.id = XACT_ID_NVAL; | 
|  | } else { | 
|  | /* Accept another provisioning attempt */ | 
|  | link.id = 0; | 
|  | atomic_clear(link.flags); | 
|  | link.rx.id = XACT_ID_MAX; | 
|  | link.tx.id = XACT_ID_NVAL; | 
|  | } | 
|  |  | 
|  | link.tx.pending_ack = XACT_ID_NVAL; | 
|  | link.rx.buf = &rx_buf; | 
|  | net_buf_simple_reset(link.rx.buf); | 
|  | } | 
|  |  | 
|  | static void close_link(enum prov_bearer_link_status reason) | 
|  | { | 
|  | const struct prov_bearer_cb *cb = link.cb; | 
|  | void *cb_data = link.cb_data; | 
|  |  | 
|  | reset_adv_link(); | 
|  | cb->link_closed(&pb_adv, cb_data, reason); | 
|  | } | 
|  |  | 
|  | static struct net_buf *adv_buf_create(uint8_t retransmits) | 
|  | { | 
|  | struct net_buf *buf; | 
|  |  | 
|  | buf = bt_mesh_adv_create(BT_MESH_ADV_PROV, | 
|  | BT_MESH_TRANSMIT(retransmits, 20), | 
|  | 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) | 
|  | { | 
|  | BT_DBG("xact 0x%x complete", (uint8_t)link.tx.pending_ack); | 
|  | atomic_clear_bit(link.flags, ADV_ACK_PENDING); | 
|  | } | 
|  |  | 
|  | static bool ack_pending(void) | 
|  | { | 
|  | return atomic_test_bit(link.flags, ADV_ACK_PENDING); | 
|  | } | 
|  |  | 
|  | static void prov_failed(uint8_t err) | 
|  | { | 
|  | BT_DBG("%u", err); | 
|  | link.cb->error(&pb_adv, link.cb_data, err); | 
|  | atomic_set_bit(link.flags, ADV_LINK_INVALID); | 
|  | } | 
|  |  | 
|  | static void prov_msg_recv(void) | 
|  | { | 
|  | k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); | 
|  |  | 
|  | if (!bt_mesh_fcs_check(link.rx.buf, link.rx.fcs)) { | 
|  | BT_ERR("Incorrect FCS"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | gen_prov_ack_send(link.rx.id); | 
|  |  | 
|  | if (atomic_test_bit(link.flags, ADV_LINK_INVALID)) { | 
|  | BT_WARN("Unexpected msg 0x%02x on invalidated link", | 
|  | link.rx.buf->data[0]); | 
|  | prov_failed(PROV_ERR_UNEXP_PDU); | 
|  | return; | 
|  | } | 
|  |  | 
|  | link.cb->recv(&pb_adv, link.cb_data, link.rx.buf); | 
|  | } | 
|  |  | 
|  | static void protocol_timeout(struct k_work *work) | 
|  | { | 
|  | BT_DBG(""); | 
|  |  | 
|  | link.rx.seg = 0U; | 
|  | prov_link_close(PROV_BEARER_LINK_STATUS_TIMEOUT); | 
|  | } | 
|  | /******************************************************************************* | 
|  | * Generic provisioning | 
|  | ******************************************************************************/ | 
|  |  | 
|  | static void gen_prov_ack_send(uint8_t xact_id) | 
|  | { | 
|  | static const struct bt_mesh_send_cb cb = { | 
|  | .start = ack_complete, | 
|  | }; | 
|  | const struct bt_mesh_send_cb *complete; | 
|  | struct net_buf *buf; | 
|  | bool pending = atomic_test_and_set_bit(link.flags, ADV_ACK_PENDING); | 
|  |  | 
|  | BT_DBG("xact_id 0x%x", xact_id); | 
|  |  | 
|  | if (pending && link.tx.pending_ack == xact_id) { | 
|  | BT_DBG("Not sending duplicate ack"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | buf = adv_buf_create(RETRANSMITS_ACK); | 
|  | if (!buf) { | 
|  | atomic_clear_bit(link.flags, ADV_ACK_PENDING); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (pending) { | 
|  | complete = NULL; | 
|  | } else { | 
|  | link.tx.pending_ack = xact_id; | 
|  | complete = &cb; | 
|  | } | 
|  |  | 
|  | net_buf_add_be32(buf, link.id); | 
|  | net_buf_add_u8(buf, xact_id); | 
|  | net_buf_add_u8(buf, GPC_ACK); | 
|  |  | 
|  | bt_mesh_adv_send(buf, complete, NULL); | 
|  | net_buf_unref(buf); | 
|  | } | 
|  |  | 
|  | static void gen_prov_cont(struct prov_rx *rx, struct net_buf_simple *buf) | 
|  | { | 
|  | uint8_t seg = CONT_SEG_INDEX(rx->gpc); | 
|  |  | 
|  | BT_DBG("len %u, seg_index %u", buf->len, seg); | 
|  |  | 
|  | if (!link.rx.seg && link.rx.id == rx->xact_id) { | 
|  | if (!ack_pending()) { | 
|  | BT_DBG("Resending ack"); | 
|  | gen_prov_ack_send(rx->xact_id); | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!link.rx.seg && | 
|  | next_transaction_id(link.rx.id) == rx->xact_id) { | 
|  | BT_DBG("Start segment lost"); | 
|  |  | 
|  | link.rx.id = rx->xact_id; | 
|  |  | 
|  | net_buf_simple_reset(link.rx.buf); | 
|  |  | 
|  | link.rx.seg = SEG_NVAL; | 
|  | link.rx.last_seg = SEG_NVAL; | 
|  |  | 
|  | prov_clear_tx(); | 
|  | } else if (rx->xact_id != link.rx.id) { | 
|  | BT_WARN("Data for unknown transaction (0x%x != 0x%x)", | 
|  | rx->xact_id, link.rx.id); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (seg > link.rx.last_seg) { | 
|  | BT_ERR("Invalid segment index %u", seg); | 
|  | prov_failed(PROV_ERR_NVAL_FMT); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!(link.rx.seg & BIT(seg))) { | 
|  | BT_DBG("Ignoring already received segment"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | memcpy(XACT_SEG_DATA(seg), buf->data, buf->len); | 
|  | XACT_SEG_RECV(seg); | 
|  |  | 
|  | if (seg == link.rx.last_seg && !(link.rx.seg & BIT(0))) { | 
|  | uint8_t expect_len; | 
|  |  | 
|  | expect_len = (link.rx.buf->len - 20U - | 
|  | ((link.rx.last_seg - 1) * 23U)); | 
|  | if (expect_len != buf->len) { | 
|  | BT_ERR("Incorrect last seg len: %u != %u", expect_len, | 
|  | buf->len); | 
|  | prov_failed(PROV_ERR_NVAL_FMT); | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!link.rx.seg) { | 
|  | prov_msg_recv(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void gen_prov_ack(struct prov_rx *rx, struct net_buf_simple *buf) | 
|  | { | 
|  | BT_DBG("len %u", buf->len); | 
|  |  | 
|  | if (!link.tx.buf[0]) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (rx->xact_id == link.tx.id) { | 
|  | /* Don't clear resending of link_close messages */ | 
|  | if (!atomic_test_bit(link.flags, ADV_LINK_CLOSING)) { | 
|  | prov_clear_tx(); | 
|  | } | 
|  |  | 
|  | if (link.tx.cb) { | 
|  | link.tx.cb(0, link.tx.cb_data); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void gen_prov_start(struct prov_rx *rx, struct net_buf_simple *buf) | 
|  | { | 
|  | uint8_t seg = SEG_NVAL; | 
|  |  | 
|  | if (rx->xact_id == link.rx.id) { | 
|  | if (!link.rx.seg) { | 
|  | if (!ack_pending()) { | 
|  | BT_DBG("Resending ack"); | 
|  | gen_prov_ack_send(rx->xact_id); | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!(link.rx.seg & BIT(0))) { | 
|  | BT_DBG("Ignoring duplicate segment"); | 
|  | return; | 
|  | } | 
|  | } else if (rx->xact_id != next_transaction_id(link.rx.id)) { | 
|  | BT_WARN("Unexpected xact 0x%x, expected 0x%x", rx->xact_id, | 
|  | next_transaction_id(link.rx.id)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | net_buf_simple_reset(link.rx.buf); | 
|  | 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); | 
|  |  | 
|  | if (link.rx.buf->len < 1) { | 
|  | BT_ERR("Ignoring zero-length provisioning PDU"); | 
|  | prov_failed(PROV_ERR_NVAL_FMT); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (link.rx.buf->len > link.rx.buf->size) { | 
|  | BT_ERR("Too large provisioning PDU (%u bytes)", | 
|  | link.rx.buf->len); | 
|  | prov_failed(PROV_ERR_NVAL_FMT); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (START_LAST_SEG(rx->gpc) > 0 && link.rx.buf->len <= 20U) { | 
|  | BT_ERR("Too small total length for multi-segment PDU"); | 
|  | prov_failed(PROV_ERR_NVAL_FMT); | 
|  | return; | 
|  | } | 
|  |  | 
|  | prov_clear_tx(); | 
|  |  | 
|  | link.rx.last_seg = START_LAST_SEG(rx->gpc); | 
|  |  | 
|  | if ((link.rx.seg & BIT(0)) && | 
|  | (find_msb_set((~link.rx.seg) & SEG_NVAL) - 1 > link.rx.last_seg)) { | 
|  | BT_ERR("Invalid segment index %u", seg); | 
|  | prov_failed(PROV_ERR_NVAL_FMT); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (link.rx.seg) { | 
|  | seg = link.rx.seg; | 
|  | } | 
|  |  | 
|  | link.rx.seg = seg & ((1 << (START_LAST_SEG(rx->gpc) + 1)) - 1); | 
|  | memcpy(link.rx.buf->data, buf->data, buf->len); | 
|  | XACT_SEG_RECV(0); | 
|  |  | 
|  | if (!link.rx.seg) { | 
|  | prov_msg_recv(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void gen_prov_ctl(struct prov_rx *rx, struct net_buf_simple *buf) | 
|  | { | 
|  | BT_DBG("op 0x%02x len %u", BEARER_CTL(rx->gpc), buf->len); | 
|  |  | 
|  | switch (BEARER_CTL(rx->gpc)) { | 
|  | case LINK_OPEN: | 
|  | link_open(rx, buf); | 
|  | break; | 
|  | case LINK_ACK: | 
|  | if (!atomic_test_bit(link.flags, ADV_LINK_ACTIVE)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | link_ack(rx, buf); | 
|  | break; | 
|  | case LINK_CLOSE: | 
|  | if (!atomic_test_bit(link.flags, ADV_LINK_ACTIVE)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | link_close(rx, buf); | 
|  | break; | 
|  | default: | 
|  | BT_ERR("Unknown bearer opcode: 0x%02x", BEARER_CTL(rx->gpc)); | 
|  |  | 
|  | if (IS_ENABLED(CONFIG_BT_TESTING)) { | 
|  | bt_test_mesh_prov_invalid_bearer(BEARER_CTL(rx->gpc)); | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | static const struct { | 
|  | void (*func)(struct prov_rx *rx, struct net_buf_simple *buf); | 
|  | bool require_link; | 
|  | uint8_t min_len; | 
|  | } gen_prov[] = { | 
|  | { gen_prov_start, true, 3 }, | 
|  | { gen_prov_ack, true, 0 }, | 
|  | { gen_prov_cont, true, 0 }, | 
|  | { gen_prov_ctl, false, 0 }, | 
|  | }; | 
|  |  | 
|  | static void gen_prov_recv(struct prov_rx *rx, struct net_buf_simple *buf) | 
|  | { | 
|  | if (buf->len < gen_prov[GPCF(rx->gpc)].min_len) { | 
|  | BT_ERR("Too short GPC message type %u", GPCF(rx->gpc)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (!atomic_test_bit(link.flags, ADV_LINK_ACTIVE) && | 
|  | gen_prov[GPCF(rx->gpc)].require_link) { | 
|  | BT_DBG("Ignoring message that requires active link"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | gen_prov[GPCF(rx->gpc)].func(rx, buf); | 
|  | } | 
|  |  | 
|  | /******************************************************************************* | 
|  | * TX | 
|  | ******************************************************************************/ | 
|  |  | 
|  | static void send_reliable(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | link.tx.start = k_uptime_get(); | 
|  |  | 
|  | for (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, NULL, NULL); | 
|  | } else { | 
|  | bt_mesh_adv_send(buf, &buf_sent_cb, NULL); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void prov_retransmit(struct k_work *work) | 
|  | { | 
|  | int32_t timeout_ms; | 
|  | int i; | 
|  |  | 
|  | BT_DBG(""); | 
|  |  | 
|  | if (!atomic_test_bit(link.flags, ADV_LINK_ACTIVE)) { | 
|  | BT_WARN("Link not active"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * According to mesh profile spec (5.3.1.4.3), the close message should | 
|  | * be restransmitted at least three times. Retransmit the link_close | 
|  | * message until CLOSING_TIMEOUT has elapsed. | 
|  | */ | 
|  | if (atomic_test_bit(link.flags, ADV_LINK_CLOSING)) { | 
|  | timeout_ms = CLOSING_TIMEOUT; | 
|  | } else { | 
|  | timeout_ms = TRANSACTION_TIMEOUT; | 
|  | } | 
|  |  | 
|  | if (k_uptime_get() - link.tx.start > timeout_ms) { | 
|  | if (atomic_test_bit(link.flags, ADV_LINK_CLOSING)) { | 
|  | close_link(PROV_BEARER_LINK_STATUS_SUCCESS); | 
|  | } else { | 
|  | BT_WARN("Giving up transaction"); | 
|  | prov_link_close(PROV_BEARER_LINK_STATUS_FAIL); | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (i = 0; i < ARRAY_SIZE(link.tx.buf); i++) { | 
|  | struct net_buf *buf = link.tx.buf[i]; | 
|  |  | 
|  | if (!buf) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (BT_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, NULL, NULL); | 
|  | } else { | 
|  | bt_mesh_adv_send(buf, &buf_sent_cb, NULL); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static int bearer_ctl_send(uint8_t op, const void *data, uint8_t data_len, | 
|  | bool reliable) | 
|  | { | 
|  | struct net_buf *buf; | 
|  |  | 
|  | BT_DBG("op 0x%02x data_len %u", op, data_len); | 
|  |  | 
|  | prov_clear_tx(); | 
|  | k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); | 
|  |  | 
|  | buf = adv_buf_create(reliable ? RETRANSMITS_RELIABLE : | 
|  | RETRANSMITS_UNRELIABLE); | 
|  | if (!buf) { | 
|  | return -ENOBUFS; | 
|  | } | 
|  |  | 
|  | net_buf_add_be32(buf, 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); | 
|  |  | 
|  | if (reliable) { | 
|  | link.tx.buf[0] = buf; | 
|  | send_reliable(); | 
|  | } else { | 
|  | bt_mesh_adv_send(buf, &buf_sent_cb, NULL); | 
|  | net_buf_unref(buf); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int prov_send_adv(struct net_buf_simple *msg, | 
|  | prov_bearer_send_complete_t cb, void *cb_data) | 
|  | { | 
|  | struct net_buf *start, *buf; | 
|  | uint8_t seg_len, seg_id; | 
|  |  | 
|  | prov_clear_tx(); | 
|  | k_delayed_work_submit(&link.prot_timer, PROTOCOL_TIMEOUT); | 
|  |  | 
|  | start = adv_buf_create(RETRANSMITS_RELIABLE); | 
|  | if (!start) { | 
|  | return -ENOBUFS; | 
|  | } | 
|  |  | 
|  | link.tx.id = next_transaction_id(link.tx.id); | 
|  | net_buf_add_be32(start, link.id); | 
|  | net_buf_add_u8(start, link.tx.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; | 
|  | link.tx.cb = cb; | 
|  | link.tx.cb_data = cb_data; | 
|  |  | 
|  | BT_DBG("xact_id: 0x%x len: %u", link.tx.id, msg->len); | 
|  |  | 
|  | 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); | 
|  |  | 
|  | buf = start; | 
|  | for (seg_id = 1U; msg->len > 0; seg_id++) { | 
|  | if (seg_id >= ARRAY_SIZE(link.tx.buf)) { | 
|  | BT_ERR("Too big message"); | 
|  | free_segments(); | 
|  | return -E2BIG; | 
|  | } | 
|  |  | 
|  | buf = adv_buf_create(RETRANSMITS_RELIABLE); | 
|  | if (!buf) { | 
|  | free_segments(); | 
|  | return -ENOBUFS; | 
|  | } | 
|  |  | 
|  | link.tx.buf[seg_id] = buf; | 
|  |  | 
|  | seg_len = MIN(msg->len, CONT_PAYLOAD_MAX); | 
|  |  | 
|  | BT_DBG("seg %u len %u: %s", seg_id, seg_len, | 
|  | bt_hex(msg->data, seg_len)); | 
|  |  | 
|  | net_buf_add_be32(buf, link.id); | 
|  | net_buf_add_u8(buf, link.tx.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(); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /******************************************************************************* | 
|  | * Link management rx | 
|  | ******************************************************************************/ | 
|  |  | 
|  | static void link_open(struct prov_rx *rx, struct net_buf_simple *buf) | 
|  | { | 
|  | BT_DBG("len %u", buf->len); | 
|  |  | 
|  | if (buf->len < 16) { | 
|  | BT_ERR("Too short bearer open message (len %u)", buf->len); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (atomic_test_bit(link.flags, ADV_LINK_ACTIVE)) { | 
|  | /* Send another link ack if the provisioner missed the last */ | 
|  | if (link.id == rx->link_id) { | 
|  | BT_DBG("Resending link ack"); | 
|  | bearer_ctl_send(LINK_ACK, NULL, 0, false); | 
|  | } else { | 
|  | BT_DBG("Ignoring bearer open: link already active"); | 
|  | } | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (memcmp(buf->data, bt_mesh_prov_get()->uuid, 16)) { | 
|  | BT_DBG("Bearer open message not for us"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | link.id = rx->link_id; | 
|  | atomic_set_bit(link.flags, ADV_LINK_ACTIVE); | 
|  | net_buf_simple_reset(link.rx.buf); | 
|  |  | 
|  | bearer_ctl_send(LINK_ACK, NULL, 0, false); | 
|  |  | 
|  | link.cb->link_opened(&pb_adv, link.cb_data); | 
|  | } | 
|  |  | 
|  | static void link_ack(struct prov_rx *rx, struct net_buf_simple *buf) | 
|  | { | 
|  | BT_DBG("len %u", buf->len); | 
|  |  | 
|  | if (atomic_test_bit(link.flags, ADV_PROVISIONER)) { | 
|  | if (atomic_test_and_set_bit(link.flags, ADV_LINK_ACK_RECVD)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | prov_clear_tx(); | 
|  |  | 
|  | link.cb->link_opened(&pb_adv, link.cb_data); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void link_close(struct prov_rx *rx, struct net_buf_simple *buf) | 
|  | { | 
|  | BT_DBG("len %u", buf->len); | 
|  |  | 
|  | if (buf->len != 1) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | close_link(net_buf_simple_pull_u8(buf)); | 
|  | } | 
|  |  | 
|  | /******************************************************************************* | 
|  | * Higher level functionality | 
|  | ******************************************************************************/ | 
|  |  | 
|  | void bt_mesh_pb_adv_recv(struct net_buf_simple *buf) | 
|  | { | 
|  | struct prov_rx rx; | 
|  |  | 
|  | if (!link.cb) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (buf->len < 6) { | 
|  | BT_WARN("Too short provisioning packet (len %u)", buf->len); | 
|  | return; | 
|  | } | 
|  |  | 
|  | rx.link_id = net_buf_simple_pull_be32(buf); | 
|  | rx.xact_id = net_buf_simple_pull_u8(buf); | 
|  | rx.gpc = net_buf_simple_pull_u8(buf); | 
|  |  | 
|  | if (atomic_test_bit(link.flags, ADV_LINK_ACTIVE) && link.id != rx.link_id) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | BT_DBG("link_id 0x%08x xact_id 0x%x", rx.link_id, rx.xact_id); | 
|  |  | 
|  | gen_prov_recv(&rx, buf); | 
|  | } | 
|  |  | 
|  | static int prov_link_open(const uint8_t uuid[16], k_timeout_t timeout, | 
|  | const struct prov_bearer_cb *cb, void *cb_data) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | BT_DBG("uuid %s", bt_hex(uuid, 16)); | 
|  |  | 
|  | err = bt_mesh_adv_enable(); | 
|  | if (err) { | 
|  | BT_ERR("Failed enabling advertiser"); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | if (atomic_test_and_set_bit(link.flags, ADV_LINK_ACTIVE)) { | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | atomic_set_bit(link.flags, ADV_PROVISIONER); | 
|  |  | 
|  | bt_rand(&link.id, sizeof(link.id)); | 
|  | link.tx.id = XACT_ID_MAX; | 
|  | link.rx.id = XACT_ID_NVAL; | 
|  | link.cb = cb; | 
|  | link.cb_data = cb_data; | 
|  |  | 
|  | net_buf_simple_reset(link.rx.buf); | 
|  |  | 
|  | bearer_ctl_send(LINK_OPEN, uuid, 16, true); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int prov_link_accept(const struct prov_bearer_cb *cb, void *cb_data) | 
|  | { | 
|  | int err; | 
|  |  | 
|  | err = bt_mesh_adv_enable(); | 
|  | if (err) { | 
|  | BT_ERR("Failed enabling advertiser"); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | if (atomic_test_bit(link.flags, ADV_LINK_ACTIVE)) { | 
|  | return -EBUSY; | 
|  | } | 
|  |  | 
|  | link.rx.id = XACT_ID_MAX; | 
|  | link.tx.id = XACT_ID_NVAL; | 
|  | link.cb = cb; | 
|  | link.cb_data = cb_data; | 
|  |  | 
|  | /* Make sure we're scanning for provisioning inviations */ | 
|  | bt_mesh_scan_enable(); | 
|  | /* Enable unprovisioned beacon sending */ | 
|  | bt_mesh_beacon_enable(); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void prov_link_close(enum prov_bearer_link_status status) | 
|  | { | 
|  | if (atomic_test_and_set_bit(link.flags, ADV_LINK_CLOSING)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | bearer_ctl_send(LINK_CLOSE, &status, 1, true); | 
|  | } | 
|  |  | 
|  | void pb_adv_init(void) | 
|  | { | 
|  | k_delayed_work_init(&link.prot_timer, protocol_timeout); | 
|  | k_delayed_work_init(&link.tx.retransmit, prov_retransmit); | 
|  | } | 
|  |  | 
|  | void pb_adv_reset(void) | 
|  | { | 
|  | reset_adv_link(); | 
|  | } | 
|  |  | 
|  | const struct prov_bearer pb_adv = { | 
|  | .type = BT_MESH_PROV_ADV, | 
|  | .link_open = prov_link_open, | 
|  | .link_accept = prov_link_accept, | 
|  | .link_close = prov_link_close, | 
|  | .send = prov_send_adv, | 
|  | .clear_tx = prov_clear_tx, | 
|  | }; |