diff --git a/.hash_db b/.hash_db index 61deb0b..302ecda 100644 --- a/.hash_db +++ b/.hash_db @@ -9,7 +9,7 @@ ./src/TinyProtocol.h:c62154a96ab5c19eea3d574b0a4cb3da ./src/proto/crc/tiny_crc.h:ec366c5c7751a822b2c4931e40150a84 ./src/proto/crc/tiny_crc.c:904b6700e7edb0c290eafe9fbad7df2e -./src/proto/hdlc/low_level/hdlc.c:20786efe6fba177fd500bafe14f46b00 +./src/proto/hdlc/low_level/hdlc.c:ccb4f8dcc65d371f2494730dd3e88176 ./src/proto/hdlc/low_level/hdlc.h:363274fa5423ccaaf4c312c9948234c4 ./src/proto/hdlc/low_level/hdlc_int.h:5086b2643f08bce859d8e367888df9f3 ./src/proto/hdlc/high_level/hdlc.c:49e72944973f5abbe7821ccfc98fcfe2 @@ -17,13 +17,13 @@ ./src/proto/fd/tiny_fd_frames.c:ce8b268ac2b5f9c06ea27efaecb2da1c ./src/proto/fd/tiny_fd_int.h:297a74e45acf3fac3032a6d1aa2eaebf ./src/proto/fd/tiny_fd_frames_int.h:883f459f0da1e8c9c9aee6e9cd930597 -./src/proto/fd/tiny_fd.c:895afbb17e4737adfac5d2dee827d840 +./src/proto/fd/tiny_fd.c:33ae52811c21356633f600eef6f83e56 ./src/proto/fd/tiny_fd.h:e4bf3d5757e5a651a3260b2fb8e5f187 ./src/proto/light/tiny_light.c:762620e03e620b0b3928078c83c06952 ./src/proto/light/tiny_light.h:6d1ac27584d2c414e83d7da46f953632 ./src/hal/tiny_debug.h:27d6374dd4deb09b1d98f62e25b0f1a5 ./src/hal/tiny_list.c:db940e41cce84752164648cb9e1613df -./src/hal/tiny_types.h:34e617f439dbf7ae0fe7a019005c65db +./src/hal/tiny_types.h:ae8a32b5f0e0fdd300f6e8378532d393 ./src/hal/tiny_serial.h:5f53d1f334637e32ccc8a1370f600641 ./src/hal/tiny_list.h:69b562367054b805e7e27581e427e5bd ./src/hal/tiny_types.c:c3639fc3c23134af643e8335450cd582 @@ -107,9 +107,14 @@ ./build/CMakeFiles/3.22.1/CompilerIdCXX/CMakeCXXCompilerId.cpp:8f6ee0d17bc4d5795212007f40d90e59 ./build/lib.linux-x86_64-3.10/tinyproto/helpers/__init__.py:9df0084a1c0b1cadec43caaf0b21cc20 ./build/lib.linux-x86_64-3.10/tinyproto/wrappers/__init__.py:d4882c0a8b1d2cd9b93c0ebb4d00f8c8 -./unittest/fd_multidrop_tests.cpp:c92b46501ad2da42ebb0bcec588e6375 +./unittest/fd_multidrop_tests.cpp:e3ec7b831de4c358c9d3f0f744fe7b57 ./src/hal/tiny_types_cpp.cpp:0153fbfd1f3f42ef78a9ca6deeaec0dc ./src/hal/freertos/freertos_hal.h:64ed002a698414e1e258434ee799e8b9 ./src/hal/freertos/freertos_hal.inl:7463e053c8b4a6d68d74614cf936dc1b ./src/hal/cpp/cpp_hal.h:f35701b7333807fc1c3f591b55112c57 ./src/hal/cpp/cpp_hal.inl:a214dca4b7ba191f4708163b36bbe4c0 +./src/proto/fd/tiny_fd_defines_int.h:d422989b820047e6199bb164464fd044 +./src/proto/fd/tiny_fd_peers_int.h:7d4b4629892bc3fa4bdb2479fd22c673 +./src/proto/fd/tiny_fd_service_queue_int.h:d6899a160b44566752ad5e069986d456 +./src/proto/fd/tiny_fd_on_rx_int.h:9eb94ddc6b12b1cab87916b387ba88e3 +./src/proto/fd/tiny_fd_data_queue_int.h:f52317491990ad67e8daf39c504081e0 diff --git a/src/hal/tiny_types.h b/src/hal/tiny_types.h index 706c8b9..8573777 100644 --- a/src/hal/tiny_types.h +++ b/src/hal/tiny_types.h @@ -103,7 +103,7 @@ extern "C" #else /// This macro is used internally for aligning the structures -#define TINY_ALIGN_STRUCT_VALUE (sizeof(uintptr_t)) +#define TINY_ALIGN_STRUCT_VALUE (uintptr_t)(sizeof(uintptr_t)) #endif diff --git a/src/proto/fd/tiny_fd.c b/src/proto/fd/tiny_fd.c index dc0a734..6360205 100644 --- a/src/proto/fd/tiny_fd.c +++ b/src/proto/fd/tiny_fd.c @@ -32,6 +32,7 @@ #include "tiny_fd_peers_int.h" #include "tiny_fd_service_queue_int.h" #include "tiny_fd_data_queue_int.h" +#include "tiny_fd_on_rx_int.h" #include "hal/tiny_types.h" #include "hal/tiny_debug.h" @@ -88,114 +89,6 @@ static inline uint32_t __time_passed_since_last_marker_seen(tiny_fd_handle_t han /////////////////////////////////////////////////////////////////////////////// -static int __check_received_frame(tiny_fd_handle_t handle, uint8_t peer, uint8_t ns) -{ - int result = TINY_SUCCESS; - if ( ns == handle->peers[peer].next_nr ) - { - // this is what, we've been waiting for - // LOG("[%p] Confirming received frame <= %d\n", handle, ns); - handle->peers[peer].next_nr = (handle->peers[peer].next_nr + 1) & seq_bits_mask; - handle->peers[peer].sent_reject = 0; - } - else - { - // definitely we need to send reject. We want to see next_nr frame - LOG(TINY_LOG_ERR, "[%p] Out of order I-Frame N(s)=%d\n", handle, ns); - if ( !handle->peers[peer].sent_reject ) - { - tiny_frame_header_t frame = { - .address = __peer_to_address_field( handle, peer ) | HDLC_CR_BIT, - .control = HDLC_S_FRAME_BITS | HDLC_S_FRAME_TYPE_REJ | (handle->peers[peer].next_nr << 5), - }; - handle->peers[peer].sent_reject = 1; - __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_S_FRAME, &frame, sizeof(tiny_frame_header_t)); - } - result = TINY_ERR_FAILED; - } - return result; -} - -/////////////////////////////////////////////////////////////////////////////// - -static void __confirm_sent_frames(tiny_fd_handle_t handle, uint8_t peer, uint8_t nr) -{ - // all frames below nr are received - while ( nr != handle->peers[peer].confirm_ns ) - { - if ( handle->peers[peer].confirm_ns == handle->peers[peer].last_ns ) - { - // TODO: Out of sync - LOG(TINY_LOG_CRIT, "[%p] Confirmation contains wrong N(r). Remote side is out of sync\n", handle); - break; - } - uint8_t address = __peer_to_address_field( handle, peer ); - // LOG("[%p] Confirming sent frames %d\n", handle, handle->peers[peer].confirm_ns); - tiny_fd_frame_info_t *slot = tiny_fd_queue_get_next( &handle->frames.i_queue, TINY_FD_QUEUE_I_FRAME, address, handle->peers[peer].confirm_ns ); - if ( slot != NULL ) - { - if ( handle->on_send_cb ) - { - tiny_mutex_unlock(&handle->frames.mutex); - handle->on_send_cb(handle->user_data, - __is_primary_station( handle ) ? (__peer_to_address_field( handle, peer ) >> 2) : TINY_FD_PRIMARY_ADDR, - &slot->payload[0], slot->len); - tiny_mutex_lock(&handle->frames.mutex); - } - tiny_fd_queue_free( &handle->frames.i_queue, slot ); - if ( tiny_fd_queue_has_free_slots( &handle->frames.i_queue ) ) - { - // Unblock tx queue to allow application to put new frames for sending - tiny_events_set(&handle->events, FD_EVENT_QUEUE_HAS_FREE_SLOTS); - } - } - else - { - // This should never happen !!! - // TODO: Add error processing - LOG(TINY_LOG_ERR, "[%p] The frame cannot be confirmed: %02X\n", handle, handle->peers[peer].confirm_ns); - } - handle->peers[peer].confirm_ns = (handle->peers[peer].confirm_ns + 1) & seq_bits_mask; - handle->peers[peer].retries = handle->retries; - } - if ( __can_accept_i_frames( handle, peer ) ) - { - // Unblock specific peer to accept new frames for sending - tiny_events_set(&handle->peers[peer].events, FD_EVENT_CAN_ACCEPT_I_FRAMES); - } - LOG(TINY_LOG_DEB, "[%p] Last confirmed frame: %02X\n", handle, handle->peers[peer].confirm_ns); - // LOG("[%p] N(S)=%d, N(R)=%d\n", handle, handle->peers[peer].confirm_ns, handle->peers[peer].next_nr); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void __resend_all_unconfirmed_frames(tiny_fd_handle_t handle, uint8_t peer, uint8_t control, uint8_t nr) -{ - // First, we need to check if that is possible. Maybe remote side is not in sync - while ( handle->peers[peer].next_ns != nr ) - { - if ( handle->peers[peer].confirm_ns == handle->peers[peer].next_ns ) - { - // consider here that remote side is not in sync, we cannot perform request - LOG(TINY_LOG_CRIT, "[%p] Remote side not in sync\n", handle); - tiny_fd_u_frame_t frame = { - .header.address = __peer_to_address_field( handle, peer ) | HDLC_CR_BIT, - .header.control = HDLC_U_FRAME_TYPE_FRMR | HDLC_U_FRAME_BITS, - .data1 = control, - .data2 = (handle->peers[peer].next_nr << 5) | (handle->peers[peer].next_ns << 1), - }; - // Send 2-byte header + 2 extra bytes - __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_U_FRAME, &frame, 4); - break; - } - handle->peers[peer].next_ns = (handle->peers[peer].next_ns - 1) & seq_bits_mask; - } - LOG(TINY_LOG_DEB, "[%p] N(s) is set to %02X\n", handle, handle->peers[peer].next_ns); - tiny_events_set(&handle->events, FD_EVENT_TX_DATA_AVAILABLE); -} - -/////////////////////////////////////////////////////////////////////////////// - static void __switch_to_connected_state(tiny_fd_handle_t handle, uint8_t peer) { if ( handle->peers[peer].state != TINY_FD_STATE_CONNECTED ) @@ -208,6 +101,8 @@ static void __switch_to_connected_state(tiny_fd_handle_t handle, uint8_t peer) handle->peers[peer].sent_nr = 0; handle->peers[peer].sent_reject = 0; tiny_fd_queue_reset_for( &handle->frames.i_queue, __peer_to_address_field( handle, peer ) ); + // Reset last arrived frame timestamp on connection. + // This is required to avoid disconnection on keep alive timeout at the beginning of connection handle->peers[peer].last_ka_ts = tiny_millis(); tiny_events_set(&handle->peers[peer].events, FD_EVENT_CAN_ACCEPT_I_FRAMES); tiny_events_set( @@ -253,134 +148,6 @@ static void __switch_to_disconnected_state(tiny_fd_handle_t handle, uint8_t peer } } -/////////////////////////////////////////////////////////////////////////////// - -static int __on_i_frame_read(tiny_fd_handle_t handle, uint8_t peer, void *data, int len) -{ - uint8_t control = ((uint8_t *)data)[1]; - uint8_t nr = control >> 5; - uint8_t ns = (control >> 1) & 0x07; - LOG(TINY_LOG_INFO, "[%p] Receiving I-Frame N(R)=%02X,N(S)=%02X with address [%02X]\n", handle, nr, ns, ((uint8_t *)data)[0]); - int result = __check_received_frame(handle, peer, ns); - __confirm_sent_frames(handle, peer, nr); - // Provide data to user only if we expect this frame - if ( result == TINY_SUCCESS ) - { - if ( handle->on_read_cb ) - { - tiny_mutex_unlock(&handle->frames.mutex); - handle->on_read_cb(handle->user_data, - __is_primary_station( handle ) ? (__peer_to_address_field( handle, peer ) >> 2) : TINY_FD_PRIMARY_ADDR, - (uint8_t *)data + 2, len - 2); - tiny_mutex_lock(&handle->frames.mutex); - } - // Decide whenever we need to send RR after user callback - // Check if we need to send confirmations separately. If we have something to send, just skip RR S-frame. - // Also at this point, since we received expected frame, sent_reject will be cleared to 0. - if ( __all_frames_are_sent(handle, peer) && handle->peers[peer].sent_nr != handle->peers[peer].next_nr ) - { - tiny_frame_header_t frame = { - .address = __peer_to_address_field( handle, peer ), - .control = HDLC_S_FRAME_BITS | HDLC_S_FRAME_TYPE_RR | (handle->peers[peer].next_nr << 5), - }; - __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_S_FRAME, &frame, 2); - } - } - return result; -} - -/////////////////////////////////////////////////////////////////////////////// - -static int __on_s_frame_read(tiny_fd_handle_t handle, uint8_t peer, void *data, int len) -{ - uint8_t address = ((uint8_t *)data)[0]; - uint8_t control = ((uint8_t *)data)[1]; - uint8_t nr = control >> 5; - int result = TINY_ERR_FAILED; - LOG(TINY_LOG_INFO, "[%p] Receiving S-Frame N(R)=%02X, type=%s with address [%02X]\n", handle, nr, - ((control >> 2) & 0x03) == 0x00 ? "RR" : "REJ", ((uint8_t *)data)[0]); - if ( (control & HDLC_S_FRAME_TYPE_MASK) == HDLC_S_FRAME_TYPE_REJ ) - { - __confirm_sent_frames(handle, peer, nr); - __resend_all_unconfirmed_frames(handle, peer, control, nr); - } - else if ( (control & HDLC_S_FRAME_TYPE_MASK) == HDLC_S_FRAME_TYPE_RR ) - { - __confirm_sent_frames(handle, peer, nr); - if ( address & HDLC_CR_BIT ) - { - // Send answer if we don't have frames to send - if ( handle->peers[peer].next_ns == handle->peers[peer].last_ns ) - { - tiny_frame_header_t frame = { - .address = __peer_to_address_field( handle, peer ), - .control = HDLC_S_FRAME_BITS | HDLC_S_FRAME_TYPE_RR | (handle->peers[peer].next_nr << 5), - }; - __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_S_FRAME, &frame, 2); - } - } - } - return result; -} - -/////////////////////////////////////////////////////////////////////////////// - -static int __on_u_frame_read(tiny_fd_handle_t handle, uint8_t peer, void *data, int len) -{ - uint8_t control = ((uint8_t *)data)[1]; - uint8_t type = control & HDLC_U_FRAME_TYPE_MASK; - int result = TINY_ERR_FAILED; - LOG(TINY_LOG_INFO, "[%p] Receiving U-Frame type=%02X with address [%02X]\n", handle, type, ((uint8_t *)data)[0]); - if ( type == HDLC_U_FRAME_TYPE_SABM || type == HDLC_U_FRAME_TYPE_SNRM ) - { - tiny_frame_header_t frame = { - .address = __peer_to_address_field( handle, peer ), - .control = HDLC_U_FRAME_TYPE_UA | HDLC_U_FRAME_BITS, - }; - __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_U_FRAME, &frame, 2); - if ( handle->peers[peer].state == TINY_FD_STATE_CONNECTED ) - { - __switch_to_disconnected_state(handle, peer); - } - __switch_to_connected_state(handle, peer); - } - else if ( type == HDLC_U_FRAME_TYPE_DISC ) - { - tiny_frame_header_t frame = { - .address = __peer_to_address_field( handle, peer ), - .control = HDLC_U_FRAME_TYPE_UA | HDLC_U_FRAME_BITS, - }; - __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_U_FRAME, &frame, 2); - __switch_to_disconnected_state(handle, peer); - } - else if ( type == HDLC_U_FRAME_TYPE_RSET ) - { - // resets N(R) = 0 in secondary, resets N(S) = 0 in primary - // expected answer UA - } - else if ( type == HDLC_U_FRAME_TYPE_FRMR ) - { - // response of secondary in case of protocol errors: invalid control field, invalid N(R), - // information field too long or not expected in this frame - } - else if ( type == HDLC_U_FRAME_TYPE_UA ) - { - if ( handle->peers[peer].state == TINY_FD_STATE_CONNECTING ) - { - // confirmation received - __switch_to_connected_state(handle, peer); - } - else if ( handle->peers[peer].state == TINY_FD_STATE_DISCONNECTING ) - { - __switch_to_disconnected_state(handle, peer); - } - } - else - { - LOG(TINY_LOG_WRN, "[%p] Unknown hdlc U-frame received\n", handle); - } - return result; -} /////////////////////////////////////////////////////////////////////////////// diff --git a/src/proto/fd/tiny_fd_on_rx_int.h b/src/proto/fd/tiny_fd_on_rx_int.h new file mode 100644 index 0000000..a977af9 --- /dev/null +++ b/src/proto/fd/tiny_fd_on_rx_int.h @@ -0,0 +1,292 @@ +/* + Copyright 2019-2024 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + GNU General Public License Usage + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . + + Commercial License Usage + + Licensees holding valid commercial Tiny Protocol licenses may use this file in + accordance with the commercial license agreement provided in accordance with + the terms contained in a written agreement between you and Alexey Dynda. + For further information contact via email on github account. +*/ + +#pragma once + +#include "tiny_fd.h" +#include "tiny_fd_int.h" +#include "tiny_fd_data_queue_int.h" +#include "tiny_fd_defines_int.h" +#include "tiny_fd_peers_int.h" +#include "tiny_fd_service_queue_int.h" +#include + +static void __switch_to_connected_state(tiny_fd_handle_t handle, uint8_t peer); +static void __switch_to_disconnected_state(tiny_fd_handle_t handle, uint8_t peer); + +/////////////////////////////////////////////////////////////////////////////// + +static void __confirm_sent_frames(tiny_fd_handle_t handle, uint8_t peer, uint8_t nr) +{ + // Repeat the loop for all frames that are not confirmed yet till we reach N(r) + while ( nr != handle->peers[peer].confirm_ns ) + { + // if we reached the last sent frame index, but we have something to confirm + // it means that remote side is out of sync. + if ( handle->peers[peer].confirm_ns == handle->peers[peer].last_ns ) + { + // TODO: Out of sync + // No solution for this part yet. + LOG(TINY_LOG_CRIT, "[%p] Confirmation contains wrong N(r). Remote side is out of sync\n", handle); + break; + } + uint8_t address = __peer_to_address_field( handle, peer ); + // LOG("[%p] Confirming sent frames %d\n", handle, handle->peers[peer].confirm_ns); + // Call on_send_cb to inform application that frame was sent + tiny_fd_frame_info_t *slot = tiny_fd_queue_get_next( &handle->frames.i_queue, TINY_FD_QUEUE_I_FRAME, address, handle->peers[peer].confirm_ns ); + if ( slot != NULL ) + { + if ( handle->on_send_cb ) + { + tiny_mutex_unlock(&handle->frames.mutex); + handle->on_send_cb(handle->user_data, + __is_primary_station( handle ) ? (__peer_to_address_field( handle, peer ) >> 2) : TINY_FD_PRIMARY_ADDR, + &slot->payload[0], slot->len); + tiny_mutex_lock(&handle->frames.mutex); + } + tiny_fd_queue_free( &handle->frames.i_queue, slot ); + if ( tiny_fd_queue_has_free_slots( &handle->frames.i_queue ) ) + { + // Unblock tx queue to allow application to put new frames for sending + tiny_events_set(&handle->events, FD_EVENT_QUEUE_HAS_FREE_SLOTS); + } + } + else + { + // This should never happen !!! + // TODO: Add error processing + LOG(TINY_LOG_ERR, "[%p] The frame cannot be confirmed: %02X\n", handle, handle->peers[peer].confirm_ns); + } + handle->peers[peer].confirm_ns = (handle->peers[peer].confirm_ns + 1) & seq_bits_mask; + handle->peers[peer].retries = handle->retries; + } + // Check if we can accept new frames from the application. + if ( __can_accept_i_frames( handle, peer ) ) + { + // Unblock specific peer to accept new frames for sending + tiny_events_set(&handle->peers[peer].events, FD_EVENT_CAN_ACCEPT_I_FRAMES); + } + LOG(TINY_LOG_DEB, "[%p] Last confirmed frame: %02X\n", handle, handle->peers[peer].confirm_ns); + // LOG("[%p] N(S)=%d, N(R)=%d\n", handle, handle->peers[peer].confirm_ns, handle->peers[peer].next_nr); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int __check_received_frame(tiny_fd_handle_t handle, uint8_t peer, uint8_t ns) +{ + int result = TINY_SUCCESS; + if ( ns == handle->peers[peer].next_nr ) + { + // This is what we've been waiting for. + // If new frame number is the one we expect, we update next expected frame number + // and we clear sent reject flag to 0 in order to allow REJ frame to be sent again. + + // LOG("[%p] Confirming received frame <= %d\n", handle, ns); + handle->peers[peer].next_nr = (handle->peers[peer].next_nr + 1) & seq_bits_mask; + handle->peers[peer].sent_reject = 0; + } + else + { + // The frame we received is not the one we expected. + // We need to inform remote side about this by sending REJ frame. + // We want to see next_nr frame + LOG(TINY_LOG_ERR, "[%p] Out of order I-Frame N(s)=%d\n", handle, ns); + if ( !handle->peers[peer].sent_reject ) + { + tiny_frame_header_t frame = { + .address = __peer_to_address_field( handle, peer ) | HDLC_CR_BIT, + .control = HDLC_S_FRAME_BITS | HDLC_S_FRAME_TYPE_REJ | (handle->peers[peer].next_nr << 5), + }; + handle->peers[peer].sent_reject = 1; + __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_S_FRAME, &frame, sizeof(tiny_frame_header_t)); + } + result = TINY_ERR_FAILED; + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void __resend_all_unconfirmed_frames(tiny_fd_handle_t handle, uint8_t peer, uint8_t control, uint8_t nr) +{ + // First, we need to check if that is possible. Maybe remote side is not in sync + while ( handle->peers[peer].next_ns != nr ) + { + if ( handle->peers[peer].confirm_ns == handle->peers[peer].next_ns ) + { + // consider here that remote side is not in sync, we cannot perform request + LOG(TINY_LOG_CRIT, "[%p] Remote side not in sync\n", handle); + tiny_fd_u_frame_t frame = { + .header.address = __peer_to_address_field( handle, peer ) | HDLC_CR_BIT, + .header.control = HDLC_U_FRAME_TYPE_FRMR | HDLC_U_FRAME_BITS, + .data1 = control, + .data2 = (handle->peers[peer].next_nr << 5) | (handle->peers[peer].next_ns << 1), + }; + // Send 2-byte header + 2 extra bytes + __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_U_FRAME, &frame, 4); + break; + } + handle->peers[peer].next_ns = (handle->peers[peer].next_ns - 1) & seq_bits_mask; + } + LOG(TINY_LOG_DEB, "[%p] N(s) is set to %02X\n", handle, handle->peers[peer].next_ns); + tiny_events_set(&handle->events, FD_EVENT_TX_DATA_AVAILABLE); +} + +/////////////////////////////////////////////////////////////////////////////// + +static int __on_i_frame_read(tiny_fd_handle_t handle, uint8_t peer, void *data, int len) +{ + uint8_t control = ((uint8_t *)data)[1]; + uint8_t nr = control >> 5; + uint8_t ns = (control >> 1) & 0x07; + LOG(TINY_LOG_INFO, "[%p] Receiving I-Frame N(R)=%02X,N(S)=%02X with address [%02X]\n", handle, nr, ns, ((uint8_t *)data)[0]); + int result = __check_received_frame(handle, peer, ns); + // Confirm all previously sent frames up to received N(R) + __confirm_sent_frames(handle, peer, nr); + // Provide data to user only if we expect this frame + if ( result == TINY_SUCCESS ) + { + if ( handle->on_read_cb ) + { + tiny_mutex_unlock(&handle->frames.mutex); + handle->on_read_cb(handle->user_data, + __is_primary_station( handle ) ? (__peer_to_address_field( handle, peer ) >> 2) : TINY_FD_PRIMARY_ADDR, + (uint8_t *)data + 2, len - 2); + tiny_mutex_lock(&handle->frames.mutex); + } + // Decide whenever we need to send RR after user callback + // Check if we need to send confirmations separately. If we have something to send, just skip RR S-frame. + // Also at this point, since we received expected frame, sent_reject will be cleared to 0. + if ( __all_frames_are_sent(handle, peer) && handle->peers[peer].sent_nr != handle->peers[peer].next_nr ) + { + tiny_frame_header_t frame = { + .address = __peer_to_address_field( handle, peer ), + .control = HDLC_S_FRAME_BITS | HDLC_S_FRAME_TYPE_RR | (handle->peers[peer].next_nr << 5), + }; + __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_S_FRAME, &frame, 2); + } + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int __on_s_frame_read(tiny_fd_handle_t handle, uint8_t peer, void *data, int len) +{ + uint8_t address = ((uint8_t *)data)[0]; + uint8_t control = ((uint8_t *)data)[1]; + uint8_t nr = control >> 5; + int result = TINY_ERR_FAILED; + LOG(TINY_LOG_INFO, "[%p] Receiving S-Frame N(R)=%02X, type=%s with address [%02X]\n", handle, nr, + ((control >> 2) & 0x03) == 0x00 ? "RR" : "REJ", ((uint8_t *)data)[0]); + if ( (control & HDLC_S_FRAME_TYPE_MASK) == HDLC_S_FRAME_TYPE_REJ ) + { + // Confirm all previously sent frames up to received N(R) + __confirm_sent_frames(handle, peer, nr); + __resend_all_unconfirmed_frames(handle, peer, control, nr); + } + else if ( (control & HDLC_S_FRAME_TYPE_MASK) == HDLC_S_FRAME_TYPE_RR ) + { + // Confirm all previously sent frames up to received N(R) + __confirm_sent_frames(handle, peer, nr); + if ( address & HDLC_CR_BIT ) + { + // Send answer if we don't have frames to send + if ( handle->peers[peer].next_ns == handle->peers[peer].last_ns ) + { + tiny_frame_header_t frame = { + .address = __peer_to_address_field( handle, peer ), + .control = HDLC_S_FRAME_BITS | HDLC_S_FRAME_TYPE_RR | (handle->peers[peer].next_nr << 5), + }; + __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_S_FRAME, &frame, 2); + } + } + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +static int __on_u_frame_read(tiny_fd_handle_t handle, uint8_t peer, void *data, int len) +{ + uint8_t control = ((uint8_t *)data)[1]; + uint8_t type = control & HDLC_U_FRAME_TYPE_MASK; + int result = TINY_ERR_FAILED; + LOG(TINY_LOG_INFO, "[%p] Receiving U-Frame type=%02X with address [%02X]\n", handle, type, ((uint8_t *)data)[0]); + if ( type == HDLC_U_FRAME_TYPE_SABM || type == HDLC_U_FRAME_TYPE_SNRM ) + { + tiny_frame_header_t frame = { + .address = __peer_to_address_field( handle, peer ), + .control = HDLC_U_FRAME_TYPE_UA | HDLC_U_FRAME_BITS, + }; + __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_U_FRAME, &frame, 2); + if ( handle->peers[peer].state == TINY_FD_STATE_CONNECTED ) + { + __switch_to_disconnected_state(handle, peer); + } + __switch_to_connected_state(handle, peer); + } + else if ( type == HDLC_U_FRAME_TYPE_DISC ) + { + tiny_frame_header_t frame = { + .address = __peer_to_address_field( handle, peer ), + .control = HDLC_U_FRAME_TYPE_UA | HDLC_U_FRAME_BITS, + }; + __put_u_s_frame_to_tx_queue(handle, TINY_FD_QUEUE_U_FRAME, &frame, 2); + __switch_to_disconnected_state(handle, peer); + } + else if ( type == HDLC_U_FRAME_TYPE_RSET ) + { + // resets N(R) = 0 in secondary, resets N(S) = 0 in primary + // expected answer UA + } + else if ( type == HDLC_U_FRAME_TYPE_FRMR ) + { + // response of secondary in case of protocol errors: invalid control field, invalid N(R), + // information field too long or not expected in this frame + } + else if ( type == HDLC_U_FRAME_TYPE_UA ) + { + if ( handle->peers[peer].state == TINY_FD_STATE_CONNECTING ) + { + // confirmation received + __switch_to_connected_state(handle, peer); + } + else if ( handle->peers[peer].state == TINY_FD_STATE_DISCONNECTING ) + { + __switch_to_disconnected_state(handle, peer); + } + } + else + { + LOG(TINY_LOG_WRN, "[%p] Unknown hdlc U-frame received\n", handle); + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/src/proto/hdlc/low_level/hdlc.c b/src/proto/hdlc/low_level/hdlc.c index 9a9a375..27e64af 100644 --- a/src/proto/hdlc/low_level/hdlc.c +++ b/src/proto/hdlc/low_level/hdlc.c @@ -70,18 +70,24 @@ static int hdlc_ll_send_end(hdlc_ll_handle_t handle); int hdlc_ll_init(hdlc_ll_handle_t *handle, hdlc_ll_init_t *init) { + if ( !init->buf ) + { + LOG(TINY_LOG_ERR, "[HDLC] failed to init hdlc. buf=%p\n", init->buf); + return TINY_ERR_INVALID_DATA; + } *handle = NULL; // Aligning provided buffer for the system - uint8_t *buf = (uint8_t *)( ((uintptr_t)init->buf + TINY_ALIGN_STRUCT_VALUE - 1) & (~(TINY_ALIGN_STRUCT_VALUE - 1)) ); - int buf_size = (int)(init->buf_size - (buf - (uint8_t *)init->buf)); - if ( !init->buf || buf_size < (int)sizeof(hdlc_ll_data_t) ) + uint8_t *aligned_buf = (uint8_t *)( ((uintptr_t)init->buf + TINY_ALIGN_STRUCT_VALUE - 1) & (~(TINY_ALIGN_STRUCT_VALUE - 1)) ); + int offset = aligned_buf - (uint8_t *)(init->buf); + int buf_size = (int)((uintptr_t)init->buf_size - offset); + if ( buf_size < (int)sizeof(hdlc_ll_data_t) ) { LOG(TINY_LOG_ERR, "[HDLC] failed to init hdlc. buf=%p, size=%i (%i required)\n", init->buf, init->buf_size, (int)(sizeof(hdlc_ll_data_t) + TINY_ALIGN_STRUCT_VALUE - 1)); return TINY_ERR_OUT_OF_MEMORY; } - *handle = (hdlc_ll_handle_t)buf; - (*handle)->rx_buf = (uint8_t *)buf + sizeof(hdlc_ll_data_t); + *handle = (hdlc_ll_handle_t)aligned_buf; + (*handle)->rx_buf = (uint8_t *)aligned_buf + sizeof(hdlc_ll_data_t); (*handle)->rx_buf_size = buf_size - sizeof(hdlc_ll_data_t); (*handle)->crc_type = init->crc_type == HDLC_CRC_OFF ? 0 : init->crc_type; (*handle)->on_frame_read = init->on_frame_read; diff --git a/unittest/fd_multidrop_tests.cpp b/unittest/fd_multidrop_tests.cpp index e7e95f5..b444ff4 100644 --- a/unittest/fd_multidrop_tests.cpp +++ b/unittest/fd_multidrop_tests.cpp @@ -1,5 +1,5 @@ /* - Copyright 2022 (C) Alexey Dynda + Copyright 2022-2024 (C) Alexey Dynda This file is part of Tiny Protocol Library. @@ -92,7 +92,7 @@ TEST(FD_MULTI, send_to_secondary_dual) FakeEndpoint &endpoint2 = conn.endpoint2(); FakeEndpoint endpoint3(conn.line2(), conn.line1(), 256, 256); TinyHelperFd primary(&endpoint1, 4096, TINY_FD_MODE_NRM, nullptr); - TinyHelperFd secondary(&endpoint2, 4096, TINY_FD_MODE_NRM, nullptr); + TinyHelperFd secondary1(&endpoint2, 4096, TINY_FD_MODE_NRM, nullptr); TinyHelperFd secondary2(&endpoint3, 4096, TINY_FD_MODE_NRM, nullptr); primary.setAddress( TINY_FD_PRIMARY_ADDR ); @@ -100,16 +100,16 @@ TEST(FD_MULTI, send_to_secondary_dual) primary.setPeersCount( 2 ); primary.init(); - secondary.setAddress( 1 ); - secondary.setTimeout( 250 ); - secondary.init(); + secondary1.setAddress( 1 ); + secondary1.setTimeout( 250 ); + secondary1.init(); secondary2.setAddress( 2 ); secondary2.setTimeout( 250 ); secondary2.init(); // Run secondary station first, as it doesn't transmit until primary sends a marker - secondary.run(true); + secondary1.run(true); secondary2.run(true); primary.run(true); @@ -122,13 +122,13 @@ TEST(FD_MULTI, send_to_secondary_dual) CHECK_EQUAL(TINY_SUCCESS, result); // wait until last frame arrives - secondary.wait_until_rx_count(1, 250); - CHECK_EQUAL(1, secondary.rx_count()); + secondary1.wait_until_rx_count(1, 250); + CHECK_EQUAL(1, secondary1.rx_count()); CHECK_EQUAL(0, secondary2.rx_count()); result = primary.sendto(2, txbuf, sizeof(txbuf)); secondary2.wait_until_rx_count(1, 250); CHECK_EQUAL(TINY_SUCCESS, result); - CHECK_EQUAL(1, secondary.rx_count()); + CHECK_EQUAL(1, secondary1.rx_count()); CHECK_EQUAL(1, secondary2.rx_count()); }