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
550 lines
15 KiB
C
550 lines
15 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 "access.h"
|
|
#include "foundation.h"
|
|
#include "mesh_common.h"
|
|
#include "health_srv.h"
|
|
|
|
#include "btc_ble_mesh_health_model.h"
|
|
|
|
#define HEALTH_TEST_STANDARD 0x00
|
|
|
|
#define HEALTH_NO_FAULT 0x00
|
|
|
|
/* Health Server context of the primary element */
|
|
struct bt_mesh_health_srv *health_srv;
|
|
|
|
/**
|
|
* When an Element receives a Health Fault Get, or a Health Fault Test, or
|
|
* a Health Fault Test Unacknowledged, or a Health Fault Clear, or a Health
|
|
* Fault Clear Unacknowledged message that is not successfully processed
|
|
* (i.e. the Company ID field that does not identify any Health Fault state
|
|
* present in the node), it shall ignore the message.
|
|
* The Health Fault state is identified by Company ID and may be present in
|
|
* the node for more than one Company ID.
|
|
*/
|
|
|
|
static u8_t health_get_curr_fault_count(struct bt_mesh_model *model)
|
|
{
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
u8_t count = 0U;
|
|
size_t i = 0U;
|
|
|
|
for (i = 0U; i < ARRAY_SIZE(srv->test.curr_faults); i++) {
|
|
if (srv->test.curr_faults[i] != HEALTH_NO_FAULT) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static void health_get_fault_value(struct bt_mesh_model *model,
|
|
struct net_buf_simple *msg,
|
|
bool current)
|
|
{
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
size_t array_size = 0U;
|
|
size_t i = 0U;
|
|
|
|
array_size = current ? ARRAY_SIZE(srv->test.curr_faults) : ARRAY_SIZE(srv->test.reg_faults);
|
|
|
|
for (i = 0U; i < array_size; i++) {
|
|
if (net_buf_simple_tailroom(msg) == 0) {
|
|
return;
|
|
}
|
|
|
|
u8_t fault = current ? srv->test.curr_faults[i] : srv->test.reg_faults[i];
|
|
if (fault != HEALTH_NO_FAULT) {
|
|
net_buf_simple_add_u8(msg, fault);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool health_is_test_id_exist(struct bt_mesh_model *model, u8_t test_id)
|
|
{
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
int i;
|
|
|
|
for (i = 0; i < srv->test.id_count; i++) {
|
|
if (srv->test.test_ids[i] == test_id) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int health_send_fault_status(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx)
|
|
{
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
struct net_buf_simple *msg = NULL;
|
|
int err = 0;
|
|
|
|
msg = bt_mesh_alloc_buf(4 + ARRAY_SIZE(srv->test.reg_faults) + 4);
|
|
if (!msg) {
|
|
BT_ERR("%s, Failed to allocate memory", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_STATUS);
|
|
net_buf_simple_add_u8(msg, srv->test.prev_test_id);
|
|
net_buf_simple_add_le16(msg, srv->test.company_id);
|
|
if (ctx->recv_op != OP_HEALTH_FAULT_CLEAR) {
|
|
/**
|
|
* For Health Fault Clear, the FaultArray field in Health Fault Status
|
|
* shall be empty.
|
|
*/
|
|
health_get_fault_value(model, msg, false);
|
|
}
|
|
|
|
err = bt_mesh_model_send(model, ctx, msg, NULL, NULL);
|
|
if (err) {
|
|
BT_ERR("%s, Failed to send Health Fault Status response", __func__);
|
|
}
|
|
|
|
bt_mesh_free_buf(msg);
|
|
return err;
|
|
}
|
|
|
|
static void health_fault_get(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
u16_t company_id = 0U;
|
|
|
|
if (!srv) {
|
|
BT_ERR("%s, No Health Server context provided", __func__);
|
|
return;
|
|
}
|
|
|
|
company_id = net_buf_simple_pull_le16(buf);
|
|
if (company_id != srv->test.company_id) {
|
|
BT_ERR("%s, Unknown Company ID 0x%04x", __func__, company_id);
|
|
return;
|
|
}
|
|
|
|
BT_DBG("company_id 0x%04x", company_id);
|
|
|
|
health_send_fault_status(model, ctx);
|
|
}
|
|
|
|
static void health_fault_clear(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
u16_t company_id = 0U;
|
|
|
|
if (!srv) {
|
|
BT_ERR("%s, No Health Server context provided", __func__);
|
|
return;
|
|
}
|
|
|
|
company_id = net_buf_simple_pull_le16(buf);
|
|
if (company_id != srv->test.company_id) {
|
|
BT_ERR("%s, Unknown Company ID 0x%04x", __func__, company_id);
|
|
return;
|
|
}
|
|
|
|
BT_DBG("company_id 0x%04x", company_id);
|
|
|
|
memset(srv->test.reg_faults, HEALTH_NO_FAULT, ARRAY_SIZE(srv->test.reg_faults));
|
|
|
|
if (srv->cb.fault_clear) {
|
|
srv->cb.fault_clear(model, company_id);
|
|
}
|
|
|
|
if (ctx->recv_op == OP_HEALTH_FAULT_CLEAR) {
|
|
health_send_fault_status(model, ctx);
|
|
}
|
|
}
|
|
|
|
static void health_fault_test(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
u16_t company_id = 0U;
|
|
u8_t test_id = 0U;
|
|
|
|
BT_DBG("%s", __func__);
|
|
|
|
if (!srv) {
|
|
BT_ERR("%s, No Health Server context provided", __func__);
|
|
return;
|
|
}
|
|
|
|
test_id = net_buf_simple_pull_u8(buf);
|
|
if (health_is_test_id_exist(model, test_id) == false) {
|
|
BT_ERR("%s, Unknown Test ID 0x%02x", __func__, test_id);
|
|
return;
|
|
}
|
|
|
|
company_id = net_buf_simple_pull_le16(buf);
|
|
if (company_id != srv->test.company_id) {
|
|
BT_ERR("%s, Unknown Company ID 0x%04x", __func__, company_id);
|
|
return;
|
|
}
|
|
|
|
BT_DBG("test 0x%02x company 0x%04x", test_id, company_id);
|
|
|
|
srv->test.prev_test_id = test_id;
|
|
|
|
if (srv->cb.fault_test) {
|
|
srv->cb.fault_test(model, test_id, company_id);
|
|
}
|
|
|
|
if (ctx->recv_op == OP_HEALTH_FAULT_TEST) {
|
|
health_send_fault_status(model, ctx);
|
|
}
|
|
}
|
|
|
|
static void send_attention_status(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx)
|
|
{
|
|
BLE_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_STATUS, 1);
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
u8_t time = 0U;
|
|
|
|
if (!srv) {
|
|
BT_ERR("%s, No Health Server context provided", __func__);
|
|
return;
|
|
}
|
|
|
|
time = k_delayed_work_remaining_get(&srv->attn_timer) / 1000;
|
|
BT_DBG("%u second%s", time, (time == 1U) ? "" : "s");
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_ATTENTION_STATUS);
|
|
net_buf_simple_add_u8(&msg, time);
|
|
|
|
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
|
|
BT_ERR("%s, Unable to send Health Attention Status", __func__);
|
|
}
|
|
}
|
|
|
|
static void attention_get(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
BT_DBG("%s", __func__);
|
|
|
|
send_attention_status(model, ctx);
|
|
}
|
|
|
|
static void health_set_attention(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
u8_t time = 0U;
|
|
|
|
time = net_buf_simple_pull_u8(buf);
|
|
|
|
BT_DBG("%u second%s", time, (time == 1U) ? "" : "s");
|
|
|
|
bt_mesh_attention(model, time);
|
|
}
|
|
|
|
static void attention_set(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
BT_DBG("%s", __func__);
|
|
|
|
health_set_attention(model, ctx, buf);
|
|
|
|
if (ctx->recv_op == OP_ATTENTION_SET) {
|
|
send_attention_status(model, ctx);
|
|
}
|
|
}
|
|
|
|
static void send_health_period_status(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx)
|
|
{
|
|
BLE_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_STATUS, 1);
|
|
|
|
bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_STATUS);
|
|
net_buf_simple_add_u8(&msg, model->pub->period_div);
|
|
|
|
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
|
|
BT_ERR("%s, Unable to send Health Period Status", __func__);
|
|
}
|
|
}
|
|
|
|
static void health_period_get(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
BT_DBG("%s", __func__);
|
|
|
|
send_health_period_status(model, ctx);
|
|
}
|
|
|
|
static void health_set_period(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
u8_t period = 0U;
|
|
|
|
period = net_buf_simple_pull_u8(buf);
|
|
if (period > 15) {
|
|
BT_WARN("%s, Prohibited period value %u", __func__, period);
|
|
return;
|
|
}
|
|
|
|
BT_DBG("period %u", period);
|
|
|
|
model->pub->period_div = period;
|
|
}
|
|
|
|
static void health_period_set(struct bt_mesh_model *model,
|
|
struct bt_mesh_msg_ctx *ctx,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
BT_DBG("%s", __func__);
|
|
|
|
health_set_period(model, ctx, buf);
|
|
|
|
if (ctx->recv_op == OP_HEALTH_PERIOD_SET) {
|
|
send_health_period_status(model, ctx);
|
|
}
|
|
}
|
|
|
|
const struct bt_mesh_model_op bt_mesh_health_srv_op[] = {
|
|
{ OP_HEALTH_FAULT_GET, 2, health_fault_get },
|
|
{ OP_HEALTH_FAULT_CLEAR, 2, health_fault_clear },
|
|
{ OP_HEALTH_FAULT_CLEAR_UNREL, 2, health_fault_clear },
|
|
{ OP_HEALTH_FAULT_TEST, 3, health_fault_test },
|
|
{ OP_HEALTH_FAULT_TEST_UNREL, 3, health_fault_test },
|
|
{ OP_HEALTH_PERIOD_GET, 0, health_period_get },
|
|
{ OP_HEALTH_PERIOD_SET, 1, health_period_set },
|
|
{ OP_HEALTH_PERIOD_SET_UNREL, 1, health_period_set },
|
|
{ OP_ATTENTION_GET, 0, attention_get },
|
|
{ OP_ATTENTION_SET, 1, attention_set },
|
|
{ OP_ATTENTION_SET_UNREL, 1, attention_set },
|
|
BLE_MESH_MODEL_OP_END,
|
|
};
|
|
|
|
static size_t health_get_current(struct bt_mesh_model *model,
|
|
struct net_buf_simple *msg)
|
|
{
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
|
|
if (!srv) {
|
|
BT_ERR("%s, No Health Server context provided", __func__);
|
|
return 0;
|
|
}
|
|
|
|
if (msg->size < 4) {
|
|
BT_ERR("%s, Too small health publication msg size %d", __func__, msg->size);
|
|
return 0;
|
|
}
|
|
|
|
bt_mesh_model_msg_init(msg, OP_HEALTH_CURRENT_STATUS);
|
|
net_buf_simple_add_u8(msg, srv->test.prev_test_id);
|
|
net_buf_simple_add_le16(msg, srv->test.company_id);
|
|
health_get_fault_value(model, msg, true);
|
|
|
|
return health_get_curr_fault_count(model);
|
|
}
|
|
|
|
static int health_pub_update(struct bt_mesh_model *model)
|
|
{
|
|
struct bt_mesh_model_pub *pub = model->pub;
|
|
size_t count = 0U;
|
|
|
|
BT_DBG("%s", __func__);
|
|
|
|
if (!pub || !pub->msg) {
|
|
BT_ERR("%s, Invalid health publication context", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
count = health_get_current(model, pub->msg);
|
|
if (count) {
|
|
pub->fast_period = 1U;
|
|
} else {
|
|
pub->fast_period = 0U;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_mesh_fault_update(struct bt_mesh_elem *elem)
|
|
{
|
|
struct bt_mesh_model *model = NULL;
|
|
|
|
model = bt_mesh_model_find(elem, BLE_MESH_MODEL_ID_HEALTH_SRV);
|
|
if (!model) {
|
|
BT_ERR("%s, Health Server does not exist", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!model->pub) {
|
|
BT_ERR("%s, Health Server has no publication support", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Let periodic publishing, if enabled, take care of sending the
|
|
* Health Current Status.
|
|
*/
|
|
if (bt_mesh_model_pub_period_get(model)) {
|
|
return 0;
|
|
}
|
|
|
|
health_pub_update(model);
|
|
|
|
return bt_mesh_model_publish(model);
|
|
}
|
|
|
|
static void attention_off(struct k_work *work)
|
|
{
|
|
struct bt_mesh_health_srv *srv = CONTAINER_OF(work,
|
|
struct bt_mesh_health_srv,
|
|
attn_timer.work);
|
|
BT_DBG("%s", __func__);
|
|
|
|
if (!srv) {
|
|
BT_ERR("%s, No Health Server context provided", __func__);
|
|
return;
|
|
}
|
|
|
|
if (srv->cb.attn_off) {
|
|
srv->cb.attn_off(srv->model);
|
|
}
|
|
srv->attn_timer_start = false;
|
|
}
|
|
|
|
int bt_mesh_health_srv_init(struct bt_mesh_model *model, bool primary)
|
|
{
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
|
|
/* Health Server Model shall be supported by a primary element and may be
|
|
* supported by any secondary elements.
|
|
*/
|
|
|
|
if (!srv) {
|
|
if (!primary) {
|
|
/* If Health Server is in the secondary element with NULL user_data. */
|
|
return 0;
|
|
}
|
|
|
|
BT_ERR("%s, No Health Server context provided", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (srv->test.id_count == 0 || !srv->test.test_ids) {
|
|
BT_ERR("%s, No Health Test ID provided", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!model->pub) {
|
|
BT_ERR("%s, Health Server has no publication support", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
model->pub->update = health_pub_update;
|
|
|
|
k_delayed_work_init(&srv->attn_timer, attention_off);
|
|
|
|
srv->model = model;
|
|
srv->attn_timer_start = false;
|
|
|
|
memset(srv->test.curr_faults, HEALTH_NO_FAULT, ARRAY_SIZE(srv->test.curr_faults));
|
|
memset(srv->test.reg_faults, HEALTH_NO_FAULT, ARRAY_SIZE(srv->test.reg_faults));
|
|
|
|
if (primary) {
|
|
health_srv = srv;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bt_mesh_health_srv_deinit(struct bt_mesh_model *model, bool primary)
|
|
{
|
|
struct bt_mesh_health_srv *srv = model->user_data;
|
|
|
|
if (!srv) {
|
|
if (!primary) {
|
|
/* If Health Server is in the secondary element with NULL user_data. */
|
|
return 0;
|
|
}
|
|
|
|
BT_ERR("%s, No Health Server context provided", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (srv->test.id_count == 0 || !srv->test.test_ids) {
|
|
BT_ERR("%s, No Health Test ID provided", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!model->pub) {
|
|
BT_ERR("%s, Health Server has no publication support", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
model->pub->addr = BLE_MESH_ADDR_UNASSIGNED;
|
|
model->pub->update = NULL;
|
|
|
|
k_delayed_work_free(&srv->attn_timer);
|
|
|
|
if (primary) {
|
|
health_srv = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void bt_mesh_attention(struct bt_mesh_model *model, u8_t time)
|
|
{
|
|
struct bt_mesh_health_srv *srv = NULL;
|
|
|
|
if (!model) {
|
|
srv = health_srv;
|
|
if (!srv) {
|
|
BT_WARN("%s, No Health Server context provided", __func__);
|
|
return;
|
|
}
|
|
|
|
model = srv->model;
|
|
} else {
|
|
srv = model->user_data;
|
|
if (!srv) {
|
|
BT_WARN("%s, No Health Server context provided", __func__);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (time) {
|
|
if (srv->cb.attn_on) {
|
|
srv->cb.attn_on(model, time);
|
|
}
|
|
|
|
k_delayed_work_submit(&srv->attn_timer, time * 1000U);
|
|
srv->attn_timer_start = true;
|
|
} else {
|
|
k_delayed_work_cancel(&srv->attn_timer);
|
|
|
|
if (srv->attn_timer_start == true) {
|
|
if (srv->cb.attn_off) {
|
|
srv->cb.attn_off(model);
|
|
}
|
|
srv->attn_timer_start = false;
|
|
}
|
|
}
|
|
}
|