xref: /btstack/src/mesh/mesh_lower_transport.c (revision 3945bdb63247a81133702322b1e08d3fa42d20ed)
177ba3d3fSMatthias Ringwald /*
277ba3d3fSMatthias Ringwald  * Copyright (C) 2014 BlueKitchen GmbH
377ba3d3fSMatthias Ringwald  *
477ba3d3fSMatthias Ringwald  * Redistribution and use in source and binary forms, with or without
577ba3d3fSMatthias Ringwald  * modification, are permitted provided that the following conditions
677ba3d3fSMatthias Ringwald  * are met:
777ba3d3fSMatthias Ringwald  *
877ba3d3fSMatthias Ringwald  * 1. Redistributions of source code must retain the above copyright
977ba3d3fSMatthias Ringwald  *    notice, this list of conditions and the following disclaimer.
1077ba3d3fSMatthias Ringwald  * 2. Redistributions in binary form must reproduce the above copyright
1177ba3d3fSMatthias Ringwald  *    notice, this list of conditions and the following disclaimer in the
1277ba3d3fSMatthias Ringwald  *    documentation and/or other materials provided with the distribution.
1377ba3d3fSMatthias Ringwald  * 3. Neither the name of the copyright holders nor the names of
1477ba3d3fSMatthias Ringwald  *    contributors may be used to endorse or promote products derived
1577ba3d3fSMatthias Ringwald  *    from this software without specific prior written permission.
1677ba3d3fSMatthias Ringwald  * 4. Any redistribution, use, or modification is done solely for
1777ba3d3fSMatthias Ringwald  *    personal benefit and not for any commercial purpose or for
1877ba3d3fSMatthias Ringwald  *    monetary gain.
1977ba3d3fSMatthias Ringwald  *
2077ba3d3fSMatthias Ringwald  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
2177ba3d3fSMatthias Ringwald  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2277ba3d3fSMatthias Ringwald  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2377ba3d3fSMatthias Ringwald  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
2477ba3d3fSMatthias Ringwald  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2577ba3d3fSMatthias Ringwald  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2677ba3d3fSMatthias Ringwald  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
2777ba3d3fSMatthias Ringwald  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2877ba3d3fSMatthias Ringwald  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2977ba3d3fSMatthias Ringwald  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
3077ba3d3fSMatthias Ringwald  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3177ba3d3fSMatthias Ringwald  * SUCH DAMAGE.
3277ba3d3fSMatthias Ringwald  *
3377ba3d3fSMatthias Ringwald  * Please inquire about commercial licensing options at
3477ba3d3fSMatthias Ringwald  * [email protected]
3577ba3d3fSMatthias Ringwald  *
3677ba3d3fSMatthias Ringwald  */
3777ba3d3fSMatthias Ringwald 
382d4000d1SMatthias Ringwald #define BTSTACK_FILE__ "mesh_lower_transport.c"
3977ba3d3fSMatthias Ringwald 
4077ba3d3fSMatthias Ringwald #include <stdio.h>
4177ba3d3fSMatthias Ringwald #include <stdlib.h>
4277ba3d3fSMatthias Ringwald #include <string.h>
43fc5e2620SMatthias Ringwald #include <btstack.h>
44f4854a5eSMatthias Ringwald 
4577ba3d3fSMatthias Ringwald #include "btstack_memory.h"
46f4854a5eSMatthias Ringwald #include "btstack_util.h"
47f4854a5eSMatthias Ringwald 
48683cf298SMatthias Ringwald #include "mesh/beacon.h"
49f4854a5eSMatthias Ringwald #include "mesh/mesh_iv_index_seq_number.h"
50f4854a5eSMatthias Ringwald #include "mesh/mesh_lower_transport.h"
51683cf298SMatthias Ringwald #include "mesh/mesh_node.h"
52f4854a5eSMatthias Ringwald #include "mesh/mesh_peer.h"
5377ba3d3fSMatthias Ringwald 
54b66a7a84SMatthias Ringwald #define LOG_LOWER_TRANSPORT
55b66a7a84SMatthias Ringwald 
5677ba3d3fSMatthias Ringwald static void (*higher_layer_handler)( mesh_transport_callback_type_t callback_type, mesh_transport_status_t status, mesh_pdu_t * pdu);
5777ba3d3fSMatthias Ringwald 
5877ba3d3fSMatthias Ringwald static void mesh_print_hex(const char * name, const uint8_t * data, uint16_t len){
5977ba3d3fSMatthias Ringwald     printf("%-20s ", name);
6077ba3d3fSMatthias Ringwald     printf_hexdump(data, len);
6177ba3d3fSMatthias Ringwald }
6277ba3d3fSMatthias Ringwald // static void mesh_print_x(const char * name, uint32_t value){
6377ba3d3fSMatthias Ringwald //     printf("%20s: 0x%x", name, (int) value);
6477ba3d3fSMatthias Ringwald // }
6577ba3d3fSMatthias Ringwald 
6677ba3d3fSMatthias Ringwald // utility
6777ba3d3fSMatthias Ringwald 
68a4bbc09dSMatthias Ringwald mesh_segmented_pdu_t * mesh_message_pdu_get(void){
69a4bbc09dSMatthias Ringwald     mesh_segmented_pdu_t * message_pdu = btstack_memory_mesh_segmented_pdu_get();
70926e8875SMatthias Ringwald     if (message_pdu){
71a4bbc09dSMatthias Ringwald         message_pdu->pdu_header.pdu_type = MESH_PDU_TYPE_SEGMENTED;
72926e8875SMatthias Ringwald     }
73926e8875SMatthias Ringwald     return message_pdu;
74926e8875SMatthias Ringwald }
75926e8875SMatthias Ringwald 
76a4bbc09dSMatthias Ringwald void mesh_message_pdu_free(mesh_segmented_pdu_t * message_pdu){
77cdcfd2c1SMatthias Ringwald     while (message_pdu->segments){
78cdcfd2c1SMatthias Ringwald         mesh_network_pdu_t * segment = (mesh_network_pdu_t *) btstack_linked_list_pop(&message_pdu->segments);
79cdcfd2c1SMatthias Ringwald         mesh_network_pdu_free(segment);
80cdcfd2c1SMatthias Ringwald     }
81a4bbc09dSMatthias Ringwald     btstack_memory_mesh_segmented_pdu_free(message_pdu);
82926e8875SMatthias Ringwald }
83926e8875SMatthias Ringwald 
84a4bbc09dSMatthias Ringwald static void mesh_lower_transport_report_segments_as_processed(mesh_segmented_pdu_t * message_pdu) {
8535699c48SMatthias Ringwald     while (message_pdu->segments){
8635699c48SMatthias Ringwald         mesh_network_pdu_t * segment = (mesh_network_pdu_t *) btstack_linked_list_pop(&message_pdu->segments);
8735699c48SMatthias Ringwald         mesh_network_message_processed_by_higher_layer(segment);
8835699c48SMatthias Ringwald     }
8935699c48SMatthias Ringwald }
9035699c48SMatthias Ringwald 
9177ba3d3fSMatthias Ringwald // lower transport
9277ba3d3fSMatthias Ringwald 
9377ba3d3fSMatthias Ringwald // prototypes
9477ba3d3fSMatthias Ringwald 
9577ba3d3fSMatthias Ringwald static void mesh_lower_transport_run(void);
96f41d95b9SMatthias Ringwald static void mesh_lower_transport_outgoing_complete(void);
97eb3826d8SMatthias Ringwald static void mesh_lower_transport_network_pdu_sent(mesh_network_pdu_t *network_pdu);
98b9d8f3adSMatthias Ringwald static void mesh_lower_transport_segment_transmission_timeout(btstack_timer_source_t * ts);
9977ba3d3fSMatthias Ringwald 
10077ba3d3fSMatthias Ringwald // state
10177ba3d3fSMatthias Ringwald static int                    lower_transport_retry_count;
10277ba3d3fSMatthias Ringwald 
10377ba3d3fSMatthias Ringwald // lower transport ougoing
10477ba3d3fSMatthias Ringwald static btstack_linked_list_t lower_transport_outgoing;
10577ba3d3fSMatthias Ringwald 
10677ba3d3fSMatthias Ringwald static mesh_network_pdu_t   * lower_transport_outgoing_segment;
10777ba3d3fSMatthias Ringwald static uint16_t               lower_transport_outgoing_seg_o;
1081e6c9225SMatthias Ringwald 
109a4bbc09dSMatthias Ringwald static mesh_segmented_pdu_t     lower_transport_outgoing_segmented_message_singleton;
110a7f44e8dSMatthias Ringwald 
111a4bbc09dSMatthias Ringwald static mesh_segmented_pdu_t   * lower_transport_outgoing_message;
1121e6c9225SMatthias Ringwald 
113e9c16304SMatthias Ringwald static mesh_network_pdu_t *   lower_transport_outgoing_network_pdu;
114b837963cSMatthias Ringwald 
1155f62b711SMatthias Ringwald // segment at network layer
1165f62b711SMatthias Ringwald static int                    lower_transport_outgoing_segment_queued;
1175f62b711SMatthias Ringwald // transmission timeout occured (while outgoing segment queued at network layer)
1185f62b711SMatthias Ringwald static int                    lower_transport_outgoing_transmission_timeout;
1191b639008SMatthias Ringwald // transmission completed either fully acked or remote aborted (while outgoing segment queued at network layer)
1201b639008SMatthias Ringwald static int                    lower_transport_outgoing_trasnmission_complete;
12177ba3d3fSMatthias Ringwald 
122fc5e2620SMatthias Ringwald // deliver to higher layer
123fc5e2620SMatthias Ringwald static mesh_pdu_t * mesh_lower_transport_higher_layer_pdu;
124fc5e2620SMatthias Ringwald static btstack_linked_list_t mesh_lower_transport_queued_for_higher_layer;
125fc5e2620SMatthias Ringwald 
12668d3bb6cSMatthias Ringwald static void mesh_lower_transport_process_segment_acknowledgement_message(mesh_network_pdu_t *network_pdu){
1278fa6125aSMatthias Ringwald     if (lower_transport_outgoing_message == NULL) return;
12868d3bb6cSMatthias Ringwald 
12977ba3d3fSMatthias Ringwald     uint8_t * lower_transport_pdu     = mesh_network_pdu_data(network_pdu);
13068d3bb6cSMatthias Ringwald     uint16_t seq_zero_pdu = big_endian_read_16(lower_transport_pdu, 1) >> 2;
131*3945bdb6SMatthias Ringwald     uint16_t seq_zero_out = lower_transport_outgoing_message->seq & 0x1fff;
13268d3bb6cSMatthias Ringwald     uint32_t block_ack = big_endian_read_32(lower_transport_pdu, 3);
133b66a7a84SMatthias Ringwald 
134b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
13577ba3d3fSMatthias Ringwald     printf("[+] Segment Acknowledgment message with seq_zero %06x, block_ack %08x - outgoing seq %06x, block_ack %08x\n",
1368fa6125aSMatthias Ringwald            seq_zero_pdu, block_ack, seq_zero_out, lower_transport_outgoing_message->block_ack);
137b66a7a84SMatthias Ringwald #endif
13868d3bb6cSMatthias Ringwald 
13977ba3d3fSMatthias Ringwald     if (block_ack == 0){
14077ba3d3fSMatthias Ringwald         // If a Segment Acknowledgment message with the BlockAck field set to 0x00000000 is received,
14177ba3d3fSMatthias Ringwald         // then the Upper Transport PDU shall be immediately cancelled and the higher layers shall be notified that
14277ba3d3fSMatthias Ringwald         // the Upper Transport PDU has been cancelled.
143b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
14477ba3d3fSMatthias Ringwald         printf("[+] Block Ack == 0 => Abort\n");
145b66a7a84SMatthias Ringwald #endif
1461b639008SMatthias Ringwald         if (lower_transport_outgoing_segment_queued){
1471b639008SMatthias Ringwald             lower_transport_outgoing_trasnmission_complete = 1;
1481b639008SMatthias Ringwald         } else {
149f41d95b9SMatthias Ringwald             mesh_lower_transport_outgoing_complete();
1501b639008SMatthias Ringwald         }
15168d3bb6cSMatthias Ringwald         return;
15277ba3d3fSMatthias Ringwald     }
15377ba3d3fSMatthias Ringwald     if (seq_zero_pdu != seq_zero_out){
15468d3bb6cSMatthias Ringwald 
155b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
15677ba3d3fSMatthias Ringwald         printf("[!] Seq Zero doesn't match\n");
157b66a7a84SMatthias Ringwald #endif
15868d3bb6cSMatthias Ringwald         return;
15977ba3d3fSMatthias Ringwald     }
16068d3bb6cSMatthias Ringwald 
1618fa6125aSMatthias Ringwald     lower_transport_outgoing_message->block_ack &= ~block_ack;
162b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
1638fa6125aSMatthias Ringwald     printf("[+] Updated block_ack %08x\n", lower_transport_outgoing_message->block_ack);
164b66a7a84SMatthias Ringwald #endif
16568d3bb6cSMatthias Ringwald 
1668fa6125aSMatthias Ringwald     if (lower_transport_outgoing_message->block_ack == 0){
167b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
16877ba3d3fSMatthias Ringwald         printf("[+] Sent complete\n");
169b66a7a84SMatthias Ringwald #endif
1701b639008SMatthias Ringwald 
1711b639008SMatthias Ringwald         if (lower_transport_outgoing_segment_queued){
1721b639008SMatthias Ringwald             lower_transport_outgoing_trasnmission_complete = 1;
1731b639008SMatthias Ringwald         } else {
174f41d95b9SMatthias Ringwald             mesh_lower_transport_outgoing_complete();
17577ba3d3fSMatthias Ringwald         }
17668d3bb6cSMatthias Ringwald     }
1771b639008SMatthias Ringwald }
178fc5e2620SMatthias Ringwald 
179fc5e2620SMatthias Ringwald static void mesh_lower_transport_deliver_to_higher_layer(void){
180fc5e2620SMatthias Ringwald     if (mesh_lower_transport_higher_layer_pdu == NULL && !btstack_linked_list_empty(&mesh_lower_transport_queued_for_higher_layer)){
1812ae11e2cSMatthias Ringwald         mesh_pdu_t * pdu = (mesh_pdu_t *) btstack_linked_list_pop(&mesh_lower_transport_queued_for_higher_layer);
1822ae11e2cSMatthias Ringwald 
1832ae11e2cSMatthias Ringwald         switch (pdu->pdu_type){
1842ae11e2cSMatthias Ringwald             case MESH_MSG_TYPE_NETWORK_PDU:
1853d9c3b8eSMatthias Ringwald                 // unsegmented pdu
186e4121a34SMatthias Ringwald                 mesh_lower_transport_higher_layer_pdu = (mesh_pdu_t *) pdu;
187e4121a34SMatthias Ringwald                 pdu->pdu_type = MESH_PDU_TYPE_UNSEGMENTED;
188e4121a34SMatthias Ringwald                 // mesh_lower_transport_higher_layer_pdu = (mesh_pdu_t *) &lower_transport_access_incoming_singleton;
189e4121a34SMatthias Ringwald                 // lower_transport_access_incoming_singleton.pdu_header.pdu_type = MESH_PDU_TYPE_UNSEGMENTED;
190e4121a34SMatthias Ringwald                 // lower_transport_access_incoming_singleton.segment = (mesh_network_pdu_t*) pdu;
1912ae11e2cSMatthias Ringwald                 break;
1922ae11e2cSMatthias Ringwald             default:
1932ae11e2cSMatthias Ringwald                 // segmented control or access pdu
1942ae11e2cSMatthias Ringwald                 mesh_lower_transport_higher_layer_pdu = pdu;
1952ae11e2cSMatthias Ringwald                 break;
1962ae11e2cSMatthias Ringwald         }
197fc5e2620SMatthias Ringwald         higher_layer_handler(MESH_TRANSPORT_PDU_RECEIVED, MESH_TRANSPORT_STATUS_SUCCESS, mesh_lower_transport_higher_layer_pdu);
198fc5e2620SMatthias Ringwald     }
199fc5e2620SMatthias Ringwald }
200fc5e2620SMatthias Ringwald 
201fc5e2620SMatthias Ringwald static void mesh_lower_transport_queue_for_higher_layer(mesh_pdu_t * pdu){
202fc5e2620SMatthias Ringwald     btstack_linked_list_add_tail(&mesh_lower_transport_queued_for_higher_layer, (btstack_linked_item_t *) pdu);
203fc5e2620SMatthias Ringwald     mesh_lower_transport_deliver_to_higher_layer();
2049a0bb1c9SMatthias Ringwald }
20568d3bb6cSMatthias Ringwald 
20668d3bb6cSMatthias Ringwald static void mesh_lower_transport_process_unsegmented_control_message(mesh_network_pdu_t *network_pdu){
20768d3bb6cSMatthias Ringwald     uint8_t * lower_transport_pdu     = mesh_network_pdu_data(network_pdu);
20868d3bb6cSMatthias Ringwald     uint8_t  opcode = lower_transport_pdu[0];
20968d3bb6cSMatthias Ringwald 
21068d3bb6cSMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
2118fa6125aSMatthias Ringwald     printf("Unsegmented Control message, outgoing message %p, opcode %x\n", lower_transport_outgoing_message, opcode);
21268d3bb6cSMatthias Ringwald #endif
21368d3bb6cSMatthias Ringwald 
21468d3bb6cSMatthias Ringwald     switch (opcode){
21568d3bb6cSMatthias Ringwald         case 0:
21668d3bb6cSMatthias Ringwald             mesh_lower_transport_process_segment_acknowledgement_message(network_pdu);
21777ba3d3fSMatthias Ringwald             mesh_network_message_processed_by_higher_layer(network_pdu);
21877ba3d3fSMatthias Ringwald             break;
21977ba3d3fSMatthias Ringwald         default:
220fc5e2620SMatthias Ringwald             mesh_lower_transport_queue_for_higher_layer((mesh_pdu_t *) network_pdu);
22177ba3d3fSMatthias Ringwald             break;
22277ba3d3fSMatthias Ringwald     }
22377ba3d3fSMatthias Ringwald }
22477ba3d3fSMatthias Ringwald 
22577ba3d3fSMatthias Ringwald // ack / incomplete message
22677ba3d3fSMatthias Ringwald 
22777ba3d3fSMatthias Ringwald static void mesh_lower_transport_setup_segmented_acknowledge_message(uint8_t * data, uint8_t obo, uint16_t seq_zero, uint32_t block_ack){
22877ba3d3fSMatthias Ringwald     // printf("ACK Upper Transport, seq_zero %x\n", seq_zero);
22977ba3d3fSMatthias Ringwald     data[0] = 0;    // SEG = 0, Opcode = 0
23077ba3d3fSMatthias Ringwald     big_endian_store_16( data, 1, (obo << 15) | (seq_zero << 2) | 0);    // OBO, SeqZero, RFU
23177ba3d3fSMatthias Ringwald     big_endian_store_32( data, 3, block_ack);
232b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
23377ba3d3fSMatthias Ringwald     mesh_print_hex("ACK Upper Transport", data, 7);
234b66a7a84SMatthias Ringwald #endif
23577ba3d3fSMatthias Ringwald }
23677ba3d3fSMatthias Ringwald 
23777ba3d3fSMatthias Ringwald static void mesh_lower_transport_send_ack(uint16_t netkey_index, uint8_t ttl, uint16_t dest, uint16_t seq_zero, uint32_t block_ack){
23877ba3d3fSMatthias Ringwald     // setup ack message
23977ba3d3fSMatthias Ringwald     uint8_t  ack_msg[7];
24077ba3d3fSMatthias Ringwald     mesh_lower_transport_setup_segmented_acknowledge_message(ack_msg, 0, seq_zero, block_ack);
24177ba3d3fSMatthias Ringwald     //
24277ba3d3fSMatthias Ringwald     // "3.4.5.2: The output filter of the interface connected to advertising or GATT bearers shall drop all messages with TTL value set to 1."
24377ba3d3fSMatthias Ringwald     // if (ttl <= 1) return 0;
24477ba3d3fSMatthias Ringwald 
24577ba3d3fSMatthias Ringwald     // TODO: check transport_pdu_len depending on ctl
24677ba3d3fSMatthias Ringwald 
24777ba3d3fSMatthias Ringwald     // lookup network by netkey_index
24877ba3d3fSMatthias Ringwald     const mesh_network_key_t * network_key = mesh_network_key_list_get(netkey_index);
24977ba3d3fSMatthias Ringwald     if (!network_key) return;
25077ba3d3fSMatthias Ringwald 
25177ba3d3fSMatthias Ringwald     // allocate network_pdu
25277ba3d3fSMatthias Ringwald     mesh_network_pdu_t * network_pdu = mesh_network_pdu_get();
25377ba3d3fSMatthias Ringwald     if (!network_pdu) return;
25477ba3d3fSMatthias Ringwald 
25577ba3d3fSMatthias Ringwald     // setup network_pdu
256001c65e0SMatthias Ringwald     mesh_network_setup_pdu(network_pdu, netkey_index, network_key->nid, 1, ttl, mesh_sequence_number_next(), mesh_node_get_primary_element_address(), dest, ack_msg, sizeof(ack_msg));
25777ba3d3fSMatthias Ringwald 
25877ba3d3fSMatthias Ringwald     // send network_pdu
25977ba3d3fSMatthias Ringwald     mesh_network_send_pdu(network_pdu);
26077ba3d3fSMatthias Ringwald }
26177ba3d3fSMatthias Ringwald 
262a4bbc09dSMatthias Ringwald static void mesh_lower_transport_send_ack_for_transport_pdu(mesh_segmented_pdu_t * message_pdu){
263*3945bdb6SMatthias Ringwald     uint16_t seq_zero = message_pdu->seq_zero;
264*3945bdb6SMatthias Ringwald     uint8_t ttl = message_pdu->ctl_ttl & 0x7f;
265*3945bdb6SMatthias Ringwald     uint16_t dest = message_pdu->src;
266926e8875SMatthias Ringwald     uint16_t netkey_index = message_pdu->netkey_index;
267b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
26877ba3d3fSMatthias Ringwald     printf("mesh_transport_send_ack_for_transport_pdu %p with netkey_index %x, TTL = %u, SeqZero = %x, SRC = %x, DST = %x\n",
269926e8875SMatthias Ringwald            message_pdu, netkey_index, ttl, seq_zero, mesh_node_get_primary_element_address(), dest);
270b66a7a84SMatthias Ringwald #endif
271926e8875SMatthias Ringwald     mesh_lower_transport_send_ack(netkey_index, ttl, dest, seq_zero, message_pdu->block_ack);
27277ba3d3fSMatthias Ringwald }
27377ba3d3fSMatthias Ringwald 
27477ba3d3fSMatthias Ringwald static void mesh_lower_transport_send_ack_for_network_pdu(mesh_network_pdu_t *network_pdu, uint16_t seq_zero, uint32_t block_ack) {
27577ba3d3fSMatthias Ringwald     uint8_t ttl = mesh_network_ttl(network_pdu);
27677ba3d3fSMatthias Ringwald     uint16_t dest = mesh_network_src(network_pdu);
27777ba3d3fSMatthias Ringwald     uint16_t netkey_index = network_pdu->netkey_index;
278b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
27977ba3d3fSMatthias Ringwald     printf("mesh_transport_send_ack_for_network_pdu %p with netkey_index %x, TTL = %u, SeqZero = %x, SRC = %x, DST = %x\n",
280001c65e0SMatthias Ringwald            network_pdu, netkey_index, ttl, seq_zero, mesh_node_get_primary_element_address(), dest);
281b66a7a84SMatthias Ringwald #endif
28277ba3d3fSMatthias Ringwald     mesh_lower_transport_send_ack(netkey_index, ttl, dest, seq_zero, block_ack);
28377ba3d3fSMatthias Ringwald }
28477ba3d3fSMatthias Ringwald 
285a4bbc09dSMatthias Ringwald static void mesh_lower_transport_incoming_stop_acknowledgment_timer(mesh_segmented_pdu_t *message_pdu){
286926e8875SMatthias Ringwald     if (!message_pdu->acknowledgement_timer_active) return;
287926e8875SMatthias Ringwald     message_pdu->acknowledgement_timer_active = 0;
288926e8875SMatthias Ringwald     btstack_run_loop_remove_timer(&message_pdu->acknowledgement_timer);
289926e8875SMatthias Ringwald }
290926e8875SMatthias Ringwald 
291a4bbc09dSMatthias Ringwald static void mesh_lower_transport_incoming_stop_incomplete_timer(mesh_segmented_pdu_t *message_pdu){
292926e8875SMatthias Ringwald     if (!message_pdu->incomplete_timer_active) return;
293926e8875SMatthias Ringwald     message_pdu->incomplete_timer_active = 0;
294926e8875SMatthias Ringwald     btstack_run_loop_remove_timer(&message_pdu->incomplete_timer);
295926e8875SMatthias Ringwald }
296926e8875SMatthias Ringwald 
297a4bbc09dSMatthias Ringwald static void mesh_lower_transport_outgoing_stop_acknowledgment_timer(mesh_segmented_pdu_t *message_pdu){
298a7f44e8dSMatthias Ringwald     if (!message_pdu->acknowledgement_timer_active) return;
299a7f44e8dSMatthias Ringwald     message_pdu->acknowledgement_timer_active = 0;
300a7f44e8dSMatthias Ringwald     btstack_run_loop_remove_timer(&message_pdu->acknowledgement_timer);
30177ba3d3fSMatthias Ringwald }
30277ba3d3fSMatthias Ringwald 
303a4bbc09dSMatthias Ringwald static void mesh_lower_transport_outgoing_stop_incomplete_timer(mesh_segmented_pdu_t *message_pdu){
304a7f44e8dSMatthias Ringwald     if (!message_pdu->incomplete_timer_active) return;
305a7f44e8dSMatthias Ringwald     message_pdu->incomplete_timer_active = 0;
306a7f44e8dSMatthias Ringwald     btstack_run_loop_remove_timer(&message_pdu->incomplete_timer);
30777ba3d3fSMatthias Ringwald }
30877ba3d3fSMatthias Ringwald 
30977ba3d3fSMatthias Ringwald // stops timers and updates reassembly engine
310a4bbc09dSMatthias Ringwald static void mesh_lower_transport_rx_segmented_message_complete(mesh_segmented_pdu_t * message_pdu){
31177ba3d3fSMatthias Ringwald     // set flag
312926e8875SMatthias Ringwald     message_pdu->message_complete = 1;
31377ba3d3fSMatthias Ringwald     // stop timers
314926e8875SMatthias Ringwald     mesh_lower_transport_incoming_stop_acknowledgment_timer(message_pdu);
315926e8875SMatthias Ringwald     mesh_lower_transport_incoming_stop_incomplete_timer(message_pdu);
31677ba3d3fSMatthias Ringwald     // stop reassembly
317*3945bdb6SMatthias Ringwald     mesh_peer_t * peer = mesh_peer_for_addr(message_pdu->src);
31877ba3d3fSMatthias Ringwald     if (peer){
319926e8875SMatthias Ringwald         peer->message_pdu = NULL;
320926e8875SMatthias Ringwald     }
32177ba3d3fSMatthias Ringwald }
32277ba3d3fSMatthias Ringwald 
32377ba3d3fSMatthias Ringwald static void mesh_lower_transport_rx_ack_timeout(btstack_timer_source_t *ts){
324a4bbc09dSMatthias Ringwald     mesh_segmented_pdu_t * message_pdu = (mesh_segmented_pdu_t *) btstack_run_loop_get_timer_context(ts);
325b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
326926e8875SMatthias Ringwald     printf("ACK: acknowledgement timer fired for %p, send ACK\n", message_pdu);
327b66a7a84SMatthias Ringwald #endif
328926e8875SMatthias Ringwald     message_pdu->acknowledgement_timer_active = 0;
329926e8875SMatthias Ringwald     mesh_lower_transport_send_ack_for_transport_pdu(message_pdu);
33077ba3d3fSMatthias Ringwald }
33177ba3d3fSMatthias Ringwald 
33277ba3d3fSMatthias Ringwald static void mesh_lower_transport_rx_incomplete_timeout(btstack_timer_source_t *ts){
333a4bbc09dSMatthias Ringwald     mesh_segmented_pdu_t * message_pdu = (mesh_segmented_pdu_t *) btstack_run_loop_get_timer_context(ts);
334b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
335926e8875SMatthias Ringwald     printf("mesh_transport_rx_incomplete_timeout for %p - give up\n", message_pdu);
336b66a7a84SMatthias Ringwald #endif
337926e8875SMatthias Ringwald     mesh_lower_transport_rx_segmented_message_complete(message_pdu);
3388a504b30SMatthias Ringwald     // free segments
3398a504b30SMatthias Ringwald     mesh_lower_transport_report_segments_as_processed(message_pdu);
34077ba3d3fSMatthias Ringwald     // free message
341a4bbc09dSMatthias Ringwald     btstack_memory_mesh_segmented_pdu_free(message_pdu);
34277ba3d3fSMatthias Ringwald }
34377ba3d3fSMatthias Ringwald 
344a4bbc09dSMatthias Ringwald static void mesh_lower_transport_start_rx_acknowledgment_timer(mesh_segmented_pdu_t * message_pdu, uint32_t timeout){
345b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
346926e8875SMatthias Ringwald     printf("ACK: start rx ack timer for %p, timeout %u ms\n", message_pdu, (int) timeout);
347b66a7a84SMatthias Ringwald #endif
348926e8875SMatthias Ringwald     btstack_run_loop_set_timer(&message_pdu->acknowledgement_timer, timeout);
349926e8875SMatthias Ringwald     btstack_run_loop_set_timer_handler(&message_pdu->acknowledgement_timer, &mesh_lower_transport_rx_ack_timeout);
350926e8875SMatthias Ringwald     btstack_run_loop_set_timer_context(&message_pdu->acknowledgement_timer, message_pdu);
351926e8875SMatthias Ringwald     btstack_run_loop_add_timer(&message_pdu->acknowledgement_timer);
352926e8875SMatthias Ringwald     message_pdu->acknowledgement_timer_active = 1;
35377ba3d3fSMatthias Ringwald }
35477ba3d3fSMatthias Ringwald 
355eb3826d8SMatthias Ringwald static void mesh_lower_transport_tx_restart_segment_transmission_timer(void){
356eb3826d8SMatthias Ringwald     // restart segment transmission timer for unicast dst
357eb3826d8SMatthias Ringwald     // - "This timer shall be set to a minimum of 200 + 50 * TTL milliseconds."
358*3945bdb6SMatthias Ringwald     uint32_t timeout = 200 + 50 * (lower_transport_outgoing_message->ctl_ttl & 0x7f);
3598fa6125aSMatthias Ringwald     if (lower_transport_outgoing_message->acknowledgement_timer_active){
3608fa6125aSMatthias Ringwald         btstack_run_loop_remove_timer(&lower_transport_outgoing_message->acknowledgement_timer);
361eb3826d8SMatthias Ringwald     }
362eb3826d8SMatthias Ringwald 
363eb3826d8SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
364*3945bdb6SMatthias Ringwald     printf("[+] Lower transport, segmented pdu %p, seq %06x: setup transmission timeout %u ms\n", lower_transport_outgoing_message,
365*3945bdb6SMatthias Ringwald            lower_transport_outgoing_message->seq, (int) timeout);
366eb3826d8SMatthias Ringwald #endif
367eb3826d8SMatthias Ringwald 
3688fa6125aSMatthias Ringwald     btstack_run_loop_set_timer(&lower_transport_outgoing_message->acknowledgement_timer, timeout);
3698fa6125aSMatthias Ringwald     btstack_run_loop_set_timer_handler(&lower_transport_outgoing_message->acknowledgement_timer, &mesh_lower_transport_segment_transmission_timeout);
3708fa6125aSMatthias Ringwald     btstack_run_loop_add_timer(&lower_transport_outgoing_message->acknowledgement_timer);
3718fa6125aSMatthias Ringwald     lower_transport_outgoing_message->acknowledgement_timer_active = 1;
372eb3826d8SMatthias Ringwald }
373eb3826d8SMatthias Ringwald 
374a4bbc09dSMatthias Ringwald static void mesh_lower_transport_incoming_restart_incomplete_timer(mesh_segmented_pdu_t * message_pdu, uint32_t timeout,
37577ba3d3fSMatthias Ringwald                                                                    void (*callback)(btstack_timer_source_t *ts)){
376b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
377926e8875SMatthias Ringwald     printf("RX-(re)start incomplete timer for %p, timeout %u ms\n", message_pdu, (int) timeout);
378b66a7a84SMatthias Ringwald #endif
379926e8875SMatthias Ringwald     if (message_pdu->incomplete_timer_active){
380926e8875SMatthias Ringwald         btstack_run_loop_remove_timer(&message_pdu->incomplete_timer);
38177ba3d3fSMatthias Ringwald     }
382926e8875SMatthias Ringwald     btstack_run_loop_set_timer(&message_pdu->incomplete_timer, timeout);
383926e8875SMatthias Ringwald     btstack_run_loop_set_timer_handler(&message_pdu->incomplete_timer, callback);
384926e8875SMatthias Ringwald     btstack_run_loop_set_timer_context(&message_pdu->incomplete_timer, message_pdu);
385926e8875SMatthias Ringwald     btstack_run_loop_add_timer(&message_pdu->incomplete_timer);
386926e8875SMatthias Ringwald     message_pdu->incomplete_timer_active = 1;
38777ba3d3fSMatthias Ringwald }
38877ba3d3fSMatthias Ringwald 
38977ba3d3fSMatthias Ringwald static void mesh_lower_transport_outgoing_complete(void){
390a7f44e8dSMatthias Ringwald     btstack_assert(lower_transport_outgoing_message != NULL);
391b9d8f3adSMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
3928fa6125aSMatthias Ringwald     printf("mesh_lower_transport_outgoing_complete %p, ack timer active %u, incomplete active %u\n", lower_transport_outgoing_message,
3938fa6125aSMatthias Ringwald            lower_transport_outgoing_message->acknowledgement_timer_active, lower_transport_outgoing_message->incomplete_timer_active);
394b9d8f3adSMatthias Ringwald #endif
3951c16f5cfSMatthias Ringwald     // stop timers
396a7f44e8dSMatthias Ringwald     mesh_lower_transport_outgoing_stop_acknowledgment_timer(lower_transport_outgoing_message);
397a7f44e8dSMatthias Ringwald     mesh_lower_transport_outgoing_stop_incomplete_timer(lower_transport_outgoing_message);
398a7f44e8dSMatthias Ringwald 
399a7f44e8dSMatthias Ringwald     lower_transport_outgoing_message = NULL;
400a7f44e8dSMatthias Ringwald 
401f41d95b9SMatthias Ringwald     // notify upper transport
402a4bbc09dSMatthias Ringwald     mesh_segmented_pdu_t * pdu   = lower_transport_outgoing_message;
4038fa6125aSMatthias Ringwald     lower_transport_outgoing_message = NULL;
40477ba3d3fSMatthias Ringwald     higher_layer_handler(MESH_TRANSPORT_PDU_SENT, MESH_TRANSPORT_STATUS_SEND_ABORT_BY_REMOTE, (mesh_pdu_t *) pdu);
40577ba3d3fSMatthias Ringwald }
40677ba3d3fSMatthias Ringwald 
407a4bbc09dSMatthias Ringwald static mesh_segmented_pdu_t * mesh_lower_transport_pdu_for_segmented_message(mesh_network_pdu_t *network_pdu){
40877ba3d3fSMatthias Ringwald     uint16_t src = mesh_network_src(network_pdu);
40977ba3d3fSMatthias Ringwald     uint16_t seq_zero = ( big_endian_read_16(mesh_network_pdu_data(network_pdu), 1) >> 2) & 0x1fff;
410b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
41177ba3d3fSMatthias Ringwald     printf("mesh_transport_pdu_for_segmented_message: seq_zero %x\n", seq_zero);
412b66a7a84SMatthias Ringwald #endif
41377ba3d3fSMatthias Ringwald     mesh_peer_t * peer = mesh_peer_for_addr(src);
41477ba3d3fSMatthias Ringwald     if (!peer) {
41577ba3d3fSMatthias Ringwald         return NULL;
41677ba3d3fSMatthias Ringwald     }
417b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
41877ba3d3fSMatthias Ringwald     printf("mesh_seq_zero_validate(%x, %x) -- last (%x, %x)\n", src, seq_zero, peer->address, peer->seq_zero);
419b66a7a84SMatthias Ringwald #endif
42077ba3d3fSMatthias Ringwald 
42177ba3d3fSMatthias Ringwald     // reception of transport message ongoing
422926e8875SMatthias Ringwald     if (peer->message_pdu){
42377ba3d3fSMatthias Ringwald         // check if segment for same seq zero
424*3945bdb6SMatthias Ringwald         uint16_t active_seq_zero = peer->message_pdu->seq_zero;
42577ba3d3fSMatthias Ringwald         if (active_seq_zero == seq_zero) {
426b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
42777ba3d3fSMatthias Ringwald             printf("mesh_transport_pdu_for_segmented_message: segment for current transport pdu with SeqZero %x\n", active_seq_zero);
428b66a7a84SMatthias Ringwald #endif
429926e8875SMatthias Ringwald             return peer->message_pdu;
43077ba3d3fSMatthias Ringwald         } else {
43177ba3d3fSMatthias Ringwald             // seq zero differs from current transport pdu, but current pdu is not complete
432b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
43377ba3d3fSMatthias Ringwald             printf("mesh_transport_pdu_for_segmented_message: drop segment. current transport pdu SeqZero %x, now %x\n", active_seq_zero, seq_zero);
434b66a7a84SMatthias Ringwald #endif
43577ba3d3fSMatthias Ringwald             return NULL;
43677ba3d3fSMatthias Ringwald         }
43777ba3d3fSMatthias Ringwald     }
43877ba3d3fSMatthias Ringwald 
43977ba3d3fSMatthias Ringwald     // send ACK if segment for previously completed transport pdu (no ongoing reception, block ack is cleared)
44077ba3d3fSMatthias Ringwald     if ((seq_zero == peer->seq_zero) && (peer->block_ack != 0)){
441b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
44277ba3d3fSMatthias Ringwald         printf("mesh_transport_pdu_for_segmented_message: segment for last completed message. send ack\n");
443b66a7a84SMatthias Ringwald #endif
44477ba3d3fSMatthias Ringwald         mesh_lower_transport_send_ack_for_network_pdu(network_pdu, seq_zero, peer->block_ack);
44577ba3d3fSMatthias Ringwald         return NULL;
44677ba3d3fSMatthias Ringwald     }
44777ba3d3fSMatthias Ringwald 
44877ba3d3fSMatthias Ringwald     // reconstruct lowest 24 bit of SeqAuth
44977ba3d3fSMatthias Ringwald     uint32_t seq = mesh_network_seq(network_pdu);
45077ba3d3fSMatthias Ringwald     uint32_t seq_auth = (seq & 0xffe000) | seq_zero;
45177ba3d3fSMatthias Ringwald     if (seq_auth > seq){
45277ba3d3fSMatthias Ringwald         seq_auth -= 0x2000;
45377ba3d3fSMatthias Ringwald     }
45477ba3d3fSMatthias Ringwald 
45577ba3d3fSMatthias Ringwald     // no transport pdu active, check new message: seq auth is greater OR seq auth is same but no segments
45677ba3d3fSMatthias Ringwald     if (seq_auth > peer->seq_auth || (seq_auth == peer->seq_auth && peer->block_ack == 0)){
457a4bbc09dSMatthias Ringwald         mesh_segmented_pdu_t * pdu = mesh_message_pdu_get();
45877ba3d3fSMatthias Ringwald         if (!pdu) return NULL;
45977ba3d3fSMatthias Ringwald 
46077ba3d3fSMatthias Ringwald         // cache network pdu header
4617cb45abfSMatthias Ringwald         pdu->ivi_nid = network_pdu->data[0];
4627cb45abfSMatthias Ringwald         pdu->ctl_ttl = network_pdu->data[1];
4637cb45abfSMatthias Ringwald         pdu->src = big_endian_read_16(network_pdu->data, 5);
4647cb45abfSMatthias Ringwald         pdu->dst = big_endian_read_16(network_pdu->data, 7);
46577ba3d3fSMatthias Ringwald         // store lower 24 bit of SeqAuth for App / Device Nonce
4667cb45abfSMatthias Ringwald         pdu->seq = seq_auth;
46777ba3d3fSMatthias Ringwald 
46877ba3d3fSMatthias Ringwald         // store meta data in new pdu
46977ba3d3fSMatthias Ringwald         pdu->netkey_index = network_pdu->netkey_index;
47077ba3d3fSMatthias Ringwald         pdu->block_ack = 0;
47177ba3d3fSMatthias Ringwald         pdu->acknowledgement_timer_active = 0;
47277ba3d3fSMatthias Ringwald         pdu->message_complete = 0;
47377ba3d3fSMatthias Ringwald         pdu->seq_zero = seq_zero;
47477ba3d3fSMatthias Ringwald 
47577ba3d3fSMatthias Ringwald         // update peer info
476926e8875SMatthias Ringwald         peer->message_pdu   = pdu;
47777ba3d3fSMatthias Ringwald         peer->seq_zero      = seq_zero;
47877ba3d3fSMatthias Ringwald         peer->seq_auth      = seq_auth;
47977ba3d3fSMatthias Ringwald         peer->block_ack     = 0;
48077ba3d3fSMatthias Ringwald 
481b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
482*3945bdb6SMatthias Ringwald         printf("mesh_transport_pdu_for_segmented_message: setup transport pdu %p for src %x, seq %06x, seq_zero %x\n", pdu, src,
483*3945bdb6SMatthias Ringwald                pdu->seq, seq_zero);
484b66a7a84SMatthias Ringwald #endif
485926e8875SMatthias Ringwald         return peer->message_pdu;
48677ba3d3fSMatthias Ringwald     }  else {
48777ba3d3fSMatthias Ringwald         // seq zero differs from current transport pdu
488b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
48977ba3d3fSMatthias Ringwald         printf("mesh_transport_pdu_for_segmented_message: drop segment for old seq %x\n", seq_zero);
490b66a7a84SMatthias Ringwald #endif
49177ba3d3fSMatthias Ringwald         return NULL;
49277ba3d3fSMatthias Ringwald     }
49377ba3d3fSMatthias Ringwald }
49477ba3d3fSMatthias Ringwald 
495a4bbc09dSMatthias Ringwald static void mesh_lower_transport_process_segment(mesh_segmented_pdu_t * message_pdu, mesh_network_pdu_t * network_pdu){
49677ba3d3fSMatthias Ringwald 
49777ba3d3fSMatthias Ringwald     uint8_t * lower_transport_pdu     = mesh_network_pdu_data(network_pdu);
49877ba3d3fSMatthias Ringwald     uint8_t   lower_transport_pdu_len = mesh_network_pdu_len(network_pdu);
49977ba3d3fSMatthias Ringwald 
50077ba3d3fSMatthias Ringwald     // get akf_aid & transmic
501926e8875SMatthias Ringwald     message_pdu->akf_aid_control = lower_transport_pdu[0] & 0x7f;
502926e8875SMatthias Ringwald     message_pdu->transmic_len    = lower_transport_pdu[1] & 0x80 ? 8 : 4;
50377ba3d3fSMatthias Ringwald 
50477ba3d3fSMatthias Ringwald     // get seq_zero
50577ba3d3fSMatthias Ringwald     uint16_t seq_zero =  ( big_endian_read_16(lower_transport_pdu, 1) >> 2) & 0x1fff;
50677ba3d3fSMatthias Ringwald 
50777ba3d3fSMatthias Ringwald     // get seg fields
50877ba3d3fSMatthias Ringwald     uint8_t  seg_o    =  ( big_endian_read_16(lower_transport_pdu, 2) >> 5) & 0x001f;
50977ba3d3fSMatthias Ringwald     uint8_t  seg_n    =  lower_transport_pdu[3] & 0x1f;
51077ba3d3fSMatthias Ringwald     uint8_t   segment_len  =  lower_transport_pdu_len - 4;
51177ba3d3fSMatthias Ringwald     uint8_t * segment_data = &lower_transport_pdu[4];
51277ba3d3fSMatthias Ringwald 
513b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
514926e8875SMatthias Ringwald     printf("mesh_lower_transport_process_segment: seq zero %04x, seg_o %02x, seg_n %02x, transmic len: %u\n", seq_zero, seg_o, seg_n, message_pdu->transmic_len * 8);
51577ba3d3fSMatthias Ringwald     mesh_print_hex("Segment", segment_data, segment_len);
516b66a7a84SMatthias Ringwald #endif
51777ba3d3fSMatthias Ringwald 
518926e8875SMatthias Ringwald     // drop if already stored
519926e8875SMatthias Ringwald     if ((message_pdu->block_ack & (1<<seg_o)) != 0){
520926e8875SMatthias Ringwald         mesh_network_message_processed_by_higher_layer(network_pdu);
521926e8875SMatthias Ringwald         return;
522926e8875SMatthias Ringwald     }
523926e8875SMatthias Ringwald 
52477ba3d3fSMatthias Ringwald     // mark as received
525926e8875SMatthias Ringwald     message_pdu->block_ack |= (1<<seg_o);
526926e8875SMatthias Ringwald 
527926e8875SMatthias Ringwald     // add to segments
528926e8875SMatthias Ringwald     btstack_linked_list_add(&message_pdu->segments, (btstack_linked_item_t *) network_pdu);
529926e8875SMatthias Ringwald 
53077ba3d3fSMatthias Ringwald     // last segment -> store len
53177ba3d3fSMatthias Ringwald     if (seg_o == seg_n){
532926e8875SMatthias Ringwald         message_pdu->len = (seg_n * 12) + segment_len;
533b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
534926e8875SMatthias Ringwald         printf("Assembled payload len %u\n", message_pdu->len);
535b66a7a84SMatthias Ringwald #endif
53677ba3d3fSMatthias Ringwald     }
53777ba3d3fSMatthias Ringwald 
53877ba3d3fSMatthias Ringwald     // check for complete
53977ba3d3fSMatthias Ringwald     int i;
54077ba3d3fSMatthias Ringwald     for (i=0;i<=seg_n;i++){
541926e8875SMatthias Ringwald         if ( (message_pdu->block_ack & (1<<i)) == 0) return;
542926e8875SMatthias Ringwald     }
543926e8875SMatthias Ringwald 
54477ba3d3fSMatthias Ringwald     // store block ack in peer info
545*3945bdb6SMatthias Ringwald     mesh_peer_t * peer = mesh_peer_for_addr(message_pdu->src);
54677ba3d3fSMatthias Ringwald     // TODO: check if NULL check can be removed
54777ba3d3fSMatthias Ringwald     if (peer){
548926e8875SMatthias Ringwald         peer->block_ack = message_pdu->block_ack;
54977ba3d3fSMatthias Ringwald     }
55077ba3d3fSMatthias Ringwald 
55177ba3d3fSMatthias Ringwald     // send ack
552926e8875SMatthias Ringwald     mesh_lower_transport_send_ack_for_transport_pdu(message_pdu);
55377ba3d3fSMatthias Ringwald 
55477ba3d3fSMatthias Ringwald     // forward to upper transport
555fc5e2620SMatthias Ringwald     mesh_lower_transport_queue_for_higher_layer((mesh_pdu_t *) message_pdu);
5562a974a69SMatthias Ringwald 
5572a974a69SMatthias Ringwald     // mark as done
5582a974a69SMatthias Ringwald     mesh_lower_transport_rx_segmented_message_complete(message_pdu);
55977ba3d3fSMatthias Ringwald }
56077ba3d3fSMatthias Ringwald 
56158bb2379SMatthias Ringwald static void mesh_lower_transport_process_network_pdu(mesh_network_pdu_t *network_pdu) {// segmented?
56258bb2379SMatthias Ringwald     if (mesh_network_segmented(network_pdu)){
563a4bbc09dSMatthias Ringwald         mesh_segmented_pdu_t * message_pdu = mesh_lower_transport_pdu_for_segmented_message(network_pdu);
56458bb2379SMatthias Ringwald         if (message_pdu) {
56558bb2379SMatthias Ringwald             // start acknowledgment timer if inactive
56658bb2379SMatthias Ringwald             if (message_pdu->acknowledgement_timer_active == 0){
56758bb2379SMatthias Ringwald                 // - "The acknowledgment timer shall be set to a minimum of 150 + 50 * TTL milliseconds"
56858bb2379SMatthias Ringwald                 uint32_t timeout = 150 + 50 * mesh_network_ttl(network_pdu);
56958bb2379SMatthias Ringwald                 mesh_lower_transport_start_rx_acknowledgment_timer(message_pdu, timeout);
57058bb2379SMatthias Ringwald             }
57158bb2379SMatthias Ringwald             // restart incomplete timer
57258bb2379SMatthias Ringwald             mesh_lower_transport_incoming_restart_incomplete_timer(message_pdu, 10000, &mesh_lower_transport_rx_incomplete_timeout);
57358bb2379SMatthias Ringwald             mesh_lower_transport_process_segment(message_pdu, network_pdu);
57458bb2379SMatthias Ringwald         } else {
57558bb2379SMatthias Ringwald             mesh_network_message_processed_by_higher_layer(network_pdu);
57658bb2379SMatthias Ringwald         }
57758bb2379SMatthias Ringwald     } else {
57858bb2379SMatthias Ringwald         // control?
57958bb2379SMatthias Ringwald         if (mesh_network_control(network_pdu)){
58058bb2379SMatthias Ringwald             // unsegmented control message (not encrypted)
58158bb2379SMatthias Ringwald             mesh_lower_transport_process_unsegmented_control_message(network_pdu);
58258bb2379SMatthias Ringwald         } else {
58358bb2379SMatthias Ringwald             // unsegmented access message (encrypted)
584fc5e2620SMatthias Ringwald             mesh_lower_transport_queue_for_higher_layer((mesh_pdu_t *) network_pdu);
58558bb2379SMatthias Ringwald         }
58658bb2379SMatthias Ringwald     }
58758bb2379SMatthias Ringwald }
58858bb2379SMatthias Ringwald 
58977ba3d3fSMatthias Ringwald void mesh_lower_transport_message_processed_by_higher_layer(mesh_pdu_t * pdu){
590fc5e2620SMatthias Ringwald     btstack_assert(pdu == mesh_lower_transport_higher_layer_pdu);
591fc5e2620SMatthias Ringwald     mesh_lower_transport_higher_layer_pdu = NULL;
59284011563SMatthias Ringwald     mesh_network_pdu_t * network_pdu;
59377ba3d3fSMatthias Ringwald     switch (pdu->pdu_type){
594a4bbc09dSMatthias Ringwald         case MESH_PDU_TYPE_SEGMENTED:
5958a504b30SMatthias Ringwald             // free segments
596a4bbc09dSMatthias Ringwald             mesh_lower_transport_report_segments_as_processed((mesh_segmented_pdu_t *) pdu);
597a4bbc09dSMatthias Ringwald             mesh_message_pdu_free((mesh_segmented_pdu_t *) pdu);
59835699c48SMatthias Ringwald             break;
5998facb3eaSMatthias Ringwald         case MESH_PDU_TYPE_UNSEGMENTED:
600e4121a34SMatthias Ringwald             network_pdu = (mesh_network_pdu_t *) pdu;
60184011563SMatthias Ringwald             mesh_network_message_processed_by_higher_layer(network_pdu);
6022ae11e2cSMatthias Ringwald             break;
60377ba3d3fSMatthias Ringwald         default:
6043d9c3b8eSMatthias Ringwald             btstack_assert(0);
60577ba3d3fSMatthias Ringwald             break;
60677ba3d3fSMatthias Ringwald     }
607fc5e2620SMatthias Ringwald     mesh_lower_transport_deliver_to_higher_layer();
60877ba3d3fSMatthias Ringwald }
60977ba3d3fSMatthias Ringwald 
61077ba3d3fSMatthias Ringwald void mesh_lower_transport_received_message(mesh_network_callback_type_t callback_type, mesh_network_pdu_t *network_pdu){
61177ba3d3fSMatthias Ringwald     mesh_peer_t * peer;
61277ba3d3fSMatthias Ringwald     uint16_t src;
61377ba3d3fSMatthias Ringwald     uint16_t seq;
61477ba3d3fSMatthias Ringwald     switch (callback_type){
61577ba3d3fSMatthias Ringwald         case MESH_NETWORK_PDU_RECEIVED:
61677ba3d3fSMatthias Ringwald             src = mesh_network_src(network_pdu);
61777ba3d3fSMatthias Ringwald             seq = mesh_network_seq(network_pdu);
61877ba3d3fSMatthias Ringwald             peer = mesh_peer_for_addr(src);
619b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
62077ba3d3fSMatthias Ringwald             printf("Transport: received message. SRC %x, SEQ %x\n", src, seq);
621b66a7a84SMatthias Ringwald #endif
62277ba3d3fSMatthias Ringwald             // validate seq
62377ba3d3fSMatthias Ringwald             if (peer && seq > peer->seq){
62477ba3d3fSMatthias Ringwald                 // track seq
62577ba3d3fSMatthias Ringwald                 peer->seq = seq;
62658bb2379SMatthias Ringwald                 // process
62758bb2379SMatthias Ringwald                 mesh_lower_transport_process_network_pdu(network_pdu);
62877ba3d3fSMatthias Ringwald                 mesh_lower_transport_run();
62977ba3d3fSMatthias Ringwald             } else {
63077ba3d3fSMatthias Ringwald                 // drop packet
631b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
63277ba3d3fSMatthias Ringwald                 printf("Transport: drop packet - src/seq auth failed\n");
633b66a7a84SMatthias Ringwald #endif
63477ba3d3fSMatthias Ringwald                 mesh_network_message_processed_by_higher_layer(network_pdu);
63577ba3d3fSMatthias Ringwald             }
63677ba3d3fSMatthias Ringwald             break;
63777ba3d3fSMatthias Ringwald         case MESH_NETWORK_PDU_SENT:
63877ba3d3fSMatthias Ringwald             mesh_lower_transport_network_pdu_sent(network_pdu);
63977ba3d3fSMatthias Ringwald             break;
64077ba3d3fSMatthias Ringwald         default:
64177ba3d3fSMatthias Ringwald             break;
64277ba3d3fSMatthias Ringwald     }
64377ba3d3fSMatthias Ringwald }
64477ba3d3fSMatthias Ringwald 
645a4bbc09dSMatthias Ringwald static void mesh_lower_transport_setup_segment(mesh_segmented_pdu_t *message_pdu, uint8_t seg_o, mesh_network_pdu_t *network_pdu){
64677ba3d3fSMatthias Ringwald 
647*3945bdb6SMatthias Ringwald     int ctl = message_pdu->ctl_ttl >> 7;
64877ba3d3fSMatthias Ringwald     uint16_t max_segment_len = ctl ? 8 : 12;    // control 8 bytes (64 bit NetMic), access 12 bytes (32 bit NetMIC)
64977ba3d3fSMatthias Ringwald 
650b16fda24SMatthias Ringwald     // use seq number from transport pdu once if MESH_TRANSPORT_FLAG_SEQ_RESERVED (to allow reserving seq number in upper transport while using all seq numbers)
651b16fda24SMatthias Ringwald     uint32_t seq;
652a7f44e8dSMatthias Ringwald     if ((message_pdu->flags & MESH_TRANSPORT_FLAG_SEQ_RESERVED) != 0){
653a7f44e8dSMatthias Ringwald         message_pdu->flags &= ~(MESH_TRANSPORT_FLAG_SEQ_RESERVED);
654*3945bdb6SMatthias Ringwald         seq = message_pdu->seq;
655b16fda24SMatthias Ringwald     } else {
656b16fda24SMatthias Ringwald         seq = mesh_sequence_number_next();
657b16fda24SMatthias Ringwald     }
658*3945bdb6SMatthias Ringwald     uint16_t seq_zero = message_pdu->seq & 0x01fff;
659a7f44e8dSMatthias Ringwald     uint8_t  seg_n    = (message_pdu->len - 1) / max_segment_len;
660a7f44e8dSMatthias Ringwald     uint8_t  szmic    = ((!ctl) && (message_pdu->transmic_len == 8)) ? 1 : 0; // only 1 for access messages with 64 bit TransMIC
661*3945bdb6SMatthias Ringwald     uint8_t  nid      = message_pdu->ivi_nid & 0x7f;
662*3945bdb6SMatthias Ringwald     uint8_t  ttl      = message_pdu->ctl_ttl & 0x7f;
663*3945bdb6SMatthias Ringwald     uint16_t src      = message_pdu->src;
664*3945bdb6SMatthias Ringwald     uint16_t dest     = message_pdu->dst;
66577ba3d3fSMatthias Ringwald 
66677ba3d3fSMatthias Ringwald     // current segment.
66777ba3d3fSMatthias Ringwald     uint16_t seg_offset = seg_o * max_segment_len;
66877ba3d3fSMatthias Ringwald 
66977ba3d3fSMatthias Ringwald     uint8_t lower_transport_pdu_data[16];
670a7f44e8dSMatthias Ringwald     lower_transport_pdu_data[0] = 0x80 | message_pdu->akf_aid_control;
67177ba3d3fSMatthias Ringwald     big_endian_store_24(lower_transport_pdu_data, 1, (szmic << 23) | (seq_zero << 10) | (seg_o << 5) | seg_n);
672a7f44e8dSMatthias Ringwald     uint16_t segment_len = btstack_min(message_pdu->len - seg_offset, max_segment_len);
673a7f44e8dSMatthias Ringwald 
67477ba3d3fSMatthias Ringwald     uint16_t lower_transport_pdu_len = 4 + segment_len;
67577ba3d3fSMatthias Ringwald 
676a7f44e8dSMatthias Ringwald     // find network-pdu with chunk for seg_offset
677a7f44e8dSMatthias Ringwald     mesh_network_pdu_t * chunk = (mesh_network_pdu_t *) lower_transport_outgoing_message->segments;
678a7f44e8dSMatthias Ringwald     uint16_t chunk_start = 0;
679a7f44e8dSMatthias Ringwald     while ((chunk_start + MESH_NETWORK_PAYLOAD_MAX) <= seg_offset){
680a7f44e8dSMatthias Ringwald         chunk = (mesh_network_pdu_t *) chunk->pdu_header.item.next;
681a7f44e8dSMatthias Ringwald         chunk_start += MESH_NETWORK_PAYLOAD_MAX;
682a7f44e8dSMatthias Ringwald     }
683a7f44e8dSMatthias Ringwald     // first part
684a7f44e8dSMatthias Ringwald     uint16_t chunk_offset = seg_offset - chunk_start;
685a7f44e8dSMatthias Ringwald     uint16_t bytes_to_copy = btstack_min(MESH_NETWORK_PAYLOAD_MAX - chunk_offset, segment_len);
686a7f44e8dSMatthias Ringwald     (void)memcpy(&lower_transport_pdu_data[4],
687a7f44e8dSMatthias Ringwald                  &chunk->data[chunk_offset], bytes_to_copy);
688a7f44e8dSMatthias Ringwald     segment_len -= bytes_to_copy;
689a7f44e8dSMatthias Ringwald     // second part
690a7f44e8dSMatthias Ringwald     if (segment_len > 0){
691a7f44e8dSMatthias Ringwald         chunk = (mesh_network_pdu_t *) chunk->pdu_header.item.next;
692a7f44e8dSMatthias Ringwald         (void)memcpy(&lower_transport_pdu_data[4+bytes_to_copy],
693a7f44e8dSMatthias Ringwald                      &chunk->data[0], segment_len);
694a7f44e8dSMatthias Ringwald     }
695a7f44e8dSMatthias Ringwald 
696a7f44e8dSMatthias Ringwald     mesh_network_setup_pdu(network_pdu, message_pdu->netkey_index, nid, 0, ttl, seq, src, dest, lower_transport_pdu_data, lower_transport_pdu_len);
69777ba3d3fSMatthias Ringwald }
69877ba3d3fSMatthias Ringwald 
69977ba3d3fSMatthias Ringwald static void mesh_lower_transport_send_next_segment(void){
7008fa6125aSMatthias Ringwald     if (!lower_transport_outgoing_message) return;
70177ba3d3fSMatthias Ringwald 
7021b639008SMatthias Ringwald     #ifdef LOG_LOWER_TRANSPORT
703*3945bdb6SMatthias Ringwald     printf("[+] Lower Transport, segmented pdu %p, seq %06x: send next segment\n", lower_transport_outgoing_message,
704*3945bdb6SMatthias Ringwald            lower_transport_outgoing_message->seq);
7051b639008SMatthias Ringwald     #endif
7061b639008SMatthias Ringwald 
707*3945bdb6SMatthias Ringwald     int ctl = lower_transport_outgoing_message->ctl_ttl >> 7;
70877ba3d3fSMatthias Ringwald     uint16_t max_segment_len = ctl ? 8 : 12;    // control 8 bytes (64 bit NetMic), access 12 bytes (32 bit NetMIC)
709a7f44e8dSMatthias Ringwald     uint8_t  seg_n = (lower_transport_outgoing_message->len - 1) / max_segment_len;
71077ba3d3fSMatthias Ringwald 
71177ba3d3fSMatthias Ringwald     // find next unacknowledged segement
712a7f44e8dSMatthias Ringwald     while ((lower_transport_outgoing_seg_o <= seg_n) && ((lower_transport_outgoing_message->block_ack & (1 << lower_transport_outgoing_seg_o)) == 0)){
71377ba3d3fSMatthias Ringwald         lower_transport_outgoing_seg_o++;
71477ba3d3fSMatthias Ringwald     }
71577ba3d3fSMatthias Ringwald 
71677ba3d3fSMatthias Ringwald     if (lower_transport_outgoing_seg_o > seg_n){
717b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
718*3945bdb6SMatthias Ringwald         printf("[+] Lower Transport, segmented pdu %p, seq %06x: send complete (dst %x)\n", lower_transport_outgoing_message,
719*3945bdb6SMatthias Ringwald                lower_transport_outgoing_message->seq,
720*3945bdb6SMatthias Ringwald                lower_transport_outgoing_message->dst);
721b66a7a84SMatthias Ringwald #endif
72277ba3d3fSMatthias Ringwald         lower_transport_outgoing_seg_o   = 0;
72377ba3d3fSMatthias Ringwald 
72477ba3d3fSMatthias Ringwald         // done for unicast, ack timer already set, too
725*3945bdb6SMatthias Ringwald         if (mesh_network_address_unicast(lower_transport_outgoing_message->dst)) return;
72677ba3d3fSMatthias Ringwald 
72777ba3d3fSMatthias Ringwald         // done, more?
72877ba3d3fSMatthias Ringwald         if (lower_transport_retry_count == 0){
729b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
73077ba3d3fSMatthias Ringwald             printf("[+] Lower Transport, message unacknowledged -> free\n");
731b66a7a84SMatthias Ringwald #endif
73277ba3d3fSMatthias Ringwald             // notify upper transport
73377ba3d3fSMatthias Ringwald             mesh_lower_transport_outgoing_complete();
73477ba3d3fSMatthias Ringwald             return;
73577ba3d3fSMatthias Ringwald         }
73677ba3d3fSMatthias Ringwald 
73777ba3d3fSMatthias Ringwald         // start retry
738b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
73977ba3d3fSMatthias Ringwald         printf("[+] Lower Transport, message unacknowledged retry count %u\n", lower_transport_retry_count);
740b66a7a84SMatthias Ringwald #endif
74177ba3d3fSMatthias Ringwald         lower_transport_retry_count--;
74277ba3d3fSMatthias Ringwald     }
74377ba3d3fSMatthias Ringwald 
744eb3826d8SMatthias Ringwald     // restart segment transmission timer for unicast dst
745*3945bdb6SMatthias Ringwald     if (mesh_network_address_unicast(lower_transport_outgoing_message->dst)){
746eb3826d8SMatthias Ringwald         mesh_lower_transport_tx_restart_segment_transmission_timer();
74777ba3d3fSMatthias Ringwald     }
74877ba3d3fSMatthias Ringwald 
749a7f44e8dSMatthias Ringwald     mesh_lower_transport_setup_segment(lower_transport_outgoing_message, lower_transport_outgoing_seg_o,
75077ba3d3fSMatthias Ringwald                                        lower_transport_outgoing_segment);
75177ba3d3fSMatthias Ringwald 
752b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
753*3945bdb6SMatthias Ringwald     printf("[+] Lower Transport, segmented pdu %p, seq %06x: send seg_o %x, seg_n %x\n", lower_transport_outgoing_message,
754*3945bdb6SMatthias Ringwald            lower_transport_outgoing_message->seq, lower_transport_outgoing_seg_o, seg_n);
755b0d2aa0bSMatthias Ringwald     mesh_print_hex("LowerTransportPDU", &lower_transport_outgoing_segment->data[9], lower_transport_outgoing_segment->len-9);
756b66a7a84SMatthias Ringwald #endif
75777ba3d3fSMatthias Ringwald 
75877ba3d3fSMatthias Ringwald     // next segment
75977ba3d3fSMatthias Ringwald     lower_transport_outgoing_seg_o++;
76077ba3d3fSMatthias Ringwald 
76177ba3d3fSMatthias Ringwald     // send network pdu
762680fc3b5SMatthias Ringwald     lower_transport_outgoing_segment_queued = 1;
76377ba3d3fSMatthias Ringwald     mesh_network_send_pdu(lower_transport_outgoing_segment);
76477ba3d3fSMatthias Ringwald }
76577ba3d3fSMatthias Ringwald 
766b489fec0SMatthias Ringwald static void mesh_lower_transport_setup_sending_segmented_pdus(void){
767*3945bdb6SMatthias Ringwald     printf("[+] Lower Transport, segmented pdu %p, seq %06x: send retry count %u\n", lower_transport_outgoing_message,
768*3945bdb6SMatthias Ringwald            lower_transport_outgoing_message->seq, lower_transport_retry_count);
769b489fec0SMatthias Ringwald     lower_transport_retry_count--;
770b489fec0SMatthias Ringwald     lower_transport_outgoing_seg_o   = 0;
771b489fec0SMatthias Ringwald }
772b489fec0SMatthias Ringwald 
773b489fec0SMatthias Ringwald static void mesh_lower_transport_segment_transmission_fired(void){
774b489fec0SMatthias Ringwald     // once more?
775b489fec0SMatthias Ringwald     if (lower_transport_retry_count == 0){
776*3945bdb6SMatthias Ringwald         printf("[!] Lower transport, segmented pdu %p, seq %06x: send failed, retries exhausted\n", lower_transport_outgoing_message,
777*3945bdb6SMatthias Ringwald                lower_transport_outgoing_message->seq);
778b489fec0SMatthias Ringwald         mesh_lower_transport_outgoing_complete();
779b489fec0SMatthias Ringwald         return;
780b489fec0SMatthias Ringwald     }
781b489fec0SMatthias Ringwald 
7821b639008SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
783*3945bdb6SMatthias Ringwald     printf("[+] Lower transport, segmented pdu %p, seq %06x: transmission fired\n", lower_transport_outgoing_message,
784*3945bdb6SMatthias Ringwald            lower_transport_outgoing_message->seq);
7851b639008SMatthias Ringwald #endif
7861b639008SMatthias Ringwald 
787b489fec0SMatthias Ringwald     // send remaining segments again
788b489fec0SMatthias Ringwald     mesh_lower_transport_setup_sending_segmented_pdus();
7895f62b711SMatthias Ringwald     // send next segment
7901b639008SMatthias Ringwald     mesh_lower_transport_send_next_segment();
7911b639008SMatthias Ringwald }
792b489fec0SMatthias Ringwald 
79377ba3d3fSMatthias Ringwald static void mesh_lower_transport_network_pdu_sent(mesh_network_pdu_t *network_pdu){
79477ba3d3fSMatthias Ringwald     // figure out what pdu was sent
79577ba3d3fSMatthias Ringwald 
7961e6c9225SMatthias Ringwald     // single segment?
79777ba3d3fSMatthias Ringwald     if (lower_transport_outgoing_segment == network_pdu){
798a7f44e8dSMatthias Ringwald         btstack_assert(lower_transport_outgoing_message != NULL);
799b02ff6bdSMatthias Ringwald 
8001e6c9225SMatthias Ringwald         // of segmented message
801b02ff6bdSMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
802*3945bdb6SMatthias Ringwald         printf("[+] Lower transport, segmented pdu %p, seq %06x: network pdu %p sent\n", lower_transport_outgoing_message,
803*3945bdb6SMatthias Ringwald                lower_transport_outgoing_message->seq, network_pdu);
804b02ff6bdSMatthias Ringwald #endif
805b02ff6bdSMatthias Ringwald 
806680fc3b5SMatthias Ringwald         lower_transport_outgoing_segment_queued = 0;
8071b639008SMatthias Ringwald         if (lower_transport_outgoing_trasnmission_complete){
8081b639008SMatthias Ringwald             // handle complete
8091b639008SMatthias Ringwald             lower_transport_outgoing_trasnmission_complete = 0;
8101b639008SMatthias Ringwald             lower_transport_outgoing_transmission_timeout  = 0;
8111b639008SMatthias Ringwald             mesh_lower_transport_outgoing_complete();
8121b639008SMatthias Ringwald             return;
8131b639008SMatthias Ringwald         }
814b489fec0SMatthias Ringwald         if (lower_transport_outgoing_transmission_timeout){
815b489fec0SMatthias Ringwald             // handle timeout
816b489fec0SMatthias Ringwald             lower_transport_outgoing_transmission_timeout = 0;
817b489fec0SMatthias Ringwald             mesh_lower_transport_segment_transmission_fired();
8181b639008SMatthias Ringwald             return;
819b489fec0SMatthias Ringwald         }
820b02ff6bdSMatthias Ringwald 
821b489fec0SMatthias Ringwald         // send next segment
82277ba3d3fSMatthias Ringwald         mesh_lower_transport_send_next_segment();
82377ba3d3fSMatthias Ringwald         return;
82477ba3d3fSMatthias Ringwald     }
82577ba3d3fSMatthias Ringwald 
82677ba3d3fSMatthias Ringwald     // Segment Acknowledgment message sent by us?
82777ba3d3fSMatthias Ringwald     if (mesh_network_control(network_pdu) && network_pdu->data[0] == 0){
82877ba3d3fSMatthias Ringwald         btstack_memory_mesh_network_pdu_free(network_pdu);
82977ba3d3fSMatthias Ringwald         return;
83077ba3d3fSMatthias Ringwald     }
83177ba3d3fSMatthias Ringwald 
83277ba3d3fSMatthias Ringwald     // other
83377ba3d3fSMatthias Ringwald     higher_layer_handler(MESH_TRANSPORT_PDU_SENT, MESH_TRANSPORT_STATUS_SUCCESS, (mesh_pdu_t *) network_pdu);
83477ba3d3fSMatthias Ringwald }
83577ba3d3fSMatthias Ringwald 
836a4bbc09dSMatthias Ringwald static void mesh_lower_transport_setup_block_ack(mesh_segmented_pdu_t *message_pdu){
837166cce4dSMatthias Ringwald     // setup block ack - set bit for segment to send, will be cleared on ack
838*3945bdb6SMatthias Ringwald     int      ctl = message_pdu->ctl_ttl >> 7;
839166cce4dSMatthias Ringwald     uint16_t max_segment_len = ctl ? 8 : 12;    // control 8 bytes (64 bit NetMic), access 12 bytes (32 bit NetMIC)
840a7f44e8dSMatthias Ringwald     uint8_t  seg_n = (message_pdu->len - 1) / max_segment_len;
841166cce4dSMatthias Ringwald     if (seg_n < 31){
842a7f44e8dSMatthias Ringwald         message_pdu->block_ack = (1 << (seg_n+1)) - 1;
843166cce4dSMatthias Ringwald     } else {
844a7f44e8dSMatthias Ringwald         message_pdu->block_ack = 0xffffffff;
845166cce4dSMatthias Ringwald     }
846166cce4dSMatthias Ringwald }
847166cce4dSMatthias Ringwald 
84877ba3d3fSMatthias Ringwald void mesh_lower_transport_send_pdu(mesh_pdu_t *pdu){
849a01ba0fdSMatthias Ringwald     mesh_network_pdu_t * network_pdu;
850a01ba0fdSMatthias Ringwald     switch (pdu->pdu_type){
851e9c16304SMatthias Ringwald         case MESH_PDU_TYPE_UPPER_UNSEGMENTED_ACCESS:
852e9c16304SMatthias Ringwald         case MESH_PDU_TYPE_UPPER_UNSEGMENTED_CONTROL:
853e9c16304SMatthias Ringwald             network_pdu = (mesh_network_pdu_t *) pdu;
854a7f44e8dSMatthias Ringwald             btstack_assert(network_pdu->len >= 9);
855a01ba0fdSMatthias Ringwald             break;
856a4bbc09dSMatthias Ringwald         case MESH_PDU_TYPE_SEGMENTED:
857a01ba0fdSMatthias Ringwald             break;
858a01ba0fdSMatthias Ringwald         default:
859a01ba0fdSMatthias Ringwald             btstack_assert(false);
860d38a7664SMatthias Ringwald             break;
86148321319SMatthias Ringwald     }
86277ba3d3fSMatthias Ringwald     btstack_linked_list_add_tail(&lower_transport_outgoing, (btstack_linked_item_t*) pdu);
86377ba3d3fSMatthias Ringwald     mesh_lower_transport_run();
86477ba3d3fSMatthias Ringwald }
86577ba3d3fSMatthias Ringwald 
866b9d8f3adSMatthias Ringwald static void mesh_lower_transport_segment_transmission_timeout(btstack_timer_source_t * ts){
867eb3826d8SMatthias Ringwald     UNUSED(ts);
868b66a7a84SMatthias Ringwald #ifdef LOG_LOWER_TRANSPORT
869*3945bdb6SMatthias Ringwald     printf("[+] Lower transport, segmented pdu %p, seq %06x: transmission timer fired\n", lower_transport_outgoing_message,
870*3945bdb6SMatthias Ringwald            lower_transport_outgoing_message->seq);
871b66a7a84SMatthias Ringwald #endif
8728fa6125aSMatthias Ringwald     lower_transport_outgoing_message->acknowledgement_timer_active = 0;
8735bce1015SMatthias Ringwald 
874b489fec0SMatthias Ringwald     if (lower_transport_outgoing_segment_queued){
875b489fec0SMatthias Ringwald         lower_transport_outgoing_transmission_timeout = 1;
876b489fec0SMatthias Ringwald     } else {
877b489fec0SMatthias Ringwald         mesh_lower_transport_segment_transmission_fired();
8785bce1015SMatthias Ringwald     }
87977ba3d3fSMatthias Ringwald }
88077ba3d3fSMatthias Ringwald 
88177ba3d3fSMatthias Ringwald static void mesh_lower_transport_run(void){
88277ba3d3fSMatthias Ringwald 
88377ba3d3fSMatthias Ringwald     // check if outgoing segmented pdu is active
8841e6c9225SMatthias Ringwald     if (lower_transport_outgoing_message) return;
88577ba3d3fSMatthias Ringwald 
88677ba3d3fSMatthias Ringwald     while(!btstack_linked_list_empty(&lower_transport_outgoing)) {
88777ba3d3fSMatthias Ringwald         // get next message
88877ba3d3fSMatthias Ringwald         mesh_network_pdu_t   * network_pdu;
889a4bbc09dSMatthias Ringwald         mesh_segmented_pdu_t   * message_pdu;
89077ba3d3fSMatthias Ringwald         mesh_pdu_t * pdu = (mesh_pdu_t *) btstack_linked_list_pop(&lower_transport_outgoing);
89177ba3d3fSMatthias Ringwald         switch (pdu->pdu_type) {
892e9c16304SMatthias Ringwald             case MESH_PDU_TYPE_UPPER_UNSEGMENTED_ACCESS:
893e9c16304SMatthias Ringwald             case MESH_PDU_TYPE_UPPER_UNSEGMENTED_CONTROL:
894e9c16304SMatthias Ringwald                 // lower_transport_outgoing_unsegmented_pdu = (mesh_unsegmented_pdu_t *) pdu;
895e9c16304SMatthias Ringwald                 // network_pdu = lower_transport_outgoing_unsegmented_pdu->segment;
896e9c16304SMatthias Ringwald                 lower_transport_outgoing_network_pdu = (mesh_network_pdu_t *) pdu;
897e9c16304SMatthias Ringwald                 mesh_network_send_pdu(lower_transport_outgoing_network_pdu);
898b837963cSMatthias Ringwald                 break;
899a4bbc09dSMatthias Ringwald             case MESH_PDU_TYPE_SEGMENTED:
900a4bbc09dSMatthias Ringwald                 message_pdu = (mesh_segmented_pdu_t *) pdu;
901a7f44e8dSMatthias Ringwald                 //
902*3945bdb6SMatthias Ringwald                 printf("[+] Lower transport, segmented pdu %p, seq %06x: run start sending now\n", message_pdu,
903*3945bdb6SMatthias Ringwald                        message_pdu->seq);
90477ba3d3fSMatthias Ringwald                 // start sending segmented pdu
9051b639008SMatthias Ringwald                 lower_transport_retry_count = 3;
906a7f44e8dSMatthias Ringwald                 lower_transport_outgoing_message = message_pdu;
907b489fec0SMatthias Ringwald                 lower_transport_outgoing_transmission_timeout  = 0;
9081b639008SMatthias Ringwald                 lower_transport_outgoing_trasnmission_complete = 0;
909a7f44e8dSMatthias Ringwald                 mesh_lower_transport_setup_block_ack(message_pdu);
910b489fec0SMatthias Ringwald                 mesh_lower_transport_setup_sending_segmented_pdus();
911b489fec0SMatthias Ringwald                 mesh_lower_transport_send_next_segment();
9128fa6125aSMatthias Ringwald                 break;
91377ba3d3fSMatthias Ringwald             default:
9148fa6125aSMatthias Ringwald                 btstack_assert(false);
91577ba3d3fSMatthias Ringwald                 break;
91677ba3d3fSMatthias Ringwald         }
91777ba3d3fSMatthias Ringwald     }
91877ba3d3fSMatthias Ringwald }
91977ba3d3fSMatthias Ringwald 
92077ba3d3fSMatthias Ringwald static void mesh_lower_transport_dump_network_pdus(const char *name, btstack_linked_list_t *list){
92177ba3d3fSMatthias Ringwald     printf("List: %s:\n", name);
92277ba3d3fSMatthias Ringwald     btstack_linked_list_iterator_t it;
92377ba3d3fSMatthias Ringwald     btstack_linked_list_iterator_init(&it, list);
92477ba3d3fSMatthias Ringwald     while (btstack_linked_list_iterator_has_next(&it)){
92577ba3d3fSMatthias Ringwald         mesh_network_pdu_t * network_pdu = (mesh_network_pdu_t*) btstack_linked_list_iterator_next(&it);
92677ba3d3fSMatthias Ringwald         printf("- %p: ", network_pdu); printf_hexdump(network_pdu->data, network_pdu->len);
92777ba3d3fSMatthias Ringwald     }
92877ba3d3fSMatthias Ringwald }
92977ba3d3fSMatthias Ringwald static void mesh_lower_transport_reset_network_pdus(btstack_linked_list_t *list){
93077ba3d3fSMatthias Ringwald     while (!btstack_linked_list_empty(list)){
93177ba3d3fSMatthias Ringwald         mesh_network_pdu_t * pdu = (mesh_network_pdu_t *) btstack_linked_list_pop(list);
93277ba3d3fSMatthias Ringwald         btstack_memory_mesh_network_pdu_free(pdu);
93377ba3d3fSMatthias Ringwald     }
93477ba3d3fSMatthias Ringwald }
93577ba3d3fSMatthias Ringwald 
93633439c2eSMilanka Ringwald bool mesh_lower_transport_can_send_to_dest(uint16_t dest){
93733439c2eSMilanka Ringwald     UNUSED(dest);
9388fa6125aSMatthias Ringwald     return (lower_transport_outgoing_message == NULL) && btstack_linked_list_empty(&lower_transport_outgoing);
939cd16791dSMatthias Ringwald }
940cd16791dSMatthias Ringwald 
941cd16791dSMatthias Ringwald void mesh_lower_transport_reserve_slot(void){
942cd16791dSMatthias Ringwald }
943cd16791dSMatthias Ringwald 
94477ba3d3fSMatthias Ringwald void mesh_lower_transport_dump(void){
94577ba3d3fSMatthias Ringwald }
94677ba3d3fSMatthias Ringwald 
94777ba3d3fSMatthias Ringwald void mesh_lower_transport_reset(void){
948a7f44e8dSMatthias Ringwald     if (lower_transport_outgoing_message){
949a7f44e8dSMatthias Ringwald         while (!btstack_linked_list_empty(&lower_transport_outgoing_message->segments)){
950a7f44e8dSMatthias Ringwald             mesh_network_pdu_t * network_pdu = (mesh_network_pdu_t *) btstack_linked_list_pop(&lower_transport_outgoing_message->segments);
951a7f44e8dSMatthias Ringwald             mesh_network_pdu_free(network_pdu);
952a7f44e8dSMatthias Ringwald         }
953a7f44e8dSMatthias Ringwald         lower_transport_outgoing_message = NULL;
954a7f44e8dSMatthias Ringwald     }
9551e6cebb8SMatthias Ringwald     mesh_network_pdu_free(lower_transport_outgoing_segment);
956680fc3b5SMatthias Ringwald     lower_transport_outgoing_segment_queued = 0;
9571e6cebb8SMatthias Ringwald     lower_transport_outgoing_segment = NULL;
95877ba3d3fSMatthias Ringwald }
95977ba3d3fSMatthias Ringwald 
96077ba3d3fSMatthias Ringwald void mesh_lower_transport_init(){
96177ba3d3fSMatthias Ringwald     // register with network layer
96277ba3d3fSMatthias Ringwald     mesh_network_set_higher_layer_handler(&mesh_lower_transport_received_message);
96377ba3d3fSMatthias Ringwald     // allocate network_pdu for segmentation
964680fc3b5SMatthias Ringwald     lower_transport_outgoing_segment_queued = 0;
96577ba3d3fSMatthias Ringwald     lower_transport_outgoing_segment = mesh_network_pdu_get();
96677ba3d3fSMatthias Ringwald }
96777ba3d3fSMatthias Ringwald 
96877ba3d3fSMatthias Ringwald void mesh_lower_transport_set_higher_layer_handler(void (*pdu_handler)( mesh_transport_callback_type_t callback_type, mesh_transport_status_t status, mesh_pdu_t * pdu)){
96977ba3d3fSMatthias Ringwald     higher_layer_handler = pdu_handler;
97077ba3d3fSMatthias Ringwald }
971d5f4cd5bSMatthias Ringwald 
972d5f4cd5bSMatthias Ringwald // buffer pool
973d5f4cd5bSMatthias Ringwald mesh_transport_pdu_t * mesh_transport_pdu_get(void){
974d5f4cd5bSMatthias Ringwald     mesh_transport_pdu_t * transport_pdu = btstack_memory_mesh_transport_pdu_get();
975d5f4cd5bSMatthias Ringwald     if (transport_pdu){
976d5f4cd5bSMatthias Ringwald         transport_pdu->pdu_header.pdu_type = MESH_PDU_TYPE_TRANSPORT;
977d5f4cd5bSMatthias Ringwald     }
978d5f4cd5bSMatthias Ringwald     return transport_pdu;
979d5f4cd5bSMatthias Ringwald }
980d5f4cd5bSMatthias Ringwald 
981d5f4cd5bSMatthias Ringwald void mesh_transport_pdu_free(mesh_transport_pdu_t * transport_pdu){
982d5f4cd5bSMatthias Ringwald     btstack_memory_mesh_transport_pdu_free(transport_pdu);
983d5f4cd5bSMatthias Ringwald }
984