diff --git a/src/system/bus/uart.c b/src/system/bus/uart.c index 97e122e..40582a7 100644 --- a/src/system/bus/uart.c +++ b/src/system/bus/uart.c @@ -41,10 +41,12 @@ struct UART_Handle { UART_Bus bus_id; CircularBuffer *rx_buffer; CircularBuffer *tx_buffer; + CircularBuffer *original_tx_buffer; /* Stored during passthrough */ uint32_t volatile rx_dma_head; UART_RxCallback rx_callback; uint32_t rx_threshold; bool initialized; + UART_Handle *passthrough_target; }; /* Global array to keep track of active UART handles */ @@ -260,10 +262,12 @@ UART_Handle *UART_init( handle->bus_id = bus_id; handle->rx_buffer = rx_buffer; handle->tx_buffer = tx_buffer; + handle->original_tx_buffer = nullptr; handle->rx_dma_head = 0; handle->rx_callback = nullptr; handle->rx_threshold = 0; handle->initialized = false; + handle->passthrough_target = nullptr; /* Initialize hardware layer */ UART_LL_init(bus_id, rx_buffer->buffer, rx_buffer->size); @@ -289,6 +293,11 @@ void UART_deinit(UART_Handle *handle) return; } + /* Cannot deinit while passthrough is active */ + if (handle->passthrough_target) { + THROW(ERROR_RESOURCE_BUSY); + } + /* Deinitialize hardware layer */ UART_LL_deinit(handle->bus_id); @@ -470,3 +479,80 @@ void UART_set_rx_callback( handle->rx_callback(handle, rx_buffer_available(handle)); } } + +/** + * @brief Passthrough callback function. + * + * This callback is triggered when data is received on a UART bus in passthrough + * mode. It starts transmission on the target bus to forward the received data. + * + * @param handle UART handle of the receiving bus + * @param bytes_available Number of bytes available (unused) + */ +static void passthrough_callback(UART_Handle *handle, uint32_t bytes_available) +{ + (void)bytes_available; /* Unused parameter */ + + if (!handle || !handle->passthrough_target) { + return; + } + + /* Start transmission on the target bus */ + start_transmission(handle->passthrough_target); +} + +void UART_enable_passthrough(UART_Handle *handle1, UART_Handle *handle2) +{ + if (!handle1 || !handle1->initialized || !handle2 || + !handle2->initialized) { + THROW(ERROR_DEVICE_NOT_READY); + } + + // Check that handles are different + if (handle1 == handle2) { + THROW(ERROR_INVALID_ARGUMENT); + } + + // Check if either handle is already in passthrough mode + if (handle1->passthrough_target || handle2->passthrough_target) { + THROW(ERROR_RESOURCE_BUSY); + } + + // Set up shared buffers: + // - Store normal tx_buffers in each handle + // - Redirect each handle's rx_buffer to the other handle's tx_buffer + handle1->original_tx_buffer = handle1->tx_buffer; + handle2->original_tx_buffer = handle2->tx_buffer; + handle1->tx_buffer = handle2->rx_buffer; + handle2->tx_buffer = handle1->rx_buffer; + + // Set up passthrough targets + handle1->passthrough_target = handle2; + handle2->passthrough_target = handle1; + + // Set up RX callbacks to trigger transmission on the other bus + UART_set_rx_callback(handle1, passthrough_callback, 1); + UART_set_rx_callback(handle2, passthrough_callback, 1); +} + +void UART_disable_passthrough(UART_Handle *handle1, UART_Handle *handle2) +{ + if (handle1->passthrough_target != handle2 || + handle2->passthrough_target != handle1) { + THROW(ERROR_INVALID_ARGUMENT); + } + + /* Disable passthrough callbacks */ + UART_set_rx_callback(handle1, nullptr, 0); + UART_set_rx_callback(handle2, nullptr, 0); + + /* Restore original TX buffers */ + handle1->tx_buffer = handle1->original_tx_buffer; + handle2->tx_buffer = handle2->original_tx_buffer; + handle1->original_tx_buffer = nullptr; + handle2->original_tx_buffer = nullptr; + + /* Clear passthrough targets */ + handle1->passthrough_target = nullptr; + handle2->passthrough_target = nullptr; +} diff --git a/src/system/bus/uart.h b/src/system/bus/uart.h index 07883c0..f58946e 100644 --- a/src/system/bus/uart.h +++ b/src/system/bus/uart.h @@ -102,6 +102,8 @@ UART_Handle *UART_init( /** * @brief Deinitialize the UART peripheral. * + * @note Bus cannot be deinitialized while passthrough mode is active. + * * @param handle Pointer to UART handle structure */ void UART_deinit(UART_Handle *handle); @@ -190,6 +192,37 @@ uint32_t UART_tx_free_space(UART_Handle *handle); */ bool UART_tx_busy(UART_Handle *handle); +/** + * @brief Enable UART passthrough mode. + * + * This function enables passthrough mode for the specified UART buses, + * forwarding data received on one bus to the other bus. + * + * @warning Do not call UART operations on either handle while passthrough is + * active. + * + * @note While passthrough is active, neither bus can be deinitialized. + * @note RX callbacks, if any, will be disabled and must be manually re-enabled + * after disabling passthrough. + * + * @param handle1 First UART bus instance + * @param handle2 Second UART bus instance + */ +void UART_enable_passthrough(UART_Handle *handle1, UART_Handle *handle2); + +/** + * @brief Disable UART passthrough mode. + * + * This function disables passthrough mode for the specified UART buses, + * restoring normal operation. + * + * @note Previous RX callbacks, if any, must be manually re-enabled. + * + * @param handle1 First UART bus instance + * @param handle2 Second UART bus instance + */ +void UART_disable_passthrough(UART_Handle *handle1, UART_Handle *handle2); + #ifdef __cplusplus } #endif diff --git a/tests/test_headers/opaque.h b/tests/test_headers/opaque.h index 7e4efb1..0128d53 100644 --- a/tests/test_headers/opaque.h +++ b/tests/test_headers/opaque.h @@ -11,6 +11,8 @@ #include +#include "platform/uart_ll.h" +#include "system/bus/uart.h" #include "system/bus/usb.h" #include "system/instrument/dmm.h" #include "system/instrument/dso.h" @@ -20,6 +22,21 @@ extern "C" { #endif +/** + * @brief UART handle structure (concrete definition for testing) + */ +struct UART_Handle { + UART_Bus bus_id; + CircularBuffer *rx_buffer; + CircularBuffer *tx_buffer; + CircularBuffer *original_tx_buffer; /* Stored during passthrough */ + uint32_t volatile rx_dma_head; + UART_RxCallback rx_callback; + uint32_t rx_threshold; + bool initialized; + UART_Handle *passthrough_target; +}; + /** * @brief DMM handle structure (concrete definition for testing) */ diff --git a/tests/test_uart.c b/tests/test_uart.c index 445d2d4..63f8e58 100644 --- a/tests/test_uart.c +++ b/tests/test_uart.c @@ -452,3 +452,629 @@ void test_UART_flush_with_null_handle(void) // Assert TEST_ASSERT_FALSE(result); } + +// ============================================================================ +// UART Passthrough Tests +// ============================================================================ + +void test_UART_enable_passthrough_success(void) +{ + // Arrange - Initialize two UART buses with separate buffers + CircularBuffer rx_buffer1, tx_buffer1; + CircularBuffer rx_buffer2, tx_buffer2; + uint8_t rx_data1[256], tx_data1[256]; + uint8_t rx_data2[256], tx_data2[256]; + + circular_buffer_init(&rx_buffer1, rx_data1, sizeof(rx_data1)); + circular_buffer_init(&tx_buffer1, tx_data1, sizeof(tx_data1)); + circular_buffer_init(&rx_buffer2, rx_data2, sizeof(rx_data2)); + circular_buffer_init(&tx_buffer2, tx_data2, sizeof(tx_data2)); + + // Set up expectations for init of handle1 + UART_LL_init_Expect(UART_BUS_0, rx_data1, sizeof(rx_data1)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle1 = UART_init(0, &rx_buffer1, &tx_buffer1); + TEST_ASSERT_NOT_NULL(handle1); + + // Set up expectations for init of handle2 + UART_LL_init_Expect(UART_BUS_1, rx_data2, sizeof(rx_data2)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle2 = UART_init(1, &rx_buffer2, &tx_buffer2); + TEST_ASSERT_NOT_NULL(handle2); + + // Mock DMA position calls during passthrough enable (called by UART_set_rx_callback) + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_0, 0); + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_1, 0); + + // Act - Enable passthrough + UART_enable_passthrough(handle1, handle2); + + // Assert - Passthrough enabled, no errors thrown (test passes if no exception) + + // Cleanup + UART_disable_passthrough(handle1, handle2); + + UART_LL_deinit_Ignore(); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_deinit(handle1); + UART_deinit(handle2); +} + +void test_UART_enable_passthrough_null_handles(void) +{ + // Arrange + Error caught_error = ERROR_NONE; + + // Act - Try to enable passthrough with null handles + TRY { + UART_enable_passthrough(nullptr, nullptr); + } CATCH(caught_error) { + // Expected to catch an error + } + + // Assert + TEST_ASSERT_EQUAL(ERROR_DEVICE_NOT_READY, caught_error); +} + +void test_UART_enable_passthrough_one_null_handle(void) +{ + // Arrange + CircularBuffer rx_buffer1, tx_buffer1; + uint8_t rx_data1[256], tx_data1[256]; + + circular_buffer_init(&rx_buffer1, rx_data1, sizeof(rx_data1)); + circular_buffer_init(&tx_buffer1, tx_data1, sizeof(tx_data1)); + + UART_LL_init_Expect(UART_BUS_0, rx_data1, sizeof(rx_data1)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle1 = UART_init(0, &rx_buffer1, &tx_buffer1); + TEST_ASSERT_NOT_NULL(handle1); + + Error caught_error = ERROR_NONE; + + // Act - Try to enable passthrough with one null handle + TRY { + UART_enable_passthrough(handle1, nullptr); + } CATCH(caught_error) { + // Expected to catch an error + } + + // Assert + TEST_ASSERT_EQUAL(ERROR_DEVICE_NOT_READY, caught_error); + + // Cleanup + UART_LL_deinit_Ignore(); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_deinit(handle1); +} + +void test_UART_passthrough_data_flow_bus0_to_bus1(void) +{ + // Arrange - Initialize two UART buses + CircularBuffer rx_buffer1, tx_buffer1; + CircularBuffer rx_buffer2, tx_buffer2; + uint8_t rx_data1[256], tx_data1[256]; + uint8_t rx_data2[256], tx_data2[256]; + + circular_buffer_init(&rx_buffer1, rx_data1, sizeof(rx_data1)); + circular_buffer_init(&tx_buffer1, tx_data1, sizeof(tx_data1)); + circular_buffer_init(&rx_buffer2, rx_data2, sizeof(rx_data2)); + circular_buffer_init(&tx_buffer2, tx_data2, sizeof(tx_data2)); + + // Set up expectations for init + UART_LL_init_Expect(UART_BUS_0, rx_data1, sizeof(rx_data1)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle1 = UART_init(0, &rx_buffer1, &tx_buffer1); + + UART_LL_init_Expect(UART_BUS_1, rx_data2, sizeof(rx_data2)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle2 = UART_init(1, &rx_buffer2, &tx_buffer2); + + // Mock DMA position calls during passthrough enable + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_0, 0); + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_1, 0); + + // Enable passthrough + UART_enable_passthrough(handle1, handle2); + + // Simulate data received on bus 0 + uint8_t test_data[] = {0xAA, 0xBB, 0xCC, 0xDD}; + uint32_t written = circular_buffer_write(handle1->rx_buffer, test_data, sizeof(test_data)); + TEST_ASSERT_EQUAL(sizeof(test_data), written); + + // Update buffer head to simulate DMA + handle1->rx_buffer->head = sizeof(test_data); + + // Mock DMA position for callback triggering + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_0, sizeof(test_data)); + + // Expect transmission to be started on bus 1 (not busy) + UART_LL_tx_busy_ExpectAndReturn(UART_BUS_1, false); + UART_LL_start_dma_tx_Expect(UART_BUS_1, test_data, sizeof(test_data)); + + // Act - Check RX available which triggers callback + uint32_t available = UART_rx_available(handle1); + + // Assert - Data should be available + TEST_ASSERT_EQUAL(sizeof(test_data), available); + + // Verify data is in the correct buffer (bus1's TX buffer points to bus0's RX buffer in passthrough) + uint8_t read_buffer[10]; + uint32_t bytes_read = circular_buffer_read(handle2->tx_buffer, read_buffer, sizeof(read_buffer)); + TEST_ASSERT_EQUAL(sizeof(test_data), bytes_read); + TEST_ASSERT_EQUAL_MEMORY(test_data, read_buffer, sizeof(test_data)); + + // Cleanup + UART_disable_passthrough(handle1, handle2); + + UART_LL_deinit_Ignore(); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_deinit(handle1); + UART_deinit(handle2); +} + +void test_UART_passthrough_data_flow_bus1_to_bus0(void) +{ + // Arrange - Initialize two UART buses + CircularBuffer rx_buffer1, tx_buffer1; + CircularBuffer rx_buffer2, tx_buffer2; + uint8_t rx_data1[256], tx_data1[256]; + uint8_t rx_data2[256], tx_data2[256]; + + circular_buffer_init(&rx_buffer1, rx_data1, sizeof(rx_data1)); + circular_buffer_init(&tx_buffer1, tx_data1, sizeof(tx_data1)); + circular_buffer_init(&rx_buffer2, rx_data2, sizeof(rx_data2)); + circular_buffer_init(&tx_buffer2, tx_data2, sizeof(tx_data2)); + + // Set up expectations for init + UART_LL_init_Expect(UART_BUS_0, rx_data1, sizeof(rx_data1)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle1 = UART_init(0, &rx_buffer1, &tx_buffer1); + + UART_LL_init_Expect(UART_BUS_1, rx_data2, sizeof(rx_data2)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle2 = UART_init(1, &rx_buffer2, &tx_buffer2); + + // Mock DMA position calls during passthrough enable + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_0, 0); + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_1, 0); + + // Enable passthrough + UART_enable_passthrough(handle1, handle2); + + // Simulate data received on bus 1 + uint8_t test_data[] = {0x11, 0x22, 0x33, 0x44, 0x55}; + uint32_t written = circular_buffer_write(handle2->rx_buffer, test_data, sizeof(test_data)); + TEST_ASSERT_EQUAL(sizeof(test_data), written); + + // Update buffer head to simulate DMA + handle2->rx_buffer->head = sizeof(test_data); + + // Mock DMA position for callback triggering + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_1, sizeof(test_data)); + + // Expect transmission to be started on bus 0 (not busy) + UART_LL_tx_busy_ExpectAndReturn(UART_BUS_0, false); + UART_LL_start_dma_tx_Expect(UART_BUS_0, test_data, sizeof(test_data)); + + // Act - Check RX available which triggers callback + uint32_t available = UART_rx_available(handle2); + + // Assert - Data should be available + TEST_ASSERT_EQUAL(sizeof(test_data), available); + + // Verify data is in the correct buffer (bus0's TX buffer points to bus1's RX buffer in passthrough) + uint8_t read_buffer[10]; + uint32_t bytes_read = circular_buffer_read(handle1->tx_buffer, read_buffer, sizeof(read_buffer)); + TEST_ASSERT_EQUAL(sizeof(test_data), bytes_read); + TEST_ASSERT_EQUAL_MEMORY(test_data, read_buffer, sizeof(test_data)); + + // Cleanup + UART_disable_passthrough(handle1, handle2); + + UART_LL_deinit_Ignore(); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_deinit(handle1); + UART_deinit(handle2); +} + +void test_UART_passthrough_bidirectional_data_flow(void) +{ + // Arrange - Initialize two UART buses + CircularBuffer rx_buffer1, tx_buffer1; + CircularBuffer rx_buffer2, tx_buffer2; + uint8_t rx_data1[256], tx_data1[256]; + uint8_t rx_data2[256], tx_data2[256]; + + circular_buffer_init(&rx_buffer1, rx_data1, sizeof(rx_data1)); + circular_buffer_init(&tx_buffer1, tx_data1, sizeof(tx_data1)); + circular_buffer_init(&rx_buffer2, rx_data2, sizeof(rx_data2)); + circular_buffer_init(&tx_buffer2, tx_data2, sizeof(tx_data2)); + + // Set up expectations for init + UART_LL_init_Expect(UART_BUS_0, rx_data1, sizeof(rx_data1)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle1 = UART_init(0, &rx_buffer1, &tx_buffer1); + + UART_LL_init_Expect(UART_BUS_1, rx_data2, sizeof(rx_data2)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle2 = UART_init(1, &rx_buffer2, &tx_buffer2); + + // Mock DMA position calls during passthrough enable + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_0, 0); + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_1, 0); + + // Enable passthrough + UART_enable_passthrough(handle1, handle2); + + // Simulate data received on both buses + uint8_t test_data1[] = {0xAA, 0xBB, 0xCC}; + uint8_t test_data2[] = {0x11, 0x22, 0x33, 0x44}; + + circular_buffer_write(handle1->rx_buffer, test_data1, sizeof(test_data1)); + handle1->rx_buffer->head = sizeof(test_data1); + + circular_buffer_write(handle2->rx_buffer, test_data2, sizeof(test_data2)); + handle2->rx_buffer->head = sizeof(test_data2); + + // Mock DMA positions and transmissions for bus 0 -> bus 1 + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_0, sizeof(test_data1)); + UART_LL_tx_busy_ExpectAndReturn(UART_BUS_1, false); + UART_LL_start_dma_tx_Expect(UART_BUS_1, test_data1, sizeof(test_data1)); + + // Act - Check RX on bus 0 + uint32_t available1 = UART_rx_available(handle1); + + // Mock DMA positions and transmissions for bus 1 -> bus 0 + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_1, sizeof(test_data2)); + UART_LL_tx_busy_ExpectAndReturn(UART_BUS_0, false); + UART_LL_start_dma_tx_Expect(UART_BUS_0, test_data2, sizeof(test_data2)); + + // Act - Check RX on bus 1 + uint32_t available2 = UART_rx_available(handle2); + + // Assert - Data should be available on both buses + TEST_ASSERT_EQUAL(sizeof(test_data1), available1); + TEST_ASSERT_EQUAL(sizeof(test_data2), available2); + + // Cleanup + UART_disable_passthrough(handle1, handle2); + + UART_LL_deinit_Ignore(); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_deinit(handle1); + UART_deinit(handle2); +} + +void test_UART_disable_passthrough_success(void) +{ + // Arrange - Initialize two UART buses + CircularBuffer rx_buffer1, tx_buffer1; + CircularBuffer rx_buffer2, tx_buffer2; + uint8_t rx_data1[256], tx_data1[256]; + uint8_t rx_data2[256], tx_data2[256]; + + circular_buffer_init(&rx_buffer1, rx_data1, sizeof(rx_data1)); + circular_buffer_init(&tx_buffer1, tx_data1, sizeof(tx_data1)); + circular_buffer_init(&rx_buffer2, rx_data2, sizeof(rx_data2)); + circular_buffer_init(&tx_buffer2, tx_data2, sizeof(tx_data2)); + + // Set up expectations for init + UART_LL_init_Expect(UART_BUS_0, rx_data1, sizeof(rx_data1)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle1 = UART_init(0, &rx_buffer1, &tx_buffer1); + + UART_LL_init_Expect(UART_BUS_1, rx_data2, sizeof(rx_data2)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle2 = UART_init(1, &rx_buffer2, &tx_buffer2); + + // Mock DMA position calls during passthrough enable + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_0, 0); + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_1, 0); + + // Enable passthrough first + UART_enable_passthrough(handle1, handle2); + + // Act - Disable passthrough + UART_disable_passthrough(handle1, handle2); + + // Assert - No errors thrown, passthrough disabled successfully (test passes if no exception) + + // Cleanup + UART_LL_deinit_Ignore(); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_deinit(handle1); + UART_deinit(handle2); +} + +void test_UART_disable_passthrough_invalid_pair(void) +{ + // Arrange - Initialize three UART buses + CircularBuffer rx_buffer1, tx_buffer1; + CircularBuffer rx_buffer2, tx_buffer2; + CircularBuffer rx_buffer3, tx_buffer3; + uint8_t rx_data1[256], tx_data1[256]; + uint8_t rx_data2[256], tx_data2[256]; + uint8_t rx_data3[256], tx_data3[256]; + + circular_buffer_init(&rx_buffer1, rx_data1, sizeof(rx_data1)); + circular_buffer_init(&tx_buffer1, tx_data1, sizeof(tx_data1)); + circular_buffer_init(&rx_buffer2, rx_data2, sizeof(rx_data2)); + circular_buffer_init(&tx_buffer2, tx_data2, sizeof(tx_data2)); + circular_buffer_init(&rx_buffer3, rx_data3, sizeof(rx_data3)); + circular_buffer_init(&tx_buffer3, tx_data3, sizeof(tx_data3)); + + // Set up expectations for init + UART_LL_init_Expect(UART_BUS_0, rx_data1, sizeof(rx_data1)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle1 = UART_init(0, &rx_buffer1, &tx_buffer1); + + UART_LL_init_Expect(UART_BUS_1, rx_data2, sizeof(rx_data2)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle2 = UART_init(1, &rx_buffer2, &tx_buffer2); + + UART_LL_init_Expect(UART_BUS_2, rx_data3, sizeof(rx_data3)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle3 = UART_init(2, &rx_buffer3, &tx_buffer3); + + // Mock DMA position calls during passthrough enable + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_0, 0); + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_1, 0); + + // Enable passthrough between handle1 and handle2 + UART_enable_passthrough(handle1, handle2); + + Error caught_error = ERROR_NONE; + + // Act - Try to disable passthrough with mismatched handles + TRY { + UART_disable_passthrough(handle1, handle3); + } CATCH(caught_error) { + // Expected to catch an error + } + + // Assert + TEST_ASSERT_EQUAL(ERROR_INVALID_ARGUMENT, caught_error); + + // Cleanup + UART_disable_passthrough(handle1, handle2); + + UART_LL_deinit_Ignore(); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_deinit(handle1); + UART_deinit(handle2); + UART_deinit(handle3); +} + +void test_UART_enable_passthrough_already_active(void) +{ + // Arrange - Initialize four UART buses + CircularBuffer rx_buffer1, tx_buffer1; + CircularBuffer rx_buffer2, tx_buffer2; + CircularBuffer rx_buffer3, tx_buffer3; + uint8_t rx_data1[256], tx_data1[256]; + uint8_t rx_data2[256], tx_data2[256]; + uint8_t rx_data3[256], tx_data3[256]; + + + circular_buffer_init(&rx_buffer1, rx_data1, sizeof(rx_data1)); + circular_buffer_init(&tx_buffer1, tx_data1, sizeof(tx_data1)); + circular_buffer_init(&rx_buffer2, rx_data2, sizeof(rx_data2)); + circular_buffer_init(&tx_buffer2, tx_data2, sizeof(tx_data2)); + circular_buffer_init(&rx_buffer3, rx_data3, sizeof(rx_data3)); + circular_buffer_init(&tx_buffer3, tx_data3, sizeof(tx_data3)); + + // Set up expectations for init + UART_LL_init_Expect(UART_BUS_0, rx_data1, sizeof(rx_data1)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle1 = UART_init(0, &rx_buffer1, &tx_buffer1); + + UART_LL_init_Expect(UART_BUS_1, rx_data2, sizeof(rx_data2)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle2 = UART_init(1, &rx_buffer2, &tx_buffer2); + + UART_LL_init_Expect(UART_BUS_2, rx_data3, sizeof(rx_data3)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle3 = UART_init(2, &rx_buffer3, &tx_buffer3); + + // Mock DMA position calls during passthrough enable + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_0, 0); + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_1, 0); + + // Enable passthrough between handle1 and handle2 + UART_enable_passthrough(handle1, handle2); + + Error caught_error = ERROR_NONE; + + // Act - Try to enable another passthrough while one is active + TRY { + UART_enable_passthrough(handle1, handle3); + } CATCH(caught_error) { + // Expected to catch an error + } + + // Assert - Should fail because only one passthrough pair is allowed at a time + TEST_ASSERT_EQUAL(ERROR_RESOURCE_BUSY, caught_error); + + // Cleanup + UART_disable_passthrough(handle1, handle2); + + UART_LL_deinit_Ignore(); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_deinit(handle1); + UART_deinit(handle2); + UART_deinit(handle3); +} + +void test_UART_deinit_with_active_passthrough(void) +{ + // Arrange - Initialize two UART buses + CircularBuffer rx_buffer1, tx_buffer1; + CircularBuffer rx_buffer2, tx_buffer2; + uint8_t rx_data1[256], tx_data1[256]; + uint8_t rx_data2[256], tx_data2[256]; + + circular_buffer_init(&rx_buffer1, rx_data1, sizeof(rx_data1)); + circular_buffer_init(&tx_buffer1, tx_data1, sizeof(tx_data1)); + circular_buffer_init(&rx_buffer2, rx_data2, sizeof(rx_data2)); + circular_buffer_init(&tx_buffer2, tx_data2, sizeof(tx_data2)); + + // Set up expectations for init + UART_LL_init_Expect(UART_BUS_0, rx_data1, sizeof(rx_data1)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle1 = UART_init(0, &rx_buffer1, &tx_buffer1); + + UART_LL_init_Expect(UART_BUS_1, rx_data2, sizeof(rx_data2)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle2 = UART_init(1, &rx_buffer2, &tx_buffer2); + + // Mock DMA position calls during passthrough enable + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_0, 0); + UART_LL_get_dma_position_ExpectAndReturn(UART_BUS_1, 0); + + // Enable passthrough + UART_enable_passthrough(handle1, handle2); + + Error caught_error = ERROR_NONE; + + // Act - Try to deinit while passthrough is active + TRY { + UART_deinit(handle1); + } CATCH(caught_error) { + // Expected to catch an error + } + + // Assert - Should fail because passthrough is active + TEST_ASSERT_EQUAL(ERROR_RESOURCE_BUSY, caught_error); + + // Cleanup - Properly disable passthrough before deinit + UART_disable_passthrough(handle1, handle2); + + UART_LL_deinit_Ignore(); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_deinit(handle1); + UART_deinit(handle2); +} + +void test_UART_enable_passthrough_with_same_handle(void) +{ + // Arrange - Initialize one UART bus + CircularBuffer rx_buffer, tx_buffer; + uint8_t rx_data[256], tx_data[256]; + + circular_buffer_init(&rx_buffer, rx_data, sizeof(rx_data)); + circular_buffer_init(&tx_buffer, tx_data, sizeof(tx_data)); + + // Set up expectations for init + UART_LL_init_Expect(UART_BUS_0, rx_data, sizeof(rx_data)); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_Handle *handle = UART_init(0, &rx_buffer, &tx_buffer); + TEST_ASSERT_NOT_NULL(handle); + + Error caught_error = ERROR_NONE; + + // Act - Try to enable passthrough with the same handle for both buses + TRY { + UART_enable_passthrough(handle, handle); + } CATCH(caught_error) { + // Expected to catch an error + } + + // Assert + TEST_ASSERT_EQUAL(ERROR_INVALID_ARGUMENT, caught_error); + + // Cleanup + UART_LL_deinit_Ignore(); + UART_LL_set_idle_callback_Ignore(); + UART_LL_set_rx_complete_callback_Ignore(); + UART_LL_set_tx_complete_callback_Ignore(); + + UART_deinit(handle); +}