2024-12-30 12:13:13 +01:00

1070 lines
33 KiB
Rust

#![no_std]
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
#![deny(unused_must_use)]
// must be first
mod fmt;
pub mod context;
use core::cell::RefCell;
use core::future::{poll_fn, Future};
use core::marker::PhantomData;
use core::mem::{self, MaybeUninit};
use core::ptr::{self, addr_of, addr_of_mut, copy_nonoverlapping};
use core::slice;
use core::sync::atomic::{compiler_fence, fence, Ordering};
use core::task::{Poll, Waker};
use cortex_m::peripheral::NVIC;
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::pipe;
use embassy_sync::waitqueue::{AtomicWaker, WakerRegistration};
use heapless::Vec;
use {embassy_net_driver_channel as ch, nrf_pac as pac};
const RX_SIZE: usize = 8 * 1024;
const TRACE_SIZE: usize = 16 * 1024;
const TRACE_BUF: usize = 1024;
const MTU: usize = 1500;
/// Network driver.
///
/// This is the type you have to pass to `embassy-net` when creating the network stack.
pub type NetDriver<'a> = ch::Device<'a, MTU>;
static WAKER: AtomicWaker = AtomicWaker::new();
/// Call this function on IPC IRQ
pub fn on_ipc_irq() {
trace!("irq");
pac::IPC_NS.inten().write(|_| ());
WAKER.wake();
}
struct Allocator<'a> {
start: *mut u8,
end: *mut u8,
_phantom: PhantomData<&'a mut u8>,
}
impl<'a> Allocator<'a> {
fn alloc_bytes(&mut self, size: usize) -> &'a mut [MaybeUninit<u8>] {
// safety: both pointers come from the same allocation.
let available_size = unsafe { self.end.offset_from(self.start) } as usize;
if size > available_size {
panic!("out of memory")
}
// safety: we've checked above this doesn't go out of bounds.
let p = self.start;
self.start = unsafe { p.add(size) };
// safety: we've checked the pointer is in-bounds.
unsafe { slice::from_raw_parts_mut(p as *mut _, size) }
}
fn alloc<T>(&mut self) -> &'a mut MaybeUninit<T> {
let align = mem::align_of::<T>();
let size = mem::size_of::<T>();
let align_size = match (self.start as usize) % align {
0 => 0,
n => align - n,
};
// safety: both pointers come from the same allocation.
let available_size = unsafe { self.end.offset_from(self.start) } as usize;
if align_size + size > available_size {
panic!("out of memory")
}
// safety: we've checked above this doesn't go out of bounds.
let p = unsafe { self.start.add(align_size) };
self.start = unsafe { p.add(size) };
// safety: we've checked the pointer is aligned and in-bounds.
unsafe { &mut *(p as *mut _) }
}
}
/// Create a new nRF91 embassy-net driver.
pub async fn new<'a>(
state: &'a mut State,
shmem: &'a mut [MaybeUninit<u8>],
) -> (NetDriver<'a>, Control<'a>, Runner<'a>) {
let (n, c, r, _) = new_internal(state, shmem, None).await;
(n, c, r)
}
/// Create a new nRF91 embassy-net driver with trace.
pub async fn new_with_trace<'a>(
state: &'a mut State,
shmem: &'a mut [MaybeUninit<u8>],
trace_buffer: &'a mut TraceBuffer,
) -> (NetDriver<'a>, Control<'a>, Runner<'a>, TraceReader<'a>) {
let (n, c, r, t) = new_internal(state, shmem, Some(trace_buffer)).await;
(n, c, r, t.unwrap())
}
/// Create a new nRF91 embassy-net driver.
async fn new_internal<'a>(
state: &'a mut State,
shmem: &'a mut [MaybeUninit<u8>],
trace_buffer: Option<&'a mut TraceBuffer>,
) -> (NetDriver<'a>, Control<'a>, Runner<'a>, Option<TraceReader<'a>>) {
let shmem_len = shmem.len();
let shmem_ptr = shmem.as_mut_ptr() as *mut u8;
const SPU_REGION_SIZE: usize = 8192; // 8kb
assert!(shmem_len != 0);
assert!(
shmem_len % SPU_REGION_SIZE == 0,
"shmem length must be a multiple of 8kb"
);
assert!(
(shmem_ptr as usize) % SPU_REGION_SIZE == 0,
"shmem length must be a multiple of 8kb"
);
assert!(
(shmem_ptr as usize + shmem_len) < 0x2002_0000,
"shmem must be in the lower 128kb of RAM"
);
let spu = pac::SPU_S;
debug!("Setting IPC RAM as nonsecure...");
let region_start = (shmem_ptr as usize - 0x2000_0000) / SPU_REGION_SIZE;
let region_end = region_start + shmem_len / SPU_REGION_SIZE;
for i in region_start..region_end {
spu.ramregion(i).perm().write(|w| {
w.set_execute(true);
w.set_write(true);
w.set_read(true);
w.set_secattr(false);
w.set_lock(false);
})
}
spu.periphid(42).perm().write(|w| w.set_secattr(false));
let mut alloc = Allocator {
start: shmem_ptr,
end: unsafe { shmem_ptr.add(shmem_len) },
_phantom: PhantomData,
};
let ipc = pac::IPC_NS;
let power = pac::POWER_S;
let cb: &mut ControlBlock = alloc.alloc().write(unsafe { mem::zeroed() });
let rx = alloc.alloc_bytes(RX_SIZE);
let trace = alloc.alloc_bytes(TRACE_SIZE);
cb.version = 0x00010000;
cb.rx_base = rx.as_mut_ptr() as _;
cb.rx_size = RX_SIZE;
cb.control_list_ptr = &mut cb.lists[0];
cb.data_list_ptr = &mut cb.lists[1];
cb.modem_info_ptr = &mut cb.modem_info;
cb.trace_ptr = &mut cb.trace;
cb.lists[0].len = LIST_LEN;
cb.lists[1].len = LIST_LEN;
cb.trace.base = trace.as_mut_ptr() as _;
cb.trace.size = TRACE_SIZE;
ipc.gpmem(0).write_value(cb as *mut _ as u32);
ipc.gpmem(1).write_value(0);
// connect task/event i to channel i
for i in 0..8 {
ipc.send_cnf(i).write(|w| w.0 = 1 << i);
ipc.receive_cnf(i).write(|w| w.0 = 1 << i);
}
compiler_fence(Ordering::SeqCst);
// POWER.LTEMODEM.STARTN = 0
// The reg is missing in the PAC??
let startn = unsafe { (power.as_ptr() as *mut u32).add(0x610 / 4) };
unsafe { startn.write_volatile(0) }
unsafe { NVIC::unmask(pac::Interrupt::IPC) };
let state_inner = &*state.inner.write(RefCell::new(StateInner {
init: false,
init_waker: WakerRegistration::new(),
cb,
requests: [const { None }; REQ_COUNT],
next_req_serial: 0x12345678,
net_fd: None,
rx_control_list: ptr::null_mut(),
rx_data_list: ptr::null_mut(),
rx_seq_no: 0,
rx_check: PointerChecker {
start: rx.as_mut_ptr() as *mut u8,
end: (rx.as_mut_ptr() as *mut u8).wrapping_add(RX_SIZE),
},
tx_seq_no: 0,
tx_buf_used: [false; TX_BUF_COUNT],
tx_waker: WakerRegistration::new(),
trace_chans: Vec::new(),
trace_check: PointerChecker {
start: trace.as_mut_ptr() as *mut u8,
end: (trace.as_mut_ptr() as *mut u8).wrapping_add(TRACE_SIZE),
},
}));
let control = Control { state: state_inner };
let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ip);
let state_ch = ch_runner.state_runner();
state_ch.set_link_state(ch::driver::LinkState::Up);
let (trace_reader, trace_writer) = if let Some(trace) = trace_buffer {
let (r, w) = trace.trace.split();
(Some(r), Some(w))
} else {
(None, None)
};
let runner = Runner {
ch: ch_runner,
state: state_inner,
trace_writer,
};
(device, control, runner, trace_reader)
}
/// State holding modem traces.
pub struct TraceBuffer {
trace: pipe::Pipe<NoopRawMutex, TRACE_BUF>,
}
/// Represents writer half of the trace buffer.
pub type TraceWriter<'a> = pipe::Writer<'a, NoopRawMutex, TRACE_BUF>;
/// Represents the reader half of the trace buffer.
pub type TraceReader<'a> = pipe::Reader<'a, NoopRawMutex, TRACE_BUF>;
impl TraceBuffer {
/// Create a new TraceBuffer.
pub const fn new() -> Self {
Self {
trace: pipe::Pipe::new(),
}
}
}
/// Shared state for the driver.
pub struct State {
ch: ch::State<MTU, 4, 4>,
inner: MaybeUninit<RefCell<StateInner>>,
}
impl State {
/// Create a new State.
pub const fn new() -> Self {
Self {
ch: ch::State::new(),
inner: MaybeUninit::uninit(),
}
}
}
const TX_BUF_COUNT: usize = 4;
const TX_BUF_SIZE: usize = 1500;
struct TraceChannelInfo {
ptr: *mut TraceChannel,
start: *mut u8,
end: *mut u8,
}
const REQ_COUNT: usize = 4;
struct PendingRequest {
req_serial: u32,
resp_msg: *mut Message,
waker: Waker,
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct NoFreeBufs;
struct StateInner {
init: bool,
init_waker: WakerRegistration,
cb: *mut ControlBlock,
requests: [Option<PendingRequest>; REQ_COUNT],
next_req_serial: u32,
net_fd: Option<u32>,
rx_control_list: *mut List,
rx_data_list: *mut List,
rx_seq_no: u16,
rx_check: PointerChecker,
tx_seq_no: u16,
tx_buf_used: [bool; TX_BUF_COUNT],
tx_waker: WakerRegistration,
trace_chans: Vec<TraceChannelInfo, TRACE_CHANNEL_COUNT>,
trace_check: PointerChecker,
}
impl StateInner {
fn poll(&mut self, trace_writer: &mut Option<TraceWriter<'_>>, ch: &mut ch::Runner<MTU>) {
trace!("poll!");
let ipc = pac::IPC_NS;
if ipc.events_receive(0).read() != 0 {
ipc.events_receive(0).write_value(0);
trace!("ipc 0");
}
if ipc.events_receive(2).read() != 0 {
ipc.events_receive(2).write_value(0);
trace!("ipc 2");
if !self.init {
let desc = unsafe { addr_of!((*self.cb).modem_info).read_volatile() };
assert_eq!(desc.version, 1);
self.rx_check.check_mut(desc.control_list_ptr);
self.rx_check.check_mut(desc.data_list_ptr);
self.rx_control_list = desc.control_list_ptr;
self.rx_data_list = desc.data_list_ptr;
let rx_control_len = unsafe { addr_of!((*self.rx_control_list).len).read_volatile() };
let rx_data_len = unsafe { addr_of!((*self.rx_data_list).len).read_volatile() };
assert_eq!(rx_control_len, LIST_LEN);
assert_eq!(rx_data_len, LIST_LEN);
self.init = true;
debug!("IPC initialized OK!");
self.init_waker.wake();
}
}
if ipc.events_receive(4).read() != 0 {
ipc.events_receive(4).write_value(0);
trace!("ipc 4");
loop {
let list = unsafe { &mut *self.rx_control_list };
let control_work = self.process(list, true, ch);
let list = unsafe { &mut *self.rx_data_list };
let data_work = self.process(list, false, ch);
if !control_work && !data_work {
break;
}
}
}
if ipc.events_receive(6).read() != 0 {
ipc.events_receive(6).write_value(0);
trace!("ipc 6");
}
if ipc.events_receive(7).read() != 0 {
ipc.events_receive(7).write_value(0);
trace!("ipc 7: trace");
let msg = unsafe { addr_of!((*self.cb).trace.rx_state).read_volatile() };
if msg != 0 {
trace!("trace msg {}", msg);
match msg {
0 => unreachable!(),
1 => {
let ctx = unsafe { addr_of!((*self.cb).trace.rx_ptr).read_volatile() } as *mut TraceContext;
debug!("trace init: {:?}", ctx);
self.trace_check.check(ctx);
let chans = unsafe { addr_of!((*ctx).chans).read_volatile() };
for chan_ptr in chans {
let chan = self.trace_check.check_read(chan_ptr);
self.trace_check.check(chan.start);
self.trace_check.check(chan.end);
assert!(chan.start < chan.end);
self.trace_chans
.push(TraceChannelInfo {
ptr: chan_ptr,
start: chan.start,
end: chan.end,
})
.map_err(|_| ())
.unwrap()
}
}
2 => {
for chan_info in &self.trace_chans {
let read_ptr = unsafe { addr_of!((*chan_info.ptr).read_ptr).read_volatile() };
let write_ptr = unsafe { addr_of!((*chan_info.ptr).write_ptr).read_volatile() };
assert!(read_ptr >= chan_info.start && read_ptr <= chan_info.end);
assert!(write_ptr >= chan_info.start && write_ptr <= chan_info.end);
if read_ptr != write_ptr {
let id = unsafe { addr_of!((*chan_info.ptr).id).read_volatile() };
fence(Ordering::SeqCst); // synchronize volatile accesses with the slice access.
if read_ptr < write_ptr {
Self::handle_trace(trace_writer, id, unsafe {
slice::from_raw_parts(read_ptr, write_ptr.offset_from(read_ptr) as _)
});
} else {
Self::handle_trace(trace_writer, id, unsafe {
slice::from_raw_parts(read_ptr, chan_info.end.offset_from(read_ptr) as _)
});
Self::handle_trace(trace_writer, id, unsafe {
slice::from_raw_parts(
chan_info.start,
write_ptr.offset_from(chan_info.start) as _,
)
});
}
fence(Ordering::SeqCst); // synchronize volatile accesses with the slice access.
unsafe { addr_of_mut!((*chan_info.ptr).read_ptr).write_volatile(write_ptr) };
}
}
}
_ => warn!("unknown trace msg {}", msg),
}
unsafe { addr_of_mut!((*self.cb).trace.rx_state).write_volatile(0) };
}
}
ipc.intenset().write(|w| {
w.set_receive0(true);
w.set_receive2(true);
w.set_receive4(true);
w.set_receive6(true);
w.set_receive7(true);
});
}
fn handle_trace(writer: &mut Option<TraceWriter<'_>>, id: u8, data: &[u8]) {
if let Some(writer) = writer {
trace!("trace: {} {}", id, data.len());
let mut header = [0u8; 5];
header[0] = 0xEF;
header[1] = 0xBE;
header[2..4].copy_from_slice(&(data.len() as u16).to_le_bytes());
header[4] = id;
writer.try_write(&header).ok();
writer.try_write(data).ok();
}
}
fn process(&mut self, list: *mut List, is_control: bool, ch: &mut ch::Runner<MTU>) -> bool {
let mut did_work = false;
for i in 0..LIST_LEN {
let item_ptr = unsafe { addr_of_mut!((*list).items[i]) };
let preamble = unsafe { addr_of!((*item_ptr).state).read_volatile() };
if preamble & 0xFF == 0x01 && preamble >> 16 == self.rx_seq_no as u32 {
let msg_ptr = unsafe { addr_of!((*item_ptr).message).read_volatile() };
let msg = self.rx_check.check_read(msg_ptr);
debug!("rx seq {} msg: {:?}", preamble >> 16, msg);
if is_control {
self.handle_control(&msg);
} else {
self.handle_data(&msg, ch);
}
unsafe { addr_of_mut!((*item_ptr).state).write_volatile(0x03) };
self.rx_seq_no = self.rx_seq_no.wrapping_add(1);
did_work = true;
}
}
did_work
}
fn find_free_message(&mut self, ch: usize) -> Option<usize> {
for i in 0..LIST_LEN {
let preamble = unsafe { addr_of!((*self.cb).lists[ch].items[i].state).read_volatile() };
if matches!(preamble & 0xFF, 0 | 3) {
trace!("using tx msg idx {}", i);
return Some(i);
}
}
return None;
}
fn find_free_tx_buf(&mut self) -> Option<usize> {
for i in 0..TX_BUF_COUNT {
if !self.tx_buf_used[i] {
trace!("using tx buf idx {}", i);
return Some(i);
}
}
return None;
}
fn send_message(&mut self, msg: &mut Message, data: &[u8]) -> Result<(), NoFreeBufs> {
if data.is_empty() {
msg.data = ptr::null_mut();
msg.data_len = 0;
self.send_message_raw(msg)
} else {
assert!(data.len() <= TX_BUF_SIZE);
let buf_idx = self.find_free_tx_buf().ok_or(NoFreeBufs)?;
let buf = unsafe { addr_of_mut!((*self.cb).tx_bufs[buf_idx]) } as *mut u8;
unsafe { copy_nonoverlapping(data.as_ptr(), buf, data.len()) }
msg.data = buf;
msg.data_len = data.len();
self.tx_buf_used[buf_idx] = true;
fence(Ordering::SeqCst); // synchronize copy_nonoverlapping (non-volatile) with volatile writes below.
if let Err(e) = self.send_message_raw(msg) {
msg.data = ptr::null_mut();
msg.data_len = 0;
self.tx_buf_used[buf_idx] = false;
self.tx_waker.wake();
Err(e)
} else {
Ok(())
}
}
}
fn send_message_raw(&mut self, msg: &Message) -> Result<(), NoFreeBufs> {
let (ch, ipc_ch) = match msg.channel {
1 => (0, 1), // control
2 => (1, 3), // data
_ => unreachable!(),
};
// allocate a msg.
let idx = self.find_free_message(ch).ok_or(NoFreeBufs)?;
debug!("tx seq {} msg: {:?}", self.tx_seq_no, msg);
let msg_slot = unsafe { addr_of_mut!((*self.cb).msgs[ch][idx]) };
unsafe { msg_slot.write_volatile(*msg) }
let list_item = unsafe { addr_of_mut!((*self.cb).lists[ch].items[idx]) };
unsafe { addr_of_mut!((*list_item).message).write_volatile(msg_slot) }
unsafe { addr_of_mut!((*list_item).state).write_volatile((self.tx_seq_no as u32) << 16 | 0x01) }
self.tx_seq_no = self.tx_seq_no.wrapping_add(1);
let ipc = pac::IPC_NS;
ipc.tasks_send(ipc_ch).write_value(1);
Ok(())
}
fn handle_control(&mut self, msg: &Message) {
match msg.id >> 16 {
1 => debug!("control msg: modem ready"),
2 => self.handle_control_free(msg.data),
_ => warn!("unknown control message id {:08x}", msg.id),
}
}
fn handle_control_free(&mut self, ptr: *mut u8) {
let base = unsafe { addr_of!((*self.cb).tx_bufs) } as usize;
let ptr = ptr as usize;
if ptr < base {
warn!("control free bad pointer {:08x}", ptr);
return;
}
let diff = ptr - base;
let idx = diff / TX_BUF_SIZE;
if idx >= TX_BUF_COUNT || idx * TX_BUF_SIZE != diff {
warn!("control free bad pointer {:08x}", ptr);
return;
}
trace!("control free pointer {:08x} idx {}", ptr, idx);
if !self.tx_buf_used[idx] {
warn!(
"control free pointer {:08x} idx {}: buffer was already free??",
ptr, idx
);
}
self.tx_buf_used[idx] = false;
self.tx_waker.wake();
}
fn handle_data(&mut self, msg: &Message, ch: &mut ch::Runner<MTU>) {
if !msg.data.is_null() {
self.rx_check.check_length(msg.data, msg.data_len);
}
let freed = match msg.id & 0xFFFF {
// AT
3 => {
match msg.id >> 16 {
// AT request ack
2 => false,
// AT response
3 => self.handle_resp(msg),
// AT notification
4 => false,
x => {
warn!("received unknown AT kind {}", x);
false
}
}
}
// IP
4 => {
match msg.id >> 28 {
// IP response
8 => self.handle_resp(msg),
// IP notification
9 => match (msg.id >> 16) & 0xFFF {
// IP receive notification
1 => {
if let Some(buf) = ch.try_rx_buf() {
let mut len = msg.data_len;
if len > buf.len() {
warn!("truncating rx'd packet from {} to {} bytes", len, buf.len());
len = buf.len();
}
fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.
unsafe { ptr::copy_nonoverlapping(msg.data, buf.as_mut_ptr(), len) }
fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.
ch.rx_done(len);
}
false
}
_ => false,
},
x => {
warn!("received unknown IP kind {}", x);
false
}
}
}
x => {
warn!("received unknown kind {}", x);
false
}
};
if !freed {
self.send_free(msg);
}
}
fn handle_resp(&mut self, msg: &Message) -> bool {
let req_serial = u32::from_le_bytes(msg.param[0..4].try_into().unwrap());
if req_serial == 0 {
return false;
}
for optr in &mut self.requests {
if let Some(r) = optr {
if r.req_serial == req_serial {
let r = optr.take().unwrap();
unsafe { r.resp_msg.write(*msg) }
r.waker.wake();
*optr = None;
return true;
}
}
}
warn!(
"resp with id {} serial {} doesn't match any pending req",
msg.id, req_serial
);
false
}
fn send_free(&mut self, msg: &Message) {
if msg.data.is_null() {
return;
}
let mut free_msg: Message = unsafe { mem::zeroed() };
free_msg.channel = 1; // control
free_msg.id = 0x20001; // free
free_msg.data = msg.data;
free_msg.data_len = msg.data_len;
unwrap!(self.send_message_raw(&free_msg));
}
}
struct PointerChecker {
start: *mut u8,
end: *mut u8,
}
impl PointerChecker {
// check the pointer is in bounds in the arena, panic otherwise.
fn check_length(&self, ptr: *const u8, len: usize) {
assert!(ptr as usize >= self.start as usize);
let end_ptr = (ptr as usize).checked_add(len).unwrap();
assert!(end_ptr <= self.end as usize);
}
// check the pointer is in bounds in the arena, panic otherwise.
fn check<T>(&self, ptr: *const T) {
assert!(ptr.is_aligned());
self.check_length(ptr as *const u8, mem::size_of::<T>());
}
// check the pointer is in bounds in the arena, panic otherwise.
fn check_read<T>(&self, ptr: *const T) -> T {
self.check(ptr);
unsafe { ptr.read_volatile() }
}
// check the pointer is in bounds in the arena, panic otherwise.
fn check_mut<T>(&self, ptr: *mut T) {
self.check(ptr as *const T)
}
}
/// Control handle for the driver.
///
/// You can use this object to control the modem at runtime, such as running AT commands.
pub struct Control<'a> {
state: &'a RefCell<StateInner>,
}
impl<'a> Control<'a> {
/// Wait for modem IPC to be initialized.
pub fn wait_init(&self) -> impl Future<Output = ()> + '_ {
poll_fn(|cx| {
let mut state = self.state.borrow_mut();
if state.init {
return Poll::Ready(());
}
state.init_waker.register(cx.waker());
Poll::Pending
})
}
async fn request(&self, msg: &mut Message, req_data: &[u8], resp_data: &mut [u8]) -> usize {
// get waker
let waker = poll_fn(|cx| Poll::Ready(cx.waker().clone())).await;
// Send request
let mut state = self.state.borrow_mut();
let mut req_serial = state.next_req_serial;
if msg.id & 0xFFFF == 3 {
// AT response seems to keep only the lower 8 bits. Others do keep the full 32 bits..??
req_serial &= 0xFF;
}
// increment next_req_serial, skip zero because we use it as an "ignore" value.
// We have to skip when the *lowest byte* is zero because AT responses.
state.next_req_serial = state.next_req_serial.wrapping_add(1);
if state.next_req_serial & 0xFF == 0 {
state.next_req_serial = state.next_req_serial.wrapping_add(1);
}
drop(state); // don't borrow state across awaits.
msg.param[0..4].copy_from_slice(&req_serial.to_le_bytes());
poll_fn(|cx| {
let mut state = self.state.borrow_mut();
state.tx_waker.register(cx.waker());
match state.send_message(msg, req_data) {
Ok(_) => Poll::Ready(()),
Err(NoFreeBufs) => Poll::Pending,
}
})
.await;
// Setup the pending request state.
let mut state = self.state.borrow_mut();
let (req_slot_idx, req_slot) = state
.requests
.iter_mut()
.enumerate()
.find(|(_, x)| x.is_none())
.unwrap();
msg.id = 0; // zero out id, so when it becomes nonzero we know the req is done.
let msg_ptr: *mut Message = msg;
*req_slot = Some(PendingRequest {
req_serial,
resp_msg: msg_ptr,
waker,
});
drop(state); // don't borrow state across awaits.
// On cancel, unregister the request slot.
let _drop = OnDrop::new(|| {
// Remove request slot.
let mut state = self.state.borrow_mut();
let slot = &mut state.requests[req_slot_idx];
if let Some(s) = slot {
if s.req_serial == req_serial {
*slot = None;
}
}
// If cancelation raced with actually receiving the response,
// we own the data, so we have to free it.
let msg = unsafe { &mut *msg_ptr };
if msg.id != 0 {
state.send_free(msg);
}
});
// Wait for response.
poll_fn(|_| {
// we have to use the raw pointer and not the original reference `msg`
// because that'd invalidate the raw ptr that's still stored in `req_slot`.
if unsafe { (*msg_ptr).id } != 0 {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
_drop.defuse();
if msg.data.is_null() {
// no response data.
return 0;
}
// Copy response data out, if any.
// Pointer was validated in StateInner::handle_data().
let mut len = msg.data_len;
if len > resp_data.len() {
warn!("truncating response data from {} to {}", len, resp_data.len());
len = resp_data.len();
}
fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.
unsafe { ptr::copy_nonoverlapping(msg.data, resp_data.as_mut_ptr(), len) }
fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.
self.state.borrow_mut().send_free(msg);
len
}
/// Run an AT command.
///
/// The response is written in `resp` and its length returned.
pub async fn at_command(&self, req: &[u8], resp: &mut [u8]) -> usize {
let mut msg: Message = unsafe { mem::zeroed() };
msg.channel = 2; // data
msg.id = 0x0001_0003; // AT command
msg.param_len = 4;
self.request(&mut msg, req, resp).await
}
/// Open the raw socket used for sending/receiving IP packets.
///
/// This must be done after `AT+CFUN=1` (?)
async fn open_raw_socket(&self) -> u32 {
let mut msg: Message = unsafe { mem::zeroed() };
msg.channel = 2; // data
msg.id = 0x7001_0004; // open socket
msg.param_len = 20;
let param = [
0xFF, 0xFF, 0xFF, 0xFF, // req_serial
0xFF, 0xFF, 0xFF, 0xFF, // ???
0x05, 0x00, 0x00, 0x00, // family
0x03, 0x00, 0x00, 0x00, // type
0x00, 0x00, 0x00, 0x00, // protocol
];
msg.param[..param.len()].copy_from_slice(&param);
self.request(&mut msg, &[], &mut []).await;
assert_eq!(msg.id, 0x80010004);
assert!(msg.param_len >= 12);
let status = u32::from_le_bytes(msg.param[8..12].try_into().unwrap());
assert_eq!(status, 0);
assert_eq!(msg.param_len, 16);
let fd = u32::from_le_bytes(msg.param[12..16].try_into().unwrap());
self.state.borrow_mut().net_fd.replace(fd);
trace!("got FD: {}", fd);
fd
}
async fn close_raw_socket(&self, fd: u32) {
let mut msg: Message = unsafe { mem::zeroed() };
msg.channel = 2; // data
msg.id = 0x7009_0004; // close socket
msg.param_len = 8;
msg.param[4..8].copy_from_slice(&fd.to_le_bytes());
self.request(&mut msg, &[], &mut []).await;
assert_eq!(msg.id, 0x80090004);
assert!(msg.param_len >= 12);
let status = u32::from_le_bytes(msg.param[8..12].try_into().unwrap());
assert_eq!(status, 0);
}
}
/// Background runner for the driver.
pub struct Runner<'a> {
ch: ch::Runner<'a, MTU>,
state: &'a RefCell<StateInner>,
trace_writer: Option<TraceWriter<'a>>,
}
impl<'a> Runner<'a> {
/// Run the driver operation in the background.
///
/// You must run this in a background task, concurrently with all network operations.
pub async fn run(mut self) -> ! {
poll_fn(|cx| {
WAKER.register(cx.waker());
let mut state = self.state.borrow_mut();
state.poll(&mut self.trace_writer, &mut self.ch);
if let Poll::Ready(buf) = self.ch.poll_tx_buf(cx) {
if let Some(fd) = state.net_fd {
let mut msg: Message = unsafe { mem::zeroed() };
msg.channel = 2; // data
msg.id = 0x7006_0004; // IP send
msg.param_len = 12;
msg.param[4..8].copy_from_slice(&fd.to_le_bytes());
if let Err(e) = state.send_message(&mut msg, buf) {
warn!("tx failed: {:?}", e);
}
self.ch.tx_done();
}
}
Poll::Pending
})
.await
}
}
const LIST_LEN: usize = 16;
#[repr(C)]
struct ControlBlock {
version: u32,
rx_base: *mut u8,
rx_size: usize,
control_list_ptr: *mut List,
data_list_ptr: *mut List,
modem_info_ptr: *mut ModemInfo,
trace_ptr: *mut Trace,
unk: u32,
modem_info: ModemInfo,
trace: Trace,
// 0 = control, 1 = data
lists: [List; 2],
msgs: [[Message; LIST_LEN]; 2],
tx_bufs: [[u8; TX_BUF_SIZE]; TX_BUF_COUNT],
}
#[repr(C)]
struct ModemInfo {
version: u32,
control_list_ptr: *mut List,
data_list_ptr: *mut List,
padding: [u32; 5],
}
#[repr(C)]
struct Trace {
size: usize,
base: *mut u8,
tx_state: u32,
tx_ptr: *mut u8,
rx_state: u32,
rx_ptr: *mut u8,
unk1: u32,
unk2: u32,
}
const TRACE_CHANNEL_COUNT: usize = 3;
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct TraceContext {
unk1: u32,
unk2: u32,
len: u32,
chans: [*mut TraceChannel; TRACE_CHANNEL_COUNT],
}
#[repr(C)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct TraceChannel {
id: u8,
unk1: u8,
unk2: u8,
unk3: u8,
write_ptr: *mut u8,
read_ptr: *mut u8,
start: *mut u8,
end: *mut u8,
}
#[repr(C)]
struct List {
len: usize,
items: [ListItem; LIST_LEN],
}
#[repr(C)]
struct ListItem {
/// top 16 bits: seqno
/// bottom 8 bits:
/// 0x01: sent
/// 0x02: held
/// 0x03: freed
state: u32,
message: *mut Message,
}
#[repr(C)]
#[derive(defmt::Format, Clone, Copy)]
struct Message {
id: u32,
/// 1 = control, 2 = data
channel: u8,
unk1: u8,
unk2: u8,
unk3: u8,
data: *mut u8,
data_len: usize,
param_len: usize,
param: [u8; 44],
}
struct OnDrop<F: FnOnce()> {
f: MaybeUninit<F>,
}
impl<F: FnOnce()> OnDrop<F> {
pub fn new(f: F) -> Self {
Self { f: MaybeUninit::new(f) }
}
pub fn defuse(self) {
mem::forget(self)
}
}
impl<F: FnOnce()> Drop for OnDrop<F> {
fn drop(&mut self) {
unsafe { self.f.as_ptr().read()() }
}
}