mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-23 13:42:35 +00:00
Raw LoRa packets to and from a host over the USB/console serial port, plus upstream's SetHardware extensions (identity, crypto, radio config, stats). - app/KissModem.cpp/h: upstream text, ZephCore changes fenced: an LBT refusal returns to CSMA until the wait-clear bound; a TX times out only once the radio has concluded (as Dispatcher::loop); SetRadio outside the ZC_RADIO_* ranges is refused; msUntilNextTx() for the event loop. - src/main_kiss_modem.cpp: event-driven composition root; runs the radio maintenance the Dispatcher would. Identity in /lfs/repeater/_main.id. - adapters/transport/ZephyrUartStream: Arduino Stream over IRQ RX/TX rings. - boards/common/kiss_modem.conf: role, BT off, console driver off. - LoRaRadio::getCurrentRSSI(), Print::availableForWrite(). - UI mesh-action stubs for every non-companion role. Tested on 2x XIAO ESP32-C3 + Wio-SX1262 at 869.423 MHz SF7/62.5/CR5. Built for xiao_esp32c3, rak4631, lora_e5_mini; host tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
157 lines
3.2 KiB
C++
157 lines
3.2 KiB
C++
/*
|
|
* SPDX-License-Identifier: MIT
|
|
* Arduino Stream over an interrupt-driven Zephyr UART.
|
|
*/
|
|
|
|
#include "ZephyrUartStream.h"
|
|
|
|
#include <zephyr/drivers/uart.h>
|
|
#include <zephyr/sys/util.h>
|
|
|
|
bool ZephyrUartStream::begin(const struct device *dev, void (*on_rx)(void),
|
|
void (*on_tx_idle)(void))
|
|
{
|
|
if (!device_is_ready(dev)) {
|
|
return false;
|
|
}
|
|
_dev = dev;
|
|
_on_rx = on_rx;
|
|
_on_tx_idle = on_tx_idle;
|
|
ring_buf_init(&_rx_ring, sizeof(_rx_data), _rx_data);
|
|
ring_buf_init(&_tx_ring, sizeof(_tx_data), _tx_data);
|
|
k_sem_init(&_tx_drained, 0, 1);
|
|
uart_irq_callback_user_data_set(dev, isr, this);
|
|
uart_irq_rx_enable(dev);
|
|
return true;
|
|
}
|
|
|
|
void ZephyrUartStream::isr(const struct device *dev, void *user_data)
|
|
{
|
|
ZephyrUartStream *self = static_cast<ZephyrUartStream *>(user_data);
|
|
|
|
for (;;) {
|
|
uart_irq_update(dev);
|
|
if (uart_irq_is_pending(dev) <= 0) {
|
|
break;
|
|
}
|
|
|
|
if (uart_irq_rx_ready(dev)) {
|
|
uint8_t buf[64];
|
|
uint32_t room = MIN(sizeof(buf), ring_buf_space_get(&self->_rx_ring));
|
|
|
|
if (room == 0) {
|
|
/* read() re-enables RX once it has made room. */
|
|
atomic_set(&self->_rx_paused, 1);
|
|
uart_irq_rx_disable(dev);
|
|
} else {
|
|
int n = uart_fifo_read(dev, buf, room);
|
|
|
|
if (n > 0) {
|
|
ring_buf_put(&self->_rx_ring, buf, n);
|
|
}
|
|
}
|
|
if (self->_on_rx) {
|
|
self->_on_rx();
|
|
}
|
|
}
|
|
|
|
if (uart_irq_tx_ready(dev)) {
|
|
uint8_t *out;
|
|
bool empty;
|
|
k_spinlock_key_t key = k_spin_lock(&self->_tx_lock);
|
|
uint32_t claimed = MIN(ring_buf_get_ptr(&self->_tx_ring, &out, 0), 64U);
|
|
|
|
if (claimed > 0) {
|
|
int sent = uart_fifo_fill(dev, out, claimed);
|
|
|
|
if (sent > 0) {
|
|
ring_buf_consume(&self->_tx_ring, sent);
|
|
}
|
|
}
|
|
empty = ring_buf_is_empty(&self->_tx_ring);
|
|
if (empty) {
|
|
/* Inside the lock: write() enables after its put. */
|
|
uart_irq_tx_disable(dev);
|
|
}
|
|
k_spin_unlock(&self->_tx_lock, key);
|
|
|
|
if (empty) {
|
|
k_sem_give(&self->_tx_drained);
|
|
if (self->_on_tx_idle) {
|
|
self->_on_tx_idle();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t ZephyrUartStream::write(const uint8_t *buffer, size_t size)
|
|
{
|
|
if (!_dev || size == 0) {
|
|
return 0;
|
|
}
|
|
k_spinlock_key_t key = k_spin_lock(&_tx_lock);
|
|
uint32_t n = ring_buf_put(&_tx_ring, buffer, size);
|
|
|
|
k_spin_unlock(&_tx_lock, key);
|
|
if (n > 0) {
|
|
uart_irq_tx_enable(_dev);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
int ZephyrUartStream::availableForWrite()
|
|
{
|
|
if (!_dev) {
|
|
return 0;
|
|
}
|
|
k_spinlock_key_t key = k_spin_lock(&_tx_lock);
|
|
uint32_t space = ring_buf_space_get(&_tx_ring);
|
|
|
|
k_spin_unlock(&_tx_lock, key);
|
|
return (int)space;
|
|
}
|
|
|
|
int ZephyrUartStream::available()
|
|
{
|
|
return _dev ? (int)ring_buf_size_get(&_rx_ring) : 0;
|
|
}
|
|
|
|
int ZephyrUartStream::read()
|
|
{
|
|
uint8_t b;
|
|
|
|
if (!_dev || ring_buf_get(&_rx_ring, &b, 1) != 1) {
|
|
return -1;
|
|
}
|
|
if (atomic_cas(&_rx_paused, 1, 0)) {
|
|
uart_irq_rx_enable(_dev);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
int ZephyrUartStream::peek()
|
|
{
|
|
uint8_t b;
|
|
|
|
if (!_dev || ring_buf_peek(&_rx_ring, &b, 1) != 1) {
|
|
return -1;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
void ZephyrUartStream::flush()
|
|
{
|
|
if (!_dev) {
|
|
return;
|
|
}
|
|
k_sem_reset(&_tx_drained);
|
|
k_spinlock_key_t key = k_spin_lock(&_tx_lock);
|
|
bool empty = ring_buf_is_empty(&_tx_ring);
|
|
|
|
k_spin_unlock(&_tx_lock, key);
|
|
if (!empty) {
|
|
k_sem_take(&_tx_drained, K_MSEC(FLUSH_TIMEOUT_MS));
|
|
}
|
|
}
|