xref: /btstack/src/mesh/mesh_access.c (revision c659f2368a2de281f02d456b95d6c149c8446a07)
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_access.c"
39 
40 #include <string.h>
41 #include <stdio.h>
42 #include <stdarg.h>
43 
44 #include "mesh/mesh_access.h"
45 
46 #include "btstack_debug.h"
47 #include "btstack_memory.h"
48 #include "btstack_tlv.h"
49 
50 #include "mesh/beacon.h"
51 #include "mesh/mesh_foundation.h"
52 #include "mesh/mesh_iv_index_seq_number.h"
53 #include "mesh/mesh_node.h"
54 #include "mesh/mesh_proxy.h"
55 #include "mesh/mesh_upper_transport.h"
56 #include "mesh/mesh.h"
57 
58 #define MEST_TRANSACTION_TIMEOUT_MS  6000
59 
60 static void mesh_access_message_process_handler(mesh_pdu_t * pdu);
61 static void mesh_access_upper_transport_handler(mesh_transport_callback_type_t callback_type, mesh_transport_status_t status, mesh_pdu_t * pdu);
62 static const mesh_operation_t * mesh_model_lookup_operation_by_opcode(mesh_model_t * model, uint32_t opcode);
63 
64 // receive
65 static uint16_t mesh_access_received_pdu_refcount;
66 
67 // acknowledged messages
68 static btstack_linked_list_t  mesh_access_acknowledged_messages;
69 static btstack_timer_source_t mesh_access_acknowledged_timer;
70 static int                    mesh_access_acknowledged_timer_active;
71 
72 // Transitions
73 static btstack_linked_list_t  transitions;
74 static btstack_timer_source_t transitions_timer;
75 static uint32_t transition_step_min_ms;
76 static uint8_t mesh_transaction_id_counter = 0;
77 
78 void mesh_access_init(void){
79     // register with upper transport
80     mesh_upper_transport_register_access_message_handler(&mesh_access_message_process_handler);
81     mesh_upper_transport_set_higher_layer_handler(&mesh_access_upper_transport_handler);
82 }
83 
84 void mesh_access_emit_state_update_bool(btstack_packet_handler_t event_handler, uint8_t element_index, uint32_t model_identifier,
85     model_state_id_t state_identifier, model_state_update_reason_t reason, uint8_t value){
86     if (event_handler == NULL) return;
87     uint8_t event[14] = {HCI_EVENT_MESH_META, 12, MESH_SUBEVENT_STATE_UPDATE_BOOL};
88     int pos = 3;
89     event[pos++] = element_index;
90     little_endian_store_32(event, pos, model_identifier);
91     pos += 4;
92     little_endian_store_32(event, pos, (uint32_t)state_identifier);
93     pos += 4;
94     event[pos++] = (uint8_t)reason;
95     event[pos++] = value;
96     (*event_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
97 }
98 
99 void mesh_access_emit_state_update_int16(btstack_packet_handler_t event_handler, uint8_t element_index, uint32_t model_identifier,
100     model_state_id_t state_identifier, model_state_update_reason_t reason, int16_t value){
101     if (event_handler == NULL) return;
102     uint8_t event[15] = {HCI_EVENT_MESH_META, 13, MESH_SUBEVENT_STATE_UPDATE_BOOL};
103     int pos = 3;
104     event[pos++] = element_index;
105     little_endian_store_32(event, pos, model_identifier);
106     pos += 4;
107     little_endian_store_32(event, pos, (uint32_t)state_identifier);
108     pos += 4;
109     event[pos++] = (uint8_t)reason;
110     little_endian_store_16(event, pos, (uint16_t) value);
111     pos += 2;
112     (*event_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
113 }
114 
115 uint8_t mesh_access_acknowledged_message_retransmissions(void){
116     return 3;
117 }
118 
119 uint32_t mesh_access_acknowledged_message_timeout_ms(void){
120     return 30000;
121 }
122 
123 #define MESH_ACCESS_OPCODE_INVALID 0xFFFFFFFFu
124 #define MESH_ACCESS_OPCODE_NOT_SET 0xFFFFFFFEu
125 
126 void mesh_access_send_unacknowledged_pdu(mesh_pdu_t * pdu){
127     pdu->ack_opcode = MESH_ACCESS_OPCODE_INVALID;
128     mesh_upper_transport_send_access_pdu(pdu);
129 }
130 
131 void mesh_access_send_acknowledged_pdu(mesh_pdu_t * pdu, uint8_t retransmissions, uint32_t ack_opcode){
132     pdu->retransmit_count = retransmissions;
133     pdu->ack_opcode = ack_opcode;
134 
135     mesh_upper_transport_send_access_pdu(pdu);
136 }
137 
138 #define MESH_SUBEVENT_MESSAGE_NOT_ACKNOWLEDGED                                        0x30
139 
140 static void mesh_access_acknowledged_run(btstack_timer_source_t * ts){
141     UNUSED(ts);
142 
143     uint32_t now = btstack_run_loop_get_time_ms();
144 
145     // handle timeouts
146     btstack_linked_list_iterator_t ack_it;
147     btstack_linked_list_iterator_init(&ack_it, &mesh_access_acknowledged_messages);
148     while (btstack_linked_list_iterator_has_next(&ack_it)){
149         mesh_pdu_t * pdu = (mesh_pdu_t *) btstack_linked_list_iterator_next(&ack_it);
150         if (btstack_time_delta(now, pdu->retransmit_timeout_ms) >= 0) {
151             // remove from list
152             btstack_linked_list_remove(&mesh_access_acknowledged_messages, (btstack_linked_item_t*) pdu);
153             // retransmit or report failure
154             if (pdu->retransmit_count){
155                 pdu->retransmit_count--;
156                 mesh_upper_transport_send_access_pdu(pdu);
157             } else {
158                 // find correct model and emit error
159                 uint16_t src = mesh_pdu_src(pdu);
160                 uint16_t dst = mesh_pdu_dst(pdu);
161                 mesh_element_t * element = mesh_node_element_for_unicast_address(src);
162                 if (element){
163                     // find
164                     mesh_model_iterator_t model_it;
165                     mesh_model_iterator_init(&model_it, element);
166                     while (mesh_model_iterator_has_next(&model_it)){
167                         mesh_model_t * model = mesh_model_iterator_next(&model_it);
168                         // find opcode in table
169                         const mesh_operation_t * operation = mesh_model_lookup_operation_by_opcode(model, pdu->ack_opcode);
170                         if (operation == NULL) continue;
171                         if (model->model_packet_handler == NULL) continue;
172                         // emit event
173                         uint8_t event[13];
174                         event[0] = HCI_EVENT_MESH_META;
175                         event[1] = sizeof(event) - 2;
176                         event[2] = element->element_index;
177                         little_endian_store_32(event, 3, model->model_identifier);
178                         little_endian_store_32(event, 7, pdu->ack_opcode);
179                         little_endian_store_16(event, 11, dst);
180                         (*model->model_packet_handler)(HCI_EVENT_PACKET, 0, event, sizeof(event));
181                     }
182                 }
183 
184                 // free
185                 mesh_upper_transport_pdu_free(pdu);
186             }
187         }
188     }
189 
190     if (mesh_access_acknowledged_timer_active) return;
191 
192     // find earliest timeout and set timer
193     btstack_linked_list_iterator_init(&ack_it, &mesh_access_acknowledged_messages);
194     int32_t next_timeout_ms = 0;
195     while (btstack_linked_list_iterator_has_next(&ack_it)){
196         mesh_pdu_t * pdu = (mesh_pdu_t *) btstack_linked_list_iterator_next(&ack_it);
197         int32_t timeout_delta_ms = btstack_time_delta(pdu->retransmit_timeout_ms, now);
198         if (next_timeout_ms == 0 || timeout_delta_ms < next_timeout_ms){
199             next_timeout_ms = timeout_delta_ms;
200         }
201     }
202 
203     // set timer
204     if (next_timeout_ms == 0) return;
205 
206     btstack_run_loop_set_timer(&mesh_access_acknowledged_timer, next_timeout_ms);
207     btstack_run_loop_set_timer_handler(&mesh_access_acknowledged_timer, mesh_access_acknowledged_run);
208     btstack_run_loop_add_timer(&mesh_access_acknowledged_timer);
209     mesh_access_acknowledged_timer_active = 1;
210 }
211 
212 static void mesh_access_acknowledged_received(uint16_t rx_src, uint32_t opcode){
213     // check if received src matches our dest
214     // free acknowledged messages if we were waiting for this message
215 
216     btstack_linked_list_iterator_t ack_it;
217     btstack_linked_list_iterator_init(&ack_it, &mesh_access_acknowledged_messages);
218     while (btstack_linked_list_iterator_has_next(&ack_it)){
219         mesh_pdu_t * tx_pdu = (mesh_pdu_t *) btstack_linked_list_iterator_next(&ack_it);
220         uint16_t tx_dest = mesh_pdu_dst(tx_pdu);
221         if (tx_dest != rx_src) continue;
222         if (tx_pdu->ack_opcode != opcode) continue;
223         // got expected response from dest, remove from outgoing messages
224         mesh_upper_transport_pdu_free(tx_pdu);
225         return;
226     }
227 }
228 
229 static void mesh_access_upper_transport_handler(mesh_transport_callback_type_t callback_type, mesh_transport_status_t status, mesh_pdu_t * pdu){
230     UNUSED(status);
231     switch (callback_type){
232         case MESH_TRANSPORT_PDU_SENT:
233             // unacknowledged -> free
234             if (pdu->ack_opcode == MESH_ACCESS_OPCODE_INVALID){
235                 mesh_upper_transport_pdu_free(pdu);
236                 break;
237             }
238             // setup timeout
239             pdu->retransmit_timeout_ms = btstack_run_loop_get_time_ms() + mesh_access_acknowledged_message_timeout_ms();
240             // add to mesh_access_acknowledged_messages
241             btstack_linked_list_add(&mesh_access_acknowledged_messages, (btstack_linked_item_t *) pdu);
242             // update timer
243             mesh_access_acknowledged_run(NULL);
244             break;
245         default:
246             break;
247     }
248 }
249 
250 // Mesh Model Transitions
251 
252 void mesh_access_transitions_setup_transaction(mesh_transition_t * transition, uint8_t transaction_identifier, uint16_t src_address, uint16_t dst_address){
253     transition->transaction_timestamp_ms = btstack_run_loop_get_time_ms();
254     transition->transaction_identifier = transaction_identifier;
255     transition->src_address = src_address;
256     transition->dst_address = dst_address;
257 }
258 
259 void mesh_access_transitions_abort_transaction(mesh_transition_t * transition){
260     mesh_access_transitions_remove(transition);
261 }
262 
263 
264 static int mesh_access_transitions_transaction_is_expired(mesh_transition_t * transition){
265     return (btstack_run_loop_get_time_ms() - transition->transaction_timestamp_ms) > MEST_TRANSACTION_TIMEOUT_MS;
266 }
267 
268 mesh_transaction_status_t mesh_access_transitions_transaction_status(mesh_transition_t * transition, uint8_t transaction_identifier, uint16_t src_address, uint16_t dst_address){
269     if (transition->src_address != src_address || transition->dst_address != dst_address) return MESH_TRANSACTION_STATUS_DIFFERENT_DST_OR_SRC;
270 
271     if (transition->transaction_identifier == transaction_identifier && !mesh_access_transitions_transaction_is_expired(transition)){
272             return MESH_TRANSACTION_STATUS_RETRANSMISSION;
273     }
274     return MESH_TRANSACTION_STATUS_NEW;
275 }
276 
277 uint8_t mesh_access_transitions_num_steps_from_gdtt(uint8_t time_gdtt){
278     return time_gdtt & 0x3fu;
279 }
280 
281 static uint32_t mesh_access_transitions_step_ms_from_gdtt(uint8_t time_gdtt){
282     mesh_default_transition_step_resolution_t step_resolution = (mesh_default_transition_step_resolution_t) (time_gdtt >> 6);
283     switch (step_resolution){
284         case MESH_DEFAULT_TRANSITION_STEP_RESOLUTION_100ms:
285             return 100;
286         case MESH_DEFAULT_TRANSITION_STEP_RESOLUTION_1s:
287             return 1000;
288         case MESH_DEFAULT_TRANSITION_STEP_RESOLUTION_10s:
289             return 10000;
290         case MESH_DEFAULT_TRANSITION_STEP_RESOLUTION_10min:
291             return 600000;
292         default:
293             return 0;
294     }
295 }
296 
297 uint32_t mesh_access_time_gdtt2ms(uint8_t time_gdtt){
298     uint8_t num_steps  = mesh_access_transitions_num_steps_from_gdtt(time_gdtt);
299     if (num_steps > 0x3E) return 0;
300 
301     return mesh_access_transitions_step_ms_from_gdtt(time_gdtt) * num_steps;
302 }
303 
304 static void mesh_access_transitions_timeout_handler(btstack_timer_source_t * timer){
305     btstack_linked_list_iterator_t it;
306     btstack_linked_list_iterator_init(&it, &transitions);
307     while (btstack_linked_list_iterator_has_next(&it)){
308         mesh_transition_t * transition = (mesh_transition_t *)btstack_linked_list_iterator_next(&it);
309         (transition->transition_callback)(transition, TRANSITION_UPDATE, btstack_run_loop_get_time_ms());
310     }
311     if (btstack_linked_list_empty(&transitions)) return;
312 
313     btstack_run_loop_set_timer(timer, transition_step_min_ms);
314     btstack_run_loop_add_timer(timer);
315 }
316 
317 static void mesh_access_transitions_timer_start(void){
318     btstack_run_loop_remove_timer(&transitions_timer);
319     btstack_run_loop_set_timer_handler(&transitions_timer, mesh_access_transitions_timeout_handler);
320     btstack_run_loop_set_timer(&transitions_timer, transition_step_min_ms);
321     btstack_run_loop_add_timer(&transitions_timer);
322 }
323 
324 static void mesh_access_transitions_timer_stop(void){
325     btstack_run_loop_remove_timer(&transitions_timer);
326 }
327 
328 static uint32_t mesh_access_transitions_get_step_min_ms(void){
329     uint32_t min_timeout_ms = 0;
330 
331     btstack_linked_list_iterator_t it;
332     btstack_linked_list_iterator_init(&it, &transitions);
333     while (btstack_linked_list_iterator_has_next(&it)){
334         mesh_transition_t * transition = (mesh_transition_t *)btstack_linked_list_iterator_next(&it);
335         if (min_timeout_ms == 0 || transition->step_duration_ms < min_timeout_ms){
336             min_timeout_ms = transition->step_duration_ms;
337         }
338     }
339     return min_timeout_ms;
340 }
341 
342 void mesh_access_transitions_setup(mesh_transition_t * transition, mesh_model_t * mesh_model,
343     uint8_t transition_time_gdtt, uint8_t delay_gdtt,
344     void (* transition_callback)(struct mesh_transition * transition, transition_event_t event, uint32_t current_timestamp)){
345 
346     //  Only values of 0x00 through 0x3E shall be used to specify the value of the Transition Number of Steps field
347     uint8_t num_steps  = mesh_access_transitions_num_steps_from_gdtt(transition_time_gdtt);
348     if (num_steps > 0x3E) return;
349 
350     transition->state = MESH_TRANSITION_STATE_IDLE;
351     transition->phase_start_ms = 0;
352 
353     transition->mesh_model = mesh_model;
354     transition->transition_callback = transition_callback;
355     transition->step_duration_ms = mesh_access_transitions_step_ms_from_gdtt(transition_time_gdtt);
356     transition->remaining_delay_time_ms = delay_gdtt * 5;
357     transition->remaining_transition_time_ms = num_steps * transition->step_duration_ms;
358 }
359 
360 void mesh_access_transitions_add(mesh_transition_t * transition){
361     if (transition->step_duration_ms == 0) return;
362 
363     if (btstack_linked_list_empty(&transitions) || transition->step_duration_ms < transition_step_min_ms){
364         transition_step_min_ms = transition->step_duration_ms;
365     }
366     mesh_access_transitions_timer_start();
367     btstack_linked_list_add(&transitions, (btstack_linked_item_t *) transition);
368     (transition->transition_callback)(transition, TRANSITION_START, btstack_run_loop_get_time_ms());
369 }
370 
371 void mesh_access_transitions_remove(mesh_transition_t * transition){
372     mesh_access_transitions_setup(transition, NULL, 0, 0, NULL);
373     btstack_linked_list_remove(&transitions, (btstack_linked_item_t *) transition);
374 
375     if (btstack_linked_list_empty(&transitions)){
376         mesh_access_transitions_timer_stop();
377     } else {
378         transition_step_min_ms = mesh_access_transitions_get_step_min_ms();
379     }
380 }
381 
382 uint8_t mesh_access_transactions_get_next_transaction_id(void){
383     mesh_transaction_id_counter++;
384     if (mesh_transaction_id_counter == 0){
385         mesh_transaction_id_counter = 1;
386     }
387     return mesh_transaction_id_counter;
388 }
389 
390 uint16_t mesh_pdu_ctl(mesh_pdu_t * pdu){
391     switch (pdu->pdu_type){
392         case MESH_PDU_TYPE_TRANSPORT:
393             return mesh_transport_ctl((mesh_transport_pdu_t*) pdu);
394         case MESH_PDU_TYPE_NETWORK:
395             return mesh_network_control((mesh_network_pdu_t *) pdu);
396         default:
397             return 0;
398     }
399 }
400 
401 uint16_t mesh_pdu_ttl(mesh_pdu_t * pdu){
402     switch (pdu->pdu_type){
403         case MESH_PDU_TYPE_TRANSPORT:
404             return mesh_transport_ttl((mesh_transport_pdu_t*) pdu);
405         case MESH_PDU_TYPE_NETWORK:
406             return mesh_network_ttl((mesh_network_pdu_t *) pdu);
407         default:
408             return 0;
409     }
410 }
411 
412 uint16_t mesh_pdu_src(mesh_pdu_t * pdu){
413     switch (pdu->pdu_type){
414         case MESH_PDU_TYPE_TRANSPORT:
415             return mesh_transport_src((mesh_transport_pdu_t*) pdu);
416         case MESH_PDU_TYPE_NETWORK:
417             return mesh_network_src((mesh_network_pdu_t *) pdu);
418         default:
419             return MESH_ADDRESS_UNSASSIGNED;
420     }
421 }
422 
423 uint16_t mesh_pdu_dst(mesh_pdu_t * pdu){
424     switch (pdu->pdu_type){
425         case MESH_PDU_TYPE_TRANSPORT:
426             return mesh_transport_dst((mesh_transport_pdu_t*) pdu);
427         case MESH_PDU_TYPE_NETWORK:
428             return mesh_network_dst((mesh_network_pdu_t *) pdu);
429         default:
430             return MESH_ADDRESS_UNSASSIGNED;
431     }
432 }
433 
434 uint16_t mesh_pdu_netkey_index(mesh_pdu_t * pdu){
435     switch (pdu->pdu_type){
436         case MESH_PDU_TYPE_TRANSPORT:
437             return ((mesh_transport_pdu_t*) pdu)->netkey_index;
438         case MESH_PDU_TYPE_NETWORK:
439             return ((mesh_network_pdu_t *) pdu)->netkey_index;
440         default:
441             return 0;
442     }
443 }
444 
445 uint16_t mesh_pdu_appkey_index(mesh_pdu_t * pdu){
446     switch (pdu->pdu_type){
447         case MESH_PDU_TYPE_TRANSPORT:
448             return ((mesh_transport_pdu_t*) pdu)->appkey_index;
449         case MESH_PDU_TYPE_NETWORK:
450             return ((mesh_network_pdu_t *) pdu)->appkey_index;
451         default:
452             return 0;
453     }
454 }
455 
456 uint16_t mesh_pdu_len(mesh_pdu_t * pdu){
457     switch (pdu->pdu_type){
458         case MESH_PDU_TYPE_TRANSPORT:
459             return ((mesh_transport_pdu_t*) pdu)->len;
460         case MESH_PDU_TYPE_NETWORK:
461             return ((mesh_network_pdu_t *) pdu)->len - 10;
462         default:
463             return 0;
464     }
465 }
466 
467 uint8_t * mesh_pdu_data(mesh_pdu_t * pdu){
468     switch (pdu->pdu_type){
469         case MESH_PDU_TYPE_TRANSPORT:
470             return ((mesh_transport_pdu_t*) pdu)->data;
471         case MESH_PDU_TYPE_NETWORK:
472             return &((mesh_network_pdu_t *) pdu)->data[10];
473         default:
474             return NULL;
475     }
476 }
477 
478 uint8_t mesh_pdu_control_opcode(mesh_pdu_t * pdu){
479     switch (pdu->pdu_type){
480         case MESH_PDU_TYPE_TRANSPORT:
481             return mesh_transport_control_opcode((mesh_transport_pdu_t*) pdu);
482         case MESH_PDU_TYPE_NETWORK:
483             return mesh_network_control_opcode((mesh_network_pdu_t *) pdu);
484         default:
485             return 0xff;
486     }
487 }
488 
489 // message parser
490 
491 static int mesh_access_get_opcode(uint8_t * buffer, uint16_t buffer_size, uint32_t * opcode, uint16_t * opcode_size){
492     switch (buffer[0] >> 6){
493         case 0:
494         case 1:
495             if (buffer[0] == 0x7f) return 0;
496             *opcode = buffer[0];
497             *opcode_size = 1;
498             return 1;
499         case 2:
500             if (buffer_size < 2) return 0;
501             *opcode = big_endian_read_16(buffer, 0);
502             *opcode_size = 2;
503             return 1;
504         case 3:
505             if (buffer_size < 3) return 0;
506             *opcode = (buffer[0] << 16) | little_endian_read_16(buffer, 1);
507             *opcode_size = 3;
508             return 1;
509         default:
510             return 0;
511     }
512 }
513 
514 static int mesh_access_transport_get_opcode(mesh_transport_pdu_t * transport_pdu, uint32_t * opcode, uint16_t * opcode_size){
515     return mesh_access_get_opcode(transport_pdu->data, transport_pdu->len, opcode, opcode_size);
516 }
517 
518 static int mesh_access_network_get_opcode(mesh_network_pdu_t * network_pdu, uint32_t * opcode, uint16_t * opcode_size){
519     // TransMIC already removed by mesh_upper_transport_validate_unsegmented_message_ccm
520     return mesh_access_get_opcode(&network_pdu->data[10], network_pdu->len - 10, opcode, opcode_size);
521 }
522 
523 int mesh_access_pdu_get_opcode(mesh_pdu_t * pdu, uint32_t * opcode, uint16_t * opcode_size){
524     switch (pdu->pdu_type){
525         case MESH_PDU_TYPE_TRANSPORT:
526             return mesh_access_transport_get_opcode((mesh_transport_pdu_t*) pdu, opcode, opcode_size);
527         case MESH_PDU_TYPE_NETWORK:
528             return mesh_access_network_get_opcode((mesh_network_pdu_t *) pdu, opcode, opcode_size);
529         default:
530             return 0;
531     }
532 }
533 
534 void mesh_access_parser_skip(mesh_access_parser_state_t * state, uint16_t bytes_to_skip){
535     state->data += bytes_to_skip;
536     state->len  -= bytes_to_skip;
537 }
538 
539 int mesh_access_parser_init(mesh_access_parser_state_t * state, mesh_pdu_t * pdu){
540     state->data = mesh_pdu_data(pdu);
541     state->len  = mesh_pdu_len(pdu);
542 
543     uint16_t opcode_size = 0;
544     int ok = mesh_access_get_opcode(state->data, state->len, &state->opcode, &opcode_size);
545     if (ok){
546         mesh_access_parser_skip(state, opcode_size);
547     }
548     return ok;
549 }
550 
551 uint16_t mesh_access_parser_available(mesh_access_parser_state_t * state){
552     return state->len;
553 }
554 
555 uint8_t mesh_access_parser_get_u8(mesh_access_parser_state_t * state){
556     uint8_t value = *state->data;
557     mesh_access_parser_skip(state, 1);
558     return value;
559 }
560 
561 uint16_t mesh_access_parser_get_u16(mesh_access_parser_state_t * state){
562     uint16_t value = little_endian_read_16(state->data, 0);
563     mesh_access_parser_skip(state, 2);
564     return value;
565 }
566 
567 uint32_t mesh_access_parser_get_u24(mesh_access_parser_state_t * state){
568     uint32_t value = little_endian_read_24(state->data, 0);
569     mesh_access_parser_skip(state, 3);
570     return value;
571 }
572 
573 uint32_t mesh_access_parser_get_u32(mesh_access_parser_state_t * state){
574     uint32_t value = little_endian_read_32(state->data, 0);
575     mesh_access_parser_skip(state, 4);
576     return value;
577 }
578 
579 void mesh_access_parser_get_u128(mesh_access_parser_state_t * state, uint8_t * dest){
580     reverse_128( state->data, dest);
581     mesh_access_parser_skip(state, 16);
582 }
583 
584 void mesh_access_parser_get_label_uuid(mesh_access_parser_state_t * state, uint8_t * dest){
585     memcpy( dest, state->data, 16);
586     mesh_access_parser_skip(state, 16);
587 }
588 
589 void mesh_access_parser_get_key(mesh_access_parser_state_t * state, uint8_t * dest){
590     memcpy( dest, state->data, 16);
591     mesh_access_parser_skip(state, 16);
592 }
593 
594 uint32_t mesh_access_parser_get_model_identifier(mesh_access_parser_state_t * parser){
595     uint16_t vendor_id = BLUETOOTH_COMPANY_ID_BLUETOOTH_SIG_INC;
596     if (mesh_access_parser_available(parser) == 4){
597         vendor_id = mesh_access_parser_get_u16(parser);
598     }
599     uint16_t model_id = mesh_access_parser_get_u16(parser);
600     return mesh_model_get_model_identifier(vendor_id, model_id);
601 }
602 
603 // Mesh Access Message Builder
604 
605 // message builder
606 
607 static int mesh_access_setup_opcode(uint8_t * buffer, uint32_t opcode){
608     if (opcode < 0x100){
609         buffer[0] = opcode;
610         return 1;
611     }
612     if (opcode < 0x10000){
613         big_endian_store_16(buffer, 0, opcode);
614         return 2;
615     }
616     buffer[0] = opcode >> 16;
617     little_endian_store_16(buffer, 1, opcode & 0xffff);
618     return 3;
619 }
620 
621 mesh_transport_pdu_t * mesh_access_transport_init(uint32_t opcode){
622     mesh_transport_pdu_t * pdu = mesh_transport_pdu_get();
623     if (!pdu) return NULL;
624 
625     pdu->len  = mesh_access_setup_opcode(pdu->data, opcode);
626     pdu->pdu_header.ack_opcode = MESH_ACCESS_OPCODE_NOT_SET;
627     return pdu;
628 }
629 
630 void mesh_access_transport_add_uint8(mesh_transport_pdu_t * pdu, uint8_t value){
631     pdu->data[pdu->len++] = value;
632 }
633 
634 void mesh_access_transport_add_uint16(mesh_transport_pdu_t * pdu, uint16_t value){
635     little_endian_store_16(pdu->data, pdu->len, value);
636     pdu->len += 2;
637 }
638 
639 void mesh_access_transport_add_uint24(mesh_transport_pdu_t * pdu, uint32_t value){
640     little_endian_store_24(pdu->data, pdu->len, value);
641     pdu->len += 3;
642 }
643 
644 void mesh_access_transport_add_uint32(mesh_transport_pdu_t * pdu, uint32_t value){
645     little_endian_store_32(pdu->data, pdu->len, value);
646     pdu->len += 4;
647 }
648 void mesh_access_transport_add_model_identifier(mesh_transport_pdu_t * pdu, uint32_t model_identifier){
649     if (!mesh_model_is_bluetooth_sig(model_identifier)){
650         mesh_access_transport_add_uint16( pdu, mesh_model_get_vendor_id(model_identifier) );
651     }
652     mesh_access_transport_add_uint16( pdu, mesh_model_get_model_id(model_identifier) );
653 }
654 
655 mesh_network_pdu_t * mesh_access_network_init(uint32_t opcode){
656     mesh_network_pdu_t * pdu = mesh_network_pdu_get();
657     if (!pdu) return NULL;
658 
659     pdu->len  = mesh_access_setup_opcode(&pdu->data[10], opcode) + 10;
660     pdu->pdu_header.ack_opcode = MESH_ACCESS_OPCODE_NOT_SET;
661     return pdu;
662 }
663 
664 void mesh_access_network_add_uint8(mesh_network_pdu_t * pdu, uint8_t value){
665     pdu->data[pdu->len++] = value;
666 }
667 
668 void mesh_access_network_add_uint16(mesh_network_pdu_t * pdu, uint16_t value){
669     little_endian_store_16(pdu->data, pdu->len, value);
670     pdu->len += 2;
671 }
672 
673 void mesh_access_network_add_uint24(mesh_network_pdu_t * pdu, uint16_t value){
674     little_endian_store_24(pdu->data, pdu->len, value);
675     pdu->len += 3;
676 }
677 
678 void mesh_access_network_add_uint32(mesh_network_pdu_t * pdu, uint16_t value){
679     little_endian_store_32(pdu->data, pdu->len, value);
680     pdu->len += 4;
681 }
682 
683 void mesh_access_network_add_model_identifier(mesh_network_pdu_t * pdu, uint32_t model_identifier){
684     if (mesh_model_is_bluetooth_sig(model_identifier)){
685         mesh_access_network_add_uint16( pdu, mesh_model_get_model_id(model_identifier) );
686     } else {
687         mesh_access_network_add_uint32( pdu, model_identifier );
688     }
689 }
690 
691 // access message template
692 
693 mesh_network_pdu_t * mesh_access_setup_unsegmented_message(const mesh_access_message_t *message_template, ...){
694     mesh_network_pdu_t * network_pdu = mesh_access_network_init(message_template->opcode);
695     if (!network_pdu) return NULL;
696 
697     va_list argptr;
698     va_start(argptr, message_template);
699 
700     // add params
701     const char * format = message_template->format;
702     uint16_t word;
703     uint32_t longword;
704     while (*format){
705         switch (*format){
706             case '1':
707                 word = va_arg(argptr, int);  // minimal va_arg is int: 2 bytes on 8+16 bit CPUs
708                 mesh_access_network_add_uint8( network_pdu, word);
709                 break;
710             case '2':
711                 word = va_arg(argptr, int);  // minimal va_arg is int: 2 bytes on 8+16 bit CPUs
712                 mesh_access_network_add_uint16( network_pdu, word);
713                 break;
714             case '3':
715                 longword = va_arg(argptr, uint32_t);
716                 mesh_access_network_add_uint24( network_pdu, longword);
717                 break;
718             case '4':
719                 longword = va_arg(argptr, uint32_t);
720                 mesh_access_network_add_uint32( network_pdu, longword);
721                 break;
722             case 'm':
723                 longword = va_arg(argptr, uint32_t);
724                 mesh_access_network_add_model_identifier( network_pdu, longword);
725                 break;
726             default:
727                 log_error("Unsupported mesh message format specifier '%c", *format);
728                 break;
729         }
730         format++;
731     }
732 
733     va_end(argptr);
734 
735     return network_pdu;
736 }
737 
738 mesh_transport_pdu_t * mesh_access_setup_segmented_message(const mesh_access_message_t *message_template, ...){
739     mesh_transport_pdu_t * transport_pdu = mesh_access_transport_init(message_template->opcode);
740     if (!transport_pdu) return NULL;
741 
742     va_list argptr;
743     va_start(argptr, message_template);
744 
745     // add params
746     const char * format = message_template->format;
747     uint16_t word;
748     uint32_t longword;
749     while (*format){
750         switch (*format++){
751             case '1':
752                 word = va_arg(argptr, int);  // minimal va_arg is int: 2 bytes on 8+16 bit CPUs
753                 mesh_access_transport_add_uint8( transport_pdu, word);
754                 break;
755             case '2':
756                 word = va_arg(argptr, int);  // minimal va_arg is int: 2 bytes on 8+16 bit CPUs
757                 mesh_access_transport_add_uint16( transport_pdu, word);
758                 break;
759             case '3':
760                 longword = va_arg(argptr, uint32_t);
761                 mesh_access_transport_add_uint24( transport_pdu, longword);
762                 break;
763             case '4':
764                 longword = va_arg(argptr, uint32_t);
765                 mesh_access_transport_add_uint32( transport_pdu, longword);
766                 break;
767             case 'm':
768                 longword = va_arg(argptr, uint32_t);
769                 mesh_access_transport_add_model_identifier( transport_pdu, longword);
770                 break;
771             default:
772                 break;
773         }
774     }
775 
776     va_end(argptr);
777 
778     return transport_pdu;
779 }
780 
781 static const mesh_operation_t * mesh_model_lookup_operation_by_opcode(mesh_model_t * model, uint32_t opcode){
782     // find opcode in table
783     const mesh_operation_t * operation = model->operations;
784     if (operation == NULL) return NULL;
785     for ( ; operation->handler != NULL ; operation++){
786         if (operation->opcode != opcode) continue;
787         return operation;
788     }
789     return NULL;
790 }
791 
792 static const mesh_operation_t * mesh_model_lookup_operation(mesh_model_t * model, mesh_pdu_t * pdu){
793 
794     uint32_t opcode = 0;
795     uint16_t opcode_size = 0;
796     int ok = mesh_access_pdu_get_opcode( pdu, &opcode, &opcode_size);
797     if (!ok) return NULL;
798 
799     uint16_t len = mesh_pdu_len(pdu);
800 
801     // find opcode in table
802     const mesh_operation_t * operation = model->operations;
803     if (operation == NULL) return NULL;
804     for ( ; operation->handler != NULL ; operation++){
805         if (operation->opcode != opcode) continue;
806         if ((opcode_size + operation->minimum_length) > len) continue;
807         return operation;
808     }
809     return NULL;
810 }
811 
812 static int mesh_access_validate_appkey_index(mesh_model_t * model, uint16_t appkey_index){
813     // DeviceKey is valid for all models
814     if (appkey_index == MESH_DEVICE_KEY_INDEX) return 1;
815     // check if AppKey that is bound to this particular model
816     return mesh_model_contains_appkey(model, appkey_index);
817 }
818 
819 // decrease use count and report as free if done
820 void mesh_access_message_processed(mesh_pdu_t * pdu){
821     if (mesh_access_received_pdu_refcount > 0){
822         mesh_access_received_pdu_refcount--;
823     }
824     if (mesh_access_received_pdu_refcount == 0){
825         mesh_upper_transport_message_processed_by_higher_layer(pdu);
826     }
827 }
828 
829 static void mesh_access_message_process_handler(mesh_pdu_t * pdu){
830 
831     // init use count
832     mesh_access_received_pdu_refcount = 1;
833 
834     // get opcode and size
835     uint32_t opcode = 0;
836     uint16_t opcode_size = 0;
837 
838     int ok = mesh_access_pdu_get_opcode( pdu, &opcode, &opcode_size);
839     if (!ok) {
840         mesh_access_message_processed(pdu);
841         return;
842     }
843 
844     uint16_t len = mesh_pdu_len(pdu);
845     printf("MESH Access Message, Opcode = %x: ", opcode);
846     printf_hexdump(mesh_pdu_data(pdu), len);
847 
848     uint16_t src = mesh_pdu_src(pdu);
849     uint16_t dst = mesh_pdu_dst(pdu);
850     uint16_t appkey_index = mesh_pdu_appkey_index(pdu);
851     if (mesh_network_address_unicast(dst)){
852         // loookup element by unicast address
853         mesh_element_t * element = mesh_node_element_for_unicast_address(dst);
854         if (element != NULL){
855             // iterate over models, look for operation
856             mesh_model_iterator_t model_it;
857             mesh_model_iterator_init(&model_it, element);
858             while (mesh_model_iterator_has_next(&model_it)){
859                 mesh_model_t * model = mesh_model_iterator_next(&model_it);
860                 // find opcode in table
861                 const mesh_operation_t * operation = mesh_model_lookup_operation(model, pdu);
862                 if (operation == NULL) continue;
863                 if (mesh_access_validate_appkey_index(model, appkey_index) == 0) continue;
864                 mesh_access_acknowledged_received(src, opcode);
865                 mesh_access_received_pdu_refcount++;
866                 operation->handler(model, pdu);
867             }
868         }
869     }
870     else if (mesh_network_address_group(dst)){
871 
872         // handle fixed group address
873         if (dst >= 0xff00){
874             int deliver_to_primary_element = 1;
875             switch (dst){
876                 case MESH_ADDRESS_ALL_PROXIES:
877                     if (mesh_foundation_gatt_proxy_get() == 1){
878                         deliver_to_primary_element = 1;
879                     }
880                     break;
881                 case MESH_ADDRESS_ALL_FRIENDS:
882                     // TODO: not implemented
883                     break;
884                 case MESH_ADDRESS_ALL_RELAYS:
885                     if (mesh_foundation_relay_get() == 1){
886                         deliver_to_primary_element = 1;
887                     }
888                     break;
889                 case MESH_ADDRESS_ALL_NODES:
890                     deliver_to_primary_element = 1;
891                     break;
892                 default:
893                     break;
894             }
895             if (deliver_to_primary_element){
896                 mesh_model_iterator_t model_it;
897                 mesh_model_iterator_init(&model_it, mesh_node_get_primary_element());
898                 while (mesh_model_iterator_has_next(&model_it)){
899                     mesh_model_t * model = mesh_model_iterator_next(&model_it);
900                     // find opcode in table
901                     const mesh_operation_t * operation = mesh_model_lookup_operation(model, pdu);
902                     if (operation == NULL) continue;
903                     if (mesh_access_validate_appkey_index(model, appkey_index) == 0) continue;
904                     mesh_access_acknowledged_received(src, opcode);
905                     mesh_access_received_pdu_refcount++;
906                     operation->handler(model, pdu);
907                 }
908             }
909         }
910         else {
911             // iterate over all elements / models, check subscription list
912             mesh_element_iterator_t it;
913             mesh_element_iterator_init(&it);
914             while (mesh_element_iterator_has_next(&it)){
915                 mesh_element_t * element = (mesh_element_t *) mesh_element_iterator_next(&it);
916                 mesh_model_iterator_t model_it;
917                 mesh_model_iterator_init(&model_it, element);
918                 while (mesh_model_iterator_has_next(&model_it)){
919                     mesh_model_t * model = mesh_model_iterator_next(&model_it);
920                     if (mesh_model_contains_subscription(model, dst)){
921                         // find opcode in table
922                         const mesh_operation_t * operation = mesh_model_lookup_operation(model, pdu);
923                         if (operation == NULL) continue;
924                         if (mesh_access_validate_appkey_index(model, appkey_index) == 0) continue;
925                         mesh_access_acknowledged_received(src, opcode);
926                         mesh_access_received_pdu_refcount++;
927                         operation->handler(model, pdu);
928                     }
929                 }
930             }
931         }
932     }
933 
934     // we're done
935     mesh_access_message_processed(pdu);
936 }
937 
938 // Mesh Model Publication
939 static btstack_timer_source_t mesh_access_publication_timer;
940 
941 static uint32_t mesh_model_publication_retransmit_count(uint8_t retransmit){
942     return retransmit & 0x07u;
943 }
944 
945 static uint32_t mesh_model_publication_retransmission_period_ms(uint8_t retransmit){
946     return ((uint32_t)((retransmit >> 3) + 1)) * 50;
947 }
948 
949 static void mesh_model_publication_setup_publication(mesh_publication_model_t * publication_model, uint32_t now){
950 
951     // set retransmit counter
952     publication_model->retransmit_count = mesh_model_publication_retransmit_count(publication_model->retransmit);
953 
954     // schedule next publication or retransmission
955     uint32_t publication_period_ms = mesh_access_time_gdtt2ms(publication_model->period) >> publication_model->period_divisor;
956 
957     // set next publication
958     if (publication_period_ms != 0){
959         publication_model->next_publication_ms = now + publication_period_ms;
960         publication_model->state = MESH_MODEL_PUBLICATION_STATE_W4_PUBLICATION_MS;
961     } else {
962         publication_model->state = MESH_MODEL_PUBLICATION_STATE_IDLE;
963     }
964 }
965 
966 // assumes retransmit_count is valid
967 static void mesh_model_publication_setup_retransmission(mesh_publication_model_t * publication_model, uint32_t now){
968     uint32_t publication_period_ms = mesh_access_time_gdtt2ms(publication_model->period) >> publication_model->period_divisor;
969 
970     // retransmission done
971     if (publication_model->retransmit_count == 0) {
972         // wait for next main event if periodic and retransmission complete
973         if (publication_period_ms != 0){
974             publication_model->state = MESH_MODEL_PUBLICATION_STATE_W4_PUBLICATION_MS;
975         } else {
976             publication_model->state = MESH_MODEL_PUBLICATION_STATE_IDLE;
977         }
978         return;
979     }
980 
981     // calc next retransmit time
982     uint32_t retransmission_period_ms = mesh_model_publication_retransmission_period_ms(publication_model->retransmit) >> publication_model->period_divisor;
983     uint32_t retransmission_ms = now + retransmission_period_ms;
984 
985     // check next publication timeout is before next retransmission
986     if (publication_period_ms != 0){
987         if (btstack_time_delta(retransmission_ms, publication_model->next_publication_ms) > 0) return;
988     }
989 
990     // schedule next retransmission
991     publication_model->next_retransmit_ms = retransmission_ms;
992     publication_model->state = MESH_MODEL_PUBLICATION_STATE_W4_RETRANSMIT_MS;
993 }
994 
995 static void mesh_model_publication_publish_now_model(mesh_model_t * mesh_model){
996     mesh_publication_model_t * publication_model = mesh_model->publication_model;
997     if (publication_model == NULL) return;
998     if (publication_model->publish_state_fn == NULL) return;
999     uint16_t dest = publication_model->address;
1000     if (dest == MESH_ADDRESS_UNSASSIGNED) return;
1001     uint16_t appkey_index = publication_model->appkey_index;
1002     mesh_transport_key_t * app_key = mesh_transport_key_get(appkey_index);
1003     if (app_key == NULL) return;
1004 
1005     // compose message
1006     mesh_pdu_t * pdu = (*publication_model->publish_state_fn)(mesh_model);
1007     if (pdu == NULL) return;
1008 
1009     // handle ttl = default
1010     uint8_t ttl = publication_model->ttl;
1011     if (ttl == 0xff){
1012         ttl = mesh_foundation_default_ttl_get();
1013     }
1014 
1015     mesh_upper_transport_setup_access_pdu_header(pdu, app_key->netkey_index, appkey_index, ttl, mesh_access_get_element_address(mesh_model), dest, 0);
1016     mesh_access_send_unacknowledged_pdu(pdu);
1017 }
1018 
1019 static void mesh_model_publication_run(btstack_timer_source_t * ts){
1020 
1021     uint32_t now = btstack_run_loop_get_time_ms();
1022 
1023     // iterate over elements and models and handle time-based transitions
1024     mesh_element_iterator_t element_it;
1025     mesh_element_iterator_init(&element_it);
1026     while (mesh_element_iterator_has_next(&element_it)){
1027         mesh_element_t * element = mesh_element_iterator_next(&element_it);
1028         mesh_model_iterator_t model_it;
1029         mesh_model_iterator_init(&model_it, element);
1030         while (mesh_model_iterator_has_next(&model_it)){
1031             mesh_model_t * mesh_model = mesh_model_iterator_next(&model_it);
1032             mesh_publication_model_t * publication_model = mesh_model->publication_model;
1033             if (publication_model == NULL) continue;
1034 
1035             // check if either timer fired
1036             switch (publication_model->state){
1037                 case MESH_MODEL_PUBLICATION_STATE_W4_PUBLICATION_MS:
1038                     if (btstack_time_delta(publication_model->next_publication_ms, now) > 0) break;
1039                     publication_model->state = MESH_MODEL_PUBLICATION_STATE_PUBLICATION_READY;
1040                     break;
1041                 case MESH_MODEL_PUBLICATION_STATE_W4_RETRANSMIT_MS:
1042                     if (btstack_time_delta(publication_model->next_retransmit_ms, now) > 0) break;
1043                     publication_model->state = MESH_MODEL_PUBLICATION_STATE_RETRANSMIT_READY;
1044                     break;
1045                 default:
1046                     break;
1047             }
1048 
1049             switch (publication_model->state){
1050                 case MESH_MODEL_PUBLICATION_STATE_PUBLICATION_READY:
1051                     // schedule next publication and retransmission
1052                     mesh_model_publication_setup_publication(publication_model, now);
1053                     mesh_model_publication_setup_retransmission(publication_model, now);
1054                     mesh_model_publication_publish_now_model(mesh_model);
1055                     break;
1056                 case MESH_MODEL_PUBLICATION_STATE_RETRANSMIT_READY:
1057                     // schedule next retransmission
1058                     publication_model->retransmit_count--;
1059                     mesh_model_publication_setup_retransmission(publication_model, now);
1060                     mesh_model_publication_publish_now_model(mesh_model);
1061                     break;
1062                 default:
1063                     break;
1064             }
1065         }
1066     }
1067 
1068     int32_t next_timeout_ms = 0;
1069     mesh_element_iterator_init(&element_it);
1070     while (mesh_element_iterator_has_next(&element_it)){
1071         mesh_element_t * element = mesh_element_iterator_next(&element_it);
1072         mesh_model_iterator_t model_it;
1073         mesh_model_iterator_init(&model_it, element);
1074         while (mesh_model_iterator_has_next(&model_it)){
1075             mesh_model_t * mesh_model = mesh_model_iterator_next(&model_it);
1076             mesh_publication_model_t * publication_model = mesh_model->publication_model;
1077             if (publication_model == NULL) continue;
1078 
1079             // schedule next
1080             int32_t timeout_delta_ms;
1081             switch (publication_model->state){
1082                 case MESH_MODEL_PUBLICATION_STATE_W4_PUBLICATION_MS:
1083                     timeout_delta_ms = btstack_time_delta(publication_model->next_publication_ms, now);
1084                     if (next_timeout_ms == 0 || timeout_delta_ms < next_timeout_ms){
1085                         next_timeout_ms = timeout_delta_ms;
1086                     }
1087                     break;
1088                 case MESH_MODEL_PUBLICATION_STATE_W4_RETRANSMIT_MS:
1089                     timeout_delta_ms = btstack_time_delta(publication_model->next_retransmit_ms, now);
1090                     if (next_timeout_ms == 0 || timeout_delta_ms < next_timeout_ms){
1091                         next_timeout_ms = timeout_delta_ms;
1092                     }
1093                     break;
1094                 default:
1095                     break;
1096             }
1097         }
1098     }
1099 
1100     // remove current timer if active
1101     if (ts == NULL){
1102         btstack_run_loop_remove_timer(&mesh_access_publication_timer);
1103 
1104     }
1105 
1106     // new timeout?
1107     if (next_timeout_ms == 0) return;
1108 
1109     // set timer
1110     btstack_run_loop_set_timer(&mesh_access_publication_timer, next_timeout_ms);
1111     btstack_run_loop_set_timer_handler(&mesh_access_publication_timer, mesh_model_publication_run);
1112     btstack_run_loop_add_timer(&mesh_access_publication_timer);
1113 }
1114 
1115 void mesh_model_publication_start(mesh_model_t * mesh_model){
1116     mesh_publication_model_t * publication_model = mesh_model->publication_model;
1117     if (publication_model == NULL) return;
1118 
1119     // publish right away
1120     publication_model->state = MESH_MODEL_PUBLICATION_STATE_PUBLICATION_READY;
1121     mesh_model_publication_run(NULL);
1122 }
1123 
1124 void mesh_model_publication_stop(mesh_model_t * mesh_model){
1125     mesh_publication_model_t * publication_model = mesh_model->publication_model;
1126     if (publication_model == NULL) return;
1127 
1128     // reset state
1129     publication_model->state = MESH_MODEL_PUBLICATION_STATE_IDLE;
1130 }
1131 
1132 void mesh_access_state_changed(mesh_model_t * mesh_model){
1133     mesh_publication_model_t * publication_model = mesh_model->publication_model;
1134     if (publication_model == NULL) return;
1135     publication_model->state = MESH_MODEL_PUBLICATION_STATE_PUBLICATION_READY;
1136     mesh_model_publication_run(NULL);
1137 }
1138 
1139