OVMS3-idf/components/bt/esp_ble_mesh/mesh_core/provisioner_prov.c
lly 81354cf24f ble_mesh: stack: Optimize Provisioner delete device function
Since we have provided separate functions for deleting node
information with node's unicast address, device uuid, etc.
So we update the behavior of this function, which will only
be used to delete device information which is not provisioned
or just under provisioning.
2020-09-25 14:04:58 +08:00

3511 lines
103 KiB
C

// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <errno.h>
#include "crypto.h"
#include "adv.h"
#include "mesh.h"
#include "access.h"
#include "settings.h"
#include "fast_prov.h"
#include "mesh_common.h"
#include "proxy_client.h"
#include "provisioner_prov.h"
#include "provisioner_main.h"
#if CONFIG_BLE_MESH_PROVISIONER
_Static_assert(BLE_MESH_MAX_CONN >= CONFIG_BLE_MESH_PBG_SAME_TIME,
"Too large BLE Mesh PB-GATT count");
/* 3 transmissions, 20ms interval */
#define PROV_XMIT BLE_MESH_TRANSMIT(2, 20)
#define AUTH_METHOD_NO_OOB 0x00
#define AUTH_METHOD_STATIC 0x01
#define AUTH_METHOD_OUTPUT 0x02
#define AUTH_METHOD_INPUT 0x03
#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
#define PROV_ERR_NONE 0x00
#define PROV_ERR_NVAL_PDU 0x01
#define PROV_ERR_NVAL_FMT 0x02
#define PROV_ERR_UNEXP_PDU 0x03
#define PROV_ERR_CFM_FAILED 0x04
#define PROV_ERR_RESOURCES 0x05
#define PROV_ERR_DECRYPT 0x06
#define PROV_ERR_UNEXP_ERR 0x07
#define PROV_ERR_ADDR 0x08
#define PROV_INVITE 0x00
#define PROV_CAPABILITIES 0x01
#define PROV_START 0x02
#define PROV_PUB_KEY 0x03
#define PROV_INPUT_COMPLETE 0x04
#define PROV_CONFIRM 0x05
#define PROV_RANDOM 0x06
#define PROV_DATA 0x07
#define PROV_COMPLETE 0x08
#define PROV_FAILED 0x09
#define PROV_ALG_P256 0x00
#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 CLOSE_REASON_SUCCESS 0x00
#define CLOSE_REASON_TIMEOUT 0x01
#define CLOSE_REASON_FAILED 0x02
#define PROV_AUTH_VAL_SIZE 0x10
#define PROV_CONF_SALT_SIZE 0x10
#define PROV_CONF_KEY_SIZE 0x10
#define PROV_DH_KEY_SIZE 0x20
#define PROV_CONFIRM_SIZE 0x10
#define PROV_PROV_SALT_SIZE 0x10
#define PROV_CONF_INPUTS_SIZE 0x91
#define XACT_SEG_DATA(_idx, _seg) (&link[_idx].rx.buf->data[20 + ((_seg - 1) * 23)])
#define XACT_SEG_RECV(_idx, _seg) (link[_idx].rx.seg &= ~(1 << (_seg)))
#define XACT_NVAL 0xff
enum {
REMOTE_PUB_KEY, /* Remote key has been received */
LOCAL_PUB_KEY, /* Local public key is available */
LINK_ACTIVE, /* Link has been opened */
WAIT_GEN_DHKEY, /* Waiting for remote public key to generate DHKey */
HAVE_DHKEY, /* DHKey has been calculated */
SEND_CONFIRM, /* Waiting to send Confirm value */
WAIT_NUMBER, /* Waiting for number input from user */
WAIT_STRING, /* Waiting for string input from user */
TIMEOUT_START, /* Provision timeout timer has started */
NUM_FLAGS,
};
/** Provisioner link structure allocation
* |--------------------------------------------------------|
* | Link(PB-ADV) | Link(PB-GATT) |
* |--------------------------------------------------------|
* |<----------------------Total Link---------------------->|
*/
struct prov_link {
BLE_MESH_ATOMIC_DEFINE(flags, NUM_FLAGS);
u8_t uuid[16]; /* check if device is being provisioned*/
u16_t oob_info; /* oob info of this device */
u8_t element_num; /* element num of device */
u8_t ki_flags; /* Key refresh flag and iv update flag */
u32_t iv_index; /* IV Index */
u8_t auth_method; /* Choosen authentication method */
u8_t auth_action; /* Choosen authentication action */
u8_t auth_size; /* Choosen authentication size */
u16_t assign_addr; /* Application assigned address for the device */
u16_t unicast_addr; /* unicast address allocated for device */
bt_mesh_addr_t addr; /* Device address */
#if defined(CONFIG_BLE_MESH_PB_GATT)
bool connecting; /* start connecting with device */
struct bt_mesh_conn *conn; /* GATT connection */
#endif
u8_t expect; /* Next expected PDU */
u8_t *dhkey; /* Calculated DHKey */
u8_t *auth; /* Authentication Value */
u8_t *conf_salt; /* ConfirmationSalt */
u8_t *conf_key; /* ConfirmationKey */
u8_t *conf_inputs; /* ConfirmationInputs */
u8_t *rand; /* Local Random */
u8_t *conf; /* Remote Confirmation */
u8_t *prov_salt; /* Provisioning Salt */
#if defined(CONFIG_BLE_MESH_PB_ADV)
bool linking; /* Linking is being establishing */
u16_t send_link_close; /* Link close is being sent flag */
u32_t link_id; /* Link ID */
u8_t pending_ack; /* Decide which transaction id ack is pending */
u8_t expect_ack_for; /* Transaction ACK expected for provisioning pdu */
u8_t tx_pdu_type; /* The current transmitted Provisioning PDU type */
struct {
u8_t trans_id; /* Transaction ID */
u8_t prev_id; /* Previous Transaction ID */
u8_t seg; /* Bit-field of unreceived segments */
u8_t last_seg; /* Last segment (to check length) */
u8_t fcs; /* Expected FCS value */
u8_t adv_buf_id; /* index of buf allocated in adv_buf_data */
struct net_buf_simple *buf;
} rx;
struct {
/* Start timestamp of the transaction */
s64_t start;
/* Transaction id*/
u8_t trans_id;
/* Pending outgoing buffer(s) */
struct net_buf *buf[3];
/* Retransmit timer */
struct k_delayed_work retransmit;
} tx;
#endif
/** Provision timeout timer. Spec P259 says: The provisioning protocol
* shall have a minimum timeout of 60 seconds that is reset each time
* a provisioning protocol PDU is sent or received.
*/
struct k_delayed_work timeout;
};
/* Number of devices can be provisioned at the same time equals to PB-ADV + PB-GATT */
#define BLE_MESH_PROV_SAME_TIME \
(CONFIG_BLE_MESH_PBA_SAME_TIME + CONFIG_BLE_MESH_PBG_SAME_TIME)
#define PROV_MAX_ADDR_TO_ASSIGN 0x7FFF
static struct prov_link link[BLE_MESH_PROV_SAME_TIME];
struct prov_rx {
u32_t link_id;
u8_t xact_id;
u8_t gpc;
};
struct bt_mesh_prov_ctx {
/* Primary element address of Provisioner */
u16_t primary_addr;
/* Provisioning bearers used by Provisioner */
bt_mesh_prov_bearer_t bearers;
/* If provisioning random have been generated, set BIT0 to 1 */
u8_t rand_gen_done;
/* Provisioner random */
u8_t random[16];
/* Current number of PB-ADV provisioned devices simultaneously */
u8_t pba_count;
/* Current number of PB-GATT provisioned devices simultaneously */
u8_t pbg_count;
/* Current unicast address going to allocated */
u16_t curr_alloc_addr;
/* Current net_idx going to be used in provisioning data */
u16_t curr_net_idx;
/* Current flags going to be used in provisioning data */
u8_t curr_flags;
/* Current iv_index going to be used in provisioning data */
u16_t curr_iv_index;
/* Length of Static OOB value */
u8_t static_oob_len;
/* Static OOB value */
u8_t static_oob_val[16];
/* Offset of the device uuid to be matched, based on zero */
u8_t match_offset;
/* Length of the device uuid to be matched (start from the match_offset) */
u8_t match_length;
/* Value of the device uuid to be matched */
u8_t match_value[16];
/* Indicate when received uuid_match adv_pkts, can provision it at once */
bool prov_after_match;
#if defined(CONFIG_BLE_MESH_PB_ADV)
/* Mutex used to protect the PB-ADV procedure */
bt_mesh_mutex_t pb_adv_lock;
/* Mutex used to protect the adv buf during PB-ADV procedure */
bt_mesh_mutex_t pb_buf_lock;
#endif
#if defined(CONFIG_BLE_MESH_PB_GATT)
/* Mutex used to protect the PB-GATT procedure */
bt_mesh_mutex_t pb_gatt_lock;
#endif
/* Fast provisioning related information */
struct {
bool enable;
u16_t net_idx;
u8_t flags;
u32_t iv_index;
u16_t unicast_addr_min;
u16_t unicast_addr_max;
} fast_prov;
};
static struct bt_mesh_prov_ctx prov_ctx;
#define FAST_PROV_ENABLE() (prov_ctx.fast_prov.enable)
struct unprov_dev_queue {
bt_mesh_addr_t addr;
u8_t uuid[16];
u16_t oob_info;
u8_t bearer;
u8_t flags;
} __packed unprov_dev[CONFIG_BLE_MESH_WAIT_FOR_PROV_MAX_DEV_NUM] = {
[0 ... (CONFIG_BLE_MESH_WAIT_FOR_PROV_MAX_DEV_NUM - 1)] = {
.addr.type = 0xff,
.bearer = 0,
.flags = false,
},
};
static unprov_adv_pkt_cb_t notify_unprov_adv_pkt_cb;
#define BUF_TIMEOUT K_MSEC(400)
#if defined(CONFIG_BLE_MESH_FAST_PROV)
#define RETRANSMIT_TIMEOUT K_MSEC(360)
#define TRANSACTION_TIMEOUT K_SECONDS(3)
#define PROVISION_TIMEOUT K_SECONDS(6)
#else
#define RETRANSMIT_TIMEOUT K_MSEC(500)
#define TRANSACTION_TIMEOUT K_SECONDS(30)
#define PROVISION_TIMEOUT K_SECONDS(60)
#endif /* CONFIG_BLE_MESH_FAST_PROV */
#if defined(CONFIG_BLE_MESH_PB_GATT)
#define PROV_BUF_HEADROOM 5
#else
#define PROV_BUF_HEADROOM 0
#endif
#define PROV_BUF(name, len) \
NET_BUF_SIMPLE_DEFINE(name, PROV_BUF_HEADROOM + len)
static const struct bt_mesh_prov *prov;
#if defined(CONFIG_BLE_MESH_PB_ADV)
static void send_link_open(const u8_t idx);
#endif
static void prov_gen_dh_key(const u8_t idx);
static void send_pub_key(const u8_t idx, u8_t oob);
static void close_link(const u8_t idx, u8_t reason);
#if defined(CONFIG_BLE_MESH_PB_ADV)
#define ADV_BUF_SIZE 65
static struct prov_adv_buf {
struct net_buf_simple buf;
} adv_buf[CONFIG_BLE_MESH_PBA_SAME_TIME];
static u8_t adv_buf_data[ADV_BUF_SIZE * CONFIG_BLE_MESH_PBA_SAME_TIME];
#endif /* CONFIG_BLE_MESH_PB_ADV */
#define PROV_FREE_MEM(_idx, member) \
{ \
if (link[_idx].member) { \
bt_mesh_free(link[_idx].member); \
link[_idx].member = NULL; \
} \
}
#if defined(CONFIG_BLE_MESH_PB_ADV)
static void bt_mesh_pb_adv_mutex_new(void)
{
if (!prov_ctx.pb_adv_lock.mutex) {
bt_mesh_mutex_create(&prov_ctx.pb_adv_lock);
}
}
static void bt_mesh_pb_adv_mutex_free(void)
{
bt_mesh_mutex_free(&prov_ctx.pb_adv_lock);
}
static void bt_mesh_pb_adv_lock(void)
{
bt_mesh_mutex_lock(&prov_ctx.pb_adv_lock);
}
static void bt_mesh_pb_adv_unlock(void)
{
bt_mesh_mutex_unlock(&prov_ctx.pb_adv_lock);
}
static void bt_mesh_pb_buf_mutex_new(void)
{
if (!prov_ctx.pb_buf_lock.mutex) {
bt_mesh_mutex_create(&prov_ctx.pb_buf_lock);
}
}
static void bt_mesh_pb_buf_mutex_free(void)
{
bt_mesh_mutex_free(&prov_ctx.pb_buf_lock);
}
static void bt_mesh_pb_buf_lock(void)
{
bt_mesh_mutex_lock(&prov_ctx.pb_buf_lock);
}
static void bt_mesh_pb_buf_unlock(void)
{
bt_mesh_mutex_unlock(&prov_ctx.pb_buf_lock);
}
#endif /* CONFIG_BLE_MESH_PB_ADV */
#if defined(CONFIG_BLE_MESH_PB_GATT)
static void bt_mesh_pb_gatt_mutex_new(void)
{
if (!prov_ctx.pb_gatt_lock.mutex) {
bt_mesh_mutex_create(&prov_ctx.pb_gatt_lock);
}
}
static void bt_mesh_pb_gatt_mutex_free(void)
{
bt_mesh_mutex_free(&prov_ctx.pb_gatt_lock);
}
static void bt_mesh_pb_gatt_lock(void)
{
bt_mesh_mutex_lock(&prov_ctx.pb_gatt_lock);
}
static void bt_mesh_pb_gatt_unlock(void)
{
bt_mesh_mutex_unlock(&prov_ctx.pb_gatt_lock);
}
#endif /* CONFIG_BLE_MESH_PB_GATT */
void bt_mesh_provisioner_pbg_count_dec(void)
{
if (prov_ctx.pbg_count) {
prov_ctx.pbg_count--;
}
}
static inline void provisioner_pbg_count_inc(void)
{
prov_ctx.pbg_count++;
}
#if defined(CONFIG_BLE_MESH_PB_GATT)
void bt_mesh_provisioner_clear_link_info(const u8_t addr[6])
{
int i;
if (!addr) {
BT_ERR("%s, Invalid parameter", __func__);
return;
}
BT_DBG("Clear device info, addr %s", bt_hex(addr, BLE_MESH_ADDR_LEN));
for (i = CONFIG_BLE_MESH_PBA_SAME_TIME; i < BLE_MESH_PROV_SAME_TIME; i++) {
if (!memcmp(link[i].addr.val, addr, BLE_MESH_ADDR_LEN)) {
link[i].connecting = false;
link[i].conn = NULL;
link[i].oob_info = 0x0;
memset(link[i].uuid, 0, 16);
memset(&link[i].addr, 0, sizeof(bt_mesh_addr_t));
bt_mesh_atomic_test_and_clear_bit(link[i].flags, LINK_ACTIVE);
if (bt_mesh_atomic_test_and_clear_bit(link[i].flags, TIMEOUT_START)) {
k_delayed_work_cancel(&link[i].timeout);
}
return;
}
}
BT_WARN("Device not found, addr %s", bt_hex(addr, BLE_MESH_ADDR_LEN));
return;
}
#endif
const struct bt_mesh_prov *bt_mesh_provisioner_get_prov_info(void)
{
return prov;
}
void bt_mesh_provisioner_restore_prov_info(u16_t primary_addr, u16_t alloc_addr)
{
prov_ctx.primary_addr = primary_addr;
prov_ctx.curr_alloc_addr = alloc_addr;
}
static int provisioner_dev_find(const bt_mesh_addr_t *addr, const u8_t uuid[16], u16_t *index)
{
bool uuid_match = false;
bool addr_match = false;
u8_t zero[16] = {0};
u16_t i = 0U, j = 0U;
int comp = 0;
if (addr) {
comp = memcmp(addr->val, zero, BLE_MESH_ADDR_LEN);
}
if ((!uuid && (!addr || (comp == 0) || (addr->type > BLE_MESH_ADDR_RANDOM))) || !index) {
return -EINVAL;
}
/** Note: user may add a device into two unprov_dev array elements,
* one with device address, address type and another only
* with device UUID. We need to take this into consideration.
*/
if (uuid && memcmp(uuid, zero, 16)) {
for (i = 0; i < ARRAY_SIZE(unprov_dev); i++) {
if (!memcmp(unprov_dev[i].uuid, uuid, 16)) {
uuid_match = true;
break;
}
}
}
if (addr && comp && (addr->type <= BLE_MESH_ADDR_RANDOM)) {
for (j = 0; j < ARRAY_SIZE(unprov_dev); j++) {
if (!memcmp(unprov_dev[j].addr.val, addr->val, BLE_MESH_ADDR_LEN) &&
unprov_dev[j].addr.type == addr->type) {
addr_match = true;
break;
}
}
}
if (!uuid_match && !addr_match) {
BT_DBG("Device not exists in queue");
return -ENODEV;
}
if (uuid_match && addr_match && (i != j)) {
/**
* In this situation, copy address & type into device uuid
* array element, reset another element, rm_flag will be
* decided by uuid element.
*/
unprov_dev[i].addr.type = unprov_dev[j].addr.type;
memcpy(unprov_dev[i].addr.val, unprov_dev[j].addr.val, BLE_MESH_ADDR_LEN);
unprov_dev[i].bearer |= unprov_dev[j].bearer;
memset(&unprov_dev[j], 0x0, sizeof(struct unprov_dev_queue));
}
*index = uuid_match ? i : j;
return 0;
}
static bool is_unprov_dev_being_provision(const u8_t uuid[16])
{
int i;
#if defined(CONFIG_BLE_MESH_FAST_PROV)
/**
* During Fast Provisioning test, we found that if a device has already being
* provisioned, there is still a chance that the Provisioner can receive the
* Unprovisioned Device Beacon from the device (because the device will stop
* Unprovisioned Device Beacon when Transaction ACK for Provisioning Complete
* is received). So in Fast Provisioning the Provisioner should ignore this.
*/
if (bt_mesh_provisioner_get_node_with_uuid(uuid)) {
BT_WARN("Device has already been provisioned");
return true;
}
#endif
for (i = 0; i < BLE_MESH_PROV_SAME_TIME; i++) {
#if defined(CONFIG_BLE_MESH_PB_ADV) && defined(CONFIG_BLE_MESH_PB_GATT)
if (link[i].linking || link[i].connecting ||
bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) {
#elif defined(CONFIG_BLE_MESH_PB_ADV) && !defined(CONFIG_BLE_MESH_PB_GATT)
if (link[i].linking || bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) {
#else
if (link[i].connecting || bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) {
#endif
if (!memcmp(link[i].uuid, uuid, 16)) {
BT_DBG("Device is being provisioning");
return true;
}
}
}
return false;
}
static bool is_unprov_dev_uuid_match(const u8_t uuid[16])
{
if (prov_ctx.match_length) {
if (memcmp(uuid + prov_ctx.match_offset,
prov_ctx.match_value, prov_ctx.match_length)) {
return false;
}
}
return true;
}
static int provisioner_check_unprov_dev_info(const u8_t uuid[16], bt_mesh_prov_bearer_t bearer)
{
if (!uuid) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
/* Check if the device uuid matches configured value */
if (is_unprov_dev_uuid_match(uuid) == false) {
BT_DBG("Device uuid mismatch");
return -EIO;
}
/* Check if this device is currently being provisioned.
* According to Zephyr's device code, if we connect with
* one device and start to provision it, we may still can
* receive the connectable prov adv pkt from this device.
* Here we check both PB-GATT and PB-ADV link status.
*/
if (is_unprov_dev_being_provision(uuid)) {
return -EALREADY;
}
/* Check if the current PB-ADV link is full */
if (bearer == BLE_MESH_PROV_ADV && prov_ctx.pba_count == CONFIG_BLE_MESH_PBA_SAME_TIME) {
BT_INFO("Current PB-ADV links reach max limit");
return -ENOMEM;
}
/* Check if the current PB-GATT link is full */
if (bearer == BLE_MESH_PROV_GATT && prov_ctx.pbg_count == CONFIG_BLE_MESH_PBG_SAME_TIME) {
BT_INFO("Current PB-GATT links reach max limit");
return -ENOMEM;
}
/* Check if the device has already been provisioned */
if (bt_mesh_provisioner_get_node_with_uuid(uuid)) {
BT_INFO("Provisioned before, start to provision again");
return 0;
}
return 0;
}
#if defined(CONFIG_BLE_MESH_PB_ADV)
static int provisioner_start_prov_pb_adv(const u8_t uuid[16], const bt_mesh_addr_t *addr,
u16_t oob_info, u16_t assign_addr)
{
u8_t zero[6] = {0};
int addr_cmp = 0;
int i;
if (!uuid || !addr) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
bt_mesh_pb_adv_lock();
/* If the unicast address of the node is going to be allocated internally,
* then we need to check if there are addresses can be allocated.
*/
if (assign_addr == BLE_MESH_ADDR_UNASSIGNED &&
prov_ctx.curr_alloc_addr == BLE_MESH_ADDR_UNASSIGNED) {
BT_ERR("No available unicast address to assign");
bt_mesh_pb_adv_unlock();
return -EIO;
}
if (is_unprov_dev_being_provision(uuid)) {
bt_mesh_pb_adv_unlock();
return -EALREADY;
}
addr_cmp = memcmp(addr->val, zero, BLE_MESH_ADDR_LEN);
for (i = 0; i < CONFIG_BLE_MESH_PBA_SAME_TIME; i++) {
if (!bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE) && !link[i].linking) {
memcpy(link[i].uuid, uuid, 16);
link[i].oob_info = oob_info;
if (addr_cmp && (addr->type <= BLE_MESH_ADDR_RANDOM)) {
link[i].addr.type = addr->type;
memcpy(link[i].addr.val, addr->val, BLE_MESH_ADDR_LEN);
}
send_link_open(i);
/* If the application layer assigned a specific unicast address for the device,
* then Provisioner will use this address in the Provisioning Data PDU.
*/
if (BLE_MESH_ADDR_IS_UNICAST(assign_addr)) {
link[i].assign_addr = assign_addr;
}
bt_mesh_pb_adv_unlock();
return 0;
}
}
BT_ERR("No PB-ADV link available");
bt_mesh_pb_adv_unlock();
return -ENOMEM;
}
#endif /* CONFIG_BLE_MESH_PB_ADV */
#if defined(CONFIG_BLE_MESH_PB_GATT)
static int provisioner_start_prov_pb_gatt(const u8_t uuid[16], const bt_mesh_addr_t *addr,
u16_t oob_info, u16_t assign_addr)
{
u8_t zero[6] = {0};
int addr_cmp = 0;
int i;
if (!uuid || !addr) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
bt_mesh_pb_gatt_lock();
/* If the unicast address of the node is going to be allocated internally,
* then we need to check if there are addresses can be allocated.
*/
if (assign_addr == BLE_MESH_ADDR_UNASSIGNED &&
prov_ctx.curr_alloc_addr == BLE_MESH_ADDR_UNASSIGNED) {
BT_ERR("No available unicast address to assign");
bt_mesh_pb_gatt_unlock();
return -EIO;
}
if (is_unprov_dev_being_provision(uuid)) {
bt_mesh_pb_gatt_unlock();
return -EALREADY;
}
addr_cmp = memcmp(addr->val, zero, BLE_MESH_ADDR_LEN);
for (i = CONFIG_BLE_MESH_PBA_SAME_TIME; i < BLE_MESH_PROV_SAME_TIME; i++) {
if (!link[i].connecting && !bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) {
memcpy(link[i].uuid, uuid, 16);
link[i].oob_info = oob_info;
if (addr_cmp && (addr->type <= BLE_MESH_ADDR_RANDOM)) {
link[i].addr.type = addr->type;
memcpy(link[i].addr.val, addr->val, BLE_MESH_ADDR_LEN);
}
if (bt_mesh_gattc_conn_create(&link[i].addr, BLE_MESH_UUID_MESH_PROV_VAL) < 0) {
memset(link[i].uuid, 0, 16);
link[i].oob_info = 0x0;
memset(&link[i].addr, 0, sizeof(bt_mesh_addr_t));
bt_mesh_pb_gatt_unlock();
return -EIO;
}
/* If the application layer assigned a specific unicast address for the device,
* then Provisioner will use this address in the Provisioning Data PDU.
*/
if (BLE_MESH_ADDR_IS_UNICAST(assign_addr)) {
link[i].assign_addr = assign_addr;
}
/* If creating connection successfully, set connecting flag to 1 */
link[i].connecting = true;
provisioner_pbg_count_inc();
bt_mesh_pb_gatt_unlock();
return 0;
}
}
BT_ERR("No PB-GATT link available");
bt_mesh_pb_gatt_unlock();
return -ENOMEM;
}
#endif /* CONFIG_BLE_MESH_PB_GATT */
int bt_mesh_provisioner_add_unprov_dev(struct bt_mesh_unprov_dev_add *add_dev, u8_t flags)
{
bt_mesh_addr_t add_addr = {0};
u8_t zero[16] = {0};
int addr_cmp = 0;
int uuid_cmp = 0;
u16_t i = 0U;
int err = 0;
if (!add_dev) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
addr_cmp = memcmp(add_dev->addr, zero, BLE_MESH_ADDR_LEN);
uuid_cmp = memcmp(add_dev->uuid, zero, 16);
if (add_dev->bearer == 0x0 || ((uuid_cmp == 0) &&
((addr_cmp == 0) || add_dev->addr_type > BLE_MESH_ADDR_RANDOM))) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
if ((add_dev->bearer & BLE_MESH_PROV_ADV) && (add_dev->bearer & BLE_MESH_PROV_GATT) &&
(flags & START_PROV_NOW)) {
BT_ERR("Can not start PB-ADV & PB-GATT simultaneously");
return -EINVAL;
}
if ((uuid_cmp == 0) && (flags & START_PROV_NOW)) {
BT_ERR("Can not start provisioning with zero uuid");
return -EINVAL;
}
if ((add_dev->bearer & BLE_MESH_PROV_GATT) && (flags & START_PROV_NOW) &&
((addr_cmp == 0) || add_dev->addr_type > BLE_MESH_ADDR_RANDOM)) {
BT_ERR("Invalid device address for PB-GATT");
return -EINVAL;
}
if (add_dev->bearer & BLE_MESH_PROV_GATT) {
#if !CONFIG_BLE_MESH_PB_GATT
BT_ERR("Not support PB-GATT");
return -EINVAL;
#endif
}
if (add_dev->bearer & BLE_MESH_PROV_ADV) {
#if !CONFIG_BLE_MESH_PB_ADV
BT_ERR("Not support PB-ADV");
return -EINVAL;
#endif
}
/* Check if the provisioned nodes array is full */
if (bt_mesh_provisioner_get_node_with_uuid(add_dev->uuid) == NULL) {
if (bt_mesh_provisioner_get_node_count() == CONFIG_BLE_MESH_MAX_PROV_NODES) {
BT_WARN("Current provisioned devices reach max limit");
return -ENOMEM;
}
}
add_addr.type = add_dev->addr_type;
memcpy(add_addr.val, add_dev->addr, BLE_MESH_ADDR_LEN);
err = provisioner_dev_find(&add_addr, add_dev->uuid, &i);
if (err == -EINVAL) {
BT_ERR("%s, Invalid parameter", __func__);
return err;
} else if (err == 0) {
if (!(add_dev->bearer & unprov_dev[i].bearer)) {
BT_WARN("Add device with only bearer updated");
unprov_dev[i].bearer |= add_dev->bearer;
} else {
BT_WARN("Device already exists in queue");
}
goto start;
}
for (i = 0U; i < ARRAY_SIZE(unprov_dev); i++) {
if (unprov_dev[i].bearer) {
continue;
}
if (addr_cmp && (add_dev->addr_type <= BLE_MESH_ADDR_RANDOM)) {
unprov_dev[i].addr.type = add_dev->addr_type;
memcpy(unprov_dev[i].addr.val, add_dev->addr, BLE_MESH_ADDR_LEN);
}
if (uuid_cmp) {
memcpy(unprov_dev[i].uuid, add_dev->uuid, 16);
}
unprov_dev[i].bearer = add_dev->bearer & BIT_MASK(2);
unprov_dev[i].flags = flags & BIT_MASK(3);
goto start;
}
/* If queue is full, find flushable device and replace it */
for (i = 0U; i < ARRAY_SIZE(unprov_dev); i++) {
if (unprov_dev[i].flags & FLUSHABLE_DEV) {
memset(&unprov_dev[i], 0, sizeof(struct unprov_dev_queue));
if (addr_cmp && (add_dev->addr_type <= BLE_MESH_ADDR_RANDOM)) {
unprov_dev[i].addr.type = add_dev->addr_type;
memcpy(unprov_dev[i].addr.val, add_dev->addr, BLE_MESH_ADDR_LEN);
}
if (uuid_cmp) {
memcpy(unprov_dev[i].uuid, add_dev->uuid, 16);
}
unprov_dev[i].bearer = add_dev->bearer & BIT_MASK(2);
unprov_dev[i].flags = flags & BIT_MASK(3);
goto start;
}
}
BT_ERR("Unprovisioned device queue is full");
return -ENOMEM;
start:
if (!(flags & START_PROV_NOW)) {
return 0;
}
/* Check if current provisioned node count + active link reach max limit */
if (bt_mesh_provisioner_get_node_with_uuid(add_dev->uuid) == NULL) {
if (bt_mesh_provisioner_get_node_count() + prov_ctx.pba_count + \
prov_ctx.pbg_count >= CONFIG_BLE_MESH_MAX_PROV_NODES) {
BT_WARN("Node count + active link count reach max limit");
return -EIO;
}
}
if ((err = provisioner_check_unprov_dev_info(add_dev->uuid, add_dev->bearer))) {
return err;
}
if (add_dev->bearer == BLE_MESH_PROV_ADV) {
#if defined(CONFIG_BLE_MESH_PB_ADV)
if ((err = provisioner_start_prov_pb_adv(
add_dev->uuid, &add_addr, add_dev->oob_info, BLE_MESH_ADDR_UNASSIGNED))) {
return err;
}
#endif
} else if (add_dev->bearer == BLE_MESH_PROV_GATT) {
#if defined(CONFIG_BLE_MESH_PB_GATT)
if ((err = provisioner_start_prov_pb_gatt(
add_dev->uuid, &add_addr, add_dev->oob_info, BLE_MESH_ADDR_UNASSIGNED))) {
return err;
}
#endif
}
return 0;
}
int bt_mesh_provisioner_prov_device_with_addr(const u8_t uuid[16], const u8_t addr[6],
u8_t addr_type, bt_mesh_prov_bearer_t bearer,
u16_t oob_info, u16_t unicast_addr)
{
bt_mesh_addr_t dev_addr = {0};
int err = 0;
if (uuid == NULL) {
BT_ERR("Invalid device uuid");
return -EINVAL;
}
if (bearer != BLE_MESH_PROV_ADV && bearer != BLE_MESH_PROV_GATT) {
BT_ERR("Invalid provisioning bearer 0x%02x", bearer);
return -EINVAL;
}
if (!IS_ENABLED(CONFIG_BLE_MESH_PB_ADV) && bearer == BLE_MESH_PROV_ADV) {
BT_ERR("Not support PB-ADV");
return -ENOTSUP;
}
if (!IS_ENABLED(CONFIG_BLE_MESH_PB_GATT) && bearer == BLE_MESH_PROV_GATT) {
BT_ERR("Not support PB-GATT");
return -ENOTSUP;
}
if (bearer == BLE_MESH_PROV_GATT && (addr == NULL || addr_type > BLE_MESH_ADDR_RANDOM)) {
BT_ERR("Invalid device address info");
return -EINVAL;
}
if (!BLE_MESH_ADDR_IS_UNICAST(unicast_addr)) {
BT_ERR("Invalid unicast address 0x%04x", unicast_addr);
return -EINVAL;
}
/* Here we will not check if the assigned unicast address is overlapped
* with the unicast addresses of other nodes or Provisioner, because:
* 1. At this moment, the element number of the device is unknown
* 2. If the node is a reprovisioned device, then the original allocated
* unicast address will be used.
* 3. Some other devices may be just being provisioning, and currently we
* can not know the exactly allocated addresses of them.
*/
if (bt_mesh_provisioner_get_node_with_uuid(uuid) == NULL) {
/* Check if the provisioned nodes array is full */
if (bt_mesh_provisioner_get_node_count() == CONFIG_BLE_MESH_MAX_PROV_NODES) {
BT_WARN("Current provisioned devices reach max limit");
return -ENOMEM;
}
/* Check if current provisioned node count + active link reach max limit */
if (bt_mesh_provisioner_get_node_count() + prov_ctx.pba_count + \
prov_ctx.pbg_count >= CONFIG_BLE_MESH_MAX_PROV_NODES) {
BT_WARN("Node count + active link count reach max limit");
return -EIO;
}
}
if ((err = provisioner_check_unprov_dev_info(uuid, bearer))) {
return err;
}
if (addr) {
dev_addr.type = addr_type;
memcpy(dev_addr.val, addr, BLE_MESH_ADDR_LEN);
}
if (bearer == BLE_MESH_PROV_ADV) {
#if defined(CONFIG_BLE_MESH_PB_ADV)
if ((err = provisioner_start_prov_pb_adv(uuid, &dev_addr, oob_info, unicast_addr))) {
return err;
}
#endif
} else if (bearer == BLE_MESH_PROV_GATT) {
#if defined(CONFIG_BLE_MESH_PB_GATT)
if ((err = provisioner_start_prov_pb_gatt(uuid, &dev_addr, oob_info, unicast_addr))) {
return err;
}
#endif
}
return 0;
}
int bt_mesh_provisioner_delete_device(struct bt_mesh_device_delete *del_dev)
{
bt_mesh_addr_t del_addr = {0};
bool addr_match = false;
bool uuid_match = false;
u8_t zero[16] = {0};
int addr_cmp = 0;
int uuid_cmp = 0;
u16_t i = 0U;
int err = 0;
if (!del_dev) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
addr_cmp = memcmp(del_dev->addr, zero, BLE_MESH_ADDR_LEN);
uuid_cmp = memcmp(del_dev->uuid, zero, 16);
if ((uuid_cmp == 0) && ((addr_cmp == 0) || del_dev->addr_type > BLE_MESH_ADDR_RANDOM)) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
del_addr.type = del_dev->addr_type;
memcpy(del_addr.val, del_dev->addr, BLE_MESH_ADDR_LEN);
/* First: find if the device is in the device queue */
err = provisioner_dev_find(&del_addr, del_dev->uuid, &i);
if (err) {
BT_DBG("Device not in queue");
} else {
memset(&unprov_dev[i], 0x0, sizeof(struct unprov_dev_queue));
}
/* Second: find if the device is being provisioned */
for (i = 0U; i < ARRAY_SIZE(link); i++) {
if (addr_cmp && (del_dev->addr_type <= BLE_MESH_ADDR_RANDOM)) {
if (!memcmp(link[i].addr.val, del_dev->addr, BLE_MESH_ADDR_LEN) &&
link[i].addr.type == del_dev->addr_type) {
addr_match = true;
}
}
if (uuid_cmp) {
if (!memcmp(link[i].uuid, del_dev->uuid, 16)) {
uuid_match = true;
}
}
if (addr_match || uuid_match) {
close_link(i, CLOSE_REASON_FAILED);
break;
}
}
return 0;
}
int bt_mesh_provisioner_set_dev_uuid_match(u8_t offset, u8_t length,
const u8_t *match, bool prov_flag)
{
if (length && (!match || (offset + length > 16))) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
(void)memset(prov_ctx.match_value, 0, 16);
prov_ctx.match_offset = offset;
prov_ctx.match_length = length;
if (length) {
memcpy(prov_ctx.match_value, match, length);
}
prov_ctx.prov_after_match = prov_flag;
return 0;
}
int bt_mesh_provisioner_adv_pkt_cb_register(unprov_adv_pkt_cb_t cb)
{
if (!cb) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
notify_unprov_adv_pkt_cb = cb;
return 0;
}
int bt_mesh_provisioner_set_prov_data_info(struct bt_mesh_prov_data_info *info)
{
const u8_t *key = NULL;
if (!info || info->flag == 0) {
return -EINVAL;
}
if (info->flag & NET_IDX_FLAG) {
key = bt_mesh_provisioner_net_key_get(info->net_idx);
if (!key) {
BT_ERR("Failed to get NetKey");
return -EINVAL;
}
prov_ctx.curr_net_idx = info->net_idx;
} else if (info->flag & FLAGS_FLAG) {
prov_ctx.curr_flags = info->flags;
} else if (info->flag & IV_INDEX_FLAG) {
prov_ctx.curr_iv_index = info->iv_index;
}
return 0;
}
int bt_mesh_provisioner_init_prov_info(void)
{
if (prov_ctx.primary_addr == BLE_MESH_ADDR_UNASSIGNED) {
/* If unicast address of primary element of Provisioner has not been set
* before, then the following initialization procedure will be used.
*/
if (!BLE_MESH_ADDR_IS_UNICAST(prov->prov_unicast_addr) ||
!BLE_MESH_ADDR_IS_UNICAST(prov->prov_start_address)) {
BT_ERR("Invalid address, own 0x%04x, start 0x%04x",
prov->prov_unicast_addr, prov->prov_start_address);
return -EINVAL;
}
const struct bt_mesh_comp *comp = bt_mesh_comp_get();
if (!comp) {
BT_ERR("Invalid composition data");
return -EINVAL;
}
if (prov->prov_unicast_addr + comp->elem_count > prov->prov_start_address) {
BT_WARN("Too small start address 0x%04x, update to 0x%04x",
prov->prov_start_address, prov->prov_unicast_addr + comp->elem_count);
prov_ctx.curr_alloc_addr = prov->prov_unicast_addr + comp->elem_count;
} else {
prov_ctx.curr_alloc_addr = prov->prov_start_address;
}
/* Update primary element address with the initialized value here. */
prov_ctx.primary_addr = prov->prov_unicast_addr;
if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) {
bt_mesh_store_prov_info(prov_ctx.primary_addr, prov_ctx.curr_alloc_addr);
}
}
prov_ctx.curr_net_idx = BLE_MESH_KEY_PRIMARY;
struct bt_mesh_subnet *sub = bt_mesh_provisioner_subnet_get(BLE_MESH_KEY_PRIMARY);
prov_ctx.curr_flags = bt_mesh_net_flags(sub);
prov_ctx.curr_iv_index = bt_mesh.iv_index;
return 0;
}
void bt_mesh_provisioner_set_prov_bearer(bt_mesh_prov_bearer_t bearers, bool clear)
{
if (clear == false) {
prov_ctx.bearers |= bearers;
} else {
prov_ctx.bearers &= ~bearers;
}
}
bt_mesh_prov_bearer_t bt_mesh_provisioner_get_prov_bearer(void)
{
return prov_ctx.bearers;
}
int bt_mesh_provisioner_set_static_oob_value(const u8_t *value, u8_t length)
{
int i;
if (value == NULL || length == 0U || length > 16U) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
/* Make sure Static OOB is not being used. */
for (i = 0; i < BLE_MESH_PROV_SAME_TIME; i++) {
if (link[i].auth_method == AUTH_METHOD_STATIC) {
BT_ERR("Static OOB is being used");
return -EINVAL;
}
}
(void)memset(prov_ctx.static_oob_val, 0, 16);
prov_ctx.static_oob_len = MIN(16, length);
memcpy(prov_ctx.static_oob_val, value, prov_ctx.static_oob_len);
return 0;
}
u16_t bt_mesh_provisioner_get_primary_elem_addr(void)
{
return prov_ctx.primary_addr;
}
int bt_mesh_provisioner_set_primary_elem_addr(u16_t addr)
{
const struct bt_mesh_comp *comp = NULL;
if (!BLE_MESH_ADDR_IS_UNICAST(addr)) {
BT_ERR("Invalid primary address 0x%04x", addr);
return -EINVAL;
}
comp = bt_mesh_comp_get();
if (!comp) {
BT_ERR("Invalid composition data");
return -EINVAL;
}
/* Make sure Provisioner address is not identical with the addresses of nodes */
if (bt_mesh_provisioner_check_is_addr_dup(addr, comp->elem_count, false)) {
BT_ERR("Address 0x%04x is duplicated with node address", addr);
return -EINVAL;
}
/* If the current can-be allocated address is bigger than primary address +
* element number, then the curr_alloc_addr will not be changed, and only
* the Provisioner related addresses will be updated.
*/
if (addr + comp->elem_count > prov_ctx.curr_alloc_addr) {
prov_ctx.curr_alloc_addr = addr + comp->elem_count;
}
BT_INFO("Primary address updated, old 0x%04x, new 0x%04x", prov_ctx.primary_addr, addr);
prov_ctx.primary_addr = addr;
if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) {
bt_mesh_store_prov_info(prov_ctx.primary_addr, prov_ctx.curr_alloc_addr);
}
bt_mesh_comp_provision(addr);
return 0;
}
#if CONFIG_BLE_MESH_TEST_AUTO_ENTER_NETWORK
int bt_mesh_test_provisioner_update_alloc_addr(u16_t unicast_addr, u16_t element_num)
{
u16_t max_addr = FAST_PROV_ENABLE() ? prov_ctx.fast_prov.unicast_addr_max : PROV_MAX_ADDR_TO_ASSIGN;
if (unicast_addr + element_num > max_addr) {
BT_WARN("Not enough unicast address to allocate");
prov_ctx.curr_alloc_addr = BLE_MESH_ADDR_UNASSIGNED;
} else {
prov_ctx.curr_alloc_addr = unicast_addr + element_num;
}
if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) {
bt_mesh_store_prov_info(prov_ctx.primary_addr, prov_ctx.curr_alloc_addr);
}
return 0;
}
#endif /* CONFIG_BLE_MESH_TEST_AUTO_ENTER_NETWORK */
/* The following APIs are for fast provisioning */
void bt_mesh_provisioner_fast_prov_enable(bool enable)
{
prov_ctx.fast_prov.enable = enable;
}
void bt_mesh_provisioner_set_fast_prov_net_idx(u16_t net_idx)
{
prov_ctx.fast_prov.net_idx = net_idx;
}
u16_t bt_mesh_provisioner_get_fast_prov_net_idx(void)
{
return prov_ctx.fast_prov.net_idx;
}
u8_t bt_mesh_set_fast_prov_unicast_addr_range(u16_t min, u16_t max)
{
if (!BLE_MESH_ADDR_IS_UNICAST(min) || !BLE_MESH_ADDR_IS_UNICAST(max)) {
BT_ERR("Invalid unicast address, min 0x%04x, max 0x%04x", min, max);
return 0x01; /* status: not a unicast address */
}
if (min > max) {
BT_ERR("Unicast address min is bigger than max");
return 0x02; /* status: min is bigger than max */
}
if (min <= prov_ctx.fast_prov.unicast_addr_max) {
BT_ERR("Unicast address overlap");
return 0x03; /* status: address overlaps with current value */
}
prov_ctx.fast_prov.unicast_addr_min = min;
prov_ctx.fast_prov.unicast_addr_max = max;
prov_ctx.curr_alloc_addr = prov_ctx.fast_prov.unicast_addr_min;
return 0x0; /* status: success */
}
void bt_mesh_set_fast_prov_flags_iv_index(u8_t flags, u32_t iv_index)
{
/* BIT0: Key Refresh flag, BIT1: IV Update flag */
prov_ctx.fast_prov.flags = flags & BIT_MASK(2);
prov_ctx.fast_prov.iv_index = iv_index;
}
#if defined(CONFIG_BLE_MESH_PB_ADV)
static struct net_buf_simple *bt_mesh_pba_get_buf(const u8_t idx)
{
struct net_buf_simple *buf = &(adv_buf[idx].buf);
net_buf_simple_reset(buf);
return buf;
}
#endif /* CONFIG_BLE_MESH_PB_ADV */
static void prov_memory_free(const u8_t idx)
{
PROV_FREE_MEM(idx, dhkey);
PROV_FREE_MEM(idx, auth);
PROV_FREE_MEM(idx, conf);
PROV_FREE_MEM(idx, conf_salt);
PROV_FREE_MEM(idx, conf_key);
PROV_FREE_MEM(idx, conf_inputs);
PROV_FREE_MEM(idx, prov_salt);
}
#if defined(CONFIG_BLE_MESH_PB_ADV)
static void buf_sent(int err, void *user_data)
{
u8_t idx = (int)user_data;
if (!link[idx].tx.buf[0]) {
return;
}
k_delayed_work_submit(&link[idx].tx.retransmit, RETRANSMIT_TIMEOUT);
}
static struct bt_mesh_send_cb buf_sent_cb = {
.end = buf_sent,
};
static void free_segments(const u8_t idx)
{
int i;
bt_mesh_pb_buf_lock();
for (i = 0; i < ARRAY_SIZE(link[idx].tx.buf); i++) {
struct net_buf *buf = link[idx].tx.buf[i];
if (!buf) {
break;
}
link[idx].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);
}
bt_mesh_pb_buf_unlock();
}
static void prov_clear_tx(const u8_t idx)
{
BT_DBG("%s", __func__);
k_delayed_work_cancel(&link[idx].tx.retransmit);
free_segments(idx);
}
static void reset_link(const u8_t idx, u8_t reason)
{
prov_clear_tx(idx);
if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) {
k_delayed_work_cancel(&link[idx].timeout);
}
if (prov->prov_link_close) {
prov->prov_link_close(BLE_MESH_PROV_ADV, reason);
}
prov_memory_free(idx);
#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN)
/* Remove the link id from exceptional list */
bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_REMOVE,
BLE_MESH_EXCEP_INFO_MESH_LINK_ID, &link[idx].link_id);
#endif
/* Clear everything except the retransmit delayed work config */
memset(&link[idx], 0, offsetof(struct prov_link, tx.retransmit));
link[idx].pending_ack = XACT_NVAL;
link[idx].rx.prev_id = XACT_NVAL;
if (bt_mesh_pub_key_get()) {
bt_mesh_atomic_set_bit(link[idx].flags, LOCAL_PUB_KEY);
}
link[idx].rx.buf = bt_mesh_pba_get_buf(idx);
if (prov_ctx.pba_count) {
prov_ctx.pba_count--;
}
}
static struct net_buf *adv_buf_create(void)
{
struct net_buf *buf = NULL;
buf = bt_mesh_adv_create(BLE_MESH_ADV_PROV, PROV_XMIT, BUF_TIMEOUT);
if (!buf) {
BT_ERR("Out of provisioning buffers");
return NULL;
}
return buf;
}
static void ack_complete(u16_t duration, int err, void *user_data)
{
u8_t idx = (int)user_data;
BT_DBG("xact %u complete", link[idx].pending_ack);
link[idx].pending_ack = XACT_NVAL;
}
static void gen_prov_ack_send(const u8_t idx, u8_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[idx].pending_ack == xact_id) {
BT_DBG("Not sending duplicate ack");
return;
}
buf = adv_buf_create();
if (!buf) {
return;
}
if (link[idx].pending_ack == XACT_NVAL) {
link[idx].pending_ack = xact_id;
complete = &cb;
} else {
complete = NULL;
}
net_buf_add_be32(buf, link[idx].link_id);
net_buf_add_u8(buf, xact_id);
net_buf_add_u8(buf, GPC_ACK);
bt_mesh_adv_send(buf, complete, (void *)(int)idx);
net_buf_unref(buf);
}
static void send_reliable(const u8_t idx)
{
int i;
link[idx].tx.start = k_uptime_get();
for (i = 0; i < ARRAY_SIZE(link[idx].tx.buf); i++) {
struct net_buf *buf = link[idx].tx.buf[i];
if (!buf) {
break;
}
if (i + 1 < ARRAY_SIZE(link[idx].tx.buf) && link[idx].tx.buf[i + 1]) {
bt_mesh_adv_send(buf, NULL, NULL);
} else {
bt_mesh_adv_send(buf, &buf_sent_cb, (void *)(int)idx);
}
}
}
static int bearer_ctl_send(const u8_t idx, u8_t op, void *data, u8_t data_len)
{
struct net_buf *buf = NULL;
BT_DBG("op 0x%02x data_len %u", op, data_len);
prov_clear_tx(idx);
buf = adv_buf_create();
if (!buf) {
return -ENOBUFS;
}
net_buf_add_be32(buf, link[idx].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[idx].tx.buf[0] = buf;
send_reliable(idx);
/** We can also use buf->ref and a flag to decide that
* link close has been sent 3 times.
* Here we use another way: use retransmit timer and need
* to make sure the timer is not cancelled during sending
* link close pdu, so we add link[i].tx.id = 0
*/
if (op == LINK_CLOSE) {
u8_t reason = *(u8_t *)data;
link[idx].send_link_close = ((reason & BIT_MASK(2)) << 1) | BIT(0);
link[idx].tx.trans_id = 0;
}
return 0;
}
static void send_link_open(const u8_t idx)
{
int j;
/** Generate link ID, and may need to check if this id is
* currently being used, which may will not happen ever.
*/
bt_mesh_rand(&link[idx].link_id, sizeof(u32_t));
while (1) {
for (j = 0; j < CONFIG_BLE_MESH_PBA_SAME_TIME; j++) {
if (bt_mesh_atomic_test_bit(link[j].flags, LINK_ACTIVE) || link[j].linking) {
if (link[idx].link_id == link[j].link_id) {
bt_mesh_rand(&link[idx].link_id, sizeof(u32_t));
break;
}
}
}
if (j == CONFIG_BLE_MESH_PBA_SAME_TIME) {
break;
}
}
#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN)
/* Add the link id into exceptional list */
bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_ADD,
BLE_MESH_EXCEP_INFO_MESH_LINK_ID, &link[idx].link_id);
#endif
bearer_ctl_send(idx, LINK_OPEN, link[idx].uuid, 16);
/* If Provisioner sets LINK_ACTIVE flag once Link Open is sent, we have
* no need to use linking flag (like PB-GATT connecting) to prevent the
* stored device info (UUID, oob_info) being replaced by other received
* unprovisioned device beacons.
* But if Provisioner sets LINK_ACTIVE flag after Link ACK is received,
* we need to use linking flag to prevent device info being replaced.
* Currently we set LINK_ACTIVE flag after sending Link Open.
*/
link[idx].linking = true;
/* Set LINK_ACTIVE just to be in compatibility with current Zephyr code */
bt_mesh_atomic_set_bit(link[idx].flags, LINK_ACTIVE);
if (prov->prov_link_open) {
prov->prov_link_open(BLE_MESH_PROV_ADV);
}
prov_ctx.pba_count++;
}
static u8_t last_seg(u8_t len)
{
if (len <= START_PAYLOAD_MAX) {
return 0;
}
len -= START_PAYLOAD_MAX;
return 1 + (len / CONT_PAYLOAD_MAX);
}
static inline u8_t next_transaction_id(const u8_t idx)
{
if (link[idx].tx.trans_id > 0x7F) {
link[idx].tx.trans_id = 0x0;
}
return link[idx].tx.trans_id++;
}
static int prov_send_adv(const u8_t idx, struct net_buf_simple *msg)
{
struct net_buf *start = NULL, *buf = NULL;
u8_t seg_len = 0U, seg_id = 0U;
u8_t xact_id = 0U;
s32_t timeout = PROVISION_TIMEOUT;
BT_DBG("len %u: %s", msg->len, bt_hex(msg->data, msg->len));
prov_clear_tx(idx);
start = adv_buf_create();
if (!start) {
return -ENOBUFS;
}
xact_id = next_transaction_id(idx);
net_buf_add_be32(start, link[idx].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[idx].tx.buf[0] = start;
/* Changed by Espressif, get message type */
link[idx].tx_pdu_type = msg->data[0];
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 = 1; msg->len > 0; seg_id++) {
if (seg_id >= ARRAY_SIZE(link[idx].tx.buf)) {
BT_ERR("Too big message (seg_id %d)", seg_id);
free_segments(idx);
return -E2BIG;
}
buf = adv_buf_create();
if (!buf) {
free_segments(idx);
return -ENOBUFS;
}
link[idx].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[idx].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(idx);
#if defined(CONFIG_BLE_MESH_FAST_PROV)
if (link[idx].tx_pdu_type >= PROV_DATA) {
timeout = K_SECONDS(60);
}
#endif
if (!bt_mesh_atomic_test_and_set_bit(link[idx].flags, TIMEOUT_START)) {
k_delayed_work_submit(&link[idx].timeout, timeout);
}
return 0;
}
#endif /* CONFIG_BLE_MESH_PB_ADV */
#if defined(CONFIG_BLE_MESH_PB_GATT)
static int prov_send_gatt(const u8_t idx, struct net_buf_simple *msg)
{
int err = 0;
if (!link[idx].conn) {
return -ENOTCONN;
}
err = bt_mesh_proxy_client_send(link[idx].conn, BLE_MESH_PROXY_PROV, msg);
if (err) {
BT_ERR("Failed to send PB-GATT pdu");
return err;
}
if (!bt_mesh_atomic_test_and_set_bit(link[idx].flags, TIMEOUT_START)) {
k_delayed_work_submit(&link[idx].timeout, PROVISION_TIMEOUT);
}
return 0;
}
#endif /* CONFIG_BLE_MESH_PB_GATT */
static inline int prov_send(const u8_t idx, struct net_buf_simple *buf)
{
#if defined(CONFIG_BLE_MESH_PB_ADV)
if (idx < CONFIG_BLE_MESH_PBA_SAME_TIME) {
return prov_send_adv(idx, buf);
}
#endif
#if defined(CONFIG_BLE_MESH_PB_GATT)
if (idx < BLE_MESH_PROV_SAME_TIME
#if defined(CONFIG_BLE_MESH_PB_ADV)
&& idx >= CONFIG_BLE_MESH_PBA_SAME_TIME
#endif
) {
return prov_send_gatt(idx, buf);
}
#endif
BT_ERR("Invalid link index %d", idx);
return -EINVAL;
}
static void prov_buf_init(struct net_buf_simple *buf, u8_t type)
{
net_buf_simple_reserve(buf, PROV_BUF_HEADROOM);
net_buf_simple_add_u8(buf, type);
}
static void prov_invite(const u8_t idx, const u8_t *data)
{
BT_DBG("%s", __func__);
}
static void prov_start(const u8_t idx, const u8_t *data)
{
BT_DBG("%s", __func__);
}
static void prov_data(const u8_t idx, const u8_t *data)
{
BT_DBG("%s", __func__);
}
static void send_invite(const u8_t idx)
{
PROV_BUF(buf, 2);
prov_buf_init(&buf, PROV_INVITE);
net_buf_simple_add_u8(&buf, prov->prov_attention);
link[idx].conf_inputs[0] = prov->prov_attention;
if (prov_send(idx, &buf)) {
BT_ERR("Failed to send Provisioning Invite");
close_link(idx, CLOSE_REASON_FAILED);
return;
}
link[idx].expect = PROV_CAPABILITIES;
}
static void prov_capabilities(const u8_t idx, const u8_t *data)
{
PROV_BUF(buf, 6);
u16_t algorithms = 0U, output_action = 0U, input_action = 0U;
u8_t element_num = 0U, pub_key_oob = 0U, static_oob = 0U,
output_size = 0U, input_size = 0U;
u8_t auth_method = 0U, auth_action = 0U, auth_size = 0U;
element_num = data[0];
BT_INFO("Elements: 0x%02x", element_num);
if (!element_num) {
BT_ERR("Invalid element number %d", element_num);
goto fail;
}
link[idx].element_num = element_num;
algorithms = sys_get_be16(&data[1]);
BT_INFO("Algorithms: 0x%04x", algorithms);
if (algorithms != BIT(PROV_ALG_P256)) {
BT_ERR("Invalid algorithms 0x%04x", algorithms);
goto fail;
}
pub_key_oob = data[3];
BT_INFO("Public Key Type: 0x%02x", pub_key_oob);
if (pub_key_oob > 0x01) {
BT_ERR("Invalid public key type 0x%02x", pub_key_oob);
goto fail;
}
pub_key_oob = ((prov->prov_pub_key_oob &&
prov->prov_pub_key_oob_cb) ? pub_key_oob : 0x00);
static_oob = data[4];
BT_INFO("Static OOB Type: 0x%02x", static_oob);
if (static_oob > 0x01) {
BT_ERR("Invalid Static OOB type 0x%02x", static_oob);
goto fail;
}
static_oob = (prov_ctx.static_oob_len ? static_oob : 0x00);
output_size = data[5];
BT_INFO("Output OOB Size: 0x%02x", output_size);
if (output_size > 0x08) {
BT_ERR("Invalid Output OOB size %d", output_size);
goto fail;
}
output_action = sys_get_be16(&data[6]);
BT_INFO("Output OOB Action: 0x%04x", output_action);
if (output_action > 0x1f) {
BT_ERR("Invalid Output OOB action 0x%04x", output_action);
goto fail;
}
/* Provisioner select output action */
if (prov->prov_input_num && output_size) {
output_action = __builtin_ctz(output_action);
} else {
output_size = 0x0;
output_action = 0x0;
}
input_size = data[8];
BT_INFO("Input OOB Size: 0x%02x", input_size);
if (input_size > 0x08) {
BT_ERR("Invalid Input OOB size %d", input_size);
goto fail;
}
input_action = sys_get_be16(&data[9]);
BT_INFO("Input OOB Action: 0x%04x", input_action);
if (input_action > 0x0f) {
BT_ERR("Invalid Input OOB action 0x%04x", input_action);
goto fail;
}
/* Make sure received pdu is ok and cancel the timeout timer */
if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) {
k_delayed_work_cancel(&link[idx].timeout);
}
/* Provisioner select input action */
if (prov->prov_output_num && input_size) {
input_action = __builtin_ctz(input_action);
} else {
input_size = 0x0;
input_action = 0x0;
}
if (static_oob) {
/* if static oob is valid, just use static oob */
auth_method = AUTH_METHOD_STATIC;
auth_action = 0x00;
auth_size = 0x00;
} else {
if (!output_size && !input_size) {
auth_method = AUTH_METHOD_NO_OOB;
auth_action = 0x00;
auth_size = 0x00;
} else if (!output_size && input_size) {
auth_method = AUTH_METHOD_INPUT;
auth_action = (u8_t)input_action;
auth_size = input_size;
} else {
auth_method = AUTH_METHOD_OUTPUT;
auth_action = (u8_t)output_action;
auth_size = output_size;
}
}
/* Store provisioning capabilities value in conf_inputs */
memcpy(&link[idx].conf_inputs[1], data, 11);
prov_buf_init(&buf, PROV_START);
net_buf_simple_add_u8(&buf, prov->prov_algorithm);
net_buf_simple_add_u8(&buf, pub_key_oob);
net_buf_simple_add_u8(&buf, auth_method);
net_buf_simple_add_u8(&buf, auth_action);
net_buf_simple_add_u8(&buf, auth_size);
memcpy(&link[idx].conf_inputs[12], &buf.data[1], 5);
if (prov_send(idx, &buf)) {
BT_ERR("Failed to send Provisioning Start");
goto fail;
}
link[idx].auth_method = auth_method;
link[idx].auth_action = auth_action;
link[idx].auth_size = auth_size;
/** After prov start sent, use OOB to get remote public key.
* And we just follow the procedure in Figure 5.15 of Section
* 5.4.2.3 of Mesh Profile Spec.
*/
if (pub_key_oob) {
if (prov->prov_pub_key_oob_cb(idx)) {
BT_ERR("Failed to notify input OOB Public Key");
goto fail;
}
}
/** If using PB-ADV, need to listen for transaction ack,
* after ack is received, provisioner can send public key.
*/
#if defined(CONFIG_BLE_MESH_PB_ADV)
if (idx < CONFIG_BLE_MESH_PBA_SAME_TIME) {
link[idx].expect_ack_for = PROV_START;
return;
}
#endif /* CONFIG_BLE_MESH_PB_ADV */
send_pub_key(idx, pub_key_oob);
return;
fail:
close_link(idx, CLOSE_REASON_FAILED);
return;
}
static bt_mesh_output_action_t output_action(u8_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;
}
}
static bt_mesh_input_action_t input_action(u8_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 int prov_auth(const u8_t idx, u8_t method, u8_t action, u8_t size)
{
bt_mesh_output_action_t output = 0U;
bt_mesh_input_action_t input = 0U;
link[idx].auth = (u8_t *)bt_mesh_calloc(PROV_AUTH_VAL_SIZE);
if (!link[idx].auth) {
BT_ERR("%s, Out of memory", __func__);
close_link(idx, CLOSE_REASON_FAILED);
return -ENOMEM;
}
switch (method) {
case AUTH_METHOD_NO_OOB:
if (action || size) {
return -EINVAL;
}
memset(link[idx].auth, 0, 16);
return 0;
case AUTH_METHOD_STATIC:
if (action || size) {
return -EINVAL;
}
memcpy(link[idx].auth + 16 - prov_ctx.static_oob_len,
prov_ctx.static_oob_val, prov_ctx.static_oob_len);
memset(link[idx].auth, 0, 16 - prov_ctx.static_oob_len);
return 0;
case AUTH_METHOD_OUTPUT:
/* Use auth_action to get device output action */
output = output_action(action);
if (!output) {
return -EINVAL;
}
return prov->prov_input_num(AUTH_METHOD_OUTPUT, output, size, idx);
case AUTH_METHOD_INPUT:
/* Use auth_action to get device input action */
input = input_action(action);
if (!input) {
return -EINVAL;
}
/* Provisioner ouput number/string and wait for device's Provisioning Input Complete PDU */
link[idx].expect = PROV_INPUT_COMPLETE;
if (input == BLE_MESH_ENTER_STRING) {
unsigned char str[9] = {'\0'};
u8_t j = 0U;
bt_mesh_rand(str, size);
/* Normalize to '0' .. '9' & 'A' .. 'Z' */
for (j = 0U; j < size; j++) {
str[j] %= 36;
if (str[j] < 10) {
str[j] += '0';
} else {
str[j] += 'A' - 10;
}
}
str[size] = '\0';
memcpy(link[idx].auth, str, size);
memset(link[idx].auth + size, 0, PROV_AUTH_VAL_SIZE - size);
return prov->prov_output_num(AUTH_METHOD_INPUT, input, str, size, idx);
} else {
u32_t div[8] = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
u32_t num = 0U;
bt_mesh_rand(&num, sizeof(num));
num %= div[size - 1];
sys_put_be32(num, &link[idx].auth[12]);
memset(link[idx].auth, 0, 12);
return prov->prov_output_num(AUTH_METHOD_INPUT, input, &num, size, idx);
}
default:
return -EINVAL;
}
}
static void send_confirm(const u8_t idx)
{
PROV_BUF(buf, 17);
BT_DBG("ConfInputs[0] %s", bt_hex(link[idx].conf_inputs, 64));
BT_DBG("ConfInputs[64] %s", bt_hex(link[idx].conf_inputs + 64, 64));
BT_DBG("ConfInputs[128] %s", bt_hex(link[idx].conf_inputs + 128, 17));
link[idx].conf_salt = (u8_t *)bt_mesh_calloc(PROV_CONF_SALT_SIZE);
if (!link[idx].conf_salt) {
BT_ERR("%s, Out of memory", __func__);
goto fail;
}
link[idx].conf_key = (u8_t *)bt_mesh_calloc(PROV_CONF_KEY_SIZE);
if (!link[idx].conf_key) {
BT_ERR("%s, Out of memory", __func__);
goto fail;
}
if (bt_mesh_prov_conf_salt(link[idx].conf_inputs, link[idx].conf_salt)) {
BT_ERR("Failed to generate confirmation salt");
goto fail;
}
BT_DBG("ConfirmationSalt: %s", bt_hex(link[idx].conf_salt, 16));
if (bt_mesh_prov_conf_key(link[idx].dhkey, link[idx].conf_salt, link[idx].conf_key)) {
BT_ERR("Failed to generate confirmation key");
goto fail;
}
BT_DBG("ConfirmationKey: %s", bt_hex(link[idx].conf_key, 16));
/** Provisioner use the same random number for each provisioning
* device, if different random need to be used, here provisioner
* should allocate memory for rand and call bt_mesh_rand() every time.
*/
if (!(prov_ctx.rand_gen_done & BIT(0))) {
if (bt_mesh_rand(prov_ctx.random, 16)) {
BT_ERR("Failed to generate random number");
goto fail;
}
link[idx].rand = prov_ctx.random;
prov_ctx.rand_gen_done |= BIT(0);
} else {
/* Provisioner random has already been generated. */
link[idx].rand = prov_ctx.random;
}
BT_DBG("LocalRandom: %s", bt_hex(link[idx].rand, 16));
prov_buf_init(&buf, PROV_CONFIRM);
if (bt_mesh_prov_conf(link[idx].conf_key, link[idx].rand, link[idx].auth,
net_buf_simple_add(&buf, 16))) {
BT_ERR("Failed to generate confirmation value");
goto fail;
}
if (prov_send(idx, &buf)) {
BT_ERR("Failed to send Provisioning Confirm");
goto fail;
}
link[idx].expect = PROV_CONFIRM;
return;
fail:
close_link(idx, CLOSE_REASON_FAILED);
return;
}
int bt_mesh_provisioner_set_oob_input_data(const u8_t idx, const u8_t *val, bool num_flag)
{
/** This function should be called in the prov_input_num
* callback, after the data output by device has been
* input by provisioner.
* Paramter size is used to indicate the length of data
* indicated by Pointer val, for example, if device output
* data is 12345678(decimal), the data in auth value will
* be 0xBC614E.
* Parameter num_flag is used to indicate whether the value
* input by provisioner is number or string.
*/
if (!link[idx].auth) {
BT_ERR("Invalid link auth");
return -EINVAL;
}
BT_INFO("Link idx %d, type %s", idx, num_flag ? "number" : "string");
memset(link[idx].auth, 0, 16);
if (num_flag) {
/* Provisioner inputs number */
memcpy(link[idx].auth + 12, val, sizeof(u32_t));
} else {
/* Provisioner inputs string */
memcpy(link[idx].auth, val, link[idx].auth_size);
}
send_confirm(idx);
return 0;
}
int bt_mesh_provisioner_set_oob_output_data(const u8_t idx, const u8_t *num,
u8_t size, bool num_flag)
{
/** This function should be called in the prov_output_num
* callback, after the data has been output by provisioner.
* Parameter size is used to indicate the length of data
* indicated by Pointer num, for example, if provisioner
* output data is 12345678(decimal), the data in auth value
* will be 0xBC614E.
* Parameter num_flag is used to indicate whether the value
* output by provisioner is number or string.
*/
if (num == NULL || size > BLE_MESH_PROV_INPUT_OOB_MAX_LEN) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
if (!link[idx].auth) {
BT_ERR("Invalid link auth");
return -EINVAL;
}
BT_INFO("Link idx %d, type %s", idx, num_flag ? "number" : "string");
if (num_flag) {
/* Provisioner output number */
memset(link[idx].auth, 0, 16);
memcpy(link[idx].auth + 16 - size, num, size);
} else {
/* Provisioner output string */
memset(link[idx].auth, 0, 16);
memcpy(link[idx].auth, num, size);
}
link[idx].expect = PROV_INPUT_COMPLETE;
return 0;
}
int bt_mesh_provisioner_read_oob_pub_key(const u8_t idx, const u8_t pub_key_x[32],
const u8_t pub_key_y[32])
{
if (!link[idx].conf_inputs) {
BT_ERR("Invalid link conf_inputs");
return -EINVAL;
}
/* Swap X and Y halves independently to big-endian */
sys_memcpy_swap(&link[idx].conf_inputs[81], pub_key_x, 32);
sys_memcpy_swap(&link[idx].conf_inputs[81] + 32, pub_key_y, 32);
bt_mesh_atomic_set_bit(link[idx].flags, REMOTE_PUB_KEY);
if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, WAIT_GEN_DHKEY)) {
prov_gen_dh_key(idx);
}
return 0;
}
static void prov_dh_key_cb(const u8_t key[32], const u8_t idx)
{
BT_DBG("%p", key);
if (!key) {
BT_ERR("Failed to generate DHKey");
goto fail;
}
link[idx].dhkey = (u8_t *)bt_mesh_calloc(PROV_DH_KEY_SIZE);
if (!link[idx].dhkey) {
BT_ERR("%s, Out of memory", __func__);
goto fail;
}
sys_memcpy_swap(link[idx].dhkey, key, 32);
BT_DBG("DHkey: %s", bt_hex(link[idx].dhkey, 32));
bt_mesh_atomic_set_bit(link[idx].flags, HAVE_DHKEY);
/** After dhkey is generated, if auth_method is No OOB or
* Static OOB, provisioner can start to send confirmation.
* If output OOB is used by the device, provisioner need
* to watch out the output number and input it as auth_val.
* If input OOB is used by the device, provisioner need
* to output a value, and wait for prov input complete pdu.
*/
if (prov_auth(idx, link[idx].auth_method,
link[idx].auth_action, link[idx].auth_size) < 0) {
BT_ERR("Failed to authenticate");
goto fail;
}
if (link[idx].auth_method == AUTH_METHOD_OUTPUT ||
link[idx].auth_method == AUTH_METHOD_INPUT) {
return;
}
if (link[idx].expect != PROV_INPUT_COMPLETE) {
send_confirm(idx);
}
return;
fail:
close_link(idx, CLOSE_REASON_FAILED);
return;
}
static void prov_gen_dh_key(const u8_t idx)
{
u8_t pub_key[64] = {0};
/* Copy device public key in little-endian for bt_mesh_dh_key_gen().
* X and Y halves are swapped independently.
*/
sys_memcpy_swap(&pub_key[0], &link[idx].conf_inputs[81], 32);
sys_memcpy_swap(&pub_key[32], &link[idx].conf_inputs[113], 32);
if (bt_mesh_dh_key_gen(pub_key, prov_dh_key_cb, idx)) {
BT_ERR("Failed to generate DHKey");
close_link(idx, CLOSE_REASON_FAILED);
return;
}
}
static void send_pub_key(const u8_t idx, u8_t oob)
{
PROV_BUF(buf, 65);
const u8_t *key = NULL;
key = bt_mesh_pub_key_get();
if (!key) {
BT_ERR("No public key available");
close_link(idx, CLOSE_REASON_FAILED);
return;
}
BT_DBG("Local Public Key: %s", bt_hex(key, 64));
bt_mesh_atomic_set_bit(link[idx].flags, LOCAL_PUB_KEY);
prov_buf_init(&buf, PROV_PUB_KEY);
/* Swap X and Y halves independently to big-endian */
sys_memcpy_swap(net_buf_simple_add(&buf, 32), key, 32);
sys_memcpy_swap(net_buf_simple_add(&buf, 32), &key[32], 32);
/* Store provisioner public key value in conf_inputs */
memcpy(&link[idx].conf_inputs[17], &buf.data[1], 64);
if (prov_send(idx, &buf)) {
BT_ERR("Failed to send Provisioning Public Key");
close_link(idx, CLOSE_REASON_FAILED);
return;
}
if (!oob) {
link[idx].expect = PROV_PUB_KEY;
} else {
/** Have already got device public key. If next is to
* send confirm(not wait for input complete), need to
* wait for transactiona ack for public key then send
* provisioning confirm pdu.
*/
#if defined(CONFIG_BLE_MESH_PB_ADV)
if (idx < CONFIG_BLE_MESH_PBA_SAME_TIME) {
link[idx].expect_ack_for = PROV_PUB_KEY;
return;
}
#endif /* CONFIG_BLE_MESH_PB_ADV */
/* If remote public key has been read, then start to generate DHkey,
* otherwise wait for device oob public key.
*/
if (bt_mesh_atomic_test_bit(link[idx].flags, REMOTE_PUB_KEY)) {
prov_gen_dh_key(idx);
} else {
bt_mesh_atomic_set_bit(link[idx].flags, WAIT_GEN_DHKEY);
}
}
}
static void prov_pub_key(const u8_t idx, const u8_t *data)
{
BT_DBG("Remote Public Key: %s", bt_hex(data, 64));
/* Make sure received pdu is ok and cancel the timeout timer */
if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) {
k_delayed_work_cancel(&link[idx].timeout);
}
memcpy(&link[idx].conf_inputs[81], data, 64);
if (!bt_mesh_atomic_test_bit(link[idx].flags, LOCAL_PUB_KEY)) {
/* Clear retransmit timer */
#if defined(CONFIG_BLE_MESH_PB_ADV)
prov_clear_tx(idx);
#endif
bt_mesh_atomic_set_bit(link[idx].flags, REMOTE_PUB_KEY);
BT_WARN("Waiting for local public key");
return;
}
prov_gen_dh_key(idx);
}
static void prov_input_complete(const u8_t idx, const u8_t *data)
{
/* Make sure received pdu is ok and cancel the timeout timer */
if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) {
k_delayed_work_cancel(&link[idx].timeout);
}
/* Provisioner receives input complete and send confirm */
send_confirm(idx);
}
static void prov_confirm(const u8_t idx, const u8_t *data)
{
/**
* Zephyr uses PROV_BUF(16). Currently test with PROV_BUF(16)
* and PROV_BUF(17) on branch feature/btdm_ble_mesh_debug both
* work fine.
*/
PROV_BUF(buf, 17);
BT_DBG("Remote Confirm: %s", bt_hex(data, 16));
/* Make sure received pdu is ok and cancel the timeout timer */
if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) {
k_delayed_work_cancel(&link[idx].timeout);
}
link[idx].conf = (u8_t *)bt_mesh_calloc(PROV_CONFIRM_SIZE);
if (!link[idx].conf) {
BT_ERR("%s, Out of memory", __func__);
close_link(idx, CLOSE_REASON_FAILED);
return;
}
memcpy(link[idx].conf, data, 16);
if (!bt_mesh_atomic_test_bit(link[idx].flags, HAVE_DHKEY)) {
#if defined(CONFIG_BLE_MESH_PB_ADV)
prov_clear_tx(idx);
#endif
bt_mesh_atomic_set_bit(link[idx].flags, SEND_CONFIRM);
}
prov_buf_init(&buf, PROV_RANDOM);
net_buf_simple_add_mem(&buf, link[idx].rand, 16);
if (prov_send(idx, &buf)) {
BT_ERR("Failed to send Provisioning Random");
close_link(idx, CLOSE_REASON_FAILED);
return;
}
link[idx].expect = PROV_RANDOM;
}
static void send_prov_data(const u8_t idx)
{
PROV_BUF(buf, 34);
u16_t prev_addr = BLE_MESH_ADDR_UNASSIGNED;
u16_t max_addr = BLE_MESH_ADDR_UNASSIGNED;
struct bt_mesh_node *node = NULL;
const u8_t *netkey = NULL;
u8_t session_key[16] = {0};
u8_t nonce[13] = {0};
u8_t pdu[25] = {0};
int err = 0;
err = bt_mesh_session_key(link[idx].dhkey, link[idx].prov_salt, session_key);
if (err) {
BT_ERR("Failed to generate session key");
goto fail;
}
BT_DBG("SessionKey: %s", bt_hex(session_key, 16));
err = bt_mesh_prov_nonce(link[idx].dhkey, link[idx].prov_salt, nonce);
if (err) {
BT_ERR("Failed to generate session nonce");
goto fail;
}
BT_DBG("Nonce: %s", bt_hex(nonce, 13));
/* Assign provisioning data for the device. Currently all provisioned devices
* will be added to the primary subnet, and may add an API to choose to which
* subnet will the device be provisioned later.
*/
if (IS_ENABLED(CONFIG_BLE_MESH_FAST_PROV) && FAST_PROV_ENABLE()) {
netkey = bt_mesh_fast_prov_net_key_get(prov_ctx.fast_prov.net_idx);
if (!netkey) {
BT_ERR("No NetKey for fast provisioning");
goto fail;
}
memcpy(pdu, netkey, 16);
sys_put_be16(prov_ctx.fast_prov.net_idx, &pdu[16]);
pdu[18] = prov_ctx.fast_prov.flags;
sys_put_be32(prov_ctx.fast_prov.iv_index, &pdu[19]);
} else {
netkey = bt_mesh_provisioner_net_key_get(prov_ctx.curr_net_idx);
if (!netkey) {
BT_ERR("No NetKey for provisioning data");
goto fail;
}
memcpy(pdu, netkey, 16);
sys_put_be16(prov_ctx.curr_net_idx, &pdu[16]);
pdu[18] = prov_ctx.curr_flags;
sys_put_be32(prov_ctx.curr_iv_index, &pdu[19]);
}
/**
* The Provisioner must not reuse unicast addresses that have been
* allocated to a device and sent in a Provisioning Data PDU until
* the Provisioner receives an Unprovisioned Device beacon or
* Service Data for the Mesh Provisioning Service from that same
* device, identified using the Device UUID of the device.
*/
/* Check if this device is a re-provisioned device */
node = bt_mesh_provisioner_get_node_with_uuid(link[idx].uuid);
if (node) {
if (link[idx].element_num <= node->element_num) {
/**
* If the device is provisioned before, but the element number of
* the device is bigger now, then we treat it as a new device.
*/
prev_addr = node->unicast_addr;
}
bt_mesh_provisioner_remove_node(link[idx].uuid);
}
max_addr = FAST_PROV_ENABLE() ? prov_ctx.fast_prov.unicast_addr_max : PROV_MAX_ADDR_TO_ASSIGN;
if (BLE_MESH_ADDR_IS_UNICAST(prev_addr)) {
sys_put_be16(prev_addr, &pdu[23]);
link[idx].unicast_addr = prev_addr;
} else {
u16_t alloc_addr = BLE_MESH_ADDR_UNASSIGNED;
if (BLE_MESH_ADDR_IS_UNICAST(link[idx].assign_addr)) {
alloc_addr = link[idx].assign_addr;
} else {
/* If this device to be provisioned is a new device */
if (prov_ctx.curr_alloc_addr == BLE_MESH_ADDR_UNASSIGNED) {
BT_ERR("Not enough unicast address to be allocated");
goto fail;
}
alloc_addr = prov_ctx.curr_alloc_addr;
}
if (alloc_addr + link[idx].element_num - 1 > max_addr) {
BT_ERR("Not enough unicast address for the device");
goto fail;
}
/* Make sure the assigned unicast address is not identical with any unicast
* address of other nodes. And make sure the address is not identical with
* any unicast address of Provisioner.
*/
if (bt_mesh_provisioner_check_is_addr_dup(alloc_addr, link[idx].element_num, true)) {
BT_ERR("Duplicate assigned address 0x%04x", alloc_addr);
goto fail;
}
sys_put_be16(alloc_addr, &pdu[23]);
link[idx].unicast_addr = alloc_addr;
}
prov_buf_init(&buf, PROV_DATA);
err = bt_mesh_prov_encrypt(session_key, nonce, pdu, net_buf_simple_add(&buf, 33));
if (err) {
BT_ERR("Failed to encrypt provisioning data");
goto fail;
}
if (prov_send(idx, &buf)) {
BT_ERR("Failed to send Provisioning Data");
goto fail;
}
/**
* We update the next unicast address to be allocated here because if
* Provisioner is provisioning two devices at the same time, we need
* to assign the unicast address for them correctly. Hence we should
* not update the prov_ctx.curr_alloc_addr after the proper provisioning
* complete pdu is received.
*/
if (!BLE_MESH_ADDR_IS_UNICAST(prev_addr)) {
if (BLE_MESH_ADDR_IS_UNICAST(link[idx].assign_addr)) {
/* Even if the unicast address of the node is assigned by the
* application, we will also update the prov_ctx.curr_alloc_addr
* here, in case Users use the two methods together (i.e. allocate
* the unicast address for the node internally and assign the
* unicast address for the node from application).
*/
if (prov_ctx.curr_alloc_addr < link[idx].assign_addr + link[idx].element_num) {
prov_ctx.curr_alloc_addr = link[idx].assign_addr + link[idx].element_num;
}
} else {
prov_ctx.curr_alloc_addr += link[idx].element_num;
if (prov_ctx.curr_alloc_addr > max_addr) {
/* No unicast address will be used for further provisioning */
prov_ctx.curr_alloc_addr = BLE_MESH_ADDR_UNASSIGNED;
}
}
/* Store the available unicast address range to flash */
if (IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) {
bt_mesh_store_prov_info(prov_ctx.primary_addr, prov_ctx.curr_alloc_addr);
}
}
if (IS_ENABLED(CONFIG_BLE_MESH_FAST_PROV) && FAST_PROV_ENABLE()) {
link[idx].ki_flags = prov_ctx.fast_prov.flags;
link[idx].iv_index = prov_ctx.fast_prov.iv_index;
} else {
link[idx].ki_flags = prov_ctx.curr_flags;
link[idx].iv_index = prov_ctx.curr_iv_index;
}
link[idx].expect = PROV_COMPLETE;
return;
fail:
close_link(idx, CLOSE_REASON_FAILED);
return;
}
static void prov_random(const u8_t idx, const u8_t *data)
{
u8_t conf_verify[16] = {0};
BT_DBG("Remote Random: %s", bt_hex(data, 16));
if (bt_mesh_prov_conf(link[idx].conf_key, data, link[idx].auth, conf_verify)) {
BT_ERR("Failed to calculate confirmation verification");
goto fail;
}
if (memcmp(conf_verify, link[idx].conf, 16)) {
BT_ERR("Invalid confirmation value");
BT_DBG("Received: %s", bt_hex(link[idx].conf, 16));
BT_DBG("Calculated: %s", bt_hex(conf_verify, 16));
goto fail;
}
/*Verify received confirm is ok and cancel the timeout timer */
if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) {
k_delayed_work_cancel(&link[idx].timeout);
}
/** After provisioner receives provisioning random from device,
* and successfully check the confirmation, the following
* should be done:
* 1. bt_mesh_calloc memory for prov_salt
* 2. calculate prov_salt
* 3. prepare provisioning data and send
*/
link[idx].prov_salt = (u8_t *)bt_mesh_calloc(PROV_PROV_SALT_SIZE);
if (!link[idx].prov_salt) {
BT_ERR("%s, Out of memory", __func__);
goto fail;
}
if (bt_mesh_prov_salt(link[idx].conf_salt, link[idx].rand, data,
link[idx].prov_salt)) {
BT_ERR("Failed to generate ProvisioningSalt");
goto fail;
}
BT_DBG("ProvisioningSalt: %s", bt_hex(link[idx].prov_salt, 16));
send_prov_data(idx);
return;
fail:
close_link(idx, CLOSE_REASON_FAILED);
return;
}
static void prov_complete(const u8_t idx, const u8_t *data)
{
u8_t device_key[16] = {0};
u16_t net_idx = 0U;
u16_t index = 0U;
u16_t rm = 0U;
int err = 0;
/* Make sure received pdu is ok and cancel the timeout timer */
if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) {
k_delayed_work_cancel(&link[idx].timeout);
}
err = bt_mesh_dev_key(link[idx].dhkey, link[idx].prov_salt, device_key);
if (err) {
BT_ERR("Failed to generate device key");
close_link(idx, CLOSE_REASON_FAILED);
return;
}
if (IS_ENABLED(CONFIG_BLE_MESH_FAST_PROV) && FAST_PROV_ENABLE()) {
net_idx = prov_ctx.fast_prov.net_idx;
} else {
net_idx = prov_ctx.curr_net_idx;
}
err = bt_mesh_provisioner_provision(&link[idx].addr, link[idx].uuid, link[idx].oob_info,
link[idx].unicast_addr, link[idx].element_num, net_idx,
link[idx].ki_flags, link[idx].iv_index, device_key, &index);
if (err) {
BT_ERR("Failed to store node info");
close_link(idx, CLOSE_REASON_FAILED);
return;
}
if (prov->prov_complete) {
prov->prov_complete(index, link[idx].uuid, link[idx].unicast_addr,
link[idx].element_num, net_idx);
}
err = provisioner_dev_find(&link[idx].addr, link[idx].uuid, &rm);
if (!err) {
if (unprov_dev[rm].flags & RM_AFTER_PROV) {
memset(&unprov_dev[rm], 0, sizeof(struct unprov_dev_queue));
}
} else if (err == -ENODEV) {
BT_DBG("Device not found in queue");
} else {
BT_ERR("Failed to remove device from queue");
}
close_link(idx, CLOSE_REASON_SUCCESS);
}
static void prov_failed(const u8_t idx, const u8_t *data)
{
BT_WARN("Error 0x%02x", data[0]);
close_link(idx, CLOSE_REASON_FAILED);
}
static const struct {
void (*func)(const u8_t idx, const u8_t *data);
u16_t len;
} prov_handlers[] = {
{ prov_invite, 1 },
{ prov_capabilities, 11 },
{ prov_start, 5 },
{ prov_pub_key, 64 },
{ prov_input_complete, 0 },
{ prov_confirm, 16 },
{ prov_random, 16 },
{ prov_data, 33 },
{ prov_complete, 0 },
{ prov_failed, 1 },
};
static void close_link(const u8_t idx, u8_t reason)
{
#if defined(CONFIG_BLE_MESH_PB_ADV)
if (idx < CONFIG_BLE_MESH_PBA_SAME_TIME) {
bearer_ctl_send(idx, LINK_CLOSE, &reason, sizeof(reason));
return;
}
#endif
#if defined(CONFIG_BLE_MESH_PB_GATT)
if (idx < BLE_MESH_PROV_SAME_TIME
#if defined(CONFIG_BLE_MESH_PB_ADV)
&& idx >= CONFIG_BLE_MESH_PBA_SAME_TIME
#endif
) {
if (link[idx].conn) {
bt_mesh_gattc_disconnect(link[idx].conn);
}
return;
}
#endif
BT_ERR("Invalid link idx %d", idx);
return;
}
static void prov_timeout(struct k_work *work)
{
u8_t idx = (u8_t)work->index;
BT_WARN("%s", __func__);
close_link(idx, CLOSE_REASON_TIMEOUT);
}
#if defined(CONFIG_BLE_MESH_PB_ADV)
static void prov_retransmit(struct k_work *work)
{
s64_t timeout = TRANSACTION_TIMEOUT;
u8_t idx = (u8_t)work->index;
int i;
BT_DBG("%s", __func__);
if (!bt_mesh_atomic_test_bit(link[idx].flags, LINK_ACTIVE)) {
BT_WARN("Link is not active");
return;
}
#if defined(CONFIG_BLE_MESH_FAST_PROV)
if (link[idx].tx_pdu_type >= PROV_DATA) {
timeout = K_SECONDS(30);
}
#endif
if (k_uptime_get() - link[idx].tx.start > timeout) {
BT_WARN("Provisioner timeout, giving up transaction");
reset_link(idx, CLOSE_REASON_TIMEOUT);
return;
}
if (link[idx].send_link_close & BIT(0)) {
u8_t reason = (link[idx].send_link_close >> 1) & BIT_MASK(2);
u16_t count = (link[idx].send_link_close >> 3);
if (count >= 2) {
reset_link(idx, reason);
return;
}
link[idx].send_link_close += BIT(3);
}
bt_mesh_pb_buf_lock();
for (i = 0; i < ARRAY_SIZE(link[idx].tx.buf); i++) {
struct net_buf *buf = link[idx].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[idx].tx.buf) && link[idx].tx.buf[i + 1]) {
bt_mesh_adv_send(buf, NULL, NULL);
} else {
bt_mesh_adv_send(buf, &buf_sent_cb, (void *)(int)idx);
}
}
bt_mesh_pb_buf_unlock();
}
static void link_ack(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf)
{
BT_DBG("len %u", buf->len);
if (buf->len) {
BT_ERR("Invalid Link ACK length %d", buf->len);
close_link(idx, CLOSE_REASON_FAILED);
return;
}
if (link[idx].expect == PROV_CAPABILITIES) {
BT_INFO("Link ACK is already received");
return;
}
link[idx].conf_inputs = (u8_t *)bt_mesh_calloc(PROV_CONF_INPUTS_SIZE);
if (!link[idx].conf_inputs) {
BT_ERR("%s, Out of memory", __func__);
close_link(idx, CLOSE_REASON_FAILED);
return;
}
send_invite(idx);
}
static void link_close(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf)
{
u8_t reason = 0U;
BT_DBG("len %u", buf->len);
reason = net_buf_simple_pull_u8(buf);
reset_link(idx, reason);
}
static void gen_prov_ctl(const u8_t idx, 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:
break;
case LINK_ACK:
if (!bt_mesh_atomic_test_bit(link[idx].flags, LINK_ACTIVE)) {
return;
}
link_ack(idx, rx, buf);
break;
case LINK_CLOSE:
if (!bt_mesh_atomic_test_bit(link[idx].flags, LINK_ACTIVE)) {
return;
}
link_close(idx, rx, buf);
break;
default:
BT_ERR("Unknown bearer opcode 0x%02x", BEARER_CTL(rx->gpc));
return;
}
}
static void prov_msg_recv(const u8_t idx)
{
u8_t type = link[idx].rx.buf->data[0];
BT_DBG("type 0x%02x len %u", type, link[idx].rx.buf->len);
/**
* Provisioner first checks information within the received
* Provisioning PDU. If the check succeeds then check fcs.
*/
if (type != PROV_FAILED && type != link[idx].expect) {
BT_ERR("Unexpected msg 0x%02x != 0x%02x", type, link[idx].expect);
goto fail;
}
if (type >= 0x0A) {
BT_ERR("Unknown provisioning PDU type 0x%02x", type);
goto fail;
}
if (1 + prov_handlers[type].len != link[idx].rx.buf->len) {
BT_ERR("Invalid length %u for type 0x%02x", link[idx].rx.buf->len, type);
goto fail;
}
if (!bt_mesh_fcs_check(link[idx].rx.buf, link[idx].rx.fcs)) {
BT_ERR("Incorrect FCS");
goto fail;
}
gen_prov_ack_send(idx, link[idx].rx.trans_id);
link[idx].rx.prev_id = link[idx].rx.trans_id;
link[idx].rx.trans_id = 0;
prov_handlers[type].func(idx, &link[idx].rx.buf->data[1]);
return;
fail:
close_link(idx, CLOSE_REASON_FAILED);
return;
}
static void gen_prov_cont(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf)
{
u8_t seg = CONT_SEG_INDEX(rx->gpc);
BT_DBG("len %u, seg_index %u", buf->len, seg);
if (!link[idx].rx.seg && link[idx].rx.prev_id == rx->xact_id) {
BT_INFO("Resending ack");
gen_prov_ack_send(idx, rx->xact_id);
return;
}
if (rx->xact_id != link[idx].rx.trans_id) {
BT_WARN("Data for unknown transaction (%u != %u)",
rx->xact_id, link[idx].rx.trans_id);
return;
}
if (seg > link[idx].rx.last_seg) {
BT_ERR("Invalid segment index %u", seg);
goto fail;
} else if (seg == link[idx].rx.last_seg) {
u8_t expect_len = 0U;
expect_len = (link[idx].rx.buf->len - 20 -
(23 * (link[idx].rx.last_seg - 1)));
if (expect_len != buf->len) {
BT_ERR("Incorrect last seg len: %u != %u",
expect_len, buf->len);
goto fail;
}
}
if (!(link[idx].rx.seg & BIT(seg))) {
BT_INFO("Ignore already received segment");
return;
}
memcpy(XACT_SEG_DATA(idx, seg), buf->data, buf->len);
XACT_SEG_RECV(idx, seg);
if (!link[idx].rx.seg) {
prov_msg_recv(idx);
}
return;
fail:
close_link(idx, CLOSE_REASON_FAILED);
return;
}
static void gen_prov_ack(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf)
{
u8_t ack_type = 0U, pub_key_oob = 0U;
BT_DBG("len %u", buf->len);
if (!link[idx].tx.buf[0]) {
return;
}
if (!link[idx].tx.trans_id) {
return;
}
if (rx->xact_id == (link[idx].tx.trans_id - 1)) {
prov_clear_tx(idx);
ack_type = link[idx].expect_ack_for;
switch (ack_type) {
case PROV_START:
pub_key_oob = link[idx].conf_inputs[13];
send_pub_key(idx, pub_key_oob);
break;
case PROV_PUB_KEY:
prov_gen_dh_key(idx);
break;
default:
break;
}
link[idx].expect_ack_for = 0x00;
}
}
static void gen_prov_start(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf)
{
if (link[idx].rx.seg) {
BT_INFO("Get Start while there are unreceived segments");
return;
}
if (link[idx].rx.prev_id == rx->xact_id) {
BT_INFO("Resending ack");
gen_prov_ack_send(idx, rx->xact_id);
return;
}
link[idx].rx.buf->len = net_buf_simple_pull_be16(buf);
link[idx].rx.trans_id = rx->xact_id;
link[idx].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[idx].rx.buf->len, link[idx].rx.fcs);
/* Provisioner can not receive zero-length provisioning pdu */
if (link[idx].rx.buf->len < 1) {
BT_ERR("Ignoring zero-length provisioning PDU");
close_link(idx, CLOSE_REASON_FAILED);
return;
}
if (link[idx].rx.buf->len > link[idx].rx.buf->size) {
BT_ERR("Too large provisioning PDU (%u bytes)",
link[idx].rx.buf->len);
close_link(idx, CLOSE_REASON_FAILED);
return;
}
if (START_LAST_SEG(rx->gpc) > 0 && link[idx].rx.buf->len <= 20) {
BT_ERR("Too small total length for multi-segment PDU");
close_link(idx, CLOSE_REASON_FAILED);
return;
}
link[idx].rx.seg = (1 << (START_LAST_SEG(rx->gpc) + 1)) - 1;
link[idx].rx.last_seg = START_LAST_SEG(rx->gpc);
memcpy(link[idx].rx.buf->data, buf->data, buf->len);
XACT_SEG_RECV(idx, 0);
if (!link[idx].rx.seg) {
prov_msg_recv(idx);
}
}
static const struct {
void (*const func)(const u8_t idx, struct prov_rx *rx, struct net_buf_simple *buf);
const u8_t require_link;
const u8_t min_len;
} gen_prov[] = {
{ gen_prov_start, true, 3 },
{ gen_prov_ack, true, 0 },
{ gen_prov_cont, true, 0 },
{ gen_prov_ctl, true, 0 },
};
static void gen_prov_recv(const u8_t idx, 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));
close_link(idx, CLOSE_REASON_FAILED);
return;
}
/**
* require_link can be used combining with link[].linking flag to
* set LINK_ACTIVE status after Link ACK is received. In this case
* there is no need to check LINK_ACTIVE status in find_link().
*/
if (!bt_mesh_atomic_test_bit(link[idx].flags, LINK_ACTIVE) &&
gen_prov[GPCF(rx->gpc)].require_link) {
BT_DBG("Ignoring message that requires active link");
return;
}
gen_prov[GPCF(rx->gpc)].func(idx, rx, buf);
}
static int find_link(u32_t link_id, u8_t *idx)
{
int i;
/* link for PB-ADV is from 0 to CONFIG_BLE_MESH_PBA_SAME_TIME */
for (i = 0; i < CONFIG_BLE_MESH_PBA_SAME_TIME; i++) {
if (bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) {
if (link[i].link_id == link_id) {
if (idx) {
*idx = i;
}
return 0;
}
}
}
return -1;
}
void bt_mesh_provisioner_pb_adv_recv(struct net_buf_simple *buf)
{
struct prov_rx rx = {0};
u8_t idx = 0U;
rx.link_id = net_buf_simple_pull_be32(buf);
if (find_link(rx.link_id, &idx) < 0) {
BT_DBG("Data for unexpected link");
return;
}
if (buf->len < 2) {
BT_ERR("Too short provisioning packet (len %u)", buf->len);
close_link(idx, CLOSE_REASON_FAILED);
return;
}
rx.xact_id = net_buf_simple_pull_u8(buf);
rx.gpc = net_buf_simple_pull_u8(buf);
BT_DBG("link_id 0x%08x xact_id %u", rx.link_id, rx.xact_id);
gen_prov_recv(idx, &rx, buf);
}
#endif /* CONFIG_BLE_MESH_PB_ADV */
#if defined(CONFIG_BLE_MESH_PB_GATT)
static struct bt_mesh_conn *find_conn(struct bt_mesh_conn *conn, u8_t *idx)
{
int i;
/* link for PB-GATT is from CONFIG_BLE_MESH_PBA_SAME_TIME to BLE_MESH_PROV_SAME_TIME */
for (i = CONFIG_BLE_MESH_PBA_SAME_TIME; i < BLE_MESH_PROV_SAME_TIME; i++) {
if (bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) {
if (link[i].conn == conn) {
if (idx) {
*idx = i;
}
return conn;
}
}
}
return NULL;
}
int bt_mesh_provisioner_pb_gatt_recv(struct bt_mesh_conn *conn, struct net_buf_simple *buf)
{
u8_t type = 0U;
u8_t idx = 0U;
BT_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len));
if (!find_conn(conn, &idx)) {
BT_ERR("Data for unexpected connection");
return -ENOTCONN;
}
if (buf->len < 1) {
BT_ERR("Too short provisioning packet (len %u)", buf->len);
goto fail;
}
type = net_buf_simple_pull_u8(buf);
if (type != PROV_FAILED && type != link[idx].expect) {
BT_ERR("Unexpected msg 0x%02x != 0x%02x", type, link[idx].expect);
goto fail;
}
if (type >= 0x0A) {
BT_ERR("Unknown provisioning PDU type 0x%02x", type);
goto fail;
}
if (prov_handlers[type].len != buf->len) {
BT_ERR("Invalid length %u for type 0x%02x", buf->len, type);
goto fail;
}
prov_handlers[type].func(idx, buf->data);
return 0;
fail:
/* Mesh Spec Section 5.4.4 Provisioning errors */
close_link(idx, CLOSE_REASON_FAILED);
return -EINVAL;
}
int bt_mesh_provisioner_set_prov_conn(const u8_t addr[6], struct bt_mesh_conn *conn)
{
int i;
if (!addr || !conn) {
BT_ERR("%s, Invalid parameter", __func__);
return -EINVAL;
}
for (i = CONFIG_BLE_MESH_PBA_SAME_TIME; i < BLE_MESH_PROV_SAME_TIME; i++) {
if (!memcmp(link[i].addr.val, addr, BLE_MESH_ADDR_LEN)) {
link[i].conn = bt_mesh_conn_ref(conn);
return 0;
}
}
BT_ERR("Addr %s not found", bt_hex(addr, BLE_MESH_ADDR_LEN));
return -ENOMEM;
}
int bt_mesh_provisioner_pb_gatt_open(struct bt_mesh_conn *conn, u8_t *addr)
{
u8_t idx = 0U;
int i;
BT_DBG("conn %p", conn);
/**
* Double check if the device is currently being provisioned using PB-ADV.
* Provisioner binds conn with proper device when proxy_prov_connected()
* is invoked, and here after proper GATT procedures are completed, we just
* check if this conn already exists in the proxy servers array.
*/
for (i = CONFIG_BLE_MESH_PBA_SAME_TIME; i < BLE_MESH_PROV_SAME_TIME; i++) {
if (link[i].conn == conn) {
idx = i;
break;
}
}
if (i == BLE_MESH_PROV_SAME_TIME) {
BT_ERR("Link not found");
return -ENOTCONN;
}
#if defined(CONFIG_BLE_MESH_PB_ADV)
for (i = 0; i < CONFIG_BLE_MESH_PBA_SAME_TIME; i++) {
if (bt_mesh_atomic_test_bit(link[i].flags, LINK_ACTIVE)) {
if (!memcmp(link[i].uuid, link[idx].uuid, 16)) {
BT_WARN("Provision using PB-GATT & PB-ADV same time");
close_link(idx, CLOSE_REASON_FAILED);
return -EALREADY;
}
}
}
#endif
bt_mesh_atomic_set_bit(link[idx].flags, LINK_ACTIVE);
link[idx].conn = bt_mesh_conn_ref(conn);
/* May use lcd to indicate starting provisioning each device */
if (prov->prov_link_open) {
prov->prov_link_open(BLE_MESH_PROV_GATT);
}
link[idx].conf_inputs = (u8_t *)bt_mesh_calloc(PROV_CONF_INPUTS_SIZE);
if (!link[idx].conf_inputs) {
/* Disconnect this connection, clear corresponding informations */
BT_ERR("%s, Out of memory", __func__);
close_link(idx, CLOSE_REASON_FAILED);
return -ENOMEM;
}
send_invite(idx);
return 0;
}
int bt_mesh_provisioner_pb_gatt_close(struct bt_mesh_conn *conn, u8_t reason)
{
u8_t idx = 0U;
BT_DBG("conn %p", conn);
if (!find_conn(conn, &idx)) {
BT_ERR("Conn %p not found", conn);
return -ENOTCONN;
}
if (bt_mesh_atomic_test_and_clear_bit(link[idx].flags, TIMEOUT_START)) {
k_delayed_work_cancel(&link[idx].timeout);
}
if (prov->prov_link_close) {
prov->prov_link_close(BLE_MESH_PROV_GATT, reason);
}
prov_memory_free(idx);
memset(&link[idx], 0, offsetof(struct prov_link, timeout));
if (bt_mesh_pub_key_get()) {
bt_mesh_atomic_set_bit(link[idx].flags, LOCAL_PUB_KEY);
}
return 0;
}
#endif /* CONFIG_BLE_MESH_PB_GATT */
int bt_mesh_provisioner_prov_init(const struct bt_mesh_prov *prov_info)
{
const u8_t *key = NULL;
int i;
if (!prov_info) {
BT_ERR("No provisioning context provided");
return -EINVAL;
}
key = bt_mesh_pub_key_get();
if (!key) {
BT_ERR("Failed to generate Public Key");
return -EIO;
}
prov = prov_info;
prov_ctx.primary_addr = BLE_MESH_ADDR_UNASSIGNED;
if (prov->prov_static_oob_val && prov->prov_static_oob_len) {
prov_ctx.static_oob_len = MIN(16, prov->prov_static_oob_len);
memcpy(prov_ctx.static_oob_val, prov->prov_static_oob_val, prov_ctx.static_oob_len);
}
#if defined(CONFIG_BLE_MESH_PB_ADV)
for (i = 0; i < CONFIG_BLE_MESH_PBA_SAME_TIME; i++) {
struct prov_adv_buf *adv = &adv_buf[i];
adv->buf.size = ADV_BUF_SIZE;
adv->buf.__buf = adv_buf_data + (i * ADV_BUF_SIZE);
link[i].pending_ack = XACT_NVAL;
k_delayed_work_init(&link[i].tx.retransmit, prov_retransmit);
link[i].tx.retransmit.work.index = (int)i;
link[i].rx.prev_id = XACT_NVAL;
link[i].rx.buf = bt_mesh_pba_get_buf(i);
}
#endif
for (i = 0; i < BLE_MESH_PROV_SAME_TIME; i++) {
k_delayed_work_init(&link[i].timeout, prov_timeout);
link[i].timeout.work.index = (int)i;
}
#if defined(CONFIG_BLE_MESH_PB_ADV)
bt_mesh_pb_adv_mutex_new();
bt_mesh_pb_buf_mutex_new();
#endif
#if defined(CONFIG_BLE_MESH_PB_GATT)
bt_mesh_pb_gatt_mutex_new();
#endif
return 0;
}
int bt_mesh_provisioner_prov_deinit(bool erase)
{
int i;
if (prov == NULL) {
BT_ERR("No provisioning context provided");
return -EINVAL;
}
#if defined(CONFIG_BLE_MESH_PB_ADV)
for (i = 0; i < CONFIG_BLE_MESH_PBA_SAME_TIME; i++) {
prov_clear_tx(i);
k_delayed_work_free(&link[i].tx.retransmit);
#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN)
/* Remove the link id from exceptional list */
bt_mesh_update_exceptional_list(BLE_MESH_EXCEP_LIST_REMOVE,
BLE_MESH_EXCEP_INFO_MESH_LINK_ID, &link[i].link_id);
#endif /* CONFIG_BLE_MESH_USE_DUPLICATE_SCAN */
}
#endif /* CONFIG_BLE_MESH_PB_ADV */
for (i = 0; i < BLE_MESH_PROV_SAME_TIME; i++) {
prov_memory_free(i);
k_delayed_work_free(&link[i].timeout);
memset(&link[i], 0, sizeof(link[i]));
}
#if defined(CONFIG_BLE_MESH_PB_ADV)
bt_mesh_pb_adv_mutex_free();
bt_mesh_pb_buf_mutex_free();
#endif
#if defined(CONFIG_BLE_MESH_PB_GATT)
bt_mesh_pb_gatt_mutex_free();
#endif
memset(&prov_ctx, 0, sizeof(prov_ctx));
#if defined(CONFIG_BLE_MESH_PB_ADV)
memset(adv_buf, 0, sizeof(adv_buf));
memset(adv_buf_data, 0, sizeof(adv_buf_data));
#endif
memset(unprov_dev, 0, sizeof(unprov_dev));
if (erase && IS_ENABLED(CONFIG_BLE_MESH_SETTINGS)) {
bt_mesh_clear_prov_info();
}
prov = NULL;
return 0;
}
static bool is_unprov_dev_info_callback_to_app(bt_mesh_prov_bearer_t bearer,
const u8_t uuid[16], const bt_mesh_addr_t *addr, u16_t oob_info, s8_t rssi)
{
u16_t index = 0U;
if (prov_ctx.prov_after_match == false) {
u8_t adv_type = (bearer == BLE_MESH_PROV_ADV) ?
BLE_MESH_ADV_NONCONN_IND : BLE_MESH_ADV_IND;
if (provisioner_dev_find(addr, uuid, &index)) {
BT_DBG("Device not in queue, notify to app layer");
if (notify_unprov_adv_pkt_cb) {
notify_unprov_adv_pkt_cb(addr->val, addr->type, adv_type, uuid, oob_info, bearer, rssi);
}
return true;
}
if (!(unprov_dev[index].bearer & bearer)) {
BT_WARN("Device in queue not support PB-%s",
(bearer == BLE_MESH_PROV_ADV) ? "ADV" : "GATT");
if (notify_unprov_adv_pkt_cb) {
notify_unprov_adv_pkt_cb(addr->val, addr->type, adv_type, uuid, oob_info, bearer, rssi);
}
return true;
}
}
return false;
}
void bt_mesh_provisioner_unprov_beacon_recv(struct net_buf_simple *buf, s8_t rssi)
{
#if defined(CONFIG_BLE_MESH_PB_ADV)
const bt_mesh_addr_t *addr = NULL;
const u8_t *uuid = NULL;
u16_t oob_info = 0U;
if (!(prov_ctx.bearers & BLE_MESH_PROV_ADV)) {
BT_WARN("Not support PB-ADV bearer");
return;
}
if (buf->len != 0x12 && buf->len != 0x16) {
BT_ERR("Invalid Unprovisioned Device Beacon length %d", buf->len);
return;
}
addr = bt_mesh_get_unprov_dev_addr();
uuid = buf->data;
net_buf_simple_pull(buf, 16);
/* Mesh beacon uses big-endian to send beacon data */
oob_info = net_buf_simple_pull_be16(buf);
if (provisioner_check_unprov_dev_info(uuid, BLE_MESH_PROV_ADV)) {
return;
}
if (is_unprov_dev_info_callback_to_app(
BLE_MESH_PROV_ADV, uuid, addr, oob_info, rssi)) {
return;
}
provisioner_start_prov_pb_adv(uuid, addr, oob_info, BLE_MESH_ADDR_UNASSIGNED);
#endif /* CONFIG_BLE_MESH_PB_ADV */
}
void bt_mesh_provisioner_prov_adv_recv(struct net_buf_simple *buf,
const bt_mesh_addr_t *addr, s8_t rssi)
{
#if defined(CONFIG_BLE_MESH_PB_GATT)
const u8_t *uuid = NULL;
u16_t oob_info = 0U;
if (!(prov_ctx.bearers & BLE_MESH_PROV_GATT)) {
BT_WARN("Not support PB-GATT bearer");
return;
}
if (bt_mesh_gattc_get_free_conn_count() == 0) {
BT_INFO("BLE connections for mesh reach max limit");
return;
}
uuid = buf->data;
net_buf_simple_pull(buf, 16);
/* Mesh beacon uses big-endian to send beacon data */
oob_info = net_buf_simple_pull_be16(buf);
if (provisioner_check_unprov_dev_info(uuid, BLE_MESH_PROV_GATT)) {
return;
}
if (is_unprov_dev_info_callback_to_app(
BLE_MESH_PROV_GATT, uuid, addr, oob_info, rssi)) {
return;
}
/* Provisioner will copy the device uuid, oob info, etc. into an unused link
* struct, and at this moment the link has not been activated. Even if we
* receive an Unprovisioned Device Beacon and a Connectable Provisioning adv
* pkt from the same device, and store the device info received within each
* adv pkt into two link structs which will has no impact on the provisioning
* of this device, because no matter which link among PB-GATT and PB-ADV is
* activated first, the other one will be dropped finally and the link struct
* occupied by the dropped link will be used by other devices (because the link
* is not activated).
* Use connecting flag to prevent if two devices's adv pkts are both received,
* the previous one info will be replaced by the second one.
*/
provisioner_start_prov_pb_gatt(uuid, addr, oob_info, BLE_MESH_ADDR_UNASSIGNED);
#endif /* CONFIG_BLE_MESH_PB_GATT */
}
#endif /* CONFIG_BLE_MESH_PROVISIONER */