diff --git a/CMakeLists.txt b/CMakeLists.txt index b6c4bae..b0f9602 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,10 @@ add_library(tiny_gea3 INTERFACE) target_sources(tiny_gea3 INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/src/tiny_erd_client.c - ${CMAKE_CURRENT_LIST_DIR}/src/tiny_gea3_interface.c + ${CMAKE_CURRENT_LIST_DIR}/src/tiny_gea2_erd_client.c + ${CMAKE_CURRENT_LIST_DIR}/src/tiny_gea3_erd_client.c ${CMAKE_CURRENT_LIST_DIR}/src/tiny_gea2_interface.c + ${CMAKE_CURRENT_LIST_DIR}/src/tiny_gea3_interface.c ) target_include_directories(tiny_gea3 INTERFACE ${CMAKE_CURRENT_LIST_DIR}/include) diff --git a/include/tiny_gea2_interface.h b/include/tiny_gea2_interface.h index d865096..140c1b8 100644 --- a/include/tiny_gea2_interface.h +++ b/include/tiny_gea2_interface.h @@ -45,18 +45,16 @@ typedef struct struct { tiny_queue_t queue; - tiny_timer_group_t* queue_timer_group; - tiny_timer_t queue_timer; - uint8_t* buffer; - uint8_t buffer_size; uint8_t state; uint8_t offset; uint16_t crc; bool escaped; - volatile bool active; - volatile bool packet_queued_in_background; + volatile bool in_progress; // Set and cleared by the non-ISR, read by the ISR + volatile bool completed; // Set by ISR, cleared by non-ISR + volatile bool packet_queued_in_background; // Set by ISR, cleared by non-ISR uint8_t expected_reflection; uint8_t retries; + uint8_t data_length; } send; struct @@ -76,15 +74,13 @@ typedef struct void tiny_gea2_interface_init( tiny_gea2_interface_t* self, i_tiny_uart_t* uart, - tiny_timer_group_t* timer_group, + i_tiny_time_source_t* time_source, i_tiny_event_t* msec_interrupt, uint8_t address, - uint8_t* send_buffer, - uint8_t send_buffer_size, - uint8_t* receive_buffer, - uint8_t receive_buffer_size, uint8_t* send_queue_buffer, size_t send_queue_buffer_size, + uint8_t* receive_buffer, + uint8_t receive_buffer_size, bool ignore_destination_address, uint8_t retries); diff --git a/include/tiny_gea3_interface.h b/include/tiny_gea3_interface.h index 62ae8ba..a9360b6 100644 --- a/include/tiny_gea3_interface.h +++ b/include/tiny_gea3_interface.h @@ -28,7 +28,6 @@ typedef struct { tiny_event_subscription_t byte_received_subscription; tiny_event_subscription_t byte_sent_subscription; i_tiny_uart_t* uart; - uint8_t* send_buffer; uint8_t* receive_buffer; tiny_queue_t send_queue; @@ -38,13 +37,14 @@ typedef struct { uint8_t address; - uint8_t send_buffer_size; uint8_t send_offset; - volatile bool send_in_progress; + uint8_t send_data_length; + volatile bool send_in_progress; // Set and cleared by the non-ISR, read by the ISR + volatile bool send_completed; // Set by ISR, cleared by non-ISR uint8_t receive_buffer_size; uint8_t receive_count; - volatile bool receive_packet_ready; // Set by ISR, cleared by background + volatile bool receive_packet_ready; // Set by ISR, cleared by non-ISR uint8_t send_state; bool send_escaped; @@ -61,12 +61,10 @@ void tiny_gea3_interface_init( tiny_gea3_interface_t* self, i_tiny_uart_t* uart, uint8_t address, - uint8_t* send_buffer, - uint8_t send_buffer_size, - uint8_t* receive_buffer, - uint8_t receive_buffer_size, uint8_t* send_queue_buffer, size_t send_queue_buffer_size, + uint8_t* receive_buffer, + uint8_t receive_buffer_size, bool ignore_destination_address); /*! diff --git a/lib/tiny b/lib/tiny index f9381a5..071dd8a 160000 --- a/lib/tiny +++ b/lib/tiny @@ -1 +1 @@ -Subproject commit f9381a5efe0aee7f4446f4cc473a8d0eb48ab52a +Subproject commit 071dd8ad6aaeb79a0ce2b840f479dd3494aa2eb1 diff --git a/library.json b/library.json index 819b898..c6203c7 100644 --- a/library.json +++ b/library.json @@ -13,7 +13,7 @@ "maintainer": true } ], - "version": "2.0.1", + "version": "3.0.0", "frameworks": "*", "platforms": "*", "export": { @@ -25,7 +25,7 @@ "dependencies": [ { "name": "tiny", - "version": "^6.4.7" + "version": "^7.0.1" } ] } diff --git a/src/tiny_gea2_erd_client.c b/src/tiny_gea2_erd_client.c index e60c701..23e99c1 100644 --- a/src/tiny_gea2_erd_client.c +++ b/src/tiny_gea2_erd_client.c @@ -90,7 +90,7 @@ static void send_write_request_worker(void* context, tiny_gea_packet_t* packet) reinterpret(payload, packet->payload, tiny_gea2_erd_api_write_request_payload_t*); write_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0); + tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0, 0); uint16_t size; tiny_queue_peek(&self->request_queue, packet->payload, &size, 0); @@ -106,7 +106,7 @@ static void send_write_request_worker(void* context, tiny_gea_packet_t* packet) static void send_write_request(self_t* self) { write_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0); + tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0, 0); tiny_gea_interface_send( self->gea2_interface, @@ -150,7 +150,7 @@ static request_type_t request_type(self_t* self) { if(request_pending(self)) { request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0); + tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0, 0); return request.type; } else { @@ -234,7 +234,7 @@ static void handle_write_failure_worker(void* _context, void* allocated_block) static void handle_write_failure(self_t* self) { write_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, offsetof(write_request_t, data), 0); + tiny_queue_peek_partial(&self->request_queue, &request, offsetof(write_request_t, data), 0, 0); tiny_stack_allocator_allocate_aligned(request.data_size + offsetof(write_request_t, data), self, handle_write_failure_worker); } @@ -267,7 +267,7 @@ static void handle_read_response_packet(self_t* self, const tiny_gea_packet_t* p { if(request_type(self) == request_type_read) { read_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0); + tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0, 0); if(packet->payload_length >= sizeof(tiny_gea2_erd_api_read_response_payload_t)) { reinterpret(payload, packet->payload, const tiny_gea2_erd_api_read_response_payload_t*); @@ -323,7 +323,7 @@ static void handle_write_response_packet(self_t* self, const tiny_gea_packet_t* { if(request_type(self) == request_type_write) { write_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, offsetof(write_request_t, data), 0); + tiny_queue_peek_partial(&self->request_queue, &request, offsetof(write_request_t, data), 0, 0); if(packet->payload_length == sizeof(tiny_gea2_erd_api_write_response_payload_t)) { reinterpret(payload, packet->payload, const tiny_gea2_erd_api_write_response_payload_t*); diff --git a/src/tiny_gea2_interface.c b/src/tiny_gea2_interface.c index 1e99a9d..4f92f21 100644 --- a/src/tiny_gea2_interface.c +++ b/src/tiny_gea2_interface.c @@ -1,6 +1,61 @@ /*! * @file * @brief + * + * # Notes on Interrupt Safety + * ## Sending + * Sending is interrupt-safe because the interrupt context only peeks from + * the first element of the queue and makes no changes to the queue. While + * sending, the non-interrupt context is free to add elements to the queue + * as long as it does not remove any elements from the queue or otherwise + * modify the first element in the queue. Only when the interrupt context + * is done sending a packet is an element removed from the queue and while + * this operation is pending the interrupt context is not free to begin + * sending any additional packets. + * + * The non-interrupt context sets the send.in_progress flag and clears + * the send.completed flag. While send.completed remains false, the first + * element of the queue is not modified. + * + * The interrupt context sets the send.completed flag to indicate that it + * is no longer reading from the queue. Until send.completed is false, it + * does not read from the queue. + * + * [Non-interrupt] [Interrupt] + * | | + * packet queued | + * | | + * |--- | + * | | send.in_progress == true | + * |<-- | + * | | + * |--- send.in_progress = true ---->| + * | | + * | packet sent + * | | + * |<------ send.completed = true ---| + * | | + * |--- send.completed = false ----->| + * |--- send.in_progress = false --->| + * | | + * ... ... + * + * ## Receiving + * Receiving is interrupt safe because the receive.packet_ready flag is used + * to ensure that only one of the interrupt and non-interrupt contexts is + * using the receive buffer at any time. + * + * The interrupt context sets the receive.packet_ready flag. While the flag is + * true, the interrupt context does not read from or write to the receive + * buffer. After a valid received packet has been completely written to the + * receive buffer, the interrupt context sets the receive.packet_ready flag + * to indicate that it is ready for use by the non-interrupt context. + * + * The non-interrupt context clears the receive.packet_ready flag. While the + * flag is false, the non-interrupt context does not read from or write to the + * receive buffer. After a received packet has been processed by the non- + * interrupt context, the it clears the flag to indicate that it is ready for + * use by the interrupt context. */ #include @@ -10,27 +65,17 @@ #include "tiny_gea3_interface.h" #include "tiny_gea_constants.h" #include "tiny_gea_packet.h" +#include "tiny_stack_allocator.h" #include "tiny_utils.h" enum { gea2_reflection_timeout_msec = 6, - tiny_gea_ack_timeout_msec = 8, - gea2_broadcast_mask = 0xF0, - default_retries = 2, + gea2_ack_timeout_msec = 8, gea2_interbyte_timeout_msec = 6, }; typedef tiny_gea2_interface_t self_t; -// send packet should match tiny_gea_packet_t, but stores data_length (per spec) instead of payload_length -// (used application convenience) -typedef struct { - uint8_t destination; - uint8_t data_length; - uint8_t source; - uint8_t data[1]; -} send_packet_t; - enum { signal_byte_received = tiny_fsm_signal_user_start, signal_interbyte_timeout, @@ -42,7 +87,6 @@ enum { }; enum { - send_packet_header_size = offsetof(send_packet_t, data), data_length_bytes_not_included_in_data = tiny_gea_packet_transmission_overhead - tiny_gea_packet_overhead, crc_size = sizeof(uint16_t), packet_bytes_not_included_in_payload = crc_size + offsetof(tiny_gea_packet_t, payload), @@ -50,12 +94,15 @@ enum { }; enum { + send_state_stx, + send_state_destination, + send_state_payload_length, + send_state_source, send_state_data, send_state_crc_msb, send_state_crc_lsb, send_state_etx, - send_state_stx, - send_state_done, + send_state_done_sending }; static void state_idle(tiny_fsm_t* fsm, const tiny_fsm_signal_t signal, const void* data); @@ -80,15 +127,13 @@ static void byte_received(void* context, const void* _args) #define needs_escape(_byte) ((_byte & 0xFC) == tiny_gea_esc) -#define is_broadcast_address(_address) ((gea2_broadcast_mask & _address) == gea2_broadcast_mask) - static void state_idle(tiny_fsm_t* fsm, const tiny_fsm_signal_t signal, const void* data) { self_t* self = interface_from_fsm(fsm); switch(signal) { case tiny_fsm_signal_entry: case signal_send_ready: - if(self->send.active) { + if(!self->send.completed && self->send.in_progress) { tiny_fsm_transition(fsm, state_send); } break; @@ -147,16 +192,54 @@ static void send_next_byte(self_t* self) self->send.state = send_state_data; break; - case send_state_data: - if(determine_byte_to_send_considering_escapes(self, self->send.buffer[self->send.offset], &byte_to_send)) { - reinterpret(send_packet, self->send.buffer, const send_packet_t*); + case send_state_destination: { + uint8_t destination; + tiny_queue_peek_partial(&self->send.queue, &destination, sizeof(destination), self->send.offset, 0); + if(determine_byte_to_send_considering_escapes(self, destination, &byte_to_send)) { + self->send.crc = tiny_crc16_byte(self->send.crc, byte_to_send); self->send.offset++; + self->send.state = send_state_payload_length; + } + break; + } - if(self->send.offset >= send_packet->data_length - data_length_bytes_not_included_in_data) { + case send_state_payload_length: { + if(determine_byte_to_send_considering_escapes(self, self->send.data_length, &byte_to_send)) { + self->send.crc = tiny_crc16_byte(self->send.crc, byte_to_send); + self->send.offset++; + self->send.state = send_state_source; + } + break; + } + + case send_state_source: { + uint8_t source; + tiny_queue_peek_partial(&self->send.queue, &source, sizeof(source), self->send.offset, 0); + if(determine_byte_to_send_considering_escapes(self, source, &byte_to_send)) { + self->send.crc = tiny_crc16_byte(self->send.crc, byte_to_send); + self->send.offset++; + if(self->send.data_length == tiny_gea_packet_transmission_overhead) { self->send.state = send_state_crc_msb; } + else { + self->send.state = send_state_data; + } } break; + } + + case send_state_data: { + uint8_t data; + tiny_queue_peek_partial(&self->send.queue, &data, sizeof(data), self->send.offset, 0); + if(determine_byte_to_send_considering_escapes(self, data, &byte_to_send)) { + self->send.crc = tiny_crc16_byte(self->send.crc, byte_to_send); + self->send.offset++; + if(self->send.offset >= self->send.data_length - data_length_bytes_not_included_in_data) { + self->send.state = send_state_crc_msb; + } + } + break; + } case send_state_crc_msb: byte_to_send = self->send.crc >> 8; @@ -174,7 +257,7 @@ static void send_next_byte(self_t* self) case send_state_etx: byte_to_send = tiny_gea_etx; - self->send.state = send_state_done; + self->send.state = send_state_done_sending; break; } @@ -182,13 +265,19 @@ static void send_next_byte(self_t* self) tiny_uart_send(self->uart, byte_to_send); } +static void handle_send_success(self_t* self) +{ + self->send.completed = true; + tiny_fsm_transition(&self->fsm, state_idle_cooldown); +} + static void handle_send_failure(self_t* self) { if(self->send.retries > 0) { self->send.retries--; } else { - self->send.active = false; + self->send.completed = true; } tiny_fsm_transition(&self->fsm, state_collision_cooldown); @@ -203,6 +292,7 @@ static void state_send(tiny_fsm_t* fsm, const tiny_fsm_signal_t signal, const vo self->send.state = send_state_stx; self->send.offset = 0; self->send.escaped = false; + self->send.crc = tiny_gea_crc_seed; send_next_byte(self); break; @@ -210,12 +300,12 @@ static void state_send(tiny_fsm_t* fsm, const tiny_fsm_signal_t signal, const vo case signal_byte_received: { const uint8_t* byte = data; if(*byte == self->send.expected_reflection) { - if(self->send.state == send_state_done) { - reinterpret(send_packet, self->send.buffer, send_packet_t*); + if(self->send.state == send_state_done_sending) { + uint8_t destination; + tiny_queue_peek_partial(&self->send.queue, &destination, sizeof(destination), offsetof(tiny_gea_packet_t, destination), 0); - if(is_broadcast_address(send_packet->destination)) { - self->send.active = false; - tiny_fsm_transition(fsm, state_idle_cooldown); + if(destination == tiny_gea_broadcast_address) { + handle_send_success(self); } else { tiny_fsm_transition(fsm, state_wait_for_ack); @@ -237,12 +327,6 @@ static void state_send(tiny_fsm_t* fsm, const tiny_fsm_signal_t signal, const vo } } -static void handle_success(self_t* self) -{ - self->send.active = false; - tiny_fsm_transition(&self->fsm, state_idle_cooldown); -} - static void ack_timeout(void* context) { self_t* self = context; @@ -254,7 +338,7 @@ static void start_ack_timeout_timer(self_t* self) tiny_timer_start( &self->timer_group, &self->timer, - tiny_gea_ack_timeout_msec, + gea2_ack_timeout_msec, self, ack_timeout); } @@ -271,7 +355,7 @@ static void state_wait_for_ack(tiny_fsm_t* fsm, const tiny_fsm_signal_t signal, case signal_byte_received: { const uint8_t* byte = data; if(*byte == tiny_gea_ack) { - handle_success(self); + handle_send_success(self); } else { handle_send_failure(self); @@ -319,13 +403,13 @@ static bool received_packet_is_addressed_to_me(self_t* self) { reinterpret(packet, self->receive.buffer, tiny_gea_packet_t*); return (packet->destination == self->address) || - is_broadcast_address(packet->destination) || + (packet->destination == tiny_gea_broadcast_address) || self->ignore_destination_address; } static void send_ack(self_t* self, uint8_t address) { - if(!is_broadcast_address(address)) { + if(address != tiny_gea_broadcast_address) { tiny_uart_send(self->uart, tiny_gea_ack); } } @@ -478,6 +562,7 @@ static void state_idle_cooldown(tiny_fsm_t* fsm, const tiny_fsm_signal_t signal, switch(signal) { case tiny_fsm_signal_entry: + tiny_timer_start( &self->timer_group, &self->timer, @@ -501,67 +586,45 @@ static void state_idle_cooldown(tiny_fsm_t* fsm, const tiny_fsm_signal_t signal, case signal_idle_cooldown_timeout: tiny_fsm_transition(fsm, state_idle); break; + + case tiny_fsm_signal_exit: + break; } } -static void prepare_buffered_packet_for_transmission(self_t* self) +static void begin_send(self_t* self) { - reinterpret(send_packet, self->send.buffer, send_packet_t*); - send_packet->data_length += tiny_gea_packet_transmission_overhead; - self->send.crc = tiny_crc16_block(tiny_gea_crc_seed, (uint8_t*)send_packet, send_packet->data_length - data_length_bytes_not_included_in_data); - self->send.state = send_state_stx; + tiny_queue_peek_partial(&self->send.queue, &self->send.data_length, sizeof(self->send.data_length), offsetof(tiny_gea_packet_t, payload_length), 0); + self->send.state = send_state_destination; self->send.offset = 0; - self->send.retries = self->retries; - self->send.active = true; + self->send.in_progress = true; self->send.packet_queued_in_background = true; + self->send.retries = self->retries; } -static void populate_send_packet( - self_t* self, - tiny_gea_packet_t* packet, - uint8_t destination, - uint8_t payload_length, - tiny_gea_interface_send_callback_t callback, - void* context, - bool set_source_address) -{ - packet->payload_length = payload_length; - callback(context, (tiny_gea_packet_t*)packet); - if(set_source_address) { - packet->source = self->address; - } - packet->destination = destination; -} - -static void stop_polling_queue(self_t* self) -{ - tiny_timer_stop(&self->timer_group, &self->send.queue_timer); -} - -static void poll_queue(void* context) -{ - self_t* self = context; - uint16_t size; - - if(tiny_queue_count(&self->send.queue) == 0) { - stop_polling_queue(self); - return; - } - - if(!self->send.active) { - tiny_queue_dequeue(&self->send.queue, self->send.buffer, &size); - prepare_buffered_packet_for_transmission(self); +typedef struct { + self_t* self; + uint8_t destination; + uint8_t payload_length; + tiny_gea_interface_send_callback_t callback; + void* context; + bool set_source_address; + bool queued; +} send_worker_context_t; + +static void send_worker_callback(void* _context, void* buffer) +{ + send_worker_context_t* context = _context; + tiny_gea_packet_t* packet = buffer; + + packet->payload_length = context->payload_length + tiny_gea_packet_transmission_overhead; + context->callback(context->context, packet); + if(context->set_source_address) { + packet->source = context->self->address; } -} + packet->destination = context->destination; -static void start_polling_queue(self_t* self) -{ - tiny_timer_start_periodic( - self->send.queue_timer_group, - &self->send.queue_timer, - 1, - self, - poll_queue); + context->queued = tiny_queue_enqueue(&context->self->send.queue, buffer, tiny_gea_packet_overhead + context->payload_length); } static bool send_worker( @@ -574,21 +637,29 @@ static bool send_worker( { reinterpret(self, _self, self_t*); - if(payload_length + send_packet_header_size > self->send.buffer_size) { + send_worker_context_t send_worker_context = { + .self = self, + .destination = destination, + .payload_length = payload_length, + .callback = callback, + .context = context, + .set_source_address = set_source_address, + .queued = false + }; + tiny_stack_allocator_allocate_aligned( + sizeof(tiny_gea_packet_t) + payload_length, + &send_worker_context, + send_worker_callback); + + if(!send_worker_context.queued) { return false; } - if(self->send.active) { - uint8_t buffer[255]; - populate_send_packet(self, (tiny_gea_packet_t*)buffer, destination, payload_length, callback, context, set_source_address); - start_polling_queue(self); - return tiny_queue_enqueue(&self->send.queue, buffer, tiny_gea_packet_overhead + payload_length); - } - else { - populate_send_packet(self, (tiny_gea_packet_t*)self->send.buffer, destination, payload_length, callback, context, set_source_address); - prepare_buffered_packet_for_transmission(self); - return true; + if(!self->send.in_progress) { + begin_send(self); } + + return true; } static void msec_interrupt_callback(void* context, const void* _args) @@ -635,37 +706,32 @@ static const i_tiny_gea_interface_api_t api = { send, forward, get_on_receive_ev void tiny_gea2_interface_init( tiny_gea2_interface_t* self, i_tiny_uart_t* uart, - tiny_timer_group_t* application_timer_group, + i_tiny_time_source_t* time_source, i_tiny_event_t* msec_interrupt, uint8_t address, - uint8_t* send_buffer, - uint8_t send_buffer_size, - uint8_t* receive_buffer, - uint8_t receive_buffer_size, uint8_t* send_queue_buffer, size_t send_queue_buffer_size, + uint8_t* receive_buffer, + uint8_t receive_buffer_size, bool ignore_destination_address, uint8_t retries) { self->interface.api = &api; self->uart = uart; self->address = address; - self->retries = default_retries; self->ignore_destination_address = ignore_destination_address; self->receive.buffer = receive_buffer; self->receive.buffer_size = receive_buffer_size; self->receive.packet_ready = false; self->receive.escaped = false; - self->send.buffer = send_buffer; - self->send.buffer_size = send_buffer_size; - self->send.active = false; + self->send.in_progress = false; + self->send.completed = false; self->send.packet_queued_in_background = false; - self->send.queue_timer_group = application_timer_group; self->retries = retries; tiny_queue_init(&self->send.queue, send_queue_buffer, send_queue_buffer_size); - tiny_timer_group_init(&self->timer_group, application_timer_group->time_source); + tiny_timer_group_init(&self->timer_group, time_source); tiny_event_subscription_init(&self->byte_received_subscription, self, byte_received); tiny_event_subscribe(tiny_uart_on_receive(uart), &self->byte_received_subscription); @@ -688,4 +754,16 @@ void tiny_gea2_interface_run(self_t* self) tiny_event_publish(&self->on_receive, &args); self->receive.packet_ready = false; } + + if(self->send.completed) { + tiny_queue_discard(&self->send.queue); + self->send.in_progress = false; + self->send.completed = false; + } + + if(!self->send.in_progress) { + if(tiny_queue_count(&self->send.queue) > 0) { + begin_send(self); + } + } } diff --git a/src/tiny_gea3_erd_client.c b/src/tiny_gea3_erd_client.c index 8ae11d9..ffed41c 100644 --- a/src/tiny_gea3_erd_client.c +++ b/src/tiny_gea3_erd_client.c @@ -232,7 +232,7 @@ static void send_write_request_worker(void* _context, tiny_gea_packet_t* packet) reinterpret(self, _context, self_t*); write_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0); + tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0, 0); uint16_t size; tiny_queue_peek(&self->request_queue, (uint8_t*)packet + 3, &size, 0); @@ -248,7 +248,7 @@ static void send_write_request_worker(void* _context, tiny_gea_packet_t* packet) static void send_write_request(self_t* self) { write_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0); + tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0, 0); tiny_gea_interface_send( self->gea3_interface, @@ -326,7 +326,7 @@ static request_type_t request_type(self_t* self) { if(request_pending(self)) { request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0); + tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0, 0); return request.type; } else { @@ -419,7 +419,7 @@ static void HandleWriteFailureWorker(void* _context, void* allocated_block) static void handle_write_failure(self_t* self, tiny_gea3_erd_client_write_failure_reason_t reason) { write_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, offsetof(write_request_t, data), 0); + tiny_queue_peek_partial(&self->request_queue, &request, offsetof(write_request_t, data), 0, 0); handle_write_failure_context_t context = { self, reason }; tiny_stack_allocator_allocate_aligned(request.data_size + offsetof(write_request_t, data), &context, HandleWriteFailureWorker); @@ -472,7 +472,7 @@ static void handle_read_response_packet(self_t* self, const tiny_gea_packet_t* p { if(request_type(self) == request_type_read) { read_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0); + tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0, 0); reinterpret(payload, packet->payload, const tiny_gea3_erd_api_read_response_payload_t*); tiny_erd_t erd = (payload->header.erd_msb << 8) + payload->header.erd_lsb; @@ -531,7 +531,7 @@ static void handle_write_response_packet(self_t* self, const tiny_gea_packet_t* { if(request_type(self) == request_type_write) { write_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, offsetof(write_request_t, data), 0); + tiny_queue_peek_partial(&self->request_queue, &request, offsetof(write_request_t, data), 0, 0); reinterpret(payload, packet->payload, const tiny_gea3_erd_api_write_response_payload_t*); tiny_erd_t erd = (payload->erd_msb << 8) + payload->erd_lsb; @@ -559,7 +559,7 @@ static void handle_subscribe_all_response_packet(self_t* self, const tiny_gea_pa { if(request_type(self) == request_type_subscribe) { subscribe_request_t request; - tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0); + tiny_queue_peek_partial(&self->request_queue, &request, sizeof(request), 0, 0); reinterpret(payload, packet->payload, const tiny_gea3_erd_api_subscribe_all_response_payload_t*); tiny_gea3_erd_api_request_id_t request_id = payload->request_id; diff --git a/src/tiny_gea3_interface.c b/src/tiny_gea3_interface.c index 1f13d0b..db5da32 100644 --- a/src/tiny_gea3_interface.c +++ b/src/tiny_gea3_interface.c @@ -1,27 +1,73 @@ /*! * @file * @brief + * + * # Notes on Interrupt Safety + * ## Sending + * Sending is interrupt-safe because the interrupt context only peeks from + * the first element of the queue and makes no changes to the queue. While + * sending, the non-interrupt context is free to add elements to the queue + * as long as it does not remove any elements from the queue or otherwise + * modify the first element in the queue. Only when the interrupt context + * is done sending a packet is an element removed from the queue and while + * this operation is pending the interrupt context is not free to begin + * sending any additional packets. + * + * The non-interrupt context sets the send.in_progress flag and clears + * the send.completed flag. While send.completed remains false, the first + * element of the queue is not modified. + * + * The interrupt context sets the send.completed flag to indicate that it + * is no longer reading from the queue. Until send.completed is false, it + * does not read from the queue. + * + * [Non-interrupt] [Interrupt] + * | | + * packet queued | + * | | + * |--- | + * | | send.in_progress == true | + * |<-- | + * | | + * |--- send.in_progress = true ---->| + * | | + * | packet sent + * | | + * |<------ send.completed = true ---| + * | | + * |--- send.completed = false ----->| + * |--- send.in_progress = false --->| + * | | + * ... ... + * + * ## Receiving + * Receiving is interrupt safe because the receive.packet_ready flag is used + * to ensure that only one of the interrupt and non-interrupt contexts is + * using the receive buffer at any time. + * + * The interrupt context sets the receive.packet_ready flag. While the flag is + * true, the interrupt context does not read from or write to the receive + * buffer. After a valid received packet has been completely written to the + * receive buffer, the interrupt context sets the receive.packet_ready flag + * to indicate that it is ready for use by the non-interrupt context. + * + * The non-interrupt context clears the receive.packet_ready flag. While the + * flag is false, the non-interrupt context does not read from or write to the + * receive buffer. After a received packet has been processed by the non- + * interrupt context, the it clears the flag to indicate that it is ready for + * use by the interrupt context. */ #include #include "tiny_crc16.h" #include "tiny_gea3_interface.h" #include "tiny_gea_constants.h" +#include "tiny_stack_allocator.h" #include "tiny_utils.h" typedef tiny_gea3_interface_t self_t; -// Send packet should match tiny_gea_packet_t, but stores data_length (per spec) instead of payload_length -// (used application convenience) -typedef struct { - uint8_t destination; - uint8_t data_length; - uint8_t source; - uint8_t data[1]; -} send_packet_t; - enum { - send_packet_header_size = offsetof(send_packet_t, data), data_length_bytes_not_included_in_data = tiny_gea_packet_transmission_overhead - tiny_gea_packet_overhead, crc_size = sizeof(uint16_t), packet_bytes_not_included_in_payload = crc_size + offsetof(tiny_gea_packet_t, payload), @@ -29,10 +75,14 @@ enum { }; enum { + send_state_destination, + send_state_payload_length, + send_state_source, send_state_data, send_state_crc_msb, send_state_crc_lsb, - send_state_etx + send_state_etx, + send_state_complete }; #define needs_escape(_byte) ((_byte & 0xFC) == tiny_gea_esc) @@ -135,13 +185,14 @@ static bool determine_byte_to_send_considering_escapes(self_t* self, uint8_t byt return !self->send_escaped; } -static void prepare_buffered_packet_for_transmission(self_t* self) +static void begin_send(self_t* self) { - reinterpret(send_packet, self->send_buffer, send_packet_t*); - send_packet->data_length += tiny_gea_packet_transmission_overhead; - self->send_crc = tiny_crc16_block(tiny_gea_crc_seed, (const uint8_t*)send_packet, send_packet->data_length - data_length_bytes_not_included_in_data); - self->send_state = send_state_data; + tiny_queue_peek_partial(&self->send_queue, &self->send_data_length, sizeof(self->send_data_length), offsetof(tiny_gea_packet_t, payload_length), 0); + self->send_crc = tiny_gea_crc_seed; + self->send_state = send_state_destination; self->send_offset = 0; + self->send_in_progress = true; + tiny_uart_send(self->uart, tiny_gea_stx); } static void byte_sent(void* context, const void* args) @@ -149,30 +200,57 @@ static void byte_sent(void* context, const void* args) reinterpret(self, context, self_t*); (void)args; - if(!self->send_in_progress) { - if(tiny_queue_count(&self->send_queue) > 0) { - uint16_t size; - tiny_queue_dequeue(&self->send_queue, self->send_buffer, &size); - prepare_buffered_packet_for_transmission(self); - self->send_in_progress = true; - tiny_uart_send(self->uart, tiny_gea_stx); - } - return; - } - uint8_t byte_to_send = 0; switch(self->send_state) { - case send_state_data: - if(determine_byte_to_send_considering_escapes(self, self->send_buffer[self->send_offset], &byte_to_send)) { - reinterpret(send_packet, self->send_buffer, const send_packet_t*); + case send_state_destination: { + uint8_t destination; + tiny_queue_peek_partial(&self->send_queue, &destination, sizeof(destination), self->send_offset, 0); + if(determine_byte_to_send_considering_escapes(self, destination, &byte_to_send)) { + self->send_crc = tiny_crc16_byte(self->send_crc, byte_to_send); + self->send_offset++; + self->send_state = send_state_payload_length; + } + break; + } + + case send_state_payload_length: { + if(determine_byte_to_send_considering_escapes(self, self->send_data_length, &byte_to_send)) { + self->send_crc = tiny_crc16_byte(self->send_crc, byte_to_send); self->send_offset++; + self->send_state = send_state_source; + } + break; + } - if(self->send_offset >= send_packet->data_length - data_length_bytes_not_included_in_data) { + case send_state_source: { + uint8_t source; + tiny_queue_peek_partial(&self->send_queue, &source, sizeof(source), self->send_offset, 0); + if(determine_byte_to_send_considering_escapes(self, source, &byte_to_send)) { + self->send_crc = tiny_crc16_byte(self->send_crc, byte_to_send); + self->send_offset++; + if(self->send_data_length == tiny_gea_packet_transmission_overhead) { + self->send_state = send_state_crc_msb; + } + else { + self->send_state = send_state_data; + } + } + break; + } + + case send_state_data: { + uint8_t data; + tiny_queue_peek_partial(&self->send_queue, &data, sizeof(data), self->send_offset, 0); + if(determine_byte_to_send_considering_escapes(self, data, &byte_to_send)) { + self->send_crc = tiny_crc16_byte(self->send_crc, byte_to_send); + self->send_offset++; + if(self->send_offset >= self->send_data_length - data_length_bytes_not_included_in_data) { self->send_state = send_state_crc_msb; } } break; + } case send_state_crc_msb: byte_to_send = self->send_crc >> 8; @@ -189,29 +267,41 @@ static void byte_sent(void* context, const void* args) break; case send_state_etx: - self->send_in_progress = false; byte_to_send = tiny_gea_etx; + self->send_state = send_state_complete; break; + + case send_state_complete: + self->send_completed = true; + return; } tiny_uart_send(self->uart, byte_to_send); } -static void populate_send_packet( - self_t* self, - tiny_gea_packet_t* packet, - uint8_t destination, - uint8_t payload_length, - tiny_gea_interface_send_callback_t callback, - void* context, - bool set_source_address) +typedef struct { + self_t* self; + uint8_t destination; + uint8_t payload_length; + tiny_gea_interface_send_callback_t callback; + void* context; + bool set_source_address; + bool queued; +} send_worker_context_t; + +static void send_worker_callback(void* _context, void* buffer) { - packet->payload_length = payload_length; - callback(context, (tiny_gea_packet_t*)packet); - if(set_source_address) { - packet->source = self->address; + send_worker_context_t* context = _context; + tiny_gea_packet_t* packet = buffer; + + packet->payload_length = context->payload_length + tiny_gea_packet_transmission_overhead; + context->callback(context->context, packet); + if(context->set_source_address) { + packet->source = context->self->address; } - packet->destination = destination; + packet->destination = context->destination; + + context->queued = tiny_queue_enqueue(&context->self->send_queue, buffer, tiny_gea_packet_overhead + context->payload_length); } static bool send_worker( @@ -224,23 +314,26 @@ static bool send_worker( { reinterpret(self, _self, self_t*); - if(payload_length + send_packet_header_size > self->send_buffer_size) { + send_worker_context_t send_worker_context = { + .self = self, + .destination = destination, + .payload_length = payload_length, + .callback = callback, + .context = context, + .set_source_address = set_source_address, + .queued = false + }; + tiny_stack_allocator_allocate_aligned( + sizeof(tiny_gea_packet_t) + payload_length, + &send_worker_context, + send_worker_callback); + + if(!send_worker_context.queued) { return false; } - if(self->send_in_progress) { - uint8_t buffer[255]; - populate_send_packet(self, (tiny_gea_packet_t*)buffer, destination, payload_length, callback, context, set_source_address); - if(!tiny_queue_enqueue(&self->send_queue, buffer, tiny_gea_packet_overhead + payload_length)) { - return false; - } - } - else { - reinterpret(send_packet, self->send_buffer, tiny_gea_packet_t*); - populate_send_packet(self, send_packet, destination, payload_length, callback, context, set_source_address); - prepare_buffered_packet_for_transmission(self); - self->send_in_progress = true; - tiny_uart_send(self->uart, tiny_gea_stx); + if(!self->send_in_progress) { + begin_send(self); } return true; @@ -278,25 +371,22 @@ void tiny_gea3_interface_init( tiny_gea3_interface_t* self, i_tiny_uart_t* uart, uint8_t address, - uint8_t* send_buffer, - uint8_t send_buffer_size, - uint8_t* receive_buffer, - uint8_t receive_buffer_size, uint8_t* send_queue_buffer, size_t send_queue_buffer_size, + uint8_t* receive_buffer, + uint8_t receive_buffer_size, bool ignore_destination_address) { self->interface.api = &api; self->uart = uart; self->address = address; - self->send_buffer = send_buffer; - self->send_buffer_size = send_buffer_size; self->receive_buffer = receive_buffer; self->receive_buffer_size = receive_buffer_size; self->ignore_destination_address = ignore_destination_address; self->receive_escaped = false; self->send_in_progress = false; + self->send_completed = false; self->send_escaped = false; self->stx_received = false; self->receive_packet_ready = false; @@ -323,4 +413,16 @@ void tiny_gea3_interface_run(self_t* self) // Can only be cleared _after_ publication so that the buffer isn't reused self->receive_packet_ready = false; } + + if(self->send_completed) { + tiny_queue_discard(&self->send_queue); + self->send_completed = false; + self->send_in_progress = false; + } + + if(!self->send_in_progress) { + if(tiny_queue_count(&self->send_queue) > 0) { + begin_send(self); + } + } } diff --git a/test/tests/tiny_gea2_interface_test.cpp b/test/tests/tiny_gea2_interface_test.cpp index 18aece6..46eb584 100644 --- a/test/tests/tiny_gea2_interface_test.cpp +++ b/test/tests/tiny_gea2_interface_test.cpp @@ -14,13 +14,12 @@ extern "C" { #include "CppUTest/TestHarness.h" #include "CppUTestExt/MockSupport.h" -#include "double/tiny_timer_group_double.hpp" +#include "double/tiny_time_source_double.hpp" #include "double/tiny_uart_double.hpp" #include "tiny_utils.h" enum { address = 0xAD, - send_buffer_size = 10, send_queue_size = 20, receive_buffer_size = 9, idle_cooldown_msec = 10 + (address & 0x1F), @@ -38,10 +37,9 @@ TEST_GROUP(tiny_gea2_interface) tiny_gea2_interface_t self; tiny_uart_double_t uart; tiny_event_subscription_t receiveSubscription; - uint8_t send_buffer[send_buffer_size]; uint8_t receive_buffer[receive_buffer_size]; uint8_t send_queue_buffer[send_queue_size]; - tiny_timer_group_double_t timer_group; + tiny_time_source_double_t time_source; tiny_event_t msec_interrupt; void setup() @@ -49,20 +47,18 @@ TEST_GROUP(tiny_gea2_interface) tiny_event_init(&msec_interrupt); tiny_uart_double_init(&uart); - tiny_timer_group_double_init(&timer_group); + tiny_time_source_double_init(&time_source); tiny_gea2_interface_init( &self, &uart.interface, - &timer_group.timer_group, + &time_source.interface, &msec_interrupt.interface, address, - send_buffer, - sizeof(send_buffer), - receive_buffer, - sizeof(receive_buffer), send_queue_buffer, sizeof(send_queue_buffer), + receive_buffer, + sizeof(receive_buffer), false, default_retries); @@ -75,15 +71,13 @@ TEST_GROUP(tiny_gea2_interface) tiny_gea2_interface_init( &self, &uart.interface, - &timer_group.timer_group, + &time_source.interface, &msec_interrupt.interface, address, - send_buffer, - sizeof(send_buffer), - receive_buffer, - sizeof(receive_buffer), send_queue_buffer, sizeof(send_queue_buffer), + receive_buffer, + sizeof(receive_buffer), true, default_retries); @@ -95,15 +89,13 @@ TEST_GROUP(tiny_gea2_interface) tiny_gea2_interface_init( &self, &uart.interface, - &timer_group.timer_group, + &time_source.interface, &msec_interrupt.interface, address, - send_buffer, - sizeof(send_buffer), - receive_buffer, - sizeof(receive_buffer), send_queue_buffer, sizeof(send_queue_buffer), + receive_buffer, + sizeof(receive_buffer), false, retries); } @@ -179,12 +171,12 @@ TEST_GROUP(tiny_gea2_interface) void after(tiny_time_source_ticks_t ticks) { for(uint32_t i = 0; i < ticks; i++) { - tiny_timer_group_double_elapse_time(&timer_group, 1); + tiny_time_source_double_tick(&time_source, 1); after_msec_interrupt_fires(); } } - void given_the_module_is_in_cooldown_after_receiving_a_message() + void given_the_module_is_in_cooldown_after_receiving_a_packet() { mock().disable(); after_bytes_are_received_via_uart( @@ -237,7 +229,7 @@ TEST_GROUP(tiny_gea2_interface) when_packet_is_sent(packet); } - void gjven_that_a_packet_has_been_sent() + void given_that_a_packet_has_been_sent() { given_uart_echoing_is_enabled(); @@ -308,7 +300,7 @@ TEST_GROUP(tiny_gea2_interface) after_bytes_are_received_via_uart(tiny_gea_ack); } - void should_be_able_to_send_a_message_after_idle_cooldown() + void should_be_able_to_send_a_packet_after_idle_cooldown() { given_uart_echoing_is_enabled(); should_send_bytes_via_uart( @@ -325,9 +317,11 @@ TEST_GROUP(tiny_gea2_interface) when_packet_is_sent(packet); after(idle_cooldown_msec); + after_the_interface_is_run(); + after_msec_interrupt_fires(); } - void should_be_able_to_send_a_message_after_collision_cooldown() + void should_be_able_to_send_a_packet_after_collision_cooldown() { given_uart_echoing_is_enabled(); tiny_gea_STATIC_ALLOC_PACKET(packet, 0); @@ -344,6 +338,8 @@ TEST_GROUP(tiny_gea2_interface) tiny_gea_etx); after(collision_timeout_msec()); + after_the_interface_is_run(); + after_msec_interrupt_fires(); } void given_the_module_is_in_collision_cooldown() @@ -358,7 +354,7 @@ TEST_GROUP(tiny_gea2_interface) tiny_time_source_ticks_t collision_timeout_msec() { - return 43 + (address & 0x1F) + ((timer_group.time_source.ticks ^ address) & 0x1F); + return 43 + (address & 0x1F) + ((time_source.ticks ^ address) & 0x1F); } void after_msec_interrupt_fires() @@ -519,26 +515,6 @@ TEST(tiny_gea2_interface, should_receive_broadcast_packets) after_the_interface_is_run(); } -TEST(tiny_gea2_interface, should_receive_product_line_specific_broadcast_packets) -{ - after_bytes_are_received_via_uart( - tiny_gea_stx, - 0xF3, // dst - 0x08, // len - 0x45, // src - 0xBF, // payload - 0xA3, // crc - 0x6C, - tiny_gea_etx); - - tiny_gea_STATIC_ALLOC_PACKET(packet, 1); - packet->destination = 0xF3; - packet->source = 0x45; - packet->payload[0] = 0xBF; - packet_should_be_received(packet); - after_the_interface_is_run(); -} - TEST(tiny_gea2_interface, should_drop_packets_addressed_to_other_nodes) { after_bytes_are_received_via_uart( @@ -806,7 +782,7 @@ TEST(tiny_gea2_interface, should_not_receive_a_packet_in_idle_if_the_packet_does TEST(tiny_gea2_interface, should_not_receive_a_packet_in_idle_cooldown_if_the_packet_does_not_start_with_stx) { - given_the_module_is_in_cooldown_after_receiving_a_message(); + given_the_module_is_in_cooldown_after_receiving_a_packet(); nothing_should_happen(); after_bytes_are_received_via_uart( @@ -908,11 +884,11 @@ TEST(tiny_gea2_interface, should_raise_a_packet_sent_event_when_a_packet_is_sent when_packet_is_sent(packet); } -TEST(tiny_gea2_interface, should_not_send_a_packet_that_is_too_large_for_the_send_buffer) +TEST(tiny_gea2_interface, should_not_send_a_packet_that_is_too_large_for_the_send_queue) { given_uart_echoing_is_enabled(); - tiny_gea_STATIC_ALLOC_PACKET(packet, 8); + tiny_gea_STATIC_ALLOC_PACKET(packet, 16); when_packet_is_sent(packet); } @@ -1034,35 +1010,35 @@ TEST(tiny_gea2_interface, should_forward_a_packet_with_max_payload_given_send_bu when_packet_is_forwarded(packet); } -TEST(tiny_gea2_interface, should_not_forward_packets_that_are_too_large_to_be_buffered) +TEST(tiny_gea2_interface, should_not_forward_packets_that_are_too_large_to_be_queued) { given_uart_echoing_is_enabled(); - tiny_gea_STATIC_ALLOC_PACKET(packet, 8); + tiny_gea_STATIC_ALLOC_PACKET(packet, 16); when_packet_is_forwarded(packet); } -TEST(tiny_gea2_interface, should_be_able_to_send_back_broadcasts_without_an_ack) +TEST(tiny_gea2_interface, should_be_able_to_send_broadcasts_packets_without_waiting_for_an_ack) { given_uart_echoing_is_enabled(); given_that_a_broadcast_packet_has_been_sent(); - should_be_able_to_send_a_message_after_idle_cooldown(); + should_be_able_to_send_a_packet_after_idle_cooldown(); } TEST(tiny_gea2_interface, should_wait_until_the_idle_cool_down_time_has_expired_before_sending_a_packet) { given_uart_echoing_is_enabled(); - given_the_module_is_in_cooldown_after_receiving_a_message(); + given_the_module_is_in_cooldown_after_receiving_a_packet(); nothing_should_happen(); tiny_gea_STATIC_ALLOC_PACKET(packet, 0); packet->destination = 0x45; when_packet_is_sent(packet); - should_be_able_to_send_a_message_after_idle_cooldown(); + should_be_able_to_send_a_packet_after_idle_cooldown(); } TEST(tiny_gea2_interface, should_retry_sending_when_the_reflection_timeout_violation_occurs_and_stop_after_retries_are_exhausted) @@ -1095,10 +1071,10 @@ TEST(tiny_gea2_interface, should_retry_sending_when_the_reflection_timeout_viola after(1); - should_be_able_to_send_a_message_after_idle_cooldown(); + should_be_able_to_send_a_packet_after_idle_cooldown(); } -TEST(tiny_gea2_interface, should_raise_reflection_timed_out_diagnostics_event_when_a_reflection_timeout_retry_sending_when_the_reflection_timeout_violation_occurs_and_stop_after_retrries_are_exhausted) +TEST(tiny_gea2_interface, should_raise_reflection_timed_out_diagnostics_event_when_a_reflection_timeout_retry_sending_when_the_reflection_timeout_violation_occurs_and_stop_after_retries_are_exhausted) { should_send_bytes_via_uart(tiny_gea_stx); tiny_gea_STATIC_ALLOC_PACKET(packet, 0); @@ -1133,7 +1109,7 @@ TEST(tiny_gea2_interface, should_retry_sending_when_a_collision_occurs_and_stop_ after_bytes_are_received_via_uart(tiny_gea_stx - 1); - should_be_able_to_send_a_message_after_collision_cooldown(); + should_be_able_to_send_a_packet_after_collision_cooldown(); } TEST(tiny_gea2_interface, should_retry_sending_when_a_collision_occurs_and_stop_after_retries_are_exhausted_with_a_custom_retry_count) @@ -1155,12 +1131,12 @@ TEST(tiny_gea2_interface, should_retry_sending_when_a_collision_occurs_and_stop_ after_bytes_are_received_via_uart(tiny_gea_stx - 1); - should_be_able_to_send_a_message_after_collision_cooldown(); + should_be_able_to_send_a_packet_after_collision_cooldown(); } TEST(tiny_gea2_interface, should_stop_sending_when_an_unexpected_byte_is_received_while_waiting_for_an_ack) { - gjven_that_a_packet_has_been_sent(); + given_that_a_packet_has_been_sent(); after_bytes_are_received_via_uart(tiny_gea_ack - 1); @@ -1180,7 +1156,7 @@ TEST(tiny_gea2_interface, should_stop_sending_when_an_unexpected_byte_is_receive after_bytes_are_received_via_uart(tiny_gea_ack - 1); - should_be_able_to_send_a_message_after_collision_cooldown(); + should_be_able_to_send_a_packet_after_collision_cooldown(); } TEST(tiny_gea2_interface, should_queue_send_requests_when_already_sending) @@ -1216,12 +1192,14 @@ TEST(tiny_gea2_interface, should_queue_send_requests_when_already_sending) 0xF7, tiny_gea_etx); after(100); + after_the_interface_is_run(); nothing_should_happen(); after(100); + after_the_interface_is_run(); } -TEST(tiny_gea2_interface, should_retry_a_message_if_no_ack_is_received) +TEST(tiny_gea2_interface, should_retry_a_packet_if_no_ack_is_received) { given_uart_echoing_is_enabled(); should_send_bytes_via_uart( @@ -1270,7 +1248,7 @@ TEST(tiny_gea2_interface, should_retry_a_message_if_no_ack_is_received) after(1); - should_be_able_to_send_a_message_after_collision_cooldown(); + should_be_able_to_send_a_packet_after_collision_cooldown(); } TEST(tiny_gea2_interface, should_successfully_receive_a_packet_while_in_collision_cooldown) diff --git a/test/tests/tiny_gea3_interface_test.cpp b/test/tests/tiny_gea3_interface_test.cpp index abe2e9d..aa88719 100644 --- a/test/tests/tiny_gea3_interface_test.cpp +++ b/test/tests/tiny_gea3_interface_test.cpp @@ -21,7 +21,6 @@ TEST_GROUP(tiny_gea3_interface) enum { address = 0xAD, - send_buffer_size = 10, receive_buffer_size = 9, send_queue_size = 20 }; @@ -29,7 +28,6 @@ TEST_GROUP(tiny_gea3_interface) tiny_gea3_interface_t self; tiny_uart_double_t uart; tiny_event_subscription_t receive_subscription; - uint8_t send_buffer[send_buffer_size]; uint8_t receive_buffer[receive_buffer_size]; uint8_t send_queue[send_queue_size]; @@ -50,12 +48,10 @@ TEST_GROUP(tiny_gea3_interface) &self, &uart.interface, address, - send_buffer, - sizeof(send_buffer), - receive_buffer, - sizeof(receive_buffer), send_queue, sizeof(send_queue), + receive_buffer, + sizeof(receive_buffer), false); tiny_event_subscription_init(&receive_subscription, NULL, packet_received); @@ -71,12 +67,10 @@ TEST_GROUP(tiny_gea3_interface) &self, &uart.interface, address, - send_buffer, - sizeof(send_buffer), - receive_buffer, - sizeof(receive_buffer), send_queue, sizeof(send_queue), + receive_buffer, + sizeof(receive_buffer), true); tiny_event_subscribe(tiny_gea_interface_on_receive(&self.interface), &receive_subscription); @@ -292,9 +286,9 @@ TEST(tiny_gea3_interface, should_send_a_packet_with_max_payload_given_send_buffe when_packet_is_sent(packet); } -TEST(tiny_gea3_interface, should_not_send_a_packet_that_is_too_large_for_the_send_buffer) +TEST(tiny_gea3_interface, should_not_send_a_packet_that_is_too_large_for_the_send_queue) { - tiny_gea_STATIC_ALLOC_PACKET(packet, 8); + tiny_gea_STATIC_ALLOC_PACKET(packet, 16); nothing_should_happen(); when_packet_is_sent(packet); @@ -389,6 +383,7 @@ TEST(tiny_gea3_interface, should_queue_sent_packets) tiny_gea_etx); after_send_completes(); + after_the_interface_is_run(); } TEST(tiny_gea3_interface, should_report_failure_to_enqueue)