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());
}