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 // used for asynchronous calls in the done command to unblock the message queue 61 static mesh_pdu_t * processed_pdu; 62 63 static void health_server_send_message(uint16_t src, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, mesh_pdu_t *pdu){ 64 uint8_t ttl = mesh_foundation_default_ttl_get(); 65 mesh_upper_transport_setup_access_pdu_header(pdu, netkey_index, appkey_index, ttl, src, dest, 0); 66 mesh_access_send_unacknowledged_pdu(pdu); 67 } 68 // Health State 69 const mesh_access_message_t mesh_foundation_health_period_status = { 70 MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_STATUS, "1" 71 }; 72 73 const mesh_access_message_t mesh_foundation_health_attention_status = { 74 MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_STATUS, "1" 75 }; 76 77 static mesh_pdu_t * health_period_status(mesh_model_t * mesh_model){ 78 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 79 // setup message 80 mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(&mesh_foundation_health_period_status, state->fast_period_divisor); 81 return (mesh_pdu_t *) transport_pdu; 82 } 83 84 static mesh_pdu_t * health_attention_status(void){ 85 // setup message 86 mesh_transport_pdu_t * transport_pdu = mesh_access_setup_segmented_message(&mesh_foundation_health_attention_status, mesh_attention_timer_get()); 87 return (mesh_pdu_t *) transport_pdu; 88 } 89 90 // dynamic 91 static mesh_pdu_t * health_current_status(mesh_model_t * mesh_model, uint32_t opcode, uint16_t company_id){ 92 mesh_transport_pdu_t * transport_pdu = mesh_access_transport_init(opcode); 93 if (!transport_pdu) return NULL; 94 95 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 96 if (state == NULL){ 97 log_error("health_current_status state == NULL"); 98 } 99 100 btstack_linked_list_iterator_t it; 101 btstack_linked_list_iterator_init(&it, &state->registered_faults); 102 while (btstack_linked_list_iterator_has_next(&it)){ 103 mesh_fault_t * fault = (mesh_fault_t *) btstack_linked_list_iterator_next(&it); 104 if (fault->company_id != company_id) continue; 105 mesh_access_transport_add_uint8(transport_pdu, fault->test_id); 106 mesh_access_transport_add_uint16(transport_pdu, fault->company_id); 107 int i; 108 for (i = 0; i < fault->num_faults; i++){ 109 mesh_access_transport_add_uint8(transport_pdu, fault->faults[i]); 110 } 111 return (mesh_pdu_t *) transport_pdu; 112 } 113 // no company with company_id found 114 mesh_access_transport_add_uint8(transport_pdu, 0); 115 mesh_access_transport_add_uint16(transport_pdu, company_id); 116 return (mesh_pdu_t *) transport_pdu; 117 } 118 119 // dynamic 120 static mesh_pdu_t * health_fault_status(btstack_linked_list_t * faults, uint32_t opcode, uint8_t test_id, uint16_t company_id){ 121 mesh_transport_pdu_t * transport_pdu = mesh_access_transport_init(opcode); 122 if (!transport_pdu) return NULL; 123 mesh_access_transport_add_uint8(transport_pdu, test_id); 124 125 btstack_linked_list_iterator_t it; 126 btstack_linked_list_iterator_init(&it, faults); 127 while (btstack_linked_list_iterator_has_next(&it)){ 128 mesh_fault_t * fault = (mesh_fault_t *) btstack_linked_list_iterator_next(&it); 129 if (fault->company_id != company_id) continue; 130 mesh_access_transport_add_uint8(transport_pdu, fault->test_id); 131 mesh_access_transport_add_uint16(transport_pdu, fault->company_id); 132 int i; 133 for (i = 0; i < fault->num_faults; i++){ 134 mesh_access_transport_add_uint8(transport_pdu, fault->faults[i]); 135 } 136 return (mesh_pdu_t *) transport_pdu; 137 } 138 // no company with company_id found 139 mesh_access_transport_add_uint8(transport_pdu, 0); 140 mesh_access_transport_add_uint16(transport_pdu, company_id); 141 return (mesh_pdu_t *) transport_pdu; 142 } 143 144 145 static void health_fault_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 146 mesh_access_parser_state_t parser; 147 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 148 uint16_t company_id = mesh_access_parser_get_u16(&parser); 149 150 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_current_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id); 151 if (!transport_pdu) return; 152 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); 153 mesh_access_message_processed(pdu); 154 } 155 156 static uint16_t process_message_fault_clear(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 157 mesh_access_parser_state_t parser; 158 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 159 uint16_t company_id = mesh_access_parser_get_u16(&parser); 160 161 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 162 if (state == NULL){ 163 log_error("health_fault_status state == NULL"); 164 } 165 166 uint8_t event[6]; 167 int pos = 0; 168 event[pos++] = HCI_EVENT_MESH_META; 169 // reserve for size 170 pos++; 171 event[pos++] = MESH_SUBEVENT_HEALTH_CLEAR_REGISTERED_FAULTS; 172 // element index 173 event[pos++] = mesh_model->element->element_index; 174 little_endian_store_16(event, pos, company_id); 175 pos += 2; 176 event[1] = pos - 2; 177 (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos); 178 179 return company_id; 180 } 181 182 static void health_fault_clear_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){ 183 uint16_t company_id = process_message_fault_clear(mesh_model, pdu); 184 185 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_current_status(mesh_model, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, company_id); 186 if (!transport_pdu) return; 187 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); 188 mesh_access_message_processed(pdu); 189 } 190 191 static void health_fault_clear_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 192 process_message_fault_clear(mesh_model, pdu); 193 mesh_access_message_processed(pdu); 194 } 195 196 197 static void health_fault_test_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 198 mesh_access_parser_state_t parser; 199 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 200 uint8_t test_id = mesh_access_parser_get_u8(&parser); 201 uint16_t company_id = mesh_access_parser_get_u16(&parser); 202 203 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 204 if (state == NULL){ 205 log_error("mesh_health_state == NULL"); 206 } 207 processed_pdu = pdu; 208 209 uint8_t event[17]; 210 int pos = 0; 211 event[pos++] = HCI_EVENT_MESH_META; 212 // reserve for size 213 pos++; 214 event[pos++] = MESH_SUBEVENT_HEALTH_PERFORM_TEST; 215 // element index 216 event[pos++] = mesh_model->element->element_index; 217 // model_id 218 little_endian_store_32(event, pos, mesh_model->model_identifier); 219 pos += 4; 220 221 little_endian_store_16(event, pos, mesh_pdu_src(pdu)); 222 pos += 2; 223 little_endian_store_16(event, pos, mesh_pdu_netkey_index(pdu)); 224 pos += 2; 225 little_endian_store_16(event, pos, mesh_pdu_appkey_index(pdu)); 226 pos += 2; 227 little_endian_store_16(event, pos, company_id); 228 pos += 2; 229 event[pos++] = test_id; 230 event[1] = pos - 2; 231 232 (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos); 233 } 234 235 void mesh_health_server_report_test_done(uint16_t element_index, uint16_t dest, uint16_t netkey_index, uint16_t appkey_index, uint8_t test_id, uint16_t company_id){ 236 mesh_element_t * element = mesh_node_element_for_index(element_index); 237 if (element == NULL) return; 238 239 mesh_model_t * mesh_model = mesh_model_get_by_identifier(element, mesh_model_get_model_identifier_bluetooth_sig(MESH_SIG_MODEL_ID_HEALTH_SERVER)); 240 if (mesh_model == NULL) return; 241 242 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 243 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_fault_status(&state->registered_faults, MESH_FOUNDATION_OPERATION_HEALTH_FAULT_STATUS, test_id, company_id); 244 if (!transport_pdu) return; 245 health_server_send_message(element_index, dest, netkey_index, appkey_index, (mesh_pdu_t *) transport_pdu); 246 mesh_access_message_processed(processed_pdu); 247 } 248 249 static void health_fault_test_unacknowledged_handler(mesh_model_t * mesh_model, mesh_pdu_t * pdu){ 250 health_fault_test_handler(mesh_model, pdu); 251 mesh_access_message_processed(pdu); 252 } 253 254 static void health_period_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 255 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_period_status(mesh_model); 256 if (!transport_pdu) return; 257 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); 258 mesh_access_message_processed(pdu); 259 } 260 261 static void process_message_period_set(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 262 mesh_access_parser_state_t parser; 263 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 264 uint8_t fast_period_divisor = mesh_access_parser_get_u8(&parser); 265 266 mesh_health_state_t * state = (mesh_health_state_t *) mesh_model->model_data; 267 if (state == NULL){ 268 log_error("health_fault_status state == NULL"); 269 } 270 271 if (state->fast_period_divisor != fast_period_divisor){ 272 state->fast_period_divisor = fast_period_divisor; 273 uint8_t event[5]; 274 int pos = 0; 275 event[pos++] = HCI_EVENT_MESH_META; 276 // reserve for size 277 pos++; 278 event[pos++] = MESH_SUBEVENT_HEALTH_FAST_PERIOD_DIVISOR_CHANGED; 279 // element index 280 event[pos++] = mesh_model->element->element_index; 281 // element index 282 event[pos++] = fast_period_divisor; 283 event[1] = pos - 2; 284 (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos); 285 } 286 } 287 288 static void health_period_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 289 process_message_period_set(mesh_model, pdu); 290 291 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_period_status(mesh_model); 292 if (!transport_pdu) return; 293 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); 294 mesh_access_message_processed(pdu); 295 } 296 297 static void health_period_set_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 298 process_message_period_set(mesh_model, pdu); 299 mesh_access_message_processed(pdu); 300 } 301 302 static void health_attention_get_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 303 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_attention_status(); 304 if (!transport_pdu) return; 305 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); 306 mesh_access_message_processed(pdu); 307 } 308 309 static void process_message_attention_set(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 310 mesh_access_parser_state_t parser; 311 mesh_access_parser_init(&parser, (mesh_pdu_t*) pdu); 312 uint8_t timer_s = mesh_access_parser_get_u8(&parser); 313 mesh_attention_timer_set(timer_s); 314 315 uint8_t event[4]; 316 int pos = 0; 317 event[pos++] = HCI_EVENT_MESH_META; 318 // reserve for size 319 pos++; 320 event[pos++] = MESH_SUBEVENT_HEALTH_ATTENTION_TIMER_CHANGED; 321 // element index 322 event[pos++] = mesh_model->element->element_index; 323 // element index 324 event[1] = pos - 2; 325 (*mesh_model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, pos); 326 } 327 328 static void health_attention_set_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 329 process_message_attention_set(mesh_model, pdu); 330 331 mesh_transport_pdu_t * transport_pdu = (mesh_transport_pdu_t *) health_attention_status(); 332 if (!transport_pdu) return; 333 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); 334 mesh_access_message_processed(pdu); 335 } 336 337 static void health_attention_set_unacknowledged_handler(mesh_model_t *mesh_model, mesh_pdu_t * pdu){ 338 process_message_attention_set(mesh_model, pdu); 339 mesh_access_message_processed(pdu); 340 } 341 342 // Health Message 343 const static mesh_operation_t mesh_health_model_operations[] = { 344 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_GET, 2, health_fault_get_handler }, 345 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_CLEAR, 2, health_fault_clear_handler }, 346 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_CLEAR_UNACKNOWLEDGED, 2, health_fault_clear_unacknowledged_handler }, 347 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_TEST, 3, health_fault_test_handler }, 348 { MESH_FOUNDATION_OPERATION_HEALTH_FAULT_TEST_UNACKNOWLEDGED, 3, health_fault_test_unacknowledged_handler }, 349 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_GET, 0, health_period_get_handler }, 350 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_SET, 1, health_period_set_handler }, 351 { MESH_FOUNDATION_OPERATION_HEALTH_PERIOD_SET_UNACKNOWLEDGED, 1, health_period_set_unacknowledged_handler }, 352 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_GET, 0, health_attention_get_handler }, 353 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_SET, 1, health_attention_set_handler }, 354 { MESH_FOUNDATION_OPERATION_HEALTH_ATTENTION_SET_UNACKNOWLEDGED, 1, health_attention_set_unacknowledged_handler }, 355 { 0, 0, NULL } 356 }; 357 358 const mesh_operation_t * mesh_health_server_get_operations(void){ 359 return mesh_health_model_operations; 360 } 361 362 void mesh_health_server_register_packet_handler(mesh_model_t *mesh_model, btstack_packet_handler_t events_packet_handler){ 363 if (events_packet_handler == NULL){ 364 log_error(" mesh_health_server_register_packet_handler called with NULL callback"); 365 return; 366 } 367 if (mesh_model == NULL){ 368 log_error(" mesh_health_server_register_packet_handler called with NULL mesh_model"); 369 return; 370 } 371 mesh_model->model_packet_handler = events_packet_handler; 372 } 373 374 void health_server_clear_faults(btstack_linked_list_t * faults, uint16_t company_id){ 375 btstack_linked_list_iterator_t it; 376 btstack_linked_list_iterator_init(&it, faults); 377 while (btstack_linked_list_iterator_has_next(&it)){ 378 mesh_fault_t * fault = (mesh_fault_t *) btstack_linked_list_iterator_next(&it); 379 if (fault->company_id != company_id) continue; 380 memset(fault->faults, 0, sizeof(fault->faults)); 381 return; 382 } 383 } 384