use core::ffi::c_void;
use core::num::NonZero;
use core::ptr::{self, NonNull};
use core::sync::atomic::{AtomicPtr, Ordering};
use crate::furi::stream_buffer::StreamBuffer;
use crate::furi::thread::{self, ThreadId};
use crate::furi::time::FuriDuration;
use crate::{debug, furi, trace, warn};
use flipperzero_sys::{self as sys, HasFlag};
use sys::furi::FuriBox;
pub type SerialId = sys::FuriHalSerialId;
pub const LPUART: SerialId = sys::FuriHalSerialIdLpuart;
pub const USART: SerialId = sys::FuriHalSerialIdUsart;
pub struct SerialHandle {
handle: NonNull<sys::FuriHalSerialHandle>,
}
impl SerialHandle {
pub fn acquire(serial_id: SerialId) -> furi::Result<Self> {
let handle = unsafe { sys::furi_hal_serial_control_acquire(serial_id) };
let handle = match NonNull::new(handle) {
None => return Err(furi::Error::ResourceBusy),
Some(h) => h,
};
Ok(SerialHandle { handle })
}
pub fn as_ptr(&self) -> *mut sys::FuriHalSerialHandle {
self.handle.as_ptr()
}
pub fn init(&self, baud: u32) {
unsafe { sys::furi_hal_serial_init(self.handle.as_ptr(), baud) }
}
pub fn deinit(&self) {
unsafe { sys::furi_hal_serial_deinit(self.handle.as_ptr()) }
}
pub fn suspend(&self) {
unsafe { sys::furi_hal_serial_suspend(self.handle.as_ptr()) }
}
pub fn resume(&self) {
unsafe { sys::furi_hal_serial_resume(self.handle.as_ptr()) }
}
pub fn is_baud_rate_supported(&self, baud: u32) -> bool {
unsafe { sys::furi_hal_serial_is_baud_rate_supported(self.handle.as_ptr(), baud) }
}
pub fn set_baud_rate(&self, baud: u32) {
unsafe { sys::furi_hal_serial_set_br(self.handle.as_ptr(), baud) }
}
pub fn tx(&self, buffer: &[u8]) {
unsafe { sys::furi_hal_serial_tx(self.handle.as_ptr(), buffer.as_ptr(), buffer.len()) }
}
pub fn tx_wait_complete(&self) {
unsafe { sys::furi_hal_serial_tx_wait_complete(self.handle.as_ptr()) }
}
pub fn async_receiver<F: FnMut(&[u8])>(&self, on_rx: F) -> AsyncSerialReceiver<'_, F> {
AsyncSerialReceiver::new(self, on_rx)
}
}
impl Drop for SerialHandle {
fn drop(&mut self) {
unsafe { sys::furi_hal_serial_control_release(self.handle.as_ptr()) }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct WorkerEvent(u32);
impl WorkerEvent {
pub const FLAG_STOP: u32 = (1 << 0);
pub const FLAG_DATA: u32 = (1 << 1);
pub const FLAG_IDLE: u32 = (1 << 2);
pub const FLAG_OVERRUN_ERROR: u32 = (1 << 3);
pub const FLAG_FRAMING_ERROR: u32 = (1 << 4);
pub const FLAG_NOISE_ERROR: u32 = (1 << 5);
pub const MASK: u32 = Self::FLAG_STOP
| Self::FLAG_DATA
| Self::FLAG_IDLE
| Self::FLAG_OVERRUN_ERROR
| Self::FLAG_FRAMING_ERROR
| Self::FLAG_NOISE_ERROR;
pub fn is_stop(self) -> bool {
self.0 & Self::FLAG_STOP != 0
}
pub fn is_rx_data(self) -> bool {
self.0 & Self::FLAG_DATA != 0
}
pub fn is_rx_idle(self) -> bool {
self.0 & Self::FLAG_IDLE != 0
}
pub fn is_error(self) -> bool {
self.0 & (Self::FLAG_OVERRUN_ERROR | Self::FLAG_FRAMING_ERROR | Self::FLAG_NOISE_ERROR) != 0
}
pub fn is_overrun_error(self) -> bool {
self.0 & Self::FLAG_OVERRUN_ERROR != 0
}
pub fn is_framing_error(self) -> bool {
self.0 & Self::FLAG_FRAMING_ERROR != 0
}
pub fn is_noise_error(self) -> bool {
self.0 & Self::FLAG_NOISE_ERROR != 0
}
}
pub struct AsyncSerialReceiver<'a, F>
where
F: FnMut(&[u8]),
{
handle: &'a SerialHandle,
context: FuriBox<Context<F>>,
}
struct Context<F: FnMut(&[u8])> {
rx_stream: StreamBuffer,
on_rx: F,
worker_thread: AtomicPtr<sys::FuriThread>,
}
impl<'a, F> AsyncSerialReceiver<'a, F>
where
F: FnMut(&[u8]),
{
fn new(handle: &'a SerialHandle, on_rx: F) -> Self {
let rx_stream = StreamBuffer::new(NonZero::new(2048).unwrap(), 1);
let mut context = FuriBox::new(Context {
rx_stream,
on_rx,
worker_thread: AtomicPtr::new(ptr::null_mut()),
});
unsafe {
let worker_thread = sys::furi_thread_alloc_ex(
c"AsyncSerialReceiverWorker".as_ptr(),
1024,
Some(async_serial_receiver_worker::<F>),
FuriBox::as_mut_ptr(&mut context) as *mut _,
);
context
.worker_thread
.store(worker_thread, Ordering::Release);
sys::furi_thread_start(worker_thread);
sys::furi_hal_serial_async_rx_start(
handle.as_ptr(),
Some(async_serial_receiver_rx_callback::<F>),
FuriBox::as_mut_ptr(&mut context).cast(),
true,
);
}
AsyncSerialReceiver { handle, context }
}
}
impl<F: FnMut(&[u8])> Drop for AsyncSerialReceiver<'_, F> {
fn drop(&mut self) {
unsafe { sys::furi_hal_serial_async_rx_stop(self.handle.as_ptr()) };
let context = FuriBox::as_mut_ptr(&mut self.context);
let worker_thread = unsafe { (*context).worker_thread.load(Ordering::Acquire) };
if !worker_thread.is_null() {
let thread_id = unsafe { thread::ThreadId::from_furi_thread(worker_thread) };
thread::set_flags(thread_id, WorkerEvent::FLAG_STOP).unwrap();
unsafe {
(*context)
.worker_thread
.store(ptr::null_mut(), Ordering::Release);
sys::furi_thread_join(worker_thread);
sys::furi_thread_free(worker_thread);
}
}
}
}
unsafe extern "C" fn async_serial_receiver_rx_callback<F: FnMut(&[u8])>(
handle: *mut sys::FuriHalSerialHandle,
event: sys::FuriHalSerialRxEvent,
context: *mut c_void,
) {
let context = context.cast_const() as *const Context<F>;
let mut flags = 0u32;
if event.has_flag(sys::FuriHalSerialRxEventData) {
let data = [unsafe { sys::furi_hal_serial_async_rx(handle) }];
unsafe { (*context).rx_stream.send(&data, FuriDuration::ZERO) };
flags |= WorkerEvent::FLAG_DATA;
}
if event.has_flag(sys::FuriHalSerialRxEventIdle) {
flags |= WorkerEvent::FLAG_IDLE;
}
if event.has_flag(sys::FuriHalSerialRxEventOverrunError) {
flags |= WorkerEvent::FLAG_OVERRUN_ERROR;
}
if event.has_flag(sys::FuriHalSerialRxEventFrameError) {
flags |= WorkerEvent::FLAG_FRAMING_ERROR;
}
if event.has_flag(sys::FuriHalSerialRxEventNoiseError) {
flags |= WorkerEvent::FLAG_NOISE_ERROR;
}
let worker_thread = unsafe { (*context).worker_thread.load(Ordering::Acquire) };
if !worker_thread.is_null() {
let thread_id = unsafe { ThreadId::from_furi_thread(worker_thread) };
thread::set_flags(thread_id, flags).unwrap();
}
}
const SERIAL_WORKER_BUFFER_LEN: usize = 64;
unsafe extern "C" fn async_serial_receiver_worker<F: FnMut(&[u8])>(context: *mut c_void) -> i32 {
debug!("Starting async worker");
assert!(!context.is_null());
let context = context.cast::<Context<F>>();
loop {
let events = WorkerEvent(
thread::wait_any_flags(WorkerEvent::MASK, true, FuriDuration::MAX).unwrap_or(0),
);
trace!("WorkerEvent: {}", events.0);
if events.is_stop() {
debug!("Stopping async worker");
break;
}
if events.is_rx_data() {
loop {
let mut data = [0u8; SERIAL_WORKER_BUFFER_LEN];
let len = unsafe { (*context).rx_stream.receive(&mut data, FuriDuration::ZERO) };
if len == 0 {
break;
}
unsafe { ((*context).on_rx)(&data[..len]) }
}
}
if events.is_rx_idle() {
trace!("idle");
}
if events.is_error() {
if events.is_overrun_error() {
warn!("overrun");
}
if events.is_framing_error() {
warn!("framing error");
}
if events.is_noise_error() {
warn!("noise error");
}
}
}
0
}