1. Add an API to set Provisioner static oob value 2. Add an API to deinit BLE Mesh stack 3. Add an API to set Provisioner unicast address 4. Add an API to provision devices with fixed address 5. Add an API to store node composition data 6. Add an API to get node with device uuid 7. Add an API to get node with unicast address 8. Add an API to delete node with device uuid 9. Add an API to delete node with unicast address 10. Add an API for Provisioner to update local AppKey 11. Add an API for Provisioner to update local NetKey 12. Support Provisioner persistent functionality 13. Fix Provisioner entering IV Update procedure 14. Fix an issue which may cause client failing to send msg 15. Use bt_mesh.flags to indicate device role 16. Remove several useless macros 17. Callback RSSI of received mesh provisioning packets 18. Modify the Provisioner disable function 19. Change some log level from debug to info 20. Add parameters to Provisioner bind AppKey completion event 21. Fix node ignoring relay messages issue 22. Support using a specific partition for BLE Mesh 23. Fix compile warning when proxy related macros are disabled 24. Clean up BLE Mesh stack included header files 25. NULL can be input if client message needs no parameters 26. Fix compile warning when BT log is disabled 27. Initilize BLE Mesh stack local variables 28. Support using PSRAM for BLE Mesh mutex, queue and task 29. Add a menuconfig option to enable using memory from PSRAM 30. Clean up sdkconfig.defaults of BLE Mesh examples
516 lines
14 KiB
C
516 lines
14 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 <string.h>
|
|
#include <errno.h>
|
|
|
|
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLE_MESH_DEBUG_MODEL)
|
|
|
|
#include "foundation.h"
|
|
#include "mesh_common.h"
|
|
#include "health_cli.h"
|
|
|
|
#include "btc_ble_mesh_health_model.h"
|
|
|
|
s32_t health_msg_timeout;
|
|
|
|
static bt_mesh_health_client_t *health_cli;
|
|
|
|
static const bt_mesh_client_op_pair_t health_op_pair[] = {
|
|
{ OP_HEALTH_FAULT_GET, OP_HEALTH_FAULT_STATUS },
|
|
{ OP_HEALTH_FAULT_CLEAR, OP_HEALTH_FAULT_STATUS },
|
|
{ OP_HEALTH_FAULT_TEST, OP_HEALTH_FAULT_STATUS },
|
|
{ OP_HEALTH_PERIOD_GET, OP_HEALTH_PERIOD_STATUS },
|
|
{ OP_HEALTH_PERIOD_SET, OP_HEALTH_PERIOD_STATUS },
|
|
{ OP_ATTENTION_GET, OP_ATTENTION_STATUS },
|
|
{ OP_ATTENTION_SET, OP_ATTENTION_STATUS },
|
|
};
|
|
|
|
static bt_mesh_mutex_t health_client_lock;
|
|
|
|
static void bt_mesh_health_client_mutex_new(void)
|
|
{
|
|
if (!health_client_lock.mutex) {
|
|
bt_mesh_mutex_create(&health_client_lock);
|
|
}
|
|
}
|
|
|
|
static void bt_mesh_health_client_mutex_free(void)
|
|
{
|
|
bt_mesh_mutex_free(&health_client_lock);
|
|
}
|
|
|
|
static void bt_mesh_health_client_lock(void)
|
|
{
|
|
bt_mesh_mutex_lock(&health_client_lock);
|
|
}
|
|
|
|
static void bt_mesh_health_client_unlock(void)
|
|
{
|
|
bt_mesh_mutex_unlock(&health_client_lock);
|
|
}
|
|
|
|
static void timeout_handler(struct k_work *work)
|
|
{
|
|
struct k_delayed_work *timer = NULL;
|
|
bt_mesh_client_node_t *node = NULL;
|
|
struct bt_mesh_msg_ctx ctx = {0};
|
|
u32_t opcode = 0U;
|
|
|
|
BT_WARN("Receive health status message timeout");
|
|
|
|
bt_mesh_health_client_lock();
|
|
|
|
timer = CONTAINER_OF(work, struct k_delayed_work, work);
|
|
|
|
if (timer && !k_delayed_work_free(timer)) {
|
|
node = CONTAINER_OF(work, bt_mesh_client_node_t, timer.work);
|
|
if (node) {
|
|
memcpy(&ctx, &node->ctx, sizeof(ctx));
|
|
opcode = node->opcode;
|
|
bt_mesh_client_free_node(node);
|
|
bt_mesh_health_client_cb_evt_to_btc(
|
|
opcode, BTC_BLE_MESH_EVT_HEALTH_CLIENT_TIMEOUT, ctx.model, &ctx, NULL, 0);
|
|
}
|
|
}
|
|
|
|
bt_mesh_health_client_unlock();
|
|
|
|
return;
|
|
}
|
|
|
|
static void health_client_cancel(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
void *status, size_t len)
|
|
{
|
|
bt_mesh_client_node_t *node = NULL;
|
|
struct net_buf_simple buf = {0};
|
|
u8_t evt_type = 0xFF;
|
|
|
|
if (!model || !ctx || !status || !len) {
|
|
BT_ERR("%s, Invalid parameter", __func__);
|
|
return;
|
|
}
|
|
|
|
/* If it is a publish message, sent to the user directly. */
|
|
buf.data = (u8_t *)status;
|
|
buf.len = (u16_t)len;
|
|
|
|
bt_mesh_health_client_lock();
|
|
|
|
node = bt_mesh_is_client_recv_publish_msg(model, ctx, &buf, true);
|
|
if (!node) {
|
|
BT_DBG("Unexpected health status message 0x%x", ctx->recv_op);
|
|
} else {
|
|
switch (node->opcode) {
|
|
case OP_HEALTH_FAULT_GET:
|
|
case OP_HEALTH_PERIOD_GET:
|
|
case OP_ATTENTION_GET:
|
|
evt_type = BTC_BLE_MESH_EVT_HEALTH_CLIENT_GET_STATE;
|
|
break;
|
|
case OP_HEALTH_FAULT_CLEAR:
|
|
case OP_HEALTH_FAULT_TEST:
|
|
case OP_HEALTH_PERIOD_SET:
|
|
case OP_ATTENTION_SET:
|
|
evt_type = BTC_BLE_MESH_EVT_HEALTH_CLIENT_SET_STATE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!k_delayed_work_free(&node->timer)) {
|
|
u32_t opcode = node->opcode;
|
|
bt_mesh_client_free_node(node);
|
|
bt_mesh_health_client_cb_evt_to_btc(
|
|
opcode, evt_type, model, ctx, (const u8_t *)status, len);
|
|
}
|
|
}
|
|
|
|
bt_mesh_health_client_unlock();
|
|
|
|
switch (ctx->recv_op) {
|
|
case OP_HEALTH_FAULT_STATUS: {
|
|
struct bt_mesh_health_fault_status *val;
|
|
val = (struct bt_mesh_health_fault_status *)status;
|
|
bt_mesh_free_buf(val->fault_array);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void health_fault_status(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_health_fault_status status = {0};
|
|
|
|
BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
|
|
ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
|
|
bt_hex(buf->data, buf->len));
|
|
|
|
status.test_id = net_buf_simple_pull_u8(buf);
|
|
status.cid = net_buf_simple_pull_le16(buf);
|
|
status.fault_array = bt_mesh_alloc_buf(buf->len);
|
|
if (!status.fault_array) {
|
|
BT_ERR("%s, Failed to allocate memory", __func__);
|
|
return;
|
|
}
|
|
|
|
net_buf_simple_add_mem(status.fault_array, buf->data, buf->len);
|
|
|
|
health_client_cancel(model, ctx, &status, sizeof(struct bt_mesh_health_fault_status));
|
|
}
|
|
|
|
static void health_current_status(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
bt_mesh_client_node_t *node = NULL;
|
|
u8_t test_id = 0U;
|
|
u16_t cid = 0U;
|
|
|
|
BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
|
|
ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
|
|
bt_hex(buf->data, buf->len));
|
|
|
|
/* Health current status is a publish message, sent to the user directly. */
|
|
if (!(node = bt_mesh_is_client_recv_publish_msg(model, ctx, buf, true))) {
|
|
return;
|
|
}
|
|
|
|
test_id = net_buf_simple_pull_u8(buf);
|
|
cid = net_buf_simple_pull_le16(buf);
|
|
|
|
BT_DBG("Test ID 0x%02x Company ID 0x%04x Fault Count %u",
|
|
test_id, cid, buf->len);
|
|
|
|
((void) test_id);
|
|
((void) cid);
|
|
}
|
|
|
|
static void health_period_status(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
u8_t status = 0U;
|
|
|
|
BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
|
|
ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
|
|
bt_hex(buf->data, buf->len));
|
|
|
|
status = net_buf_simple_pull_u8(buf);
|
|
|
|
health_client_cancel(model, ctx, &status, sizeof(u8_t));
|
|
}
|
|
|
|
static void health_attention_status(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
u8_t status = 0U;
|
|
|
|
BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
|
|
ctx->net_idx, ctx->app_idx, ctx->addr, buf->len,
|
|
bt_hex(buf->data, buf->len));
|
|
|
|
status = net_buf_simple_pull_u8(buf);
|
|
|
|
health_client_cancel(model, ctx, &status, sizeof(u8_t));
|
|
}
|
|
|
|
const struct bt_mesh_model_op bt_mesh_health_cli_op[] = {
|
|
{ OP_HEALTH_FAULT_STATUS, 3, health_fault_status },
|
|
{ OP_HEALTH_CURRENT_STATUS, 3, health_current_status },
|
|
{ OP_HEALTH_PERIOD_STATUS, 1, health_period_status },
|
|
{ OP_ATTENTION_STATUS, 1, health_attention_status },
|
|
BLE_MESH_MODEL_OP_END,
|
|
};
|
|
|
|
int bt_mesh_health_attention_get(struct bt_mesh_msg_ctx *ctx)
|
|
{
|
|
BLE_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_GET, 0);
|
|
int err = 0;
|
|
|
|
if (!ctx || !ctx->addr) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_ATTENTION_GET);
|
|
|
|
err = bt_mesh_client_send_msg(health_cli->model, OP_ATTENTION_GET, ctx,
|
|
&msg, timeout_handler, health_msg_timeout,
|
|
true, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("%s, send failed (err %d)", __func__, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int bt_mesh_health_attention_set(struct bt_mesh_msg_ctx *ctx,
|
|
u8_t attention, bool need_ack)
|
|
{
|
|
BLE_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_SET, 1);
|
|
u32_t opcode = 0U;
|
|
int err = 0;
|
|
|
|
if (!ctx || !ctx->addr) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (need_ack) {
|
|
opcode = OP_ATTENTION_SET;
|
|
} else {
|
|
opcode = OP_ATTENTION_SET_UNREL;
|
|
}
|
|
bt_mesh_model_msg_init(&msg, opcode);
|
|
net_buf_simple_add_u8(&msg, attention);
|
|
|
|
err = bt_mesh_client_send_msg(health_cli->model, opcode, ctx, &msg,
|
|
timeout_handler, health_msg_timeout,
|
|
need_ack, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("%s, send failed (err %d)", __func__, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int bt_mesh_health_period_get(struct bt_mesh_msg_ctx *ctx)
|
|
{
|
|
BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_GET, 0);
|
|
int err = 0;
|
|
|
|
if (!ctx || !ctx->addr) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_GET);
|
|
|
|
err = bt_mesh_client_send_msg(health_cli->model, OP_HEALTH_PERIOD_GET,
|
|
ctx, &msg, timeout_handler, health_msg_timeout,
|
|
true, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("%s, send failed (err %d)", __func__, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int bt_mesh_health_period_set(struct bt_mesh_msg_ctx *ctx,
|
|
u8_t divisor, bool need_ack)
|
|
{
|
|
BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_SET, 1);
|
|
u32_t opcode = 0U;
|
|
int err = 0;
|
|
|
|
if (!ctx || !ctx->addr) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (need_ack) {
|
|
opcode = OP_HEALTH_PERIOD_SET;
|
|
} else {
|
|
opcode = OP_HEALTH_PERIOD_SET_UNREL;
|
|
}
|
|
bt_mesh_model_msg_init(&msg, opcode);
|
|
net_buf_simple_add_u8(&msg, divisor);
|
|
|
|
err = bt_mesh_client_send_msg(health_cli->model, opcode, ctx, &msg,
|
|
timeout_handler, health_msg_timeout,
|
|
need_ack, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("%s, send failed (err %d)", __func__, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int bt_mesh_health_fault_test(struct bt_mesh_msg_ctx *ctx,
|
|
u16_t cid, u8_t test_id, bool need_ack)
|
|
{
|
|
BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_TEST, 3);
|
|
u32_t opcode = 0U;
|
|
int err = 0;
|
|
|
|
if (!ctx || !ctx->addr) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (need_ack) {
|
|
opcode = OP_HEALTH_FAULT_TEST;
|
|
} else {
|
|
opcode = OP_HEALTH_FAULT_TEST_UNREL;
|
|
}
|
|
bt_mesh_model_msg_init(&msg, opcode);
|
|
net_buf_simple_add_u8(&msg, test_id);
|
|
net_buf_simple_add_le16(&msg, cid);
|
|
|
|
err = bt_mesh_client_send_msg(health_cli->model, opcode, ctx, &msg,
|
|
timeout_handler, health_msg_timeout,
|
|
need_ack, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("%s, send failed (err %d)", __func__, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int bt_mesh_health_fault_clear(struct bt_mesh_msg_ctx *ctx,
|
|
u16_t cid, bool need_ack)
|
|
{
|
|
BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_CLEAR, 2);
|
|
u32_t opcode = 0U;
|
|
int err = 0;
|
|
|
|
if (!ctx || !ctx->addr) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (need_ack) {
|
|
opcode = OP_HEALTH_FAULT_CLEAR;
|
|
} else {
|
|
opcode = OP_HEALTH_FAULT_CLEAR_UNREL;
|
|
}
|
|
bt_mesh_model_msg_init(&msg, opcode);
|
|
net_buf_simple_add_le16(&msg, cid);
|
|
|
|
err = bt_mesh_client_send_msg(health_cli->model, opcode, ctx, &msg,
|
|
timeout_handler, health_msg_timeout,
|
|
need_ack, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("%s, send failed (err %d)", __func__, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
int bt_mesh_health_fault_get(struct bt_mesh_msg_ctx *ctx, u16_t cid)
|
|
{
|
|
BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_GET, 2);
|
|
int err = 0;
|
|
|
|
if (!ctx || !ctx->addr) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_GET);
|
|
net_buf_simple_add_le16(&msg, cid);
|
|
|
|
err = bt_mesh_client_send_msg(health_cli->model, OP_HEALTH_FAULT_GET, ctx,
|
|
&msg, timeout_handler, health_msg_timeout,
|
|
true, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("%s, send failed (err %d)", __func__, err);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
s32_t bt_mesh_health_cli_timeout_get(void)
|
|
{
|
|
return health_msg_timeout;
|
|
}
|
|
|
|
void bt_mesh_health_cli_timeout_set(s32_t timeout)
|
|
{
|
|
health_msg_timeout = timeout;
|
|
}
|
|
|
|
int bt_mesh_health_cli_set(struct bt_mesh_model *model)
|
|
{
|
|
if (!model || !model->user_data) {
|
|
BT_ERR("%s, No Health Client context for given model", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
health_cli = model->user_data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_mesh_health_cli_init(struct bt_mesh_model *model, bool primary)
|
|
{
|
|
health_internal_data_t *internal = NULL;
|
|
bt_mesh_health_client_t *client = NULL;
|
|
|
|
BT_DBG("primary %u", primary);
|
|
|
|
if (!model) {
|
|
BT_ERR("%s, Invalid parameter", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
client = (bt_mesh_health_client_t *)model->user_data;
|
|
if (!client) {
|
|
BT_ERR("%s, No Health Client context provided", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!client->internal_data) {
|
|
internal = bt_mesh_calloc(sizeof(health_internal_data_t));
|
|
if (!internal) {
|
|
BT_ERR("%s, Failed to allocate memory", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
sys_slist_init(&internal->queue);
|
|
|
|
client->model = model;
|
|
client->op_pair_size = ARRAY_SIZE(health_op_pair);
|
|
client->op_pair = health_op_pair;
|
|
client->internal_data = internal;
|
|
} else {
|
|
bt_mesh_client_clear_list(client->internal_data);
|
|
}
|
|
|
|
bt_mesh_health_client_mutex_new();
|
|
|
|
/* Set the default health client pointer */
|
|
if (!health_cli) {
|
|
health_cli = client;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_mesh_health_cli_deinit(struct bt_mesh_model *model, bool primary)
|
|
{
|
|
bt_mesh_health_client_t *client = NULL;
|
|
|
|
if (!model) {
|
|
BT_ERR("%s, Invalid parameter", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
client = (bt_mesh_health_client_t *)model->user_data;
|
|
if (!client) {
|
|
BT_ERR("%s, No Health Client context provided", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (client->internal_data) {
|
|
/* Remove items from the list */
|
|
bt_mesh_client_clear_list(client->internal_data);
|
|
|
|
/* Free the allocated internal data */
|
|
bt_mesh_free(client->internal_data);
|
|
client->internal_data = NULL;
|
|
}
|
|
|
|
bt_mesh_health_client_mutex_free();
|
|
|
|
if (health_cli) {
|
|
health_cli = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|