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 MATTHIAS 24 * RINGWALD 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_network_pdu_t * transport_pdu = mesh_access_setup_unsegmented_message(&mesh_foundation_health_period_status, state->fast_period_divisor); 112 return (mesh_pdu_t *) transport_pdu; 113 } 114 115 static mesh_pdu_t * health_attention_status(void){ 116 // setup message 117 mesh_network_pdu_t * transport_pdu = mesh_access_setup_unsegmented_message(&mesh_foundation_health_attention_status, mesh_attention_timer_get()); 118 return (mesh_pdu_t *) transport_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_network_pdu_t * transport_pdu = mesh_access_network_init(opcode); 124 if (!transport_pdu) return NULL; 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_network_add_uint8(transport_pdu, 0); 130 mesh_access_network_add_uint16(transport_pdu, company_id); 131 } else { 132 mesh_access_network_add_uint8(transport_pdu, fault->test_id); 133 mesh_access_network_add_uint16(transport_pdu, fault->company_id); 134 int i; 135 if (registered_faults){ 136 for (i = 0; i < fault->num_registered_faults; i++){ 137 mesh_access_network_add_uint8(transport_pdu, fault->registered_faults[i]); 138 } 139 } else { 140 for (i = 0; i < fault->num_current_faults; i++){ 141 mesh_access_network_add_uint8(transport_pdu, fault->current_faults[i]); 142 } 143 } 144 } 145 return (mesh_pdu_t *) transport_pdu; 146 } 147 148 static void health_fault_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 149 mesh_access_parser_state_t parser; 150 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 151 uint16_t company_id = mesh_access_parser_get_u16(&parser); 152 153 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id, true); 154 if (!transport_pdu) return; 155 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); 156 mesh_access_message_processed(pdu); 157 } 158 159 static uint16_t process_message_fault_clear(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 160 mesh_access_parser_state_t parser; 161 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 162 uint16_t company_id = mesh_access_parser_get_u16(&parser); 163 164 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 165 if (fault != NULL){ 166 fault->num_registered_faults = 0; 167 memset(fault->registered_faults, 0, sizeof(fault->registered_faults)); 168 } 169 return company_id; 170 } 171 172 static void health_fault_clear_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){ 173 uint16_t company_id = process_message_fault_clear(mesh_model, pdu); 174 175 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id, true); 176 if (!transport_pdu) return; 177 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); 178 mesh_access_message_processed(pdu); 179 } 180 181 static void health_fault_clear_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 182 (void) process_message_fault_clear(mesh_model, pdu); 183 mesh_access_message_processed(pdu); 184 } 185 186 187 static void health_fault_test_process_message(mesh_model_t *mesh_model, mesh_pdu_t * pdu, bool acknowledged){ 188 mesh_access_parser_state_t parser; 189 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 190 uint8_t test_id = mesh_access_parser_get_u8(&parser); 191 uint16_t company_id = mesh_access_parser_get_u16(&parser); 192 193 uint16_t dest = mesh_pdu_src(pdu); 194 uint16_t netkey_index = mesh_pdu_netkey_index(pdu); 195 uint16_t appkey_index = mesh_pdu_appkey_index(pdu); 196 197 // check if fault state exists for company id 198 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 199 if (fault == NULL){ 200 return; 201 } 202 203 // short-cut if not packet handler set, but only for standard test 204 if (mesh_model->model_packet_handler == NULL){ 205 if (test_id == 0) { 206 mesh_health_server_report_test_done(dest, netkey_index, appkey_index, test_id, company_id, acknowledged); 207 } 208 return; 209 } 210 211 uint8_t event[13]; 212 int pos = 0; 213 event[pos++] = HCI_EVENT_MESH_META; 214 event[pos++] = sizeof(event) - 2; 215 event[pos++] = MESH_SUBEVENT_HEALTH_PERFORM_TEST; 216 217 little_endian_store_16(event, pos, dest); 218 pos += 2; 219 little_endian_store_16(event, pos, netkey_index); 220 pos += 2; 221 little_endian_store_16(event, pos, appkey_index); 222 pos += 2; 223 little_endian_store_16(event, pos, company_id); 224 pos += 2; 225 226 event[pos++] = test_id; 227 event[pos++] = acknowledged; 228 229 (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos); 230 } 231 232 static void health_fault_test_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 233 health_fault_test_process_message(mesh_model, pdu, true); 234 mesh_access_message_processed(pdu); 235 } 236 237 static void health_fault_test_unacknowledged_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){ 238 health_fault_test_process_message(mesh_model, pdu, false); 239 mesh_access_message_processed(pdu); 240 } 241 242 static void health_period_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 243 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_period_status(mesh_model); 244 if (!transport_pdu) return; 245 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); 246 mesh_access_message_processed(pdu); 247 } 248 249 static void process_message_period_set(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 250 mesh_access_parser_state_t parser; 251 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 252 uint8_t fast_period_divisor = mesh_access_parser_get_u8(&parser); 253 254 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 255 state->fast_period_divisor = fast_period_divisor; 256 257 mesh_health_server_update_publication_model_period_divisor(mesh_model); 258 } 259 260 static void health_period_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 261 process_message_period_set(mesh_model, pdu); 262 263 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_period_status(mesh_model); 264 if (!transport_pdu) return; 265 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); 266 mesh_access_message_processed(pdu); 267 } 268 269 static void health_period_set_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 270 process_message_period_set(mesh_model, pdu); 271 mesh_access_message_processed(pdu); 272 } 273 274 static void health_attention_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 275 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_attention_status(); 276 if (!transport_pdu) return; 277 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); 278 mesh_access_message_processed(pdu); 279 } 280 281 static void process_message_attention_set(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 282 mesh_access_parser_state_t parser; 283 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 284 uint8_t timer_s = mesh_access_parser_get_u8(&parser); 285 mesh_attention_timer_set(timer_s); 286 287 if (mesh_model->model_packet_handler == NULL) return; 288 289 uint8_t event[4]; 290 int pos = 0; 291 event[pos++] = HCI_EVENT_MESH_META; 292 // reserve for size 293 pos++; 294 event[pos++] = MESH_SUBEVENT_HEALTH_ATTENTION_TIMER_CHANGED; 295 // element index 296 event[pos++] = mesh_model->element->element_index; 297 // element index 298 event[1] = pos - 2; 299 (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos); 300 } 301 302 static void health_attention_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 303 process_message_attention_set(mesh_model, pdu); 304 305 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_attention_status(); 306 if (!transport_pdu) return; 307 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); 308 mesh_access_message_processed(pdu); 309 } 310 311 static void health_attention_set_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 312 process_message_attention_set(mesh_model, pdu); 313 mesh_access_message_processed(pdu); 314 } 315 316 static mesh_pdu_t * mesh_health_server_publish_state_fn(struct mesh_model * mesh_model){ 317 uint16_t company_id = mesh_node_get_company_id(); 318 mesh_health_fault_t * fault = mesh_health_server_active_fault(mesh_model); 319 if (fault != NULL){ 320 company_id = fault->company_id; 321 } 322 // create current status 323 return health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_CURRENT_STATUS, company_id, false); 324 } 325 326 // Health Message 327 const static mesh_operation_t mesh_health_model_operations[] = { 328 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_GET, 2, health_fault_get_handler }, 329 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_CLEAR, 2, health_fault_clear_handler }, 330 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_CLEAR_UNACKNOWLEDGED, 2, health_fault_clear_unacknowledged_handler }, 331 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_TEST, 3, health_fault_test_handler }, 332 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_TEST_UNACKNOWLEDGED, 3, health_fault_test_unacknowledged_handler }, 333 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_GET, 0, health_period_get_handler }, 334 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_SET, 1, health_period_set_handler }, 335 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_SET_UNACKNOWLEDGED, 1, health_period_set_unacknowledged_handler }, 336 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_GET, 0, health_attention_get_handler }, 337 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_SET, 1, health_attention_set_handler }, 338 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_SET_UNACKNOWLEDGED, 1, health_attention_set_unacknowledged_handler }, 339 { 0, 0, NULL } 340 }; 341 342 const mesh_operation_t * mesh_health_server_get_operations(void){ 343 return mesh_health_model_operations; 344 } 345 346 void mesh_health_server_register_packet_handler(mesh_model_t *mesh_model, btstack_packet_handler_t events_packet_handler){ 347 mesh_model->model_packet_handler = events_packet_handler; 348 } 349 350 void mesh_health_server_add_fault_state(mesh_model_t *mesh_model, uint16_t company_id, mesh_health_fault_t * fault_state){ 351 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 352 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 353 btstack_assert(fault == NULL); 354 (void) fault; 355 fault_state->company_id = company_id; 356 btstack_linked_list_add(&state->faults, (btstack_linked_item_t *) fault_state); 357 } 358 359 void mesh_health_server_set_fault(mesh_model_t *mesh_model, uint16_t company_id, uint8_t fault_code){ 360 uint16_t i; 361 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 362 btstack_assert(fault != NULL); 363 364 // add to registered faults 365 bool add_registered_fault = true; 366 for (i = 0; i < fault->num_registered_faults; i++){ 367 if (fault->registered_faults[i] == fault_code){ 368 add_registered_fault = false; 369 break; 370 } 371 } 372 if (add_registered_fault && (fault->num_registered_faults < MESH_MAX_NUM_FAULTS)){ 373 fault->registered_faults[fault->num_registered_faults] = fault_code; 374 fault->num_registered_faults++; 375 } 376 377 // add to current faults 378 bool add_current_fault = true; 379 for (i = 0; i < fault->num_current_faults; i++){ 380 if (fault->registered_faults[i] == fault_code){ 381 add_current_fault = false; 382 break; 383 } 384 } 385 if (add_current_fault && (fault->num_current_faults < MESH_MAX_NUM_FAULTS)){ 386 fault->registered_faults[fault->num_current_faults] = fault_code; 387 fault->num_current_faults++; 388 } 389 390 // update publication model 391 mesh_health_server_update_publication_model_period_divisor(mesh_model); 392 } 393 394 void mesh_health_server_clear_fault(mesh_model_t *mesh_model, uint16_t company_id, uint8_t fault_code){ 395 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 396 btstack_assert(fault != NULL); 397 398 // remove from current faults 399 uint16_t i; 400 bool shift_faults = false; 401 for (i = 0; i < fault->num_current_faults; i++){ 402 if (!shift_faults){ 403 if (fault->registered_faults[i] == fault_code){ 404 shift_faults = true; 405 } 406 } 407 if (i < (MESH_MAX_NUM_FAULTS - 1)){ 408 fault->registered_faults[i] = fault->registered_faults[i+1]; 409 } 410 } 411 412 // update count 413 if (shift_faults){ 414 fault->num_current_faults--; 415 } 416 417 // update publication model 418 mesh_health_server_update_publication_model_period_divisor(mesh_model); 419 } 420 421 void mesh_health_server_set_publication_model(mesh_model_t * mesh_model, mesh_publication_model_t * publication_model){ 422 btstack_assert(mesh_model != NULL); 423 btstack_assert(publication_model != NULL); 424 publication_model->publish_state_fn = &mesh_health_server_publish_state_fn; 425 mesh_model->publication_model = publication_model; 426 } 427 428 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){ 429 mesh_model_t * mesh_model = mesh_node_get_health_server(); 430 if (mesh_model == NULL) return; 431 432 mesh_health_fault_t * fault = mesh_health_server_fault_for_company_id(mesh_model, company_id); 433 fault->test_id = test_id; 434 435 // response for acknowledged health fault test 436 if (acknowledged){ 437 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_fault_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id, company_id); 438 if (!transport_pdu) return; 439 health_server_send_message(mesh_node_get_primary_element_address(), dest, netkey_index, appkey_index, (mesh_pdu_t *) transport_pdu); 440 } 441 } 442