1. BLE Mesh Core * Provisioning: Node Role * Advertising and GATT bearer * Authentication OOB * Provisioning: Provisioner Role * Advertising and GATT bearer * Authentication OOB * Networking * Relay * Segmentation and Reassembly * Key Refresh * IV Update * Proxy Support * Multiple Client Models Run Simultaneously * Support multiple client models send packets to different nodes simultaneously * No blocking between client model and server * NVS Storage * Store Provisioning Data of BLE Mesh Nodes in Flash 2. BLE Mesh Applications * BLE Mesh Node & Provisioner * Node Example * Provisioner Example * Node + Generic OnOff Client Example * Fast Provisioning * Vendor Fast Prov Server Model * Vendor Fast Prov Client Model * Examples * Wi-Fi & BLE Mesh Coexistence * Example * BLE Mesh Console Commands * Example 3. BLE Mesh Models * Foundation Models * Configuration Server Model * Configuration Client Model * Health Server Model * Health Client Model * Generic Client Models * Generic OnOff Client * Generic Level Client * Generic Location Client * Generic Default Transition Timer Client * Generic Power OnOff Client * Generic Power Level Client * Generic Battery Client * Generic Property Client * Generic Server Models * Generic OnOff Server (Example) * Lighting Client Models * Light Lightness Client * Light CTL Client * Light HSL Client * Light xyL Client * Light LC Client * Sensor Client Model * Sensor Client * Time and Scenes Client Models * Time Client * Scene Client * Scheduler Client
411 lines
11 KiB
C
411 lines
11 KiB
C
/* Bluetooth Mesh */
|
|
|
|
/*
|
|
* Copyright (c) 2017 Intel Corporation
|
|
* Additional Copyright (c) 2018 Espressif Systems (Shanghai) PTE LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/queue.h"
|
|
#include "freertos/task.h"
|
|
|
|
#include "osi/thread.h"
|
|
#include "sdkconfig.h"
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_ADV)
|
|
|
|
#include "mesh_util.h"
|
|
#include "mesh_buf.h"
|
|
#include "mesh_bearer_adapt.h"
|
|
#include "mesh_trace.h"
|
|
#include "mesh_hci.h"
|
|
|
|
#include "mesh.h"
|
|
#include "adv.h"
|
|
#include "net.h"
|
|
#include "foundation.h"
|
|
#include "beacon.h"
|
|
#include "prov.h"
|
|
#include "proxy.h"
|
|
|
|
#include "provisioner_prov.h"
|
|
#include "provisioner_proxy.h"
|
|
#include "provisioner_beacon.h"
|
|
|
|
/* Convert from ms to 0.625ms units */
|
|
#define ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5)
|
|
|
|
/* Window and Interval are equal for continuous scanning */
|
|
#define MESH_SCAN_INTERVAL 0x20
|
|
#define MESH_SCAN_WINDOW 0x20
|
|
|
|
/* Pre-5.0 controllers enforce a minimum interval of 100ms
|
|
* whereas 5.0+ controllers can go down to 20ms.
|
|
*/
|
|
#define ADV_INT_DEFAULT_MS 100
|
|
#define ADV_INT_FAST_MS 20
|
|
|
|
#if defined(CONFIG_BT_HOST_CRYPTO)
|
|
#define ADV_STACK_SIZE 1024
|
|
#else
|
|
#define ADV_STACK_SIZE 768
|
|
#endif
|
|
|
|
static xQueueHandle xBleMeshQueue;
|
|
static const bt_mesh_addr_t *dev_addr;
|
|
|
|
static const u8_t adv_type[] = {
|
|
[BLE_MESH_ADV_PROV] = BLE_MESH_DATA_MESH_PROV,
|
|
[BLE_MESH_ADV_DATA] = BLE_MESH_DATA_MESH_MESSAGE,
|
|
[BLE_MESH_ADV_BEACON] = BLE_MESH_DATA_MESH_BEACON,
|
|
[BLE_MESH_ADV_URI] = BLE_MESH_DATA_URI,
|
|
};
|
|
|
|
NET_BUF_POOL_DEFINE(adv_buf_pool, CONFIG_BLE_MESH_ADV_BUF_COUNT + 3 * CONFIG_BLE_MESH_PBA_SAME_TIME,
|
|
BLE_MESH_ADV_DATA_SIZE, BLE_MESH_ADV_USER_DATA_SIZE, NULL);
|
|
|
|
static struct bt_mesh_adv adv_pool[CONFIG_BLE_MESH_ADV_BUF_COUNT + 3 * CONFIG_BLE_MESH_PBA_SAME_TIME];
|
|
|
|
static struct bt_mesh_adv *adv_alloc(int id)
|
|
{
|
|
return &adv_pool[id];
|
|
}
|
|
|
|
static inline void adv_send_start(u16_t duration, int err,
|
|
const struct bt_mesh_send_cb *cb,
|
|
void *cb_data)
|
|
{
|
|
if (cb && cb->start) {
|
|
cb->start(duration, err, cb_data);
|
|
}
|
|
}
|
|
|
|
static inline void adv_send_end(int err, const struct bt_mesh_send_cb *cb,
|
|
void *cb_data)
|
|
{
|
|
if (cb && cb->end) {
|
|
cb->end(err, cb_data);
|
|
}
|
|
}
|
|
|
|
static inline int adv_send(struct net_buf *buf)
|
|
{
|
|
const s32_t adv_int_min = ((bt_mesh_dev.hci_version >= BLE_MESH_HCI_VERSION_5_0) ?
|
|
ADV_INT_FAST_MS : ADV_INT_DEFAULT_MS);
|
|
const struct bt_mesh_send_cb *cb = BLE_MESH_ADV(buf)->cb;
|
|
void *cb_data = BLE_MESH_ADV(buf)->cb_data;
|
|
struct bt_mesh_adv_param param = {0};
|
|
u16_t duration, adv_int;
|
|
struct bt_mesh_adv_data ad = {0};
|
|
int err;
|
|
|
|
adv_int = MAX(adv_int_min,
|
|
BLE_MESH_TRANSMIT_INT(BLE_MESH_ADV(buf)->xmit));
|
|
duration = (BLE_MESH_TRANSMIT_COUNT(BLE_MESH_ADV(buf)->xmit) + 1) *
|
|
(adv_int + 10);
|
|
|
|
BT_DBG("type %u len %u: %s", BLE_MESH_ADV(buf)->type,
|
|
buf->len, bt_hex(buf->data, buf->len));
|
|
BT_DBG("count %u interval %ums duration %ums",
|
|
BLE_MESH_TRANSMIT_COUNT(BLE_MESH_ADV(buf)->xmit) + 1, adv_int,
|
|
duration);
|
|
|
|
ad.type = adv_type[BLE_MESH_ADV(buf)->type];
|
|
ad.data_len = buf->len;
|
|
ad.data = buf->data;
|
|
|
|
param.options = 0U;
|
|
param.interval_min = ADV_SCAN_UNIT(adv_int);
|
|
param.interval_max = param.interval_min;
|
|
|
|
err = bt_le_adv_start(¶m, &ad, 1, NULL, 0);
|
|
net_buf_unref(buf);
|
|
adv_send_start(duration, err, cb, cb_data);
|
|
if (err) {
|
|
BT_ERR("%s, Advertising failed: err %d", __func__, err);
|
|
return err;
|
|
}
|
|
|
|
BT_DBG("Advertising started. Sleeping %u ms", duration);
|
|
|
|
k_sleep(K_MSEC(duration));
|
|
|
|
err = bt_le_adv_stop();
|
|
adv_send_end(err, cb, cb_data);
|
|
if (err) {
|
|
BT_ERR("%s, Stop advertising failed: err %d", __func__, err);
|
|
return 0;
|
|
}
|
|
|
|
BT_DBG("Advertising stopped");
|
|
return 0;
|
|
}
|
|
|
|
static void adv_thread(void *p)
|
|
{
|
|
struct net_buf **buf = NULL;
|
|
bt_mesh_msg_t msg = {0};
|
|
int status;
|
|
|
|
BT_DBG("started");
|
|
|
|
buf = (struct net_buf **)(&msg.arg);
|
|
|
|
while (1) {
|
|
*buf = NULL;
|
|
#if CONFIG_BLE_MESH_NODE
|
|
if (IS_ENABLED(CONFIG_BLE_MESH_PROXY)) {
|
|
xQueueReceive(xBleMeshQueue, &msg, K_NO_WAIT);
|
|
while (!(*buf)) {
|
|
s32_t timeout;
|
|
BT_DBG("Proxy advertising start");
|
|
timeout = bt_mesh_proxy_adv_start();
|
|
BT_DBG("Proxy Advertising up to %d ms", timeout);
|
|
xQueueReceive(xBleMeshQueue, &msg, timeout);
|
|
BT_DBG("Proxy advertising stop");
|
|
bt_mesh_proxy_adv_stop();
|
|
}
|
|
} else {
|
|
xQueueReceive(xBleMeshQueue, &msg, (portTickType)portMAX_DELAY);
|
|
}
|
|
#else
|
|
xQueueReceive(xBleMeshQueue, &msg, (portTickType)portMAX_DELAY);
|
|
#endif
|
|
|
|
if (!(*buf)) {
|
|
continue;
|
|
}
|
|
|
|
/* busy == 0 means this was canceled */
|
|
if (BLE_MESH_ADV(*buf)->busy) {
|
|
BLE_MESH_ADV(*buf)->busy = 0U;
|
|
status = adv_send(*buf);
|
|
if (status) {
|
|
if (xQueueSendToFront(xBleMeshQueue, &msg, K_NO_WAIT) != pdTRUE) {
|
|
BT_ERR("%s, xQueueSendToFront failed", __func__);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Give other threads a chance to run */
|
|
taskYIELD();
|
|
}
|
|
}
|
|
|
|
void bt_mesh_adv_update(void)
|
|
{
|
|
BT_DBG("%s", __func__);
|
|
bt_mesh_msg_t msg = {0};
|
|
bt_mesh_task_post(&msg, 0);
|
|
}
|
|
|
|
struct net_buf *bt_mesh_adv_create_from_pool(struct net_buf_pool *pool,
|
|
bt_mesh_adv_alloc_t get_id,
|
|
enum bt_mesh_adv_type type,
|
|
u8_t xmit, s32_t timeout)
|
|
{
|
|
struct bt_mesh_adv *adv;
|
|
struct net_buf *buf;
|
|
|
|
if (bt_mesh_atomic_test_bit(bt_mesh.flags, BLE_MESH_SUSPENDED)) {
|
|
BT_WARN("Refusing to allocate buffer while suspended");
|
|
return NULL;
|
|
}
|
|
|
|
buf = net_buf_alloc(pool, timeout);
|
|
if (!buf) {
|
|
return NULL;
|
|
}
|
|
|
|
BT_DBG("%s, pool = %p, buf_count = %d, uinit_count = %d", __func__,
|
|
buf->pool, pool->buf_count, pool->uninit_count);
|
|
|
|
adv = get_id(net_buf_id(buf));
|
|
BLE_MESH_ADV(buf) = adv;
|
|
|
|
(void)memset(adv, 0, sizeof(*adv));
|
|
|
|
adv->type = type;
|
|
adv->xmit = xmit;
|
|
|
|
return buf;
|
|
}
|
|
|
|
struct net_buf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit,
|
|
s32_t timeout)
|
|
{
|
|
return bt_mesh_adv_create_from_pool(&adv_buf_pool, adv_alloc, type,
|
|
xmit, timeout);
|
|
}
|
|
|
|
void bt_mesh_task_post(bt_mesh_msg_t *msg, uint32_t timeout)
|
|
{
|
|
BT_DBG("%s", __func__);
|
|
if (xQueueSend(xBleMeshQueue, msg, timeout) != pdTRUE) {
|
|
BT_ERR("%s, Failed to post msg to queue", __func__);
|
|
}
|
|
}
|
|
|
|
void bt_mesh_adv_send(struct net_buf *buf, const struct bt_mesh_send_cb *cb,
|
|
void *cb_data)
|
|
{
|
|
BT_DBG("type 0x%02x len %u: %s", BLE_MESH_ADV(buf)->type, buf->len,
|
|
bt_hex(buf->data, buf->len));
|
|
|
|
BLE_MESH_ADV(buf)->cb = cb;
|
|
BLE_MESH_ADV(buf)->cb_data = cb_data;
|
|
BLE_MESH_ADV(buf)->busy = 1U;
|
|
|
|
bt_mesh_msg_t msg = {0};
|
|
msg.arg = (void *)net_buf_ref(buf);
|
|
bt_mesh_task_post(&msg, portMAX_DELAY);
|
|
}
|
|
|
|
const bt_mesh_addr_t *bt_mesh_pba_get_addr(void)
|
|
{
|
|
return dev_addr;
|
|
}
|
|
|
|
static void bt_mesh_scan_cb(const bt_mesh_addr_t *addr, s8_t rssi,
|
|
u8_t adv_type, struct net_buf_simple *buf)
|
|
{
|
|
#if CONFIG_BLE_MESH_PROVISIONER && CONFIG_BLE_MESH_PB_GATT
|
|
u16_t uuid = 0;
|
|
#endif
|
|
|
|
if (adv_type != BLE_MESH_ADV_NONCONN_IND && adv_type != BLE_MESH_ADV_IND) {
|
|
return;
|
|
}
|
|
|
|
BT_DBG("%s, len %u: %s", __func__, buf->len, bt_hex(buf->data, buf->len));
|
|
|
|
dev_addr = addr;
|
|
|
|
while (buf->len > 1) {
|
|
struct net_buf_simple_state state;
|
|
u8_t len, type;
|
|
|
|
len = net_buf_simple_pull_u8(buf);
|
|
/* Check for early termination */
|
|
if (len == 0U) {
|
|
return;
|
|
}
|
|
|
|
if (len > buf->len) {
|
|
BT_WARN("AD malformed");
|
|
return;
|
|
}
|
|
|
|
net_buf_simple_save(buf, &state);
|
|
|
|
type = net_buf_simple_pull_u8(buf);
|
|
|
|
buf->len = len - 1;
|
|
|
|
#if 0
|
|
/* TODO: Check with BLE Mesh BQB test cases */
|
|
if ((type == BLE_MESH_DATA_MESH_PROV || type == BLE_MESH_DATA_MESH_MESSAGE ||
|
|
type == BLE_MESH_DATA_MESH_BEACON) && (adv_type != BLE_MESH_ADV_NONCONN_IND)) {
|
|
BT_DBG("%s, ignore BLE Mesh packet (type 0x%02x) with adv_type 0x%02x",
|
|
__func__, type, adv_type);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
switch (type) {
|
|
case BLE_MESH_DATA_MESH_MESSAGE:
|
|
bt_mesh_net_recv(buf, rssi, BLE_MESH_NET_IF_ADV);
|
|
break;
|
|
#if CONFIG_BLE_MESH_PB_ADV
|
|
case BLE_MESH_DATA_MESH_PROV:
|
|
#if CONFIG_BLE_MESH_NODE
|
|
if (!bt_mesh_is_provisioner_en()) {
|
|
bt_mesh_pb_adv_recv(buf);
|
|
}
|
|
#endif
|
|
#if CONFIG_BLE_MESH_PROVISIONER
|
|
if (bt_mesh_is_provisioner_en()) {
|
|
provisioner_pb_adv_recv(buf);
|
|
}
|
|
#endif
|
|
break;
|
|
#endif /* CONFIG_BLE_MESH_PB_ADV */
|
|
case BLE_MESH_DATA_MESH_BEACON:
|
|
#if CONFIG_BLE_MESH_NODE
|
|
if (!bt_mesh_is_provisioner_en()) {
|
|
bt_mesh_beacon_recv(buf);
|
|
}
|
|
#endif
|
|
#if CONFIG_BLE_MESH_PROVISIONER
|
|
if (bt_mesh_is_provisioner_en()) {
|
|
provisioner_beacon_recv(buf);
|
|
}
|
|
#endif
|
|
break;
|
|
#if CONFIG_BLE_MESH_PROVISIONER && CONFIG_BLE_MESH_PB_GATT
|
|
case BLE_MESH_DATA_FLAGS:
|
|
if (bt_mesh_is_provisioner_en()) {
|
|
if (!provisioner_flags_match(buf)) {
|
|
BT_DBG("Flags mismatch, ignore this adv pkt");
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case BLE_MESH_DATA_UUID16_ALL:
|
|
if (bt_mesh_is_provisioner_en()) {
|
|
uuid = provisioner_srv_uuid_recv(buf);
|
|
if (!uuid) {
|
|
BT_DBG("Service UUID mismatch, ignore this adv pkt");
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case BLE_MESH_DATA_SVC_DATA16:
|
|
if (bt_mesh_is_provisioner_en()) {
|
|
provisioner_srv_data_recv(buf, addr, uuid);
|
|
}
|
|
break;
|
|
#endif /* CONFIG_BLE_MESH_PROVISIONER && CONFIG_BLE_MESH_PB_GATT */
|
|
default:
|
|
break;
|
|
}
|
|
|
|
net_buf_simple_restore(buf, &state);
|
|
net_buf_simple_pull(buf, len);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void bt_mesh_adv_init(void)
|
|
{
|
|
xBleMeshQueue = xQueueCreate(150, sizeof(bt_mesh_msg_t));
|
|
xTaskCreatePinnedToCore(adv_thread, "BLE_Mesh_ADV_Task", 3072, NULL,
|
|
configMAX_PRIORITIES - 7, NULL, TASK_PINNED_TO_CORE);
|
|
}
|
|
|
|
int bt_mesh_scan_enable(void)
|
|
{
|
|
struct bt_mesh_scan_param scan_param = {
|
|
.type = BLE_MESH_SCAN_PASSIVE,
|
|
#if defined(CONFIG_BLE_MESH_USE_DUPLICATE_SCAN)
|
|
.filter_dup = BLE_MESH_SCAN_FILTER_DUP_ENABLE,
|
|
#else
|
|
.filter_dup = BLE_MESH_SCAN_FILTER_DUP_DISABLE,
|
|
#endif
|
|
.interval = MESH_SCAN_INTERVAL,
|
|
.window = MESH_SCAN_WINDOW
|
|
};
|
|
|
|
BT_DBG("%s", __func__);
|
|
|
|
return bt_le_scan_start(&scan_param, bt_mesh_scan_cb);
|
|
}
|
|
|
|
int bt_mesh_scan_disable(void)
|
|
{
|
|
BT_DBG("%s", __func__);
|
|
|
|
return bt_le_scan_stop();
|
|
}
|