#include "uart.h" #include "async.h" #include "buffer.h" #include "ring_buffer.h" #include #include #include namespace { using async::AwaitableType; constexpr size_t kUartTxBufferSize = 256; std::array tx_buffer = {}; RingBuffer tx_ring_buffer{.buffer = tx_buffer}; uint8_t* tx_buff = nullptr; uint16_t tx_size = 0; uint16_t tx_size_orig = 0; uint8_t* rx_buff = nullptr; uint16_t rx_size = 0; uint16_t rx_size_orig = 0; std::vector rx_overflow; std::mutex interrupt_lock; std::mutex fu_mutex; } // namespace //// Internal API void FakeUart_Send(uint8_t* ptr, uint16_t size) { std::scoped_lock lock(fu_mutex); tx_buff = ptr; tx_size = size; tx_size_orig = size; } size_t FakeUart_Recv(uint8_t* ptr, uint16_t size) { std::scoped_lock lock(fu_mutex); if (size <= rx_overflow.size()) { std::copy(rx_overflow.begin(), rx_overflow.begin() + size, std::as_writable_bytes(std::span{ptr, size}).begin()); std::rotate(rx_overflow.begin(), rx_overflow.begin() + size, rx_overflow.end()); rx_overflow.resize(rx_overflow.size() - size); return size; } rx_buff = ptr; rx_size = size; if (rx_overflow.empty()) { return 0; } std::copy(rx_overflow.begin(), rx_overflow.end(), std::as_writable_bytes(std::span{ptr, size}).begin()); size_t copied = rx_overflow.size(); rx_overflow.clear(); rx_buff += copied; rx_size -= copied; return copied; } uint8_t FakeUart_IsSending() { std::scoped_lock lock(fu_mutex); return tx_size > 0; } //// Backdoor access API /// Those do trigger UART interrupts when tx/rx buffer state changes buffer FakeUart_Drain(uint16_t size) { std::scoped_lock isr_lock(interrupt_lock); // not really the same, but that'll do std::scoped_lock lock(fu_mutex); size_t drained = std::min(size, tx_size); if (drained < 1) { return buffer{}; } buffer buff = buffer::make(drained); auto txb = std::as_bytes(std::span{tx_buff, tx_size}); std::copy(txb.begin(), txb.begin() + drained, buff.data.begin()); tx_buff += drained; tx_size -= drained; if (tx_size < 1) { fu_mutex.unlock(); HandleUartTxFromIsr(nullptr, tx_size_orig); fu_mutex.lock(); } return buff; } void FakeUart_Feed(std::span data) { std::scoped_lock lock(fu_mutex); if (data.empty()) { return; } auto rxb = std::as_writable_bytes(std::span{rx_buff, rx_size}); size_t fed = std::min(static_cast(rx_size), data.size()); if (data.size() > fed) { rx_overflow.insert(rx_overflow.end(), data.begin() + fed, data.end()); } if (fed > 0) { std::copy(data.begin(), data.begin() + fed, rxb.begin()); rx_buff += fed; rx_size -= fed; if (rx_size < 1) { HandleUartRxFromIsr(nullptr, rx_size_orig); } } } void FakeUart_Reset() { rx_overflow.clear(); rx_buff = nullptr; rx_size = 0; tx_buff = nullptr; tx_size = 0; } //// Public API void InitUarts() {} void UartWriteCrash(std::span) {} void LogStuff() {} void UartReadBlocking(std::span) {} void UartWriteBlocking(std::span) {} void HandleUartIsr() {} async::task UartRead(int size) { co_return buffer{}; } void HandleUartTxFromIsr(void*, unsigned int transmitted) { tx_ring_buffer.Pop(transmitted); if (tx_ring_buffer.AvailableData() > 0 && !FakeUart_IsSending()) { FakeUart_Send(tx_ring_buffer.RawReadPointer(), tx_ring_buffer.ContiguousAvailableData()); } async::resume(AwaitableType::kUartTx); } void HandleUartRxFromIsr(void*, unsigned int) { async::resume(AwaitableType::kUartRx); } async::task<> UartWrite(std::span data) { while (!tx_ring_buffer.Store(data)) { co_await async::await(AwaitableType::kUartTx); } { std::scoped_lock lock(interrupt_lock); if (!FakeUart_IsSending()) { FakeUart_Send(tx_ring_buffer.RawReadPointer(), tx_ring_buffer.ContiguousAvailableData()); } } } async::task<> UartWriteLoop(async::gimme>& data_gen) { while (1) { auto data = co_await data_gen; while (!tx_ring_buffer.Store(data)) { co_await async::await(AwaitableType::kUartTx); } { std::scoped_lock lock(interrupt_lock); if (tx_ring_buffer.AvailableData() && !FakeUart_IsSending()) { FakeUart_Send(tx_ring_buffer.RawReadPointer(), tx_ring_buffer.ContiguousAvailableData()); } } } } async::task UartReadLoop() { uint8_t c; while (1) { size_t received = FakeUart_Recv(&c, 1); // some data may already be in the fifo, but if not, wait: if (received < 1) { co_await async::await(AwaitableType::kUartRx); } co_yield c; } }