/* Bluetooth Mesh */ /* * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include "adaptation.h" #include "mesh_net.h" #include "foundation.h" #define LOG_TAG "[MESH-healthcli]" #define LOG_INFO_ENABLE #define LOG_DEBUG_ENABLE #define LOG_WARN_ENABLE #define LOG_ERROR_ENABLE #define LOG_DUMP_ENABLE #include "mesh_log.h" #if MESH_RAM_AND_CODE_MAP_DETAIL #ifdef SUPPORT_MS_EXTENSIONS #pragma bss_seg(".ble_mesh_healthcli_bss") #pragma data_seg(".ble_mesh_healthcli_data") #pragma const_seg(".ble_mesh_healthcli_const") #pragma code_seg(".ble_mesh_healthcli_code") #endif #else /* MESH_RAM_AND_CODE_MAP_DETAIL */ #pragma bss_seg(".ble_mesh_bss") #pragma data_seg(".ble_mesh_data") #pragma const_seg(".ble_mesh_const") #pragma code_seg(".ble_mesh_code") #endif /* MESH_RAM_AND_CODE_MAP_DETAIL */ #if defined(CONFIG_BT_MESH_HEALTH_CLI) static s32_t msg_timeout = K_SECONDS(2); static struct bt_mesh_health_cli *health_cli; struct health_fault_param { u16_t cid; u8_t *expect_test_id; u8_t *test_id; u8_t *faults; size_t *fault_count; }; static void health_fault_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct health_fault_param *param; u8_t test_id; u16_t cid; 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)); if (health_cli->op_pending != OP_HEALTH_FAULT_STATUS) { BT_WARN("Unexpected Health Fault Status message"); return; } param = health_cli->op_param; test_id = net_buf_simple_pull_u8(buf); if (param->expect_test_id && test_id != *param->expect_test_id) { BT_WARN("Health fault with unexpected Test ID"); return; } cid = net_buf_simple_pull_le16(buf); if (cid != param->cid) { BT_WARN("Health fault with unexpected Company ID"); return; } if (param->test_id) { *param->test_id = test_id; } if (buf->len > *param->fault_count) { BT_WARN("Got more faults than there's space for"); } else { *param->fault_count = buf->len; } memcpy(param->faults, buf->data, *param->fault_count); k_sem_give(&health_cli->op_sync); } static void health_current_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct bt_mesh_health_cli *cli = model->user_data; u8_t test_id; u16_t cid; 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)); 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); if (!cli->current_status) { BT_WARN("No Current Status callback available"); return; } cli->current_status(cli, ctx->addr, test_id, cid, buf->data, buf->len); } struct health_period_param { u8_t *divisor; }; static void health_period_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct health_period_param *param; 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)); if (health_cli->op_pending != OP_HEALTH_PERIOD_STATUS) { BT_WARN("Unexpected Health Period Status message"); return; } param = health_cli->op_param; *param->divisor = net_buf_simple_pull_u8(buf); k_sem_give(&health_cli->op_sync); } struct health_attention_param { u8_t *attention; }; static void health_attention_status(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, struct net_buf_simple *buf) { struct health_attention_param *param; 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)); if (health_cli->op_pending != OP_ATTENTION_STATUS) { BT_WARN("Unexpected Health Attention Status message"); return; } param = health_cli->op_param; if (param->attention) { *param->attention = net_buf_simple_pull_u8(buf); } k_sem_give(&health_cli->op_sync); } 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 }, BT_MESH_MODEL_OP_END, }; static int cli_prepare(void *param, u32_t op) { if (!health_cli) { BT_ERR("No available Health Client context!"); return -EINVAL; } if (health_cli->op_pending) { BT_WARN("Another synchronous operation pending"); return -EBUSY; } health_cli->op_param = param; health_cli->op_pending = op; return 0; } static void cli_reset(void) { health_cli->op_pending = 0; health_cli->op_param = NULL; } static int cli_wait(void) { int err; err = k_sem_take(&health_cli->op_sync, msg_timeout); cli_reset(); return err; } int bt_mesh_health_attention_get(u16_t net_idx, u16_t addr, u16_t app_idx, u8_t *attention) { NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, .app_idx = app_idx, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; struct health_attention_param param = { .attention = attention, }; int err; err = cli_prepare(¶m, OP_ATTENTION_STATUS); if (err) { return err; } bt_mesh_model_msg_init(&msg, OP_ATTENTION_GET); err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL); if (err) { BT_ERR("model_send() failed (err %d)", err); cli_reset(); return err; } return cli_wait(); } int bt_mesh_health_attention_set(u16_t net_idx, u16_t addr, u16_t app_idx, u8_t attention, u8_t *updated_attention) { NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, .app_idx = app_idx, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; struct health_attention_param param = { .attention = updated_attention, }; int err; err = cli_prepare(¶m, OP_ATTENTION_STATUS); if (err) { return err; } if (updated_attention) { bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET); } else { bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET_UNREL); } net_buf_simple_add_u8(&msg, attention); err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL); if (err) { BT_ERR("model_send() failed (err %d)", err); cli_reset(); return err; } if (!updated_attention) { cli_reset(); return 0; } return cli_wait(); } int bt_mesh_health_period_get(u16_t net_idx, u16_t addr, u16_t app_idx, u8_t *divisor) { NET_BUF_SIMPLE_DEFINE(msg, 2 + 0 + 4); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, .app_idx = app_idx, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; struct health_period_param param = { .divisor = divisor, }; int err; err = cli_prepare(¶m, OP_HEALTH_PERIOD_STATUS); if (err) { return err; } bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_GET); err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL); if (err) { BT_ERR("model_send() failed (err %d)", err); cli_reset(); return err; } return cli_wait(); } int bt_mesh_health_period_set(u16_t net_idx, u16_t addr, u16_t app_idx, u8_t divisor, u8_t *updated_divisor) { NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, .app_idx = app_idx, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; struct health_period_param param = { .divisor = updated_divisor, }; int err; err = cli_prepare(¶m, OP_HEALTH_PERIOD_STATUS); if (err) { return err; } if (updated_divisor) { bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET); } else { bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET_UNREL); } net_buf_simple_add_u8(&msg, divisor); err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL); if (err) { BT_ERR("model_send() failed (err %d)", err); cli_reset(); return err; } if (!updated_divisor) { cli_reset(); return 0; } return cli_wait(); } int bt_mesh_health_fault_test(u16_t net_idx, u16_t addr, u16_t app_idx, u16_t cid, u8_t test_id, u8_t *faults, size_t *fault_count) { NET_BUF_SIMPLE_DEFINE(msg, 2 + 3 + 4); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, .app_idx = app_idx, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; struct health_fault_param param = { .cid = cid, .expect_test_id = &test_id, .faults = faults, .fault_count = fault_count, }; int err; err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS); if (err) { return err; } if (faults) { bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST); } else { bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST_UNREL); } net_buf_simple_add_u8(&msg, test_id); net_buf_simple_add_le16(&msg, cid); err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL); if (err) { BT_ERR("model_send() failed (err %d)", err); cli_reset(); return err; } if (!faults) { cli_reset(); return 0; } return cli_wait(); } int bt_mesh_health_fault_clear(u16_t net_idx, u16_t addr, u16_t app_idx, u16_t cid, u8_t *test_id, u8_t *faults, size_t *fault_count) { NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, .app_idx = app_idx, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; struct health_fault_param param = { .cid = cid, .test_id = test_id, .faults = faults, .fault_count = fault_count, }; int err; err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS); if (err) { return err; } if (test_id) { bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR); } else { bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR_UNREL); } net_buf_simple_add_le16(&msg, cid); err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL); if (err) { BT_ERR("model_send() failed (err %d)", err); cli_reset(); return err; } if (!test_id) { cli_reset(); return 0; } return cli_wait(); } int bt_mesh_health_fault_get(u16_t net_idx, u16_t addr, u16_t app_idx, u16_t cid, u8_t *test_id, u8_t *faults, size_t *fault_count) { NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4); struct bt_mesh_msg_ctx ctx = { .net_idx = net_idx, .app_idx = app_idx, .addr = addr, .send_ttl = BT_MESH_TTL_DEFAULT, }; struct health_fault_param param = { .cid = cid, .test_id = test_id, .faults = faults, .fault_count = fault_count, }; int err; err = cli_prepare(¶m, OP_HEALTH_FAULT_STATUS); if (err) { return err; } bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_GET); net_buf_simple_add_le16(&msg, cid); err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL); if (err) { BT_ERR("model_send() failed (err %d)", err); cli_reset(); return err; } return cli_wait(); } s32_t bt_mesh_health_cli_timeout_get(void) { return msg_timeout; } void bt_mesh_health_cli_timeout_set(s32_t timeout) { msg_timeout = timeout; } int bt_mesh_health_cli_set(struct bt_mesh_model *model) { if (!model->user_data) { BT_ERR("No Health Client context for given model"); return -EINVAL; } health_cli = model->user_data; return 0; } int bt_mesh_health_cli_init(struct bt_mesh_model *model, bool primary) { struct bt_mesh_health_cli *cli = model->user_data; BT_DBG("primary %u", primary); if (!cli) { BT_ERR("No Health Client context provided"); return -EINVAL; } cli = model->user_data; cli->model = model; k_sem_init(&cli->op_sync, 0, 1); /* Set the default health client pointer */ if (!health_cli) { health_cli = cli; } return 0; } #endif /* defined(CONFIG_BT_MESH_HEALTH_CLI) */