OVMS3-idf/components/bt/esp_ble_mesh/mesh_core/health_cli.c
lly e24641cc89 ble_mesh: Miscellaneous modifications
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
2020-02-27 14:42:25 +08:00

517 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;
}