diff --git a/include/bluetooth/mesh/health_cli.h b/include/bluetooth/mesh/health_cli.h index ff1aa83415a..2fda6f39429 100644 --- a/include/bluetooth/mesh/health_cli.h +++ b/include/bluetooth/mesh/health_cli.h @@ -26,6 +26,49 @@ struct bt_mesh_health_cli { /** Composition data model entry pointer. */ struct bt_mesh_model *model; + /** @brief Optional callback for Health Period Status messages. + * + * Handles received Health Period Status messages from a Health + * server. The @c divisor param represents the period divisor value. + * + * @param cli Health client that received the status message. + * @param addr Address of the sender. + * @param divisor Health Period Divisor value. + */ + void (*period_status)(struct bt_mesh_health_cli *cli, uint16_t addr, + uint8_t divisor); + + /** @brief Optional callback for Health Attention Status messages. + * + * Handles received Health Attention Status messages from a Health + * server. The @c attention param represents the current attention value. + * + * @param cli Health client that received the status message. + * @param addr Address of the sender. + * @param attention Current attention value. + */ + void (*attention_status)(struct bt_mesh_health_cli *cli, uint16_t addr, + uint8_t attention); + + /** @brief Optional callback for Health Fault Status messages. + * + * Handles received Health Fault Status messages from a Health + * server. The @c fault array represents all faults that are + * currently present in the server's element. + * + * @see bt_mesh_health_faults + * + * @param cli Health client that received the status message. + * @param addr Address of the sender. + * @param test_id Identifier of a most recently performed test. + * @param cid Company Identifier of the node. + * @param faults Array of faults. + * @param fault_count Number of faults in the fault array. + */ + void (*fault_status)(struct bt_mesh_health_cli *cli, uint16_t addr, + uint8_t test_id, uint16_t cid, uint8_t *faults, + size_t fault_count); + /** @brief Optional callback for Health Current Status messages. * * Handles received Health Current Status messages from a Health @@ -49,7 +92,6 @@ struct bt_mesh_health_cli { struct bt_mesh_msg_ack_ctx ack_ctx; }; - /** @def BT_MESH_MODEL_HEALTH_CLI * * @brief Generic Health Client model composition data entry. @@ -82,8 +124,8 @@ int bt_mesh_health_cli_set(struct bt_mesh_model *model); * @return 0 on success, or (negative) error code on failure. */ int bt_mesh_health_fault_get(uint16_t addr, uint16_t app_idx, uint16_t cid, - uint8_t *test_id, uint8_t *faults, - size_t *fault_count); + uint8_t *test_id, uint8_t *faults, + size_t *fault_count); /** @brief Clear the registered faults for the given Company ID. * @@ -99,8 +141,21 @@ int bt_mesh_health_fault_get(uint16_t addr, uint16_t app_idx, uint16_t cid, * @return 0 on success, or (negative) error code on failure. */ int bt_mesh_health_fault_clear(uint16_t addr, uint16_t app_idx, uint16_t cid, - uint8_t *test_id, uint8_t *faults, - size_t *fault_count); + uint8_t *test_id, uint8_t *faults, + size_t *fault_count); + +/** @brief Clear the registered faults for the given Company ID (unacked). + * + * @see bt_mesh_health_faults + * + * @param addr Target node element address. + * @param app_idx Application index to encrypt with. + * @param cid Company ID to clear the registered faults for. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_health_fault_clear_unack(uint16_t addr, uint16_t app_idx, + uint16_t cid); /** @brief Invoke a self-test procedure for the given Company ID. * @@ -114,8 +169,20 @@ int bt_mesh_health_fault_clear(uint16_t addr, uint16_t app_idx, uint16_t cid, * @return 0 on success, or (negative) error code on failure. */ int bt_mesh_health_fault_test(uint16_t addr, uint16_t app_idx, uint16_t cid, - uint8_t test_id, uint8_t *faults, - size_t *fault_count); + uint8_t test_id, uint8_t *faults, + size_t *fault_count); + +/** @brief Invoke a self-test procedure for the given Company ID (unacked). + * + * @param addr Target node element address. + * @param app_idx Application index to encrypt with. + * @param cid Company ID to invoke the test for. + * @param test_id Test ID response buffer. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_health_fault_test_unack(uint16_t addr, uint16_t app_idx, + uint16_t cid, uint8_t test_id); /** @brief Get the target node's Health fast period divisor. * @@ -133,7 +200,8 @@ int bt_mesh_health_fault_test(uint16_t addr, uint16_t app_idx, uint16_t cid, * * @return 0 on success, or (negative) error code on failure. */ -int bt_mesh_health_period_get(uint16_t addr, uint16_t app_idx, uint8_t *divisor); +int bt_mesh_health_period_get(uint16_t addr, uint16_t app_idx, + uint8_t *divisor); /** @brief Set the target node's Health fast period divisor. * @@ -153,7 +221,20 @@ int bt_mesh_health_period_get(uint16_t addr, uint16_t app_idx, uint8_t *divisor) * @return 0 on success, or (negative) error code on failure. */ int bt_mesh_health_period_set(uint16_t addr, uint16_t app_idx, uint8_t divisor, - uint8_t *updated_divisor); + uint8_t *updated_divisor); + +/** @brief Set the target node's Health fast period divisor (unacknowledged). + * + * This is an unacknowledged version of this API. + * + * @param addr Target node element address. + * @param app_idx Application index to encrypt with. + * @param divisor New Health period divisor. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_health_period_set_unack(uint16_t addr, uint16_t app_idx, + uint8_t divisor); /** @brief Get the current attention timer value. * @@ -163,7 +244,8 @@ int bt_mesh_health_period_set(uint16_t addr, uint16_t app_idx, uint8_t divisor, * * @return 0 on success, or (negative) error code on failure. */ -int bt_mesh_health_attention_get(uint16_t addr, uint16_t app_idx, uint8_t *attention); +int bt_mesh_health_attention_get(uint16_t addr, uint16_t app_idx, + uint8_t *attention); /** @brief Set the attention timer. * @@ -175,8 +257,19 @@ int bt_mesh_health_attention_get(uint16_t addr, uint16_t app_idx, uint8_t *atten * * @return 0 on success, or (negative) error code on failure. */ -int bt_mesh_health_attention_set(uint16_t addr, uint16_t app_idx, uint8_t attention, - uint8_t *updated_attention); +int bt_mesh_health_attention_set(uint16_t addr, uint16_t app_idx, + uint8_t attention, uint8_t *updated_attention); + +/** @brief Set the attention timer (unacknowledged). + * + * @param addr Target node element address. + * @param app_idx Application index to encrypt with. + * @param attention New attention timer time, in seconds. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_health_attention_set_unack(uint16_t addr, uint16_t app_idx, + uint8_t attention); /** @brief Get the current transmission timeout value. * diff --git a/subsys/bluetooth/mesh/health_cli.c b/subsys/bluetooth/mesh/health_cli.c index b33e7736dfc..ae3bc66921d 100644 --- a/subsys/bluetooth/mesh/health_cli.c +++ b/subsys/bluetooth/mesh/health_cli.c @@ -47,37 +47,42 @@ static int health_fault_status(struct bt_mesh_model *model, ctx->net_idx, ctx->app_idx, ctx->addr, buf->len, bt_hex(buf->data, buf->len)); - if (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_HEALTH_FAULT_STATUS, ctx->addr, - (void **)¶m)) { - return -ENOENT; - } - 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 -ENOENT; - } - cid = net_buf_simple_pull_le16(buf); - if (cid != param->cid) { - BT_WARN("Health fault with unexpected Company ID"); - return -ENOENT; + + if (bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, + OP_HEALTH_FAULT_STATUS, ctx->addr, + (void **)¶m)) { + if (param->expect_test_id && + (test_id != *param->expect_test_id)) { + goto done; + } + + if (cid != param->cid) { + goto done; + } + + 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); + + bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx); } - if (param->test_id) { - *param->test_id = test_id; +done: + if (health_cli->fault_status) { + health_cli->fault_status(health_cli, ctx->addr, test_id, cid, + buf->data, buf->len); } - 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); - - bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx); - return 0; } @@ -85,7 +90,6 @@ static int 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; uint8_t test_id; uint16_t cid; @@ -96,16 +100,14 @@ static int health_current_status(struct bt_mesh_model *model, 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); + 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 0; + if (health_cli->current_status) { + health_cli->current_status(health_cli, ctx->addr, test_id, cid, + buf->data, buf->len); } - cli->current_status(cli, ctx->addr, test_id, cid, buf->data, buf->len); - return 0; } @@ -118,19 +120,25 @@ static int health_period_status(struct bt_mesh_model *model, struct net_buf_simple *buf) { struct health_period_param *param; + uint8_t divisor; 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 (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_HEALTH_PERIOD_STATUS, ctx->addr, - (void **)¶m)) { - return -ENOENT; + divisor = net_buf_simple_pull_u8(buf); + + if (bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, + OP_HEALTH_PERIOD_STATUS, ctx->addr, + (void **)¶m)) { + *param->divisor = divisor; + + bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx); } - *param->divisor = net_buf_simple_pull_u8(buf); - - bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx); + if (health_cli->period_status) { + health_cli->period_status(health_cli, ctx->addr, divisor); + } return 0; } @@ -144,22 +152,26 @@ static int health_attention_status(struct bt_mesh_model *model, struct net_buf_simple *buf) { struct health_attention_param *param; + uint8_t attention; 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 (!bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_ATTENTION_STATUS, ctx->addr, - (void **)¶m)) { - return -ENOENT; + attention = net_buf_simple_pull_u8(buf); + + if (bt_mesh_msg_ack_ctx_match(&health_cli->ack_ctx, OP_ATTENTION_STATUS, + ctx->addr, (void **)¶m)) { + if (param->attention) { + *param->attention = attention; + } + + bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx); } - if (param->attention) { - *param->attention = net_buf_simple_pull_u8(buf); + if (health_cli->attention_status) { + health_cli->attention_status(health_cli, ctx->addr, attention); } - - bt_mesh_msg_ack_ctx_rx(&health_cli->ack_ctx); - return 0; } @@ -173,11 +185,6 @@ const struct bt_mesh_model_op bt_mesh_health_cli_op[] = { static int cli_prepare(void *param, uint32_t op, uint16_t addr) { - if (!health_cli) { - BT_ERR("No available Health Client context!"); - return -EINVAL; - } - return bt_mesh_msg_ack_ctx_prepare(&health_cli->ack_ctx, op, addr, param); } @@ -211,8 +218,8 @@ int bt_mesh_health_attention_get(uint16_t addr, uint16_t app_idx, uint8_t *atten return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout)); } -int bt_mesh_health_attention_set(uint16_t addr, uint16_t app_idx, uint8_t attention, - uint8_t *updated_attention) +int bt_mesh_health_attention_set(uint16_t addr, uint16_t app_idx, + uint8_t attention, uint8_t *updated_attention) { BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_SET, 1); struct bt_mesh_msg_ctx ctx = { @@ -230,12 +237,7 @@ int bt_mesh_health_attention_set(uint16_t addr, uint16_t app_idx, uint8_t attent 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); - } - + bt_mesh_model_msg_init(&msg, OP_ATTENTION_SET); net_buf_simple_add_u8(&msg, attention); err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL); @@ -253,6 +255,36 @@ int bt_mesh_health_attention_set(uint16_t addr, uint16_t app_idx, uint8_t attent return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout)); } +int bt_mesh_health_attention_set_unack(uint16_t addr, uint16_t app_idx, + uint8_t attention) +{ + BT_MESH_MODEL_BUF_DEFINE(msg, OP_ATTENTION_SET_UNREL, 1); + struct bt_mesh_msg_ctx ctx = { + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = cli_prepare(NULL, OP_ATTENTION_STATUS, addr); + if (err) { + return err; + } + + 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); + bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx); + return err; + } + + bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx); + return 0; +} + int bt_mesh_health_period_get(uint16_t addr, uint16_t app_idx, uint8_t *divisor) { BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_GET, 0); @@ -280,11 +312,12 @@ int bt_mesh_health_period_get(uint16_t addr, uint16_t app_idx, uint8_t *divisor) return err; } - return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout)); + return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, + K_MSEC(msg_timeout)); } int bt_mesh_health_period_set(uint16_t addr, uint16_t app_idx, uint8_t divisor, - uint8_t *updated_divisor) + uint8_t *updated_divisor) { BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_SET, 1); struct bt_mesh_msg_ctx ctx = { @@ -302,12 +335,7 @@ int bt_mesh_health_period_set(uint16_t addr, uint16_t app_idx, uint8_t divisor, 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); - } - + bt_mesh_model_msg_init(&msg, OP_HEALTH_PERIOD_SET); net_buf_simple_add_u8(&msg, divisor); err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL); @@ -322,7 +350,38 @@ int bt_mesh_health_period_set(uint16_t addr, uint16_t app_idx, uint8_t divisor, return 0; } - return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout)); + return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, + K_MSEC(msg_timeout)); +} + +int bt_mesh_health_period_set_unack(uint16_t addr, uint16_t app_idx, + uint8_t divisor) +{ + BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_PERIOD_SET_UNREL, 1); + struct bt_mesh_msg_ctx ctx = { + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = cli_prepare(NULL, OP_HEALTH_PERIOD_STATUS, addr); + if (err) { + return err; + } + + 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); + bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx); + return err; + } + + bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx); + return 0; } int bt_mesh_health_fault_test(uint16_t addr, uint16_t app_idx, uint16_t cid, @@ -348,12 +407,7 @@ int bt_mesh_health_fault_test(uint16_t addr, uint16_t app_idx, uint16_t cid, 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); - } - + bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_TEST); net_buf_simple_add_u8(&msg, test_id); net_buf_simple_add_le16(&msg, cid); @@ -372,6 +426,37 @@ int bt_mesh_health_fault_test(uint16_t addr, uint16_t app_idx, uint16_t cid, return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout)); } +int bt_mesh_health_fault_test_unack(uint16_t addr, uint16_t app_idx, + uint16_t cid, uint8_t test_id) +{ + BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_TEST, 3); + struct bt_mesh_msg_ctx ctx = { + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = cli_prepare(NULL, OP_HEALTH_FAULT_STATUS, addr); + if (err) { + return err; + } + + 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); + bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx); + return err; + } + + bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx); + return 0; +} + int bt_mesh_health_fault_clear(uint16_t addr, uint16_t app_idx, uint16_t cid, uint8_t *test_id, uint8_t *faults, size_t *fault_count) @@ -395,12 +480,7 @@ int bt_mesh_health_fault_clear(uint16_t addr, uint16_t app_idx, uint16_t cid, 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); - } - + bt_mesh_model_msg_init(&msg, OP_HEALTH_FAULT_CLEAR); net_buf_simple_add_le16(&msg, cid); err = bt_mesh_model_send(health_cli->model, &ctx, &msg, NULL, NULL); @@ -418,6 +498,36 @@ int bt_mesh_health_fault_clear(uint16_t addr, uint16_t app_idx, uint16_t cid, return bt_mesh_msg_ack_ctx_wait(&health_cli->ack_ctx, K_MSEC(msg_timeout)); } +int bt_mesh_health_fault_clear_unack(uint16_t addr, uint16_t app_idx, + uint16_t cid) +{ + BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEALTH_FAULT_CLEAR_UNREL, 2); + struct bt_mesh_msg_ctx ctx = { + .app_idx = app_idx, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + int err; + + err = cli_prepare(NULL, OP_HEALTH_FAULT_STATUS, addr); + if (err) { + return err; + } + + 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); + bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx); + return err; + } + + bt_mesh_msg_ack_ctx_clear(&health_cli->ack_ctx); + return 0; +} + int bt_mesh_health_fault_get(uint16_t addr, uint16_t app_idx, uint16_t cid, uint8_t *test_id, uint8_t *faults, size_t *fault_count) diff --git a/subsys/bluetooth/mesh/shell.c b/subsys/bluetooth/mesh/shell.c index 0eeea0e7782..604cef97de5 100644 --- a/subsys/bluetooth/mesh/shell.c +++ b/subsys/bluetooth/mesh/shell.c @@ -162,8 +162,34 @@ static void health_current_status(struct bt_mesh_health_cli *cli, uint16_t addr, show_faults(test_id, cid, faults, fault_count); } +static void health_fault_status(struct bt_mesh_health_cli *cli, uint16_t addr, + uint8_t test_id, uint16_t cid, uint8_t *faults, + size_t fault_count) +{ + shell_print(ctx_shell, "Health Fault Status from 0x%04x", addr); + show_faults(test_id, cid, faults, fault_count); +} + +static void health_attention_status(struct bt_mesh_health_cli *cli, + uint16_t addr, uint8_t attention) +{ + shell_print(ctx_shell, "Health Attention Status from 0x%04x: %u", addr, + attention); +} + +static void health_period_status(struct bt_mesh_health_cli *cli, uint16_t addr, + uint8_t period) +{ + shell_print(ctx_shell, + "Health Fast Period Divisor Status from 0x%04x: %u", addr, + period); +} + struct bt_mesh_health_cli bt_mesh_shell_health_cli = { .current_status = health_current_status, + .fault_status = health_fault_status, + .attention_status = health_attention_status, + .period_status = health_period_status, }; #endif /* CONFIG_BT_MESH_HEALTH_CLI */