1 /* 2 * Copyright (C) 2019 BlueKitchen GmbH 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of the copyright holders nor the names of 14 * contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 4. Any redistribution, use, or modification is done solely for 17 * personal benefit and not for any commercial purpose or for 18 * monetary gain. 19 * 20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN 24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * Please inquire about commercial licensing options at 34 * [email protected] 35 * 36 */ 37 38 #define BTSTACK_FILE__ "mesh_health_server.c" 39 40 #include <string.h> 41 #include <stdio.h> 42 43 #include "mesh/mesh_health_server.h" 44 45 #include "bluetooth_company_id.h" 46 #include "btstack_debug.h" 47 #include "btstack_memory.h" 48 #include "btstack_util.h" 49 50 #include "mesh/mesh.h" 51 #include "mesh/mesh_access.h" 52 #include "mesh/mesh_node.h" 53 #include "mesh/mesh_foundation.h" 54 #include "mesh/mesh_generic_model.h" 55 #include "mesh/mesh_generic_on_off_server.h" 56 #include "mesh/mesh_keys.h" 57 #include "mesh/mesh_network.h" 58 #include "mesh/mesh_upper_transport.h" 59 60 static void health_server_send_message(uint16_t src, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, mesh_pdu_t *pdu){ 61 uint8_t ttl = mesh_foundation_default_ttl_get(); 62 mesh_upper_transport_setup_access_pdu_header(pdu, netkey_index, appkey_index, ttl, src, dest, 0); 63 mesh_access_send_unacknowledged_pdu(pdu); 64 } 65 66 static mesh_health_fault_t * mesh_health_server_fault_for_company_id(mesh_model_t *mesh_model, uint16_t company_id){ 67 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 68 btstack_linked_list_iterator_t it; 69 btstack_linked_list_iterator_init(&it, &state->faults); 70 while (btstack_linked_list_iterator_has_next(&it)){ 71 mesh_health_fault_t * fault = (mesh_health_fault_t *) btstack_linked_list_iterator_next(&it); 72 if (fault->company_id == company_id) return fault; 73 } 74 return NULL; 75 } 76 static mesh_health_fault_t * mesh_health_server_active_fault(mesh_model_t *mesh_model){ 77 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 78 btstack_linked_list_iterator_t it; 79 btstack_linked_list_iterator_init(&it, &state->faults); 80 while (btstack_linked_list_iterator_has_next(&it)){ 81 mesh_health_fault_t * fault = (mesh_health_fault_t *) btstack_linked_list_iterator_next(&it); 82 if (fault->num_current_faults > 0) return fault; 83 } 84 return NULL; 85 } 86 87 static void mesh_health_server_update_publication_model_period_divisor(mesh_model_t * mesh_model){ 88 if (mesh_model->publication_model == NULL) return; 89 mesh_health_fault_t * fault = mesh_health_server_active_fault(mesh_model); 90 mesh_health_state_t * health_state = (mesh_health_state_t *) mesh_model->model_data; 91 if (fault == NULL){ 92 mesh_model->publication_model->period_divisor = health_state->fast_period_divisor; 93 } else { 94 mesh_model->publication_model->period_divisor = 0; 95 } 96 } 97 98 99 // Health State 100 const mesh_access_message_t mesh_foundation_health_period_status = { 101 MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_STATUS, "1" 102 }; 103 104 const mesh_access_message_t mesh_foundation_health_attention_status = { 105 MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_STATUS, "1" 106 }; 107 108 static mesh_pdu_t * health_period_status(mesh_model_t * mesh_model){ 109 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 110 // setup message 111 mesh_upper_transport_pdu_t * message_pdu = mesh_access_setup_message(&mesh_foundation_health_period_status, state->fast_period_divisor); 112 return (mesh_pdu_t *) message_pdu; 113 } 114 115 static mesh_pdu_t * health_attention_status(void){ 116 // setup message 117 mesh_upper_transport_pdu_t * message_pdu = mesh_access_setup_message(&mesh_foundation_health_attention_status, mesh_attention_timer_get()); 118 return (mesh_pdu_t *) message_pdu; 119 } 120 121 // report fault status - used for both current as well as registered faults, see registered_faults param 122 static mesh_pdu_t * health_fault_status(mesh_model_t * mesh_model, uint32_t opcode, uint16_t company_id, bool registered_faults){ 123 mesh_upper_transport_builder_t builder; 124 mesh_access_message_init(&builder, opcode); 125 126 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 127 if (fault == NULL){ 128 // no fault state with company_id found 129 mesh_access_message_add_uint8(&builder, 0); 130 mesh_access_message_add_uint16(&builder, company_id); 131 } else { 132 mesh_access_message_add_uint8(&builder, fault->test_id); 133 mesh_access_message_add_uint16(&builder, fault->company_id); 134 int i; 135 if (registered_faults){ 136 for (i = 0; i < fault->num_registered_faults; i++){ 137 mesh_access_message_add_uint8(&builder, fault->registered_faults[i]); 138 } 139 } else { 140 for (i = 0; i < fault->num_current_faults; i++){ 141 mesh_access_message_add_uint8(&builder, fault->current_faults[i]); 142 } 143 } 144 } 145 146 mesh_upper_transport_pdu_t * upper_pdu = mesh_access_message_finalize(&builder); 147 148 return (mesh_pdu_t *) upper_pdu; 149 } 150 151 static void health_fault_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 152 mesh_access_parser_state_t parser; 153 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 154 uint16_t company_id = mesh_access_parser_get_uint16(&parser); 155 156 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id, true); 157 if (!transport_pdu) return; 158 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu); 159 mesh_access_message_processed(pdu); 160 } 161 162 static uint16_t process_message_fault_clear(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 163 mesh_access_parser_state_t parser; 164 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 165 uint16_t company_id = mesh_access_parser_get_uint16(&parser); 166 167 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 168 if (fault != NULL){ 169 fault->num_registered_faults = 0; 170 memset(fault->registered_faults, 0, sizeof(fault->registered_faults)); 171 } 172 return company_id; 173 } 174 175 static void health_fault_clear_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){ 176 uint16_t company_id = process_message_fault_clear(mesh_model, pdu); 177 178 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id, true); 179 if (!transport_pdu) return; 180 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu); 181 mesh_access_message_processed(pdu); 182 } 183 184 static void health_fault_clear_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 185 (void) process_message_fault_clear(mesh_model, pdu); 186 mesh_access_message_processed(pdu); 187 } 188 189 190 static void health_fault_test_process_message(mesh_model_t *mesh_model, mesh_pdu_t * pdu, bool acknowledged){ 191 mesh_access_parser_state_t parser; 192 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 193 uint8_t test_id = mesh_access_parser_get_uint8(&parser); 194 uint16_t company_id = mesh_access_parser_get_uint16(&parser); 195 196 uint16_t dest = mesh_pdu_src(pdu); 197 uint16_t netkey_index = mesh_pdu_netkey_index(pdu); 198 uint16_t appkey_index = mesh_pdu_appkey_index(pdu); 199 200 // check if fault state exists for company id 201 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 202 if (fault == NULL){ 203 return; 204 } 205 206 // short-cut if not packet handler set, but only for standard test 207 if (mesh_model->model_packet_handler == NULL){ 208 if (test_id == 0) { 209 mesh_health_server_report_test_done(dest, netkey_index, appkey_index, test_id, company_id, acknowledged); 210 } 211 return; 212 } 213 214 uint8_t event[13]; 215 int pos = 0; 216 event[pos++] = HCI_EVENT_MESH_META; 217 event[pos++] = sizeof(event) - 2; 218 event[pos++] = MESH_SUBEVENT_HEALTH_PERFORM_TEST; 219 220 little_endian_store_16(event, pos, dest); 221 pos += 2; 222 little_endian_store_16(event, pos, netkey_index); 223 pos += 2; 224 little_endian_store_16(event, pos, appkey_index); 225 pos += 2; 226 little_endian_store_16(event, pos, company_id); 227 pos += 2; 228 229 event[pos++] = test_id; 230 event[pos++] = acknowledged; 231 232 (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos); 233 } 234 235 static void health_fault_test_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 236 health_fault_test_process_message(mesh_model, pdu, true); 237 mesh_access_message_processed(pdu); 238 } 239 240 static void health_fault_test_unacknowledged_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){ 241 health_fault_test_process_message(mesh_model, pdu, false); 242 mesh_access_message_processed(pdu); 243 } 244 245 static void health_period_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 246 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_period_status(mesh_model); 247 if (!transport_pdu) return; 248 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu); 249 mesh_access_message_processed(pdu); 250 } 251 252 static void process_message_period_set(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 253 mesh_access_parser_state_t parser; 254 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 255 uint8_t fast_period_divisor = mesh_access_parser_get_uint8(&parser); 256 257 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 258 state->fast_period_divisor = fast_period_divisor; 259 260 mesh_health_server_update_publication_model_period_divisor(mesh_model); 261 } 262 263 static void health_period_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 264 process_message_period_set(mesh_model, pdu); 265 266 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_period_status(mesh_model); 267 if (!transport_pdu) return; 268 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu); 269 mesh_access_message_processed(pdu); 270 } 271 272 static void health_period_set_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 273 process_message_period_set(mesh_model, pdu); 274 mesh_access_message_processed(pdu); 275 } 276 277 static void health_attention_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 278 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_attention_status(); 279 if (!transport_pdu) return; 280 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu); 281 mesh_access_message_processed(pdu); 282 } 283 284 static void process_message_attention_set(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 285 mesh_access_parser_state_t parser; 286 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 287 uint8_t timer_s = mesh_access_parser_get_uint8(&parser); 288 mesh_attention_timer_set(timer_s); 289 290 if (mesh_model->model_packet_handler == NULL) return; 291 292 uint8_t event[4]; 293 int pos = 0; 294 event[pos++] = HCI_EVENT_MESH_META; 295 // reserve for size 296 pos++; 297 event[pos++] = MESH_SUBEVENT_HEALTH_ATTENTION_TIMER_CHANGED; 298 // element index 299 event[pos++] = mesh_model->element->element_index; 300 // element index 301 event[1] = pos - 2; 302 (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos); 303 } 304 305 static void health_attention_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 306 process_message_attention_set(mesh_model, pdu); 307 308 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_attention_status(); 309 if (!transport_pdu) return; 310 health_server_send_message(mesh_access_get_element_address(mesh_model), mesh_pdu_src(pdu), mesh_pdu_netkey_index(pdu), mesh_pdu_appkey_index(pdu),(mesh_pdu_t *) transport_pdu); 311 mesh_access_message_processed(pdu); 312 } 313 314 static void health_attention_set_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 315 process_message_attention_set(mesh_model, pdu); 316 mesh_access_message_processed(pdu); 317 } 318 319 static mesh_pdu_t * mesh_health_server_publish_state_fn(struct mesh_model * mesh_model){ 320 uint16_t company_id = mesh_node_get_company_id(); 321 mesh_health_fault_t * fault = mesh_health_server_active_fault(mesh_model); 322 if (fault != NULL){ 323 company_id = fault->company_id; 324 } 325 // create current status 326 return health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_CURRENT_STATUS, company_id, false); 327 } 328 329 // Health Message 330 static const mesh_operation_t mesh_health_model_operations[] = { 331 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_GET, 2, health_fault_get_handler }, 332 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_CLEAR, 2, health_fault_clear_handler }, 333 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_CLEAR_UNACKNOWLEDGED, 2, health_fault_clear_unacknowledged_handler }, 334 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_TEST, 3, health_fault_test_handler }, 335 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_TEST_UNACKNOWLEDGED, 3, health_fault_test_unacknowledged_handler }, 336 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_GET, 0, health_period_get_handler }, 337 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_SET, 1, health_period_set_handler }, 338 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_SET_UNACKNOWLEDGED, 1, health_period_set_unacknowledged_handler }, 339 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_GET, 0, health_attention_get_handler }, 340 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_SET, 1, health_attention_set_handler }, 341 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_SET_UNACKNOWLEDGED, 1, health_attention_set_unacknowledged_handler }, 342 { 0, 0, NULL } 343 }; 344 345 const mesh_operation_t * mesh_health_server_get_operations(void){ 346 return mesh_health_model_operations; 347 } 348 349 void mesh_health_server_register_packet_handler(mesh_model_t *mesh_model, btstack_packet_handler_t events_packet_handler){ 350 mesh_model->model_packet_handler = events_packet_handler; 351 } 352 353 void mesh_health_server_add_fault_state(mesh_model_t *mesh_model, uint16_t company_id, mesh_health_fault_t * fault_state){ 354 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 355 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 356 btstack_assert(fault == NULL); 357 (void) fault; 358 fault_state->company_id = company_id; 359 btstack_linked_list_add(&state->faults, (btstack_linked_item_t *) fault_state); 360 } 361 362 void mesh_health_server_set_fault(mesh_model_t *mesh_model, uint16_t company_id, uint8_t fault_code){ 363 uint16_t i; 364 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 365 btstack_assert(fault != NULL); 366 367 // add to registered faults 368 bool add_registered_fault = true; 369 for (i = 0; i < fault->num_registered_faults; i++){ 370 if (fault->registered_faults[i] == fault_code){ 371 add_registered_fault = false; 372 break; 373 } 374 } 375 if (add_registered_fault && (fault->num_registered_faults < MESH_MAX_NUM_FAULTS)){ 376 fault->registered_faults[fault->num_registered_faults] = fault_code; 377 fault->num_registered_faults++; 378 } 379 380 // add to current faults 381 bool add_current_fault = true; 382 for (i = 0; i < fault->num_current_faults; i++){ 383 if (fault->registered_faults[i] == fault_code){ 384 add_current_fault = false; 385 break; 386 } 387 } 388 if (add_current_fault && (fault->num_current_faults < MESH_MAX_NUM_FAULTS)){ 389 fault->registered_faults[fault->num_current_faults] = fault_code; 390 fault->num_current_faults++; 391 } 392 393 // update publication model 394 mesh_health_server_update_publication_model_period_divisor(mesh_model); 395 } 396 397 void mesh_health_server_clear_fault(mesh_model_t *mesh_model, uint16_t company_id, uint8_t fault_code){ 398 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 399 btstack_assert(fault != NULL); 400 401 // remove from current faults 402 uint16_t i; 403 bool shift_faults = false; 404 for (i = 0; i < fault->num_current_faults; i++){ 405 if (!shift_faults){ 406 if (fault->registered_faults[i] == fault_code){ 407 shift_faults = true; 408 } 409 } 410 if (i < (MESH_MAX_NUM_FAULTS - 1)){ 411 fault->registered_faults[i] = fault->registered_faults[i+1]; 412 } 413 } 414 415 // update count 416 if (shift_faults){ 417 fault->num_current_faults--; 418 } 419 420 // update publication model 421 mesh_health_server_update_publication_model_period_divisor(mesh_model); 422 } 423 424 void mesh_health_server_set_publication_model(mesh_model_t * mesh_model, mesh_publication_model_t * publication_model){ 425 btstack_assert(mesh_model != NULL); 426 btstack_assert(publication_model != NULL); 427 publication_model->publish_state_fn = &mesh_health_server_publish_state_fn; 428 mesh_model->publication_model = publication_model; 429 } 430 431 void mesh_health_server_report_test_done(uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint8_t test_id, uint16_t company_id, bool acknowledged){ 432 mesh_model_t * mesh_model = mesh_node_get_health_server(); 433 if (mesh_model == NULL) return; 434 435 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 436 fault->test_id = test_id; 437 438 // response for acknowledged health fault test 439 if (acknowledged){ 440 mesh_upper_transport_pdu_t * transport_pdu = (mesh_upper_transport_pdu_t *) health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id, company_id); 441 if (!transport_pdu) return; 442 health_server_send_message(mesh_node_get_primary_element_address(), dest, netkey_index, appkey_index, (mesh_pdu_t *) transport_pdu); 443 } 444 } 445