Paul Mathieu
f274749050
All host tests currently pass Some async refactors may not work well on device, will try later
202 lines
5.0 KiB
C++
202 lines
5.0 KiB
C++
#include "uart.h"
|
|
|
|
#include "async.h"
|
|
#include "buffer.h"
|
|
#include "ring_buffer.h"
|
|
|
|
#include <array>
|
|
#include <mutex>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
using async::AwaitableType;
|
|
|
|
constexpr size_t kUartTxBufferSize = 256;
|
|
std::array<std::byte, kUartTxBufferSize> 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<std::byte> 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<const std::byte> 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<size_t>(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<const std::byte>) {}
|
|
void LogStuff() {}
|
|
void UartReadBlocking(std::span<std::byte>) {}
|
|
void UartWriteBlocking(std::span<const std::byte>) {}
|
|
void HandleUartIsr() {}
|
|
async::task<buffer> 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<const std::byte> 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<std::span<const std::byte>>& 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<uint8_t> 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;
|
|
}
|
|
} |