#![allow(non_snake_case)]
use cobs::CobsEncoder;
use crate::sdk::serial::{self, Channel};
pub fn enable_auto_muxing() {
unsafe {
crate::sdk::redirect_function(
vex_sdk::vexSerialWriteBuffer as *mut (),
user::write_buffer as *const (),
);
crate::sdk::redirect_function(
vex_sdk::vexSerialWriteChar as *mut (),
user::write_char as *const (),
);
crate::sdk::redirect_function(
vex_sdk::vexSerialReadChar as *mut (),
user::read_char as *const (),
);
crate::sdk::redirect_function(
vex_sdk::vexSerialPeekChar as *mut (),
user::peek_char as *const (),
);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ChannelId {
User = b'u',
Debug = b'd',
}
const PACKET_BUF_SIZE: usize = 128;
pub fn write_all(channel: ChannelId, mut buf: &[u8]) {
while !buf.is_empty() {
let mut out_buf = [0u8; PACKET_BUF_SIZE];
let max_len = out_buf.len() - 1;
let out_buf_without_delimiter = &mut out_buf[..max_len];
let mut encoder = CobsEncoder::new(out_buf_without_delimiter);
encoder.push(&[channel as u8]).unwrap();
while let Some(&byte) = buf.first() {
let Ok(_) = encoder.push(&[byte]) else {
break;
};
buf = &buf[1..];
}
let length = encoder.finalize();
out_buf[length] = 0;
serial::write_all(serial::Channel::USER, &out_buf[..=length]).unwrap();
}
}
pub fn flush_serial() {
unsafe {
while serial::write_buf_capacity(serial::Channel::USER).unwrap() != serial::OUT_BUF_SIZE {
vex_sdk::vexTasksRun();
}
}
}
mod user {
use core::sync::atomic::{AtomicBool, Ordering};
use super::*;
pub extern "C" fn write_char(channel: u32, c: u8) -> i32 {
let channel = Channel::from(channel);
if channel == Channel::USER {
write_all(ChannelId::User, &[c]);
return 1;
}
serial::write_byte(channel, c) as i32
}
pub unsafe extern "C" fn write_buffer(channel: u32, data: *const u8, data_len: u32) -> i32 {
let channel = Channel::from(channel);
if channel == Channel::USER {
let user_data = unsafe { core::slice::from_raw_parts(data, data_len as usize) };
write_all(ChannelId::User, user_data);
return data_len as i32;
}
unsafe {
serial::write_buf(channel, data, data_len as usize)
.map(|n| n as i32)
.unwrap_or(-1)
}
}
fn tried_to_read() {
static PROGRAM_TRIED_TO_READ: AtomicBool = AtomicBool::new(false);
if !PROGRAM_TRIED_TO_READ.swap(true, Ordering::Relaxed) {
log::warn!("Reading from serial while the debugger is active is unimplemented!");
}
}
pub extern "C" fn read_char(channel: u32) -> i32 {
let channel = Channel::from(channel);
if channel == Channel::USER {
tried_to_read();
return -1;
}
serial::read_byte(channel).map(|n| n as i32).unwrap_or(-1)
}
pub extern "C" fn peek_char(channel: u32) -> i32 {
let channel = Channel::from(channel);
if channel == Channel::USER {
tried_to_read();
return -1;
}
serial::peek_byte(channel).map(|n| n as i32).unwrap_or(-1)
}
}