Forráskód Böngészése

ble_mesh: modify health server model callbacks

lly 6 éve
szülő
commit
805bc06127

+ 5 - 1
components/bt/esp_ble_mesh/api/models/esp_ble_mesh_health_model_api.c

@@ -83,12 +83,16 @@ esp_err_t esp_ble_mesh_health_server_fault_update(esp_ble_mesh_elem_t *element)
     btc_ble_mesh_health_server_args_t arg = {0};
     btc_msg_t msg = {0};
 
+    if (element == NULL) {
+        return ESP_ERR_INVALID_ARG;
+    }
+
     ESP_BLE_HOST_STATUS_CHECK(ESP_BLE_HOST_STATUS_ENABLED);
 
     msg.sig = BTC_SIG_API_CALL;
     msg.pid = BTC_PID_HEALTH_SERVER;
     msg.act = BTC_BLE_MESH_ACT_HEALTH_SERVER_FAULT_UPDATE;
-    arg.fault_update.element = element;
+    arg.health_fault_update.element = element;
 
     return (btc_transfer_context(&msg, &arg, sizeof(btc_ble_mesh_health_server_args_t), NULL)
             == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL);

+ 148 - 24
components/bt/esp_ble_mesh/api/models/include/esp_ble_mesh_health_model_api.h

@@ -47,39 +47,123 @@
         ESP_BLE_MESH_SIG_MODEL(ESP_BLE_MESH_MODEL_ID_HEALTH_CLI,          \
                            NULL, NULL, cli_data)
 
-/** Health Server Model callbacks */
-typedef struct {
-    /** Fetch current faults */
-    int (*fault_get_cur)(esp_ble_mesh_model_t *model, uint8_t *test_id,
-                         uint16_t *company_id, uint8_t *faults, uint8_t *fault_count);
+/** @def ESP_BLE_MESH_HEALTH_PUB_DEFINE
+ *
+ *  A helper to define a health publication context
+ *
+ *  @param _name Name given to the publication context variable.
+ *  @param _max  Maximum number of faults the element can have.
+ *  @param _role Role of the device which contains the model.
+ */
+#define ESP_BLE_MESH_HEALTH_PUB_DEFINE(_name, _max, _role) \
+        ESP_BLE_MESH_MODEL_PUB_DEFINE(_name, (1 + 3 + (_max)), _role)
 
-    /** Fetch registered faults */
-    int (*fault_get_reg)(esp_ble_mesh_model_t *model, uint16_t company_id,
-                         uint8_t *test_id, uint8_t *faults, uint8_t *fault_count);
+/**
+ * SIG identifier of Health Fault Test.
+ * 0x01 ~ 0xFF: Vendor Specific Test.
+ */
+#define ESP_BLE_MESH_HEALTH_STANDARD_TEST               0x00
 
-    /** Clear registered faults */
-    int (*fault_clear)(esp_ble_mesh_model_t *model, uint16_t company_id);
+/**
+ * Fault values of Health Fault Test.
+ * 0x33 ~ 0x7F: Reserved for Future Use.
+ * 0x80 ~ 0xFF: Vendor Specific Warning/Error.
+ */
+#define ESP_BLE_MESH_NO_FAULT                           0x00
+#define ESP_BLE_MESH_BATTERY_LOW_WARNING                0x01
+#define ESP_BLE_MESH_BATTERY_LOW_ERROR                  0x02
+#define ESP_BLE_MESH_SUPPLY_VOLTAGE_TOO_LOW_WARNING     0x03
+#define ESP_BLE_MESH_SUPPLY_VOLTAGE_TOO_LOW_ERROR       0x04
+#define ESP_BLE_MESH_SUPPLY_VOLTAGE_TOO_HIGH_WARNING    0x05
+#define ESP_BLE_MESH_SUPPLY_VOLTAGE_TOO_HIGH_ERROR      0x06
+#define ESP_BLE_MESH_POWER_SUPPLY_INTERRUPTED_WARNING   0x07
+#define ESP_BLE_MESH_POWER_SUPPLY_INTERRUPTED_ERROR     0x08
+#define ESP_BLE_MESH_NO_LOAD_WARNING                    0x09
+#define ESP_BLE_MESH_NO_LOAD_ERROR                      0x0A
+#define ESP_BLE_MESH_OVERLOAD_WARNING                   0x0B
+#define ESP_BLE_MESH_OVERLOAD_ERROR                     0x0C
+#define ESP_BLE_MESH_OVERHEAT_WARNING                   0x0D
+#define ESP_BLE_MESH_OVERHEAT_ERROR                     0x0E
+#define ESP_BLE_MESH_CONDENSATION_WARNING               0x0F
+#define ESP_BLE_MESH_CONDENSATION_ERROR                 0x10
+#define ESP_BLE_MESH_VIBRATION_WARNING                  0x11
+#define ESP_BLE_MESH_VIBRATION_ERROR                    0x12
+#define ESP_BLE_MESH_CONFIGURATION_WARNING              0x13
+#define ESP_BLE_MESH_CONFIGURATION_ERROR                0x14
+#define ESP_BLE_MESH_ELEMENT_NOT_CALIBRATED_WARNING     0x15
+#define ESP_BLE_MESH_ELEMENT_NOT_CALIBRATED_ERROR       0x16
+#define ESP_BLE_MESH_MEMORY_WARNING                     0x17
+#define ESP_BLE_MESH_MEMORY_ERROR                       0x18
+#define ESP_BLE_MESH_SELF_TEST_WARNING                  0x19
+#define ESP_BLE_MESH_SELF_TEST_ERROR                    0x1A
+#define ESP_BLE_MESH_INPUT_TOO_LOW_WARNING              0x1B
+#define ESP_BLE_MESH_INPUT_TOO_LOW_ERROR                0x1C
+#define ESP_BLE_MESH_INPUT_TOO_HIGH_WARNING             0x1D
+#define ESP_BLE_MESH_INPUT_TOO_HIGH_ERROR               0x1E
+#define ESP_BLE_MESH_INPUT_NO_CHANGE_WARNING            0x1F
+#define ESP_BLE_MESH_INPUT_NO_CHANGE_ERROR              0x20
+#define ESP_BLE_MESH_ACTUATOR_BLOCKED_WARNING           0x21
+#define ESP_BLE_MESH_ACTUATOR_BLOCKED_ERROR             0x22
+#define ESP_BLE_MESH_HOUSING_OPENED_WARNING             0x23
+#define ESP_BLE_MESH_HOUSING_OPENED_ERROR               0x24
+#define ESP_BLE_MESH_TAMPER_WARNING                     0x25
+#define ESP_BLE_MESH_TAMPER_ERROR                       0x26
+#define ESP_BLE_MESH_DEVICE_MOVED_WARNING               0x27
+#define ESP_BLE_MESH_DEVICE_MOVED_ERROR                 0x28
+#define ESP_BLE_MESH_DEVICE_DROPPED_WARNING             0x29
+#define ESP_BLE_MESH_DEVICE_DROPPED_ERROR               0x2A
+#define ESP_BLE_MESH_OVERFLOW_WARNING                   0x2B
+#define ESP_BLE_MESH_OVERFLOW_ERROR                     0x2C
+#define ESP_BLE_MESH_EMPTY_WARNING                      0x2D
+#define ESP_BLE_MESH_EMPTY_ERROR                        0x2E
+#define ESP_BLE_MESH_INTERNAL_BUS_WARNING               0x2F
+#define ESP_BLE_MESH_INTERNAL_BUS_ERROR                 0x30
+#define ESP_BLE_MESH_MECHANISM_JAMMED_WARNING           0x31
+#define ESP_BLE_MESH_MECHANISM_JAMMED_ERROR             0x32
+
+/** ESP BLE Mesh Health Server callback */
+typedef struct {
+    /** Clear health registered faults. Initialized by the stack. */
+    esp_ble_mesh_cb_t fault_clear;
 
-    /** Run a specific test */
-    int (*fault_test)(esp_ble_mesh_model_t *model, uint8_t test_id, uint16_t company_id);
+    /** Run a specific health test. Initialized by the stack. */
+    esp_ble_mesh_cb_t fault_test;
 
-    /** Attention on */
-    void (*attn_on)(esp_ble_mesh_model_t *model);
+    /** Health attention on callback. Initialized by the stack. */
+    esp_ble_mesh_cb_t attention_on;
 
-    /** Attention off */
-    void (*attn_off)(esp_ble_mesh_model_t *model);
+    /** Health attention off callback. Initialized by the stack. */
+    esp_ble_mesh_cb_t attention_off;
 } esp_ble_mesh_health_srv_cb_t;
 
-/** Health Server Model Context */
+#define ESP_BLE_MESH_HEALTH_FAULT_ARRAY_SIZE    32
+
+/** ESP BLE Mesh Health Server test Context */
+typedef struct {
+    uint8_t  id_count;          /*!< Number of Health self-test ID */
+    const uint8_t *test_ids;    /*!< Array of Health self-test IDs */
+    uint16_t company_id;        /*!< Company ID used to identify the Health Fault state */
+    uint8_t  prev_test_id;      /*!< Current test ID of the health fault test */
+    uint8_t  current_faults[ESP_BLE_MESH_HEALTH_FAULT_ARRAY_SIZE];      /*!< Array of current faults */
+    uint8_t  registered_faults[ESP_BLE_MESH_HEALTH_FAULT_ARRAY_SIZE];   /*!< Array of registered faults */
+} __attribute__((packed)) esp_ble_mesh_health_test_t;
+
+/** ESP BLE Mesh Health Server Model Context */
 typedef struct {
     /** Pointer to Health Server Model */
     esp_ble_mesh_model_t *model;
 
-    /** Optional callback struct */
-    const esp_ble_mesh_health_srv_cb_t *cb;
+    /** Health callback struct */
+    esp_ble_mesh_health_srv_cb_t health_cb;
 
     /** Attention Timer state */
-    struct k_delayed_work attn_timer;
+    struct k_delayed_work attention_timer;
+
+    /** Attention Timer start flag */
+    bool attention_timer_start;
+
+    /** Health Server fault test */
+    esp_ble_mesh_health_test_t health_test;
 } esp_ble_mesh_health_srv_t;
 
 /** Parameter of Health Fault Get */
@@ -186,14 +270,54 @@ typedef enum {
     ESP_BLE_MESH_HEALTH_CLIENT_EVT_MAX,
 } esp_ble_mesh_health_client_cb_event_t;
 
-/** Health Server Model callback parameter */
+/** Parameter of publishing Health Current Status completion event */
 typedef struct {
-    int error_code;                                       /*!< Appropriate error code */
+    int error_code;                 /*!< The result of publishing Health Current Status */
+    esp_ble_mesh_elem_t *element;   /*!< Pointer to the element which contains the Health Server Model */
+} esp_ble_mesh_health_fault_update_comp_cb_t;
+
+/** Parameters of Health Fault Clear event */
+typedef struct {
+    esp_ble_mesh_model_t *model;    /*!< Pointer to the Health Server Model */
+    uint16_t company_id;            /*!< Bluetooth assigned 16-bit Company ID */
+} esp_ble_mesh_health_fault_clear_cb_t;
+
+/** Parameters of Health Fault Test event */
+typedef struct {
+    esp_ble_mesh_model_t *model;    /*!< Pointer to the Health Server Model */
+    uint8_t  test_id;               /*!< ID of a specific test to be performed */
+    uint16_t company_id;            /*!< Bluetooth assigned 16-bit Company ID */
+} esp_ble_mesh_health_fault_test_cb_t;
+
+/** Parameter of Health Attention On event */
+typedef struct {
+    esp_ble_mesh_model_t *model;    /*!< Pointer to the Health Server Model */
+    uint8_t time;                   /*!< Duration of attention timer on (in seconds) */
+} esp_ble_mesh_health_attention_on_cb_t;
+
+/** Parameter of Health Attention Off event */
+typedef struct {
+    esp_ble_mesh_model_t *model;    /*!< Pointer to the Health Server Model */
+} esp_ble_mesh_health_attention_off_cb_t;
+
+/**
+ * @brief Health Server Model callback parameters union
+ */
+typedef union {
+    esp_ble_mesh_health_fault_update_comp_cb_t  fault_update_comp;  /*!< ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMP_EVT */
+    esp_ble_mesh_health_fault_clear_cb_t        fault_clear;        /*!< ESP_BLE_MESH_HEALTH_SERVER_FAULT_CLEAR_EVT */
+    esp_ble_mesh_health_fault_test_cb_t         fault_test;         /*!< ESP_BLE_MESH_HEALTH_SERVER_FAULT_TEST_EVT */
+    esp_ble_mesh_health_attention_on_cb_t       attention_on;       /*!< ESP_BLE_MESH_HEALTH_SERVER_ATTENTION_ON_EVT */
+    esp_ble_mesh_health_attention_off_cb_t      attention_off;      /*!< ESP_BLE_MESH_HEALTH_SERVER_ATTENTION_OFF_EVT */
 } esp_ble_mesh_health_server_cb_param_t;
 
 /** This enum value is the event of Health Server Model */
 typedef enum {
-    ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMPLETE_EVT,
+    ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMP_EVT,
+    ESP_BLE_MESH_HEALTH_SERVER_FAULT_CLEAR_EVT,
+    ESP_BLE_MESH_HEALTH_SERVER_FAULT_TEST_EVT,
+    ESP_BLE_MESH_HEALTH_SERVER_ATTENTION_ON_EVT,
+    ESP_BLE_MESH_HEALTH_SERVER_ATTENTION_OFF_EVT,
     ESP_BLE_MESH_HEALTH_SERVER_EVT_MAX,
 } esp_ble_mesh_health_server_cb_event_t;
 
@@ -270,7 +394,7 @@ esp_err_t esp_ble_mesh_health_client_set_state(esp_ble_mesh_client_common_param_
         esp_ble_mesh_health_client_set_state_t *set_state);
 
 /**
- * @brief         This function is called by the Health Server Model to start to publish its Current Health Fault.
+ * @brief         This function is called by the Health Server Model to update the context of its Health Current status.
  *
  * @param[in]     element: The element to which the Health Server Model belongs.
  *

+ 47 - 7
components/bt/esp_ble_mesh/btc/btc_ble_mesh_health_model.c

@@ -517,7 +517,7 @@ static void btc_ble_mesh_health_server_copy_req_data(btc_msg_t *msg, void *p_des
     }
 
     switch (msg->act) {
-    case ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMPLETE_EVT:
+    case ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMP_EVT:
         break;
     default:
         break;
@@ -532,7 +532,7 @@ static void btc_ble_mesh_health_server_free_req_data(btc_msg_t *msg)
     }
 
     switch (msg->act) {
-    case ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMPLETE_EVT:
+    case ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMP_EVT:
         break;
     default:
         break;
@@ -555,7 +555,7 @@ static void btc_ble_mesh_health_server_callback(esp_ble_mesh_health_server_cb_pa
 
 void btc_ble_mesh_health_server_call_handler(btc_msg_t *msg)
 {
-    esp_ble_mesh_health_server_cb_param_t health_server_cb = {0};
+    esp_ble_mesh_health_server_cb_param_t param = {0};
     btc_ble_mesh_health_server_args_t *arg = NULL;
 
     if (!msg || !msg->arg) {
@@ -567,10 +567,10 @@ void btc_ble_mesh_health_server_call_handler(btc_msg_t *msg)
 
     switch (msg->act) {
     case BTC_BLE_MESH_ACT_HEALTH_SERVER_FAULT_UPDATE:
-        health_server_cb.error_code =
-            bt_mesh_fault_update((struct bt_mesh_elem *)arg->fault_update.element);
-        btc_ble_mesh_health_server_callback(
-            &health_server_cb, ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMPLETE_EVT);
+        param.fault_update_comp.element = arg->health_fault_update.element;
+        param.fault_update_comp.error_code =
+            bt_mesh_fault_update((struct bt_mesh_elem *)arg->health_fault_update.element);
+        btc_ble_mesh_health_server_callback(&param, ESP_BLE_MESH_HEALTH_SERVER_FAULT_UPDATE_COMP_EVT);
         break;
     default:
         break;
@@ -600,3 +600,43 @@ void btc_ble_mesh_health_server_cb_handler(btc_msg_t *msg)
     btc_ble_mesh_health_server_free_req_data(msg);
     return;
 }
+
+void btc_ble_mesh_health_server_fault_clear(struct bt_mesh_model *model, u16_t company_id)
+{
+    esp_ble_mesh_health_server_cb_param_t param = {0};
+
+    param.fault_clear.model = (esp_ble_mesh_model_t *)model;
+    param.fault_clear.company_id = company_id;
+
+    btc_ble_mesh_health_server_callback(&param, ESP_BLE_MESH_HEALTH_SERVER_FAULT_CLEAR_EVT);
+}
+
+void btc_ble_mesh_health_server_fault_test(struct bt_mesh_model *model, u8_t test_id, u16_t company_id)
+{
+    esp_ble_mesh_health_server_cb_param_t param = {0};
+
+    param.fault_test.model = (esp_ble_mesh_model_t *)model;
+    param.fault_test.test_id = test_id;
+    param.fault_test.company_id = company_id;
+
+    btc_ble_mesh_health_server_callback(&param, ESP_BLE_MESH_HEALTH_SERVER_FAULT_TEST_EVT);
+}
+
+void btc_ble_mesh_health_server_attention_on(struct bt_mesh_model *model, u8_t time)
+{
+    esp_ble_mesh_health_server_cb_param_t param = {0};
+
+    param.attention_on.model = (esp_ble_mesh_model_t *)model;
+    param.attention_on.time = time;
+
+    btc_ble_mesh_health_server_callback(&param, ESP_BLE_MESH_HEALTH_SERVER_ATTENTION_ON_EVT);
+}
+
+void btc_ble_mesh_health_server_attention_off(struct bt_mesh_model *model)
+{
+    esp_ble_mesh_health_server_cb_param_t param = {0};
+
+    param.attention_off.model = (esp_ble_mesh_model_t *)model;
+
+    btc_ble_mesh_health_server_callback(&param, ESP_BLE_MESH_HEALTH_SERVER_ATTENTION_OFF_EVT);
+}

+ 8 - 0
components/bt/esp_ble_mesh/btc/btc_ble_mesh_prov.c

@@ -31,6 +31,7 @@
 #include "mesh_proxy.h"
 #include "cfg_cli.h"
 #include "health_cli.h"
+#include "health_srv.h"
 
 #include "mesh.h"
 #include "access.h"
@@ -975,6 +976,13 @@ static void btc_ble_mesh_model_op_add(esp_ble_mesh_model_t *model)
     }
     case BLE_MESH_MODEL_ID_HEALTH_SRV: {
         model->op = (esp_ble_mesh_model_op_t *)bt_mesh_health_srv_op;
+        struct bt_mesh_health_srv *srv = (struct bt_mesh_health_srv *)model->user_data;
+        if (srv) {
+            srv->cb.fault_clear = btc_ble_mesh_health_server_fault_clear;
+            srv->cb.fault_test = btc_ble_mesh_health_server_fault_test;
+            srv->cb.attn_on = btc_ble_mesh_health_server_attention_on;
+            srv->cb.attn_off = btc_ble_mesh_health_server_attention_off;
+        }
         break;
     }
     case BLE_MESH_MODEL_ID_HEALTH_CLI: {

+ 9 - 1
components/bt/esp_ble_mesh/btc/include/btc_ble_mesh_health_model.h

@@ -68,7 +68,7 @@ typedef enum {
 typedef union {
     struct ble_mesh_health_server_fault_update_args {
         esp_ble_mesh_elem_t *element;
-    } fault_update;
+    } health_fault_update;
 } btc_ble_mesh_health_server_args_t;
 
 void btc_ble_mesh_health_server_call_handler(btc_msg_t *msg);
@@ -77,4 +77,12 @@ void btc_ble_mesh_health_server_cb_handler(btc_msg_t *msg);
 
 void btc_ble_mesh_health_server_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src);
 
+void btc_ble_mesh_health_server_fault_clear(struct bt_mesh_model *model, u16_t company_id);
+
+void btc_ble_mesh_health_server_fault_test(struct bt_mesh_model *model, u8_t test_id, u16_t company_id);
+
+void btc_ble_mesh_health_server_attention_on(struct bt_mesh_model *model, u8_t time);
+
+void btc_ble_mesh_health_server_attention_off(struct bt_mesh_model *model);
+
 #endif /* _BTC_BLE_MESH_HEALTH_MODEL_H_ */

+ 190 - 193
components/bt/esp_ble_mesh/mesh_core/health_srv.c

@@ -27,130 +27,112 @@
 #include "foundation.h"
 #include "mesh_common.h"
 
-#define HEALTH_TEST_STANDARD 0x00
+#include "btc_ble_mesh_health_model.h"
 
-/* Maximum message length is 384 in BLE Mesh. Here for health fault status,
- * due to 1 octet opcode and 4 octets TransMIC, 379 octets can be used to
- * store health fault status.
- */
-#define HEALTH_FAULT_MAX_LEN    379
+#define HEALTH_TEST_STANDARD    0x00
+
+#define HEALTH_NO_FAULT         0x00
 
 /* Health Server context of the primary element */
 struct bt_mesh_health_srv *health_srv;
 
-static void health_get_registered(struct bt_mesh_model *mod,
-                                  u16_t company_id,
-                                  struct net_buf_simple *msg)
-{
-    struct bt_mesh_health_srv *srv = mod->user_data;
-    u8_t *test_id;
-
-    BT_DBG("Company ID 0x%04x", company_id);
-
-    if (!srv) {
-        BT_ERR("%s, No Health Server context provided", __func__);
-        return;
-    }
+/**
+ * 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.
+ */
 
-    bt_mesh_model_msg_init(msg, OP_HEALTH_FAULT_STATUS);
+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 = 0;
+    size_t i;
 
-    test_id = net_buf_simple_add(msg, 1);
-    net_buf_simple_add_le16(msg, company_id);
-
-    if (srv->cb && srv->cb->fault_get_reg) {
-        u8_t fault_count = net_buf_simple_tailroom(msg) - 4;
-        int err;
-
-        err = srv->cb->fault_get_reg(mod, company_id, test_id,
-                                     net_buf_simple_tail(msg),
-                                     &fault_count);
-        if (err) {
-            BT_ERR("%s, Failed to get faults (err %d)", __func__, err);
-            *test_id = HEALTH_TEST_STANDARD;
-        } else {
-            net_buf_simple_add(msg, fault_count);
+    for (i = 0U; i < ARRAY_SIZE(srv->test.curr_faults); i++) {
+        if (srv->test.curr_faults[i] != HEALTH_NO_FAULT) {
+            count++;
         }
-    } else {
-        BT_WARN("No callback for getting faults");
-        *test_id = HEALTH_TEST_STANDARD;
     }
+
+    return count;
 }
 
-static size_t health_get_current(struct bt_mesh_model *mod,
-                                 struct net_buf_simple *msg)
+static void health_get_fault_value(struct bt_mesh_model *model,
+                                   struct net_buf_simple *msg,
+                                   bool current)
 {
-    struct bt_mesh_health_srv *srv = mod->user_data;
-    const struct bt_mesh_comp *comp;
-    u8_t *test_id, *company_ptr;
-    u16_t company_id;
-    u8_t fault_count;
-    int err;
+    struct bt_mesh_health_srv *srv = model->user_data;
+    size_t array_size;
+    size_t i;
 
-    if (!srv) {
-        BT_ERR("%s, No Health Server context provided", __func__);
-        return 0;
-    }
+    array_size = current ? ARRAY_SIZE(srv->test.curr_faults) : ARRAY_SIZE(srv->test.reg_faults);
 
-    bt_mesh_model_msg_init(msg, OP_HEALTH_CURRENT_STATUS);
+    for (i = 0U; i < array_size; i++) {
+        if (net_buf_simple_tailroom(msg) == 0) {
+            return;
+        }
 
-    test_id = net_buf_simple_add(msg, 1);
-    company_ptr = net_buf_simple_add(msg, sizeof(company_id));
-    comp = bt_mesh_comp_get();
-
-    if (srv->cb && srv->cb->fault_get_cur) {
-        fault_count = net_buf_simple_tailroom(msg);
-        err = srv->cb->fault_get_cur(mod, test_id, &company_id,
-                                     net_buf_simple_tail(msg),
-                                     &fault_count);
-        if (err) {
-            BT_ERR("%s, Failed to get faults (err %d)", __func__, err);
-            sys_put_le16(comp->cid, company_ptr);
-            *test_id = HEALTH_TEST_STANDARD;
-            fault_count = 0U;
-        } else {
-            sys_put_le16(company_id, company_ptr);
-            net_buf_simple_add(msg, fault_count);
+        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);
         }
-    } else {
-        BT_WARN("No callback for getting faults");
-        sys_put_le16(comp->cid, company_ptr);
-        *test_id = HEALTH_TEST_STANDARD;
-        fault_count = 0U;
     }
-
-    return fault_count;
 }
 
-static void health_fault_get(struct bt_mesh_model *model,
-                             struct bt_mesh_msg_ctx *ctx,
-                             struct net_buf_simple *buf)
+static bool health_is_test_id_exist(struct bt_mesh_model *model, u8_t test_id)
 {
-    struct net_buf_simple *sdu = NULL;
-    u16_t company_id;
+    struct bt_mesh_health_srv *srv = model->user_data;
+    u8_t i;
 
-    company_id = net_buf_simple_pull_le16(buf);
+    for (i = 0U; i < srv->test.id_count; i++) {
+        if (srv->test.test_ids[i] == test_id) {
+            return true;
+        }
+    }
 
-    BT_DBG("company_id 0x%04x", company_id);
+    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;
 
-    sdu = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, HEALTH_FAULT_MAX_LEN));
-    if (!sdu) {
+    msg = bt_mesh_alloc_buf(4 + ARRAY_SIZE(srv->test.reg_faults) + 4);
+    if (!msg) {
         BT_ERR("%s, Failed to allocate memory", __func__);
-        return;
+        return -ENOMEM;
     }
 
-    health_get_registered(model, company_id, sdu);
+    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);
+    }
 
-    if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) {
-        BT_ERR("%s, Unable to send Health Current Status", __func__);
+    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(sdu);
-    return;
+    bt_mesh_free_buf(msg);
+    return err;
 }
 
-static void health_fault_clear_unrel(struct bt_mesh_model *model,
-                                     struct bt_mesh_msg_ctx *ctx,
-                                     struct net_buf_simple *buf)
+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;
@@ -161,12 +143,14 @@ static void health_fault_clear_unrel(struct bt_mesh_model *model,
     }
 
     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);
 
-    if (srv->cb && srv->cb->fault_clear) {
-        srv->cb->fault_clear(model, company_id);
-    }
+    health_send_fault_status(model, ctx);
 }
 
 static void health_fault_clear(struct bt_mesh_model *model,
@@ -174,7 +158,6 @@ static void health_fault_clear(struct bt_mesh_model *model,
                                struct net_buf_simple *buf)
 {
     struct bt_mesh_health_srv *srv = model->user_data;
-    struct net_buf_simple *sdu = NULL;
     u16_t company_id;
 
     if (!srv) {
@@ -183,49 +166,21 @@ static void health_fault_clear(struct bt_mesh_model *model,
     }
 
     company_id = net_buf_simple_pull_le16(buf);
-
-    BT_DBG("company_id 0x%04x", company_id);
-
-    if (srv->cb && srv->cb->fault_clear) {
-        srv->cb->fault_clear(model, company_id);
-    }
-
-    sdu = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, HEALTH_FAULT_MAX_LEN));
-    if (!sdu) {
-        BT_ERR("%s, Failed to allocate memory", __func__);
+    if (company_id != srv->test.company_id) {
+        BT_ERR("%s, Unknown Company ID 0x%04x", __func__, company_id);
         return;
     }
 
-    health_get_registered(model, company_id, sdu);
-
-    if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) {
-        BT_ERR("%s, Unable to send Health Current Status", __func__);
-    }
+    BT_DBG("company_id 0x%04x", company_id);
 
-    bt_mesh_free_buf(sdu);
-    return;
-}
+    memset(srv->test.reg_faults, HEALTH_NO_FAULT, ARRAY_SIZE(srv->test.reg_faults));
 
-static void health_fault_test_unrel(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;
-    u8_t test_id;
-
-    if (!srv) {
-        BT_ERR("%s, No Health Server context provided", __func__);
-        return;
+    if (srv->cb.fault_clear) {
+        srv->cb.fault_clear(model, company_id);
     }
 
-    test_id = net_buf_simple_pull_u8(buf);
-    company_id = net_buf_simple_pull_le16(buf);
-
-    BT_DBG("test 0x%02x company 0x%04x", test_id, company_id);
-
-    if (srv->cb && srv->cb->fault_test) {
-        srv->cb->fault_test(model, test_id, company_id);
+    if (ctx->recv_op == OP_HEALTH_FAULT_CLEAR) {
+        health_send_fault_status(model, ctx);
     }
 }
 
@@ -234,7 +189,6 @@ static void health_fault_test(struct bt_mesh_model *model,
                               struct net_buf_simple *buf)
 {
     struct bt_mesh_health_srv *srv = model->user_data;
-    struct net_buf_simple *sdu = NULL;
     u16_t company_id;
     u8_t test_id;
 
@@ -246,34 +200,28 @@ static void health_fault_test(struct bt_mesh_model *model,
     }
 
     test_id = net_buf_simple_pull_u8(buf);
-    company_id = net_buf_simple_pull_le16(buf);
-
-    BT_DBG("test 0x%02x company 0x%04x", test_id, company_id);
-
-    if (srv->cb && srv->cb->fault_test) {
-        int err;
-
-        err = srv->cb->fault_test(model, test_id, company_id);
-        if (err) {
-            BT_WARN("Running fault test failed with err %d", err);
-            return;
-        }
+    if (health_is_test_id_exist(model, test_id) == false) {
+        BT_ERR("%s, Unknown Test ID 0x%02x", __func__, test_id);
+        return;
     }
 
-    sdu = bt_mesh_alloc_buf(MIN(BLE_MESH_TX_SDU_MAX, HEALTH_FAULT_MAX_LEN));
-    if (!sdu) {
-        BT_ERR("%s, Failed to allocate memory", __func__);
+    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;
     }
 
-    health_get_registered(model, company_id, sdu);
+    BT_DBG("test 0x%02x company 0x%04x", test_id, company_id);
+
+    srv->test.prev_test_id = test_id;
 
-    if (bt_mesh_model_send(model, ctx, sdu, NULL, NULL)) {
-        BT_ERR("%s, Unable to send Health Current Status", __func__);
+    if (srv->cb.fault_test) {
+        srv->cb.fault_test(model, test_id, company_id);
     }
 
-    bt_mesh_free_buf(sdu);
-    return;
+    if (ctx->recv_op == OP_HEALTH_FAULT_TEST) {
+        health_send_fault_status(model, ctx);
+    }
 }
 
 static void send_attention_status(struct bt_mesh_model *model,
@@ -293,7 +241,6 @@ static void send_attention_status(struct bt_mesh_model *model,
     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)) {
@@ -310,9 +257,9 @@ static void attention_get(struct bt_mesh_model *model,
     send_attention_status(model, ctx);
 }
 
-static void attention_set_unrel(struct bt_mesh_model *model,
-                                struct bt_mesh_msg_ctx *ctx,
-                                struct net_buf_simple *buf)
+static void health_set_attention(struct bt_mesh_model *model,
+                                 struct bt_mesh_msg_ctx *ctx,
+                                 struct net_buf_simple *buf)
 {
     u8_t time;
 
@@ -329,9 +276,11 @@ static void attention_set(struct bt_mesh_model *model,
 {
     BT_DBG("%s", __func__);
 
-    attention_set_unrel(model, ctx, buf);
+    health_set_attention(model, ctx, buf);
 
-    send_attention_status(model, ctx);
+    if (ctx->recv_op == OP_ATTENTION_SET) {
+        send_attention_status(model, ctx);
+    }
 }
 
 static void send_health_period_status(struct bt_mesh_model *model,
@@ -341,7 +290,6 @@ static void send_health_period_status(struct bt_mesh_model *model,
     NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4);
 
     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)) {
@@ -358,9 +306,9 @@ static void health_period_get(struct bt_mesh_model *model,
     send_health_period_status(model, ctx);
 }
 
-static void health_period_set_unrel(struct bt_mesh_model *model,
-                                    struct bt_mesh_msg_ctx *ctx,
-                                    struct net_buf_simple *buf)
+static void health_set_period(struct bt_mesh_model *model,
+                              struct bt_mesh_msg_ctx *ctx,
+                              struct net_buf_simple *buf)
 {
     u8_t period;
 
@@ -381,34 +329,64 @@ static void health_period_set(struct bt_mesh_model *model,
 {
     BT_DBG("%s", __func__);
 
-    health_period_set_unrel(model, ctx, buf);
+    health_set_period(model, ctx, buf);
 
-    send_health_period_status(model, ctx);
+    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_GET,         2,   health_fault_get   },
     { OP_HEALTH_FAULT_CLEAR,       2,   health_fault_clear },
-    { OP_HEALTH_FAULT_CLEAR_UNREL, 2,   health_fault_clear_unrel },
-    { OP_HEALTH_FAULT_TEST,        3,   health_fault_test },
-    { OP_HEALTH_FAULT_TEST_UNREL,  3,   health_fault_test_unrel },
-    { 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_unrel },
-    { OP_ATTENTION_GET,            0,   attention_get },
-    { OP_ATTENTION_SET,            1,   attention_set },
-    { OP_ATTENTION_SET_UNREL,      1,   attention_set_unrel },
+    { 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 int health_pub_update(struct bt_mesh_model *mod)
+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 = mod->pub;
+    struct bt_mesh_model_pub *pub = model->pub;
     size_t count;
 
     BT_DBG("%s", __func__);
 
-    count = health_get_current(mod, pub->msg);
+    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 {
@@ -420,29 +398,29 @@ static int health_pub_update(struct bt_mesh_model *mod)
 
 int bt_mesh_fault_update(struct bt_mesh_elem *elem)
 {
-    struct bt_mesh_model *mod;
+    struct bt_mesh_model *model;
 
-    mod = bt_mesh_model_find(elem, BLE_MESH_MODEL_ID_HEALTH_SRV);
-    if (!mod) {
+    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 (!mod->pub) {
+    if (!model->pub) {
         BT_ERR("%s, Health Server has no publication support", __func__);
-        return -EIO;
+        return -EINVAL;
     }
 
     /* Let periodic publishing, if enabled, take care of sending the
      * Health Current Status.
      */
-    if (bt_mesh_model_pub_period_get(mod)) {
+    if (bt_mesh_model_pub_period_get(model)) {
         return 0;
     }
 
-    health_pub_update(mod);
+    health_pub_update(model);
 
-    return bt_mesh_model_publish(mod);
+    return bt_mesh_model_publish(model);
 }
 
 static void attention_off(struct k_work *work)
@@ -457,17 +435,23 @@ static void attention_off(struct k_work *work)
         return;
     }
 
-    if (srv->cb && srv->cb->attn_off) {
-        srv->cb->attn_off(srv->model);
+    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;
         }
 
@@ -475,6 +459,11 @@ int bt_mesh_health_srv_init(struct bt_mesh_model *model, bool primary)
         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;
@@ -485,6 +474,10 @@ int bt_mesh_health_srv_init(struct bt_mesh_model *model, bool primary)
     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;
@@ -514,16 +507,20 @@ void bt_mesh_attention(struct bt_mesh_model *model, u8_t time)
     }
 
     if (time) {
-        if (srv->cb && srv->cb->attn_on) {
-            srv->cb->attn_on(model);
+        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->cb && srv->cb->attn_off) {
-            srv->cb->attn_off(model);
+        if (srv->attn_timer_start == true) {
+            if (srv->cb.attn_off) {
+                srv->cb.attn_off(model);
+            }
+            srv->attn_timer_start = false;
         }
     }
 }

+ 20 - 15
components/bt/esp_ble_mesh/mesh_core/include/health_srv.h

@@ -21,25 +21,15 @@
  */
 
 struct bt_mesh_health_srv_cb {
-    /* Fetch current faults */
-    int (*fault_get_cur)(struct bt_mesh_model *model, u8_t *test_id,
-                         u16_t *company_id, u8_t *faults,
-                         u8_t *fault_count);
-
-    /* Fetch registered faults */
-    int (*fault_get_reg)(struct bt_mesh_model *model, u16_t company_id,
-                         u8_t *test_id, u8_t *faults,
-                         u8_t *fault_count);
-
     /* Clear registered faults */
-    int (*fault_clear)(struct bt_mesh_model *model, u16_t company_id);
+    void (*fault_clear)(struct bt_mesh_model *model, u16_t company_id);
 
     /* Run a specific test */
-    int (*fault_test)(struct bt_mesh_model *model, u8_t test_id,
-                      u16_t company_id);
+    void (*fault_test)(struct bt_mesh_model *model, u8_t test_id,
+                       u16_t company_id);
 
     /* Attention on */
-    void (*attn_on)(struct bt_mesh_model *model);
+    void (*attn_on)(struct bt_mesh_model *model, u8_t time);
 
     /* Attention off */
     void (*attn_off)(struct bt_mesh_model *model);
@@ -55,15 +45,30 @@ struct bt_mesh_health_srv_cb {
 #define BLE_MESH_HEALTH_PUB_DEFINE(_name, _max_faults) \
     BLE_MESH_MODEL_PUB_DEFINE(_name, NULL, (1 + 3 + (_max_faults)))
 
+struct bt_mesh_health_test {
+    u8_t  id_count;         /* Number of Health self-test ID */
+    const u8_t *test_ids;   /* Array of Health self-test IDs */
+    u16_t company_id;       /* Company ID used to identify the Health Fault state */
+    u8_t  prev_test_id;     /* Most currently performed test id */
+    u8_t  curr_faults[32];  /* Array of current faults */
+    u8_t  reg_faults[32];   /* Array of registered faults */
+} __attribute__((packed));
+
 /** Mesh Health Server Model Context */
 struct bt_mesh_health_srv {
     struct bt_mesh_model *model;
 
     /* Optional callback struct */
-    const struct bt_mesh_health_srv_cb *cb;
+    struct bt_mesh_health_srv_cb cb;
 
     /* Attention Timer state */
     struct k_delayed_work attn_timer;
+
+    /* Attention Timer start flag */
+    bool attn_timer_start;
+
+    /* Health Server fault test */
+    struct bt_mesh_health_test test;
 };
 
 extern const struct bt_mesh_model_op bt_mesh_health_srv_op[];