/* Bluetooth Mesh */ /* * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include "syscfg/syscfg.h" #define BT_DBG_ENABLED (MYNEWT_VAL(BLE_MESH_DEBUG_MODEL)) #include "host/ble_hs_log.h" #include "mesh/mesh.h" #include "mesh_priv.h" #include "adv.h" #include "net.h" #include "transport.h" #include "foundation.h" #include "mesh/health_cli.h" static s32_t msg_timeout = K_SECONDS(5); 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 os_mbuf *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->om_len, bt_hex(buf->om_data, buf->om_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->om_len > *param->fault_count) { BT_WARN("Got more faults than there's space for"); } else { *param->fault_count = buf->om_len; } memcpy(param->faults, buf->om_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 os_mbuf *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->om_len, bt_hex(buf->om_data, buf->om_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->om_len); if (!cli->current_status) { BT_WARN("No Current Status callback available"); return; } cli->current_status(cli, ctx->addr, test_id, cid, buf->om_data, buf->om_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 os_mbuf *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->om_len, bt_hex(buf->om_data, buf->om_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 os_mbuf *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->om_len, bt_hex(buf->om_data, buf->om_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) { struct os_mbuf *msg = NET_BUF_SIMPLE(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) { goto done; } 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(); goto done; } err = cli_wait(); done: os_mbuf_free_chain(msg); return err; } int bt_mesh_health_attention_set(u16_t net_idx, u16_t addr, u16_t app_idx, u8_t attention, u8_t *updated_attention) { struct os_mbuf *msg = NET_BUF_SIMPLE(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) { goto done; } 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(); goto done; } if (!updated_attention) { cli_reset(); goto done; } err = cli_wait(); done: os_mbuf_free_chain(msg); return err; } int bt_mesh_health_period_get(u16_t net_idx, u16_t addr, u16_t app_idx, u8_t *divisor) { struct os_mbuf *msg = NET_BUF_SIMPLE(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) { goto done; } 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(); goto done; } err = cli_wait(); done: os_mbuf_free_chain(msg); return err; } int bt_mesh_health_period_set(u16_t net_idx, u16_t addr, u16_t app_idx, u8_t divisor, u8_t *updated_divisor) { struct os_mbuf *msg = NET_BUF_SIMPLE(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) { goto done; } 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(); goto done; } if (!updated_divisor) { cli_reset(); goto done; } err = cli_wait(); done: os_mbuf_free_chain(msg); return err; } 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) { struct os_mbuf *msg = NET_BUF_SIMPLE(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) { goto done; } 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(); goto done; } if (!faults) { cli_reset(); goto done; } err = cli_wait(); done: os_mbuf_free_chain(msg); return err; } 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) { struct os_mbuf *msg = NET_BUF_SIMPLE(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) { goto done; } 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(); goto done; } if (!test_id) { cli_reset(); goto done; } err = cli_wait(); done: os_mbuf_free_chain(msg); return err; } 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) { struct os_mbuf *msg = NET_BUF_SIMPLE(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) { goto done; } 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(); goto done; } err = cli_wait(); done: os_mbuf_free_chain(msg); return err; } 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; }