aboutsummaryrefslogtreecommitdiff
path: root/embassy-net-nrf91
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-06-21 20:19:54 +0200
committerUlf Lilleengen <[email protected]>2024-08-21 12:44:07 +0200
commit160e1c38ceab0ae8876c2bf5f12438edd4d9b018 (patch)
tree9eb219e82e95eb198e03bd9207cd08dd055a21a3 /embassy-net-nrf91
parentaff66b9695a70222b20c19585f04df2ecbabccb1 (diff)
Add embassy-net-nrf91.
Diffstat (limited to 'embassy-net-nrf91')
-rw-r--r--embassy-net-nrf91/Cargo.toml37
-rw-r--r--embassy-net-nrf91/README.md9
-rw-r--r--embassy-net-nrf91/src/fmt.rs274
-rw-r--r--embassy-net-nrf91/src/lib.rs970
4 files changed, 1290 insertions, 0 deletions
diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml
new file mode 100644
index 000000000..2156346a4
--- /dev/null
+++ b/embassy-net-nrf91/Cargo.toml
@@ -0,0 +1,37 @@
1[package]
2name = "embassy-net-nrf91"
3version = "0.1.0"
4edition = "2021"
5description = "embassy-net driver for Nordic nRF91-series cellular modems"
6keywords = ["embedded", "nrf91", "embassy-net", "cellular"]
7categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"]
8license = "MIT OR Apache-2.0"
9repository = "https://github.com/embassy-rs/embassy"
10documentation = "https://docs.embassy.dev/embassy-net-nrf91"
11
12[features]
13defmt = [ "dep:defmt", "heapless/defmt-03" ]
14log = [ "dep:log" ]
15
16[dependencies]
17defmt = { version = "0.3", optional = true }
18log = { version = "0.4.14", optional = true }
19
20nrf9160-pac = { version = "0.12.0" }
21
22embassy-time = { version = "0.3.1", path = "../embassy-time" }
23embassy-sync = { version = "0.6.0", path = "../embassy-sync"}
24embassy-futures = { version = "0.1.0", path = "../embassy-futures"}
25embassy-net-driver-channel = { version = "0.2.0", path = "../embassy-net-driver-channel"}
26
27heapless = "0.8"
28embedded-io = "0.6.1"
29
30[package.metadata.embassy_docs]
31src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-nrf91-v$VERSION/embassy-net-nrf91/src/"
32src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-net-nrf91/src/"
33target = "thumbv7em-none-eabi"
34features = ["defmt"]
35
36[package.metadata.docs.rs]
37features = ["defmt"]
diff --git a/embassy-net-nrf91/README.md b/embassy-net-nrf91/README.md
new file mode 100644
index 000000000..30da71787
--- /dev/null
+++ b/embassy-net-nrf91/README.md
@@ -0,0 +1,9 @@
1# nRF91 `embassy-net` integration
2
3[`embassy-net`](https://crates.io/crates/embassy-net) driver for Nordic nRF91-series cellular modems.
4
5See the [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/nrf9160) directory for usage examples with the nRF9160.
6
7## Interoperability
8
9This crate can run on any executor.
diff --git a/embassy-net-nrf91/src/fmt.rs b/embassy-net-nrf91/src/fmt.rs
new file mode 100644
index 000000000..35b929fde
--- /dev/null
+++ b/embassy-net-nrf91/src/fmt.rs
@@ -0,0 +1,274 @@
1#![macro_use]
2#![allow(unused)]
3
4use core::fmt::{Debug, Display, LowerHex};
5
6#[cfg(all(feature = "defmt", feature = "log"))]
7compile_error!("You may not enable both `defmt` and `log` features.");
8
9#[collapse_debuginfo(yes)]
10macro_rules! assert {
11 ($($x:tt)*) => {
12 {
13 #[cfg(not(feature = "defmt"))]
14 ::core::assert!($($x)*);
15 #[cfg(feature = "defmt")]
16 ::defmt::assert!($($x)*);
17 }
18 };
19}
20
21#[collapse_debuginfo(yes)]
22macro_rules! assert_eq {
23 ($($x:tt)*) => {
24 {
25 #[cfg(not(feature = "defmt"))]
26 ::core::assert_eq!($($x)*);
27 #[cfg(feature = "defmt")]
28 ::defmt::assert_eq!($($x)*);
29 }
30 };
31}
32
33#[collapse_debuginfo(yes)]
34macro_rules! assert_ne {
35 ($($x:tt)*) => {
36 {
37 #[cfg(not(feature = "defmt"))]
38 ::core::assert_ne!($($x)*);
39 #[cfg(feature = "defmt")]
40 ::defmt::assert_ne!($($x)*);
41 }
42 };
43}
44
45#[collapse_debuginfo(yes)]
46macro_rules! debug_assert {
47 ($($x:tt)*) => {
48 {
49 #[cfg(not(feature = "defmt"))]
50 ::core::debug_assert!($($x)*);
51 #[cfg(feature = "defmt")]
52 ::defmt::debug_assert!($($x)*);
53 }
54 };
55}
56
57#[collapse_debuginfo(yes)]
58macro_rules! debug_assert_eq {
59 ($($x:tt)*) => {
60 {
61 #[cfg(not(feature = "defmt"))]
62 ::core::debug_assert_eq!($($x)*);
63 #[cfg(feature = "defmt")]
64 ::defmt::debug_assert_eq!($($x)*);
65 }
66 };
67}
68
69#[collapse_debuginfo(yes)]
70macro_rules! debug_assert_ne {
71 ($($x:tt)*) => {
72 {
73 #[cfg(not(feature = "defmt"))]
74 ::core::debug_assert_ne!($($x)*);
75 #[cfg(feature = "defmt")]
76 ::defmt::debug_assert_ne!($($x)*);
77 }
78 };
79}
80
81#[collapse_debuginfo(yes)]
82macro_rules! todo {
83 ($($x:tt)*) => {
84 {
85 #[cfg(not(feature = "defmt"))]
86 ::core::todo!($($x)*);
87 #[cfg(feature = "defmt")]
88 ::defmt::todo!($($x)*);
89 }
90 };
91}
92
93#[cfg(not(feature = "defmt"))]
94#[collapse_debuginfo(yes)]
95macro_rules! unreachable {
96 ($($x:tt)*) => {
97 ::core::unreachable!($($x)*)
98 };
99}
100
101#[cfg(feature = "defmt")]
102#[collapse_debuginfo(yes)]
103macro_rules! unreachable {
104 ($($x:tt)*) => {
105 ::defmt::unreachable!($($x)*)
106 };
107}
108
109#[collapse_debuginfo(yes)]
110macro_rules! panic {
111 ($($x:tt)*) => {
112 {
113 #[cfg(not(feature = "defmt"))]
114 ::core::panic!($($x)*);
115 #[cfg(feature = "defmt")]
116 ::defmt::panic!($($x)*);
117 }
118 };
119}
120
121#[collapse_debuginfo(yes)]
122macro_rules! trace {
123 ($s:literal $(, $x:expr)* $(,)?) => {
124 {
125 #[cfg(feature = "log")]
126 ::log::trace!($s $(, $x)*);
127 #[cfg(feature = "defmt")]
128 ::defmt::trace!($s $(, $x)*);
129 #[cfg(not(any(feature = "log", feature="defmt")))]
130 let _ = ($( & $x ),*);
131 }
132 };
133}
134
135#[collapse_debuginfo(yes)]
136macro_rules! debug {
137 ($s:literal $(, $x:expr)* $(,)?) => {
138 {
139 #[cfg(feature = "log")]
140 ::log::debug!($s $(, $x)*);
141 #[cfg(feature = "defmt")]
142 ::defmt::debug!($s $(, $x)*);
143 #[cfg(not(any(feature = "log", feature="defmt")))]
144 let _ = ($( & $x ),*);
145 }
146 };
147}
148
149#[collapse_debuginfo(yes)]
150macro_rules! info {
151 ($s:literal $(, $x:expr)* $(,)?) => {
152 {
153 #[cfg(feature = "log")]
154 ::log::info!($s $(, $x)*);
155 #[cfg(feature = "defmt")]
156 ::defmt::info!($s $(, $x)*);
157 #[cfg(not(any(feature = "log", feature="defmt")))]
158 let _ = ($( & $x ),*);
159 }
160 };
161}
162
163#[collapse_debuginfo(yes)]
164macro_rules! warn {
165 ($s:literal $(, $x:expr)* $(,)?) => {
166 {
167 #[cfg(feature = "log")]
168 ::log::warn!($s $(, $x)*);
169 #[cfg(feature = "defmt")]
170 ::defmt::warn!($s $(, $x)*);
171 #[cfg(not(any(feature = "log", feature="defmt")))]
172 let _ = ($( & $x ),*);
173 }
174 };
175}
176
177#[collapse_debuginfo(yes)]
178macro_rules! error {
179 ($s:literal $(, $x:expr)* $(,)?) => {
180 {
181 #[cfg(feature = "log")]
182 ::log::error!($s $(, $x)*);
183 #[cfg(feature = "defmt")]
184 ::defmt::error!($s $(, $x)*);
185 #[cfg(not(any(feature = "log", feature="defmt")))]
186 let _ = ($( & $x ),*);
187 }
188 };
189}
190
191#[cfg(feature = "defmt")]
192#[collapse_debuginfo(yes)]
193macro_rules! unwrap {
194 ($($x:tt)*) => {
195 ::defmt::unwrap!($($x)*)
196 };
197}
198
199#[cfg(not(feature = "defmt"))]
200#[collapse_debuginfo(yes)]
201macro_rules! unwrap {
202 ($arg:expr) => {
203 match $crate::fmt::Try::into_result($arg) {
204 ::core::result::Result::Ok(t) => t,
205 ::core::result::Result::Err(e) => {
206 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
207 }
208 }
209 };
210 ($arg:expr, $($msg:expr),+ $(,)? ) => {
211 match $crate::fmt::Try::into_result($arg) {
212 ::core::result::Result::Ok(t) => t,
213 ::core::result::Result::Err(e) => {
214 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
215 }
216 }
217 }
218}
219
220#[derive(Debug, Copy, Clone, Eq, PartialEq)]
221pub struct NoneError;
222
223pub trait Try {
224 type Ok;
225 type Error;
226 fn into_result(self) -> Result<Self::Ok, Self::Error>;
227}
228
229impl<T> Try for Option<T> {
230 type Ok = T;
231 type Error = NoneError;
232
233 #[inline]
234 fn into_result(self) -> Result<T, NoneError> {
235 self.ok_or(NoneError)
236 }
237}
238
239impl<T, E> Try for Result<T, E> {
240 type Ok = T;
241 type Error = E;
242
243 #[inline]
244 fn into_result(self) -> Self {
245 self
246 }
247}
248
249pub(crate) struct Bytes<'a>(pub &'a [u8]);
250
251impl<'a> Debug for Bytes<'a> {
252 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
253 write!(f, "{:#02x?}", self.0)
254 }
255}
256
257impl<'a> Display for Bytes<'a> {
258 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
259 write!(f, "{:#02x?}", self.0)
260 }
261}
262
263impl<'a> LowerHex for Bytes<'a> {
264 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
265 write!(f, "{:#02x?}", self.0)
266 }
267}
268
269#[cfg(feature = "defmt")]
270impl<'a> defmt::Format for Bytes<'a> {
271 fn format(&self, fmt: defmt::Formatter) {
272 defmt::write!(fmt, "{:02x}", self.0)
273 }
274}
diff --git a/embassy-net-nrf91/src/lib.rs b/embassy-net-nrf91/src/lib.rs
new file mode 100644
index 000000000..62ade6dd3
--- /dev/null
+++ b/embassy-net-nrf91/src/lib.rs
@@ -0,0 +1,970 @@
1#![no_std]
2#![doc = include_str!("../README.md")]
3#![warn(missing_docs)]
4
5// must be first
6mod fmt;
7
8use core::cell::RefCell;
9use core::future::poll_fn;
10use core::marker::PhantomData;
11use core::mem::{self, MaybeUninit};
12use core::ptr::{self, addr_of, addr_of_mut, copy_nonoverlapping};
13use core::slice;
14use core::sync::atomic::{compiler_fence, fence, Ordering};
15use core::task::{Poll, Waker};
16
17use embassy_net_driver_channel as ch;
18use embassy_sync::waitqueue::{AtomicWaker, WakerRegistration};
19use heapless::Vec;
20use nrf9160_pac as pac;
21use pac::NVIC;
22
23const RX_SIZE: usize = 8 * 1024;
24const TRACE_SIZE: usize = 16 * 1024;
25const MTU: usize = 1500;
26
27/// Network driver.
28///
29/// This is the type you have to pass to `embassy-net` when creating the network stack.
30pub type NetDriver<'a> = ch::Device<'a, MTU>;
31
32static WAKER: AtomicWaker = AtomicWaker::new();
33
34/// Call this function on IPC IRQ
35pub fn on_ipc_irq() {
36 let ipc = unsafe { &*pac::IPC_NS::ptr() };
37
38 trace!("irq");
39
40 ipc.inten.write(|w| w);
41 WAKER.wake();
42}
43
44struct Allocator<'a> {
45 start: *mut u8,
46 end: *mut u8,
47 _phantom: PhantomData<&'a mut u8>,
48}
49
50impl<'a> Allocator<'a> {
51 fn alloc_bytes(&mut self, size: usize) -> &'a mut [MaybeUninit<u8>] {
52 // safety: both pointers come from the same allocation.
53 let available_size = unsafe { self.end.offset_from(self.start) } as usize;
54 if size > available_size {
55 panic!("out of memory")
56 }
57
58 // safety: we've checked above this doesn't go out of bounds.
59 let p = self.start;
60 self.start = unsafe { p.add(size) };
61
62 // safety: we've checked the pointer is in-bounds.
63 unsafe { slice::from_raw_parts_mut(p as *mut _, size) }
64 }
65
66 fn alloc<T>(&mut self) -> &'a mut MaybeUninit<T> {
67 let align = mem::align_of::<T>();
68 let size = mem::size_of::<T>();
69
70 let align_size = match (self.start as usize) % align {
71 0 => 0,
72 n => align - n,
73 };
74
75 // safety: both pointers come from the same allocation.
76 let available_size = unsafe { self.end.offset_from(self.start) } as usize;
77 if align_size + size > available_size {
78 panic!("out of memory")
79 }
80
81 // safety: we've checked above this doesn't go out of bounds.
82 let p = unsafe { self.start.add(align_size) };
83 self.start = unsafe { p.add(size) };
84
85 // safety: we've checked the pointer is aligned and in-bounds.
86 unsafe { &mut *(p as *mut _) }
87 }
88}
89
90/// Create a new nRF91 embassy-net driver.
91pub async fn new<'a, TW: embedded_io::Write>(
92 state: &'a mut State,
93 shmem: &'a mut [MaybeUninit<u8>],
94 trace_writer: TW,
95) -> (NetDriver<'a>, Control<'a>, Runner<'a, TW>) {
96 let shmem_len = shmem.len();
97 let shmem_ptr = shmem.as_mut_ptr() as *mut u8;
98
99 const SPU_REGION_SIZE: usize = 8192; // 8kb
100 assert!(shmem_len != 0);
101 assert!(
102 shmem_len % SPU_REGION_SIZE == 0,
103 "shmem length must be a multiple of 8kb"
104 );
105 assert!(
106 (shmem_ptr as usize) % SPU_REGION_SIZE == 0,
107 "shmem length must be a multiple of 8kb"
108 );
109 assert!(
110 (shmem_ptr as usize + shmem_len) < 0x2002_0000,
111 "shmem must be in the lower 128kb of RAM"
112 );
113
114 let spu = unsafe { &*pac::SPU_S::ptr() };
115 debug!("Setting IPC RAM as nonsecure...");
116 let region_start = (shmem_ptr as usize - 0x2000_0000) / SPU_REGION_SIZE;
117 let region_end = region_start + shmem_len / SPU_REGION_SIZE;
118 for i in region_start..region_end {
119 spu.ramregion[i].perm.write(|w| {
120 w.execute().set_bit();
121 w.write().set_bit();
122 w.read().set_bit();
123 w.secattr().clear_bit();
124 w.lock().clear_bit();
125 w
126 })
127 }
128
129 spu.periphid[42].perm.write(|w| w.secattr().non_secure());
130
131 let mut alloc = Allocator {
132 start: shmem_ptr,
133 end: unsafe { shmem_ptr.add(shmem_len) },
134 _phantom: PhantomData,
135 };
136
137 let ipc = unsafe { &*pac::IPC_NS::ptr() };
138 let power = unsafe { &*pac::POWER_S::ptr() };
139
140 let cb: &mut ControlBlock = alloc.alloc().write(unsafe { mem::zeroed() });
141 let rx = alloc.alloc_bytes(RX_SIZE);
142 let trace = alloc.alloc_bytes(TRACE_SIZE);
143
144 cb.version = 0x00010000;
145 cb.rx_base = rx.as_mut_ptr() as _;
146 cb.rx_size = RX_SIZE;
147 cb.control_list_ptr = &mut cb.lists[0];
148 cb.data_list_ptr = &mut cb.lists[1];
149 cb.modem_info_ptr = &mut cb.modem_info;
150 cb.trace_ptr = &mut cb.trace;
151 cb.lists[0].len = LIST_LEN;
152 cb.lists[1].len = LIST_LEN;
153 cb.trace.base = trace.as_mut_ptr() as _;
154 cb.trace.size = TRACE_SIZE;
155
156 ipc.gpmem[0].write(|w| unsafe { w.bits(cb as *mut _ as u32) });
157 ipc.gpmem[1].write(|w| unsafe { w.bits(0) });
158
159 // connect task/event i to channel i
160 for i in 0..8 {
161 ipc.send_cnf[i].write(|w| unsafe { w.bits(1 << i) });
162 ipc.receive_cnf[i].write(|w| unsafe { w.bits(1 << i) });
163 }
164
165 compiler_fence(Ordering::SeqCst);
166
167 // POWER.LTEMODEM.STARTN = 0
168 // The reg is missing in the PAC??
169 let startn = unsafe { (power as *const _ as *mut u32).add(0x610 / 4) };
170 unsafe { startn.write_volatile(0) }
171
172 unsafe { NVIC::unmask(pac::Interrupt::IPC) };
173
174 let state_inner = &*state.inner.write(RefCell::new(StateInner {
175 init: false,
176 init_waker: WakerRegistration::new(),
177 cb,
178 requests: [const { None }; REQ_COUNT],
179 next_req_serial: 0x12345678,
180
181 rx_control_list: ptr::null_mut(),
182 rx_data_list: ptr::null_mut(),
183 rx_seq_no: 0,
184 rx_check: PointerChecker {
185 start: rx.as_mut_ptr() as *mut u8,
186 end: (rx.as_mut_ptr() as *mut u8).wrapping_add(RX_SIZE),
187 },
188
189 tx_seq_no: 0,
190 tx_buf_used: [false; TX_BUF_COUNT],
191
192 trace_chans: Vec::new(),
193 trace_check: PointerChecker {
194 start: trace.as_mut_ptr() as *mut u8,
195 end: (trace.as_mut_ptr() as *mut u8).wrapping_add(TRACE_SIZE),
196 },
197 }));
198
199 let control = Control { state: state_inner };
200
201 let (ch_runner, device) = ch::new(&mut state.ch, ch::driver::HardwareAddress::Ip);
202 let state_ch = ch_runner.state_runner();
203 state_ch.set_link_state(ch::driver::LinkState::Up);
204
205 let runner = Runner {
206 ch: ch_runner,
207 state: state_inner,
208 trace_writer,
209 };
210
211 (device, control, runner)
212}
213
214/// Shared state for the drivver.
215pub struct State {
216 ch: ch::State<MTU, 4, 4>,
217 inner: MaybeUninit<RefCell<StateInner>>,
218}
219
220impl State {
221 /// Create a new State.
222 pub const fn new() -> Self {
223 Self {
224 ch: ch::State::new(),
225 inner: MaybeUninit::uninit(),
226 }
227 }
228}
229
230const TX_BUF_COUNT: usize = 4;
231const TX_BUF_SIZE: usize = 1024;
232
233struct TraceChannelInfo {
234 ptr: *mut TraceChannel,
235 start: *mut u8,
236 end: *mut u8,
237}
238
239const REQ_COUNT: usize = 4;
240
241struct PendingRequest {
242 req_serial: u32,
243 resp_msg: *mut Message,
244 waker: Waker,
245}
246
247struct StateInner {
248 init: bool,
249 init_waker: WakerRegistration,
250
251 cb: *mut ControlBlock,
252 requests: [Option<PendingRequest>; REQ_COUNT],
253 next_req_serial: u32,
254
255 rx_control_list: *mut List,
256 rx_data_list: *mut List,
257 rx_seq_no: u16,
258 rx_check: PointerChecker,
259
260 tx_seq_no: u16,
261 tx_buf_used: [bool; TX_BUF_COUNT],
262
263 trace_chans: Vec<TraceChannelInfo, TRACE_CHANNEL_COUNT>,
264 trace_check: PointerChecker,
265}
266
267impl StateInner {
268 fn poll(&mut self, trace_writer: &mut impl embedded_io::Write, ch: &mut ch::Runner<MTU>) {
269 trace!("poll!");
270 let ipc = unsafe { &*pac::IPC_NS::ptr() };
271
272 if ipc.events_receive[0].read().bits() != 0 {
273 ipc.events_receive[0].reset();
274 trace!("ipc 0");
275 }
276
277 if ipc.events_receive[2].read().bits() != 0 {
278 ipc.events_receive[2].reset();
279 trace!("ipc 2");
280
281 if !self.init {
282 let desc = unsafe { addr_of!((*self.cb).modem_info).read_volatile() };
283 assert_eq!(desc.version, 1);
284
285 self.rx_check.check_mut(desc.control_list_ptr);
286 self.rx_check.check_mut(desc.data_list_ptr);
287
288 self.rx_control_list = desc.control_list_ptr;
289 self.rx_data_list = desc.data_list_ptr;
290 let rx_control_len = unsafe { addr_of!((*self.rx_control_list).len).read_volatile() };
291 let rx_data_len = unsafe { addr_of!((*self.rx_data_list).len).read_volatile() };
292 assert_eq!(rx_control_len, LIST_LEN);
293 assert_eq!(rx_data_len, LIST_LEN);
294 self.init = true;
295
296 debug!("IPC initialized OK!");
297 self.init_waker.wake();
298 }
299 }
300
301 if ipc.events_receive[4].read().bits() != 0 {
302 ipc.events_receive[4].reset();
303 trace!("ipc 4");
304
305 loop {
306 let list = unsafe { &mut *self.rx_control_list };
307 let control_work = self.process(list, true, ch);
308 let list = unsafe { &mut *self.rx_data_list };
309 let data_work = self.process(list, false, ch);
310 if !control_work && !data_work {
311 break;
312 }
313 }
314 }
315
316 if ipc.events_receive[6].read().bits() != 0 {
317 ipc.events_receive[6].reset();
318 trace!("ipc 6");
319 }
320
321 if ipc.events_receive[7].read().bits() != 0 {
322 ipc.events_receive[7].reset();
323 trace!("ipc 7: trace");
324
325 let msg = unsafe { addr_of!((*self.cb).trace.rx_state).read_volatile() };
326 if msg != 0 {
327 trace!("trace msg {}", msg);
328 match msg {
329 0 => unreachable!(),
330 1 => {
331 let ctx = unsafe { addr_of!((*self.cb).trace.rx_ptr).read_volatile() } as *mut TraceContext;
332 debug!("trace init: {:?}", ctx);
333 self.trace_check.check(ctx);
334 let chans = unsafe { addr_of!((*ctx).chans).read_volatile() };
335 for chan_ptr in chans {
336 let chan = self.trace_check.check_read(chan_ptr);
337 self.trace_check.check(chan.start);
338 self.trace_check.check(chan.end);
339 assert!(chan.start < chan.end);
340 self.trace_chans
341 .push(TraceChannelInfo {
342 ptr: chan_ptr,
343 start: chan.start,
344 end: chan.end,
345 })
346 .map_err(|_| ())
347 .unwrap()
348 }
349 }
350 2 => {
351 for chan_info in &self.trace_chans {
352 let read_ptr = unsafe { addr_of!((*chan_info.ptr).read_ptr).read_volatile() };
353 let write_ptr = unsafe { addr_of!((*chan_info.ptr).write_ptr).read_volatile() };
354 assert!(read_ptr >= chan_info.start && read_ptr <= chan_info.end);
355 assert!(write_ptr >= chan_info.start && write_ptr <= chan_info.end);
356 if read_ptr != write_ptr {
357 let id = unsafe { addr_of!((*chan_info.ptr).id).read_volatile() };
358 fence(Ordering::SeqCst); // synchronize volatile accesses with the slice access.
359 if read_ptr < write_ptr {
360 Self::handle_trace(trace_writer, id, unsafe {
361 slice::from_raw_parts(read_ptr, write_ptr.offset_from(read_ptr) as _)
362 });
363 } else {
364 Self::handle_trace(trace_writer, id, unsafe {
365 slice::from_raw_parts(read_ptr, chan_info.end.offset_from(read_ptr) as _)
366 });
367 Self::handle_trace(trace_writer, id, unsafe {
368 slice::from_raw_parts(
369 chan_info.start,
370 write_ptr.offset_from(chan_info.start) as _,
371 )
372 });
373 }
374 fence(Ordering::SeqCst); // synchronize volatile accesses with the slice access.
375 unsafe { addr_of_mut!((*chan_info.ptr).read_ptr).write_volatile(write_ptr) };
376 }
377 }
378 }
379 _ => warn!("unknown trace msg {}", msg),
380 }
381 unsafe { addr_of_mut!((*self.cb).trace.rx_state).write_volatile(0) };
382 }
383 }
384
385 ipc.intenset.write(|w| {
386 w.receive0().set_bit();
387 w.receive2().set_bit();
388 w.receive4().set_bit();
389 w.receive6().set_bit();
390 w.receive7().set_bit();
391 w
392 });
393 }
394
395 fn handle_trace(writer: &mut impl embedded_io::Write, id: u8, data: &[u8]) {
396 trace!("trace: {} {}", id, data.len());
397 let mut header = [0u8; 5];
398 header[0] = 0xEF;
399 header[1] = 0xBE;
400 header[2..4].copy_from_slice(&(data.len() as u16).to_le_bytes());
401 header[4] = id;
402 writer.write_all(&header).unwrap();
403 writer.write_all(data).unwrap();
404 }
405
406 fn process(&mut self, list: *mut List, is_control: bool, ch: &mut ch::Runner<MTU>) -> bool {
407 let mut did_work = false;
408 for i in 0..LIST_LEN {
409 let item_ptr = unsafe { addr_of_mut!((*list).items[i]) };
410 let preamble = unsafe { addr_of!((*item_ptr).state).read_volatile() };
411 if preamble & 0xFF == 0x01 && preamble >> 16 == self.rx_seq_no as u32 {
412 let msg_ptr = unsafe { addr_of!((*item_ptr).message).read_volatile() };
413 let msg = self.rx_check.check_read(msg_ptr);
414
415 debug!("rx seq {} msg: {:?}", preamble >> 16, msg);
416
417 if is_control {
418 self.handle_control(&msg);
419 } else {
420 self.handle_data(&msg, ch);
421 }
422
423 unsafe { addr_of_mut!((*item_ptr).state).write_volatile(0x03) };
424 self.rx_seq_no = self.rx_seq_no.wrapping_add(1);
425
426 did_work = true;
427 }
428 }
429 did_work
430 }
431
432 fn find_free_message(&mut self, ch: usize) -> Option<usize> {
433 for i in 0..LIST_LEN {
434 let preamble = unsafe { addr_of!((*self.cb).lists[ch].items[i].state).read_volatile() };
435 if matches!(preamble & 0xFF, 0 | 3) {
436 trace!("using tx msg idx {}", i);
437 return Some(i);
438 }
439 }
440 return None;
441 }
442
443 fn find_free_tx_buf(&mut self) -> Option<usize> {
444 for i in 0..TX_BUF_COUNT {
445 if !self.tx_buf_used[i] {
446 trace!("using tx buf idx {}", i);
447 return Some(i);
448 }
449 }
450 return None;
451 }
452
453 fn send_message(&mut self, msg: &mut Message, data: &[u8]) {
454 if data.is_empty() {
455 msg.data = ptr::null_mut();
456 msg.data_len = 0;
457 } else {
458 assert!(data.len() <= TX_BUF_SIZE);
459 let buf_idx = self.find_free_tx_buf().unwrap(); // TODO handle out of bufs
460 let buf = unsafe { addr_of_mut!((*self.cb).tx_bufs[buf_idx]) } as *mut u8;
461 unsafe { copy_nonoverlapping(data.as_ptr(), buf, data.len()) }
462 msg.data = buf;
463 msg.data_len = data.len();
464 self.tx_buf_used[buf_idx] = true;
465
466 fence(Ordering::SeqCst); // synchronize copy_nonoverlapping (non-volatile) with volatile writes below.
467 }
468
469 // TODO free data buf if send_message_raw fails.
470 self.send_message_raw(msg);
471 }
472
473 fn send_message_raw(&mut self, msg: &Message) {
474 let (ch, ipc_ch) = match msg.channel {
475 1 => (0, 1), // control
476 2 => (1, 3), // data
477 _ => unreachable!(),
478 };
479
480 // allocate a msg.
481 let idx = self.find_free_message(ch).unwrap(); // TODO handle list full
482
483 debug!("tx seq {} msg: {:?}", self.tx_seq_no, msg);
484
485 let msg_slot = unsafe { addr_of_mut!((*self.cb).msgs[ch][idx]) };
486 unsafe { msg_slot.write_volatile(*msg) }
487 let list_item = unsafe { addr_of_mut!((*self.cb).lists[ch].items[idx]) };
488 unsafe { addr_of_mut!((*list_item).message).write_volatile(msg_slot) }
489 unsafe { addr_of_mut!((*list_item).state).write_volatile((self.tx_seq_no as u32) << 16 | 0x01) }
490 self.tx_seq_no = self.tx_seq_no.wrapping_add(1);
491
492 let ipc = unsafe { &*pac::IPC_NS::ptr() };
493 ipc.tasks_send[ipc_ch].write(|w| unsafe { w.bits(1) });
494 }
495
496 fn handle_control(&mut self, msg: &Message) {
497 match msg.id >> 16 {
498 1 => debug!("control msg: modem ready"),
499 2 => self.handle_control_free(msg.data),
500 _ => warn!("unknown control message id {:08x}", msg.id),
501 }
502 }
503
504 fn handle_control_free(&mut self, ptr: *mut u8) {
505 let base = unsafe { addr_of!((*self.cb).tx_bufs) } as usize;
506 let ptr = ptr as usize;
507
508 if ptr < base {
509 warn!("control free bad pointer {:08x}", ptr);
510 return;
511 }
512
513 let diff = ptr - base;
514 let idx = diff / TX_BUF_SIZE;
515
516 if idx >= TX_BUF_COUNT || idx * TX_BUF_SIZE != diff {
517 warn!("control free bad pointer {:08x}", ptr);
518 return;
519 }
520
521 trace!("control free pointer {:08x} idx {}", ptr, idx);
522 if !self.tx_buf_used[idx] {
523 warn!(
524 "control free pointer {:08x} idx {}: buffer was already free??",
525 ptr, idx
526 );
527 }
528 self.tx_buf_used[idx] = false;
529 }
530
531 fn handle_data(&mut self, msg: &Message, ch: &mut ch::Runner<MTU>) {
532 if !msg.data.is_null() {
533 self.rx_check.check_length(msg.data, msg.data_len);
534 }
535
536 let freed = match msg.id & 0xFFFF {
537 // AT
538 3 => {
539 match msg.id >> 16 {
540 // AT request ack
541 2 => false,
542 // AT response
543 3 => self.handle_resp(msg),
544 // AT notification
545 4 => false,
546 x => {
547 warn!("received unknown AT kind {}", x);
548 false
549 }
550 }
551 }
552 // IP
553 4 => {
554 match msg.id >> 28 {
555 // IP response
556 8 => self.handle_resp(msg),
557 // IP notification
558 9 => match (msg.id >> 16) & 0xFFF {
559 // IP receive notification
560 1 => {
561 if let Some(buf) = ch.try_rx_buf() {
562 let mut len = msg.data_len;
563 if len > buf.len() {
564 warn!("truncating rx'd packet from {} to {} bytes", len, buf.len());
565 len = buf.len();
566 }
567 fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.
568 unsafe { ptr::copy_nonoverlapping(msg.data, buf.as_mut_ptr(), len) }
569 fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.
570 ch.rx_done(len);
571 }
572 false
573 }
574 _ => false,
575 },
576 x => {
577 warn!("received unknown IP kind {}", x);
578 false
579 }
580 }
581 }
582 x => {
583 warn!("received unknown kind {}", x);
584 false
585 }
586 };
587
588 if !freed {
589 self.send_free(msg);
590 }
591 }
592
593 fn handle_resp(&mut self, msg: &Message) -> bool {
594 let req_serial = u32::from_le_bytes(msg.param[0..4].try_into().unwrap());
595 if req_serial == 0 {
596 return false;
597 }
598
599 for optr in &mut self.requests {
600 if let Some(r) = optr {
601 if r.req_serial == req_serial {
602 let r = optr.take().unwrap();
603 unsafe { r.resp_msg.write(*msg) }
604 r.waker.wake();
605 *optr = None;
606 return true;
607 }
608 }
609 }
610
611 warn!(
612 "resp with id {} serial {} doesn't match any pending req",
613 msg.id, req_serial
614 );
615 false
616 }
617
618 fn send_free(&mut self, msg: &Message) {
619 if msg.data.is_null() {
620 return;
621 }
622
623 let mut free_msg: Message = unsafe { mem::zeroed() };
624 free_msg.channel = 1; // control
625 free_msg.id = 0x20001; // free
626 free_msg.data = msg.data;
627 free_msg.data_len = msg.data_len;
628
629 self.send_message_raw(&free_msg);
630 }
631}
632
633struct PointerChecker {
634 start: *mut u8,
635 end: *mut u8,
636}
637
638impl PointerChecker {
639 // check the pointer is in bounds in the arena, panic otherwise.
640 fn check_length(&self, ptr: *const u8, len: usize) {
641 assert!(ptr as usize >= self.start as usize);
642 let end_ptr = (ptr as usize).checked_add(len).unwrap();
643 assert!(end_ptr <= self.end as usize);
644 }
645
646 // check the pointer is in bounds in the arena, panic otherwise.
647 fn check<T>(&self, ptr: *const T) {
648 assert!(ptr.is_aligned());
649 self.check_length(ptr as *const u8, mem::size_of::<T>());
650 }
651
652 // check the pointer is in bounds in the arena, panic otherwise.
653 fn check_read<T>(&self, ptr: *const T) -> T {
654 self.check(ptr);
655 unsafe { ptr.read_volatile() }
656 }
657
658 // check the pointer is in bounds in the arena, panic otherwise.
659 fn check_mut<T>(&self, ptr: *mut T) {
660 self.check(ptr as *const T)
661 }
662}
663
664/// Control handle for the driver.
665///
666/// You can use this object to control the modem at runtime, such as running AT commands.
667pub struct Control<'a> {
668 state: &'a RefCell<StateInner>,
669}
670
671impl<'a> Control<'a> {
672 /// Wait for modem IPC to be initialized.
673 pub async fn wait_init(&self) {
674 poll_fn(|cx| {
675 let mut state = self.state.borrow_mut();
676 if state.init {
677 return Poll::Ready(());
678 }
679 state.init_waker.register(cx.waker());
680 Poll::Pending
681 })
682 .await
683 }
684
685 async fn request(&self, msg: &mut Message, req_data: &[u8], resp_data: &mut [u8]) -> usize {
686 // get waker
687 let waker = poll_fn(|cx| Poll::Ready(cx.waker().clone())).await;
688
689 // Send request
690 let mut state = self.state.borrow_mut();
691 let mut req_serial = state.next_req_serial;
692 if msg.id & 0xFFFF == 3 {
693 // AT response seems to keep only the lower 8 bits. Others do keep the full 32 bits..??
694 req_serial &= 0xFF;
695 }
696
697 // increment next_req_serial, skip zero because we use it as an "ignore" value.
698 // We have to skip when the *lowest byte* is zero because AT responses.
699 state.next_req_serial = state.next_req_serial.wrapping_add(1);
700 if state.next_req_serial & 0xFF == 0 {
701 state.next_req_serial = state.next_req_serial.wrapping_add(1);
702 }
703
704 msg.param[0..4].copy_from_slice(&req_serial.to_le_bytes());
705 state.send_message(msg, req_data);
706
707 // Setup the pending request state.
708 let (req_slot_idx, req_slot) = state
709 .requests
710 .iter_mut()
711 .enumerate()
712 .find(|(_, x)| x.is_none())
713 .unwrap();
714 msg.id = 0; // zero out id, so when it becomes nonzero we know the req is done.
715 let msg_ptr: *mut Message = msg;
716 *req_slot = Some(PendingRequest {
717 req_serial,
718 resp_msg: msg_ptr,
719 waker,
720 });
721
722 drop(state); // don't borrow state across awaits.
723
724 // On cancel, unregister the request slot.
725 let _drop = OnDrop::new(|| {
726 // Remove request slot.
727 let mut state = self.state.borrow_mut();
728 let slot = &mut state.requests[req_slot_idx];
729 if let Some(s) = slot {
730 if s.req_serial == req_serial {
731 *slot = None;
732 }
733 }
734
735 // If cancelation raced with actually receiving the response,
736 // we own the data, so we have to free it.
737 let msg = unsafe { &mut *msg_ptr };
738 if msg.id != 0 {
739 state.send_free(msg);
740 }
741 });
742 // Wait for response.
743 poll_fn(|_| {
744 // we have to use the raw pointer and not the original reference `msg`
745 // because that'd invalidate the raw ptr that's still stored in `req_slot`.
746 if unsafe { (*msg_ptr).id } != 0 {
747 Poll::Ready(())
748 } else {
749 Poll::Pending
750 }
751 })
752 .await;
753 _drop.defuse();
754
755 if msg.data.is_null() {
756 // no response data.
757 return 0;
758 }
759
760 // Copy response data out, if any.
761 // Pointer was validated in StateInner::handle_data().
762 let mut len = msg.data_len;
763 if len > resp_data.len() {
764 warn!("truncating response data from {} to {}", len, resp_data.len());
765 len = resp_data.len();
766 }
767 fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.
768 unsafe { ptr::copy_nonoverlapping(msg.data, resp_data.as_mut_ptr(), len) }
769 fence(Ordering::SeqCst); // synchronize volatile accesses with the nonvolatile copy_nonoverlapping.
770 self.state.borrow_mut().send_free(msg);
771 len
772 }
773
774 /// Run an AT command.
775 ///
776 /// The response is written in `resp` and its length returned.
777 pub async fn at_command(&self, req: &[u8], resp: &mut [u8]) -> usize {
778 let mut msg: Message = unsafe { mem::zeroed() };
779 msg.channel = 2; // data
780 msg.id = 0x0001_0003; // AT command
781 msg.param_len = 4;
782
783 self.request(&mut msg, req, resp).await
784 }
785
786 /// Open the raw socket used for sending/receiving IP packets.
787 ///
788 /// This must be done after `AT+CFUN=1` (?)
789 pub async fn open_raw_socket(&self) {
790 let mut msg: Message = unsafe { mem::zeroed() };
791 msg.channel = 2; // data
792 msg.id = 0x7001_0004; // open socket
793 msg.param_len = 20;
794
795 let param = [
796 0xFF, 0xFF, 0xFF, 0xFF, // req_serial
797 0xFF, 0xFF, 0xFF, 0xFF, // ???
798 0x05, 0x00, 0x00, 0x00, // family
799 0x03, 0x00, 0x00, 0x00, // type
800 0x00, 0x00, 0x00, 0x00, // protocol
801 ];
802 msg.param[..param.len()].copy_from_slice(&param);
803
804 self.request(&mut msg, &[], &mut []).await;
805
806 assert_eq!(msg.id, 0x80010004);
807 assert!(msg.param_len >= 12);
808 let status = u32::from_le_bytes(msg.param[8..12].try_into().unwrap());
809 assert_eq!(status, 0);
810 assert_eq!(msg.param_len, 16);
811 let fd = u32::from_le_bytes(msg.param[12..16].try_into().unwrap());
812 debug!("got FD: {}", fd);
813 }
814}
815
816/// Background runner for the driver.
817pub struct Runner<'a, TW: embedded_io::Write> {
818 ch: ch::Runner<'a, MTU>,
819 state: &'a RefCell<StateInner>,
820 trace_writer: TW,
821}
822
823impl<'a, TW: embedded_io::Write> Runner<'a, TW> {
824 /// Run the driver operation in the background.
825 ///
826 /// You must run this in a background task, concurrently with all network operations.
827 pub async fn run(mut self) -> ! {
828 poll_fn(|cx| {
829 WAKER.register(cx.waker());
830
831 let mut state = self.state.borrow_mut();
832 state.poll(&mut self.trace_writer, &mut self.ch);
833
834 if let Poll::Ready(buf) = self.ch.poll_tx_buf(cx) {
835 let fd = 128u32; // TODO unhardcode
836 let mut msg: Message = unsafe { mem::zeroed() };
837 msg.channel = 2; // data
838 msg.id = 0x7006_0004; // IP send
839 msg.param_len = 12;
840 msg.param[4..8].copy_from_slice(&fd.to_le_bytes());
841 state.send_message(&mut msg, buf);
842 self.ch.tx_done();
843 }
844
845 Poll::Pending
846 })
847 .await
848 }
849}
850
851const LIST_LEN: usize = 16;
852
853#[repr(C)]
854struct ControlBlock {
855 version: u32,
856 rx_base: *mut u8,
857 rx_size: usize,
858 control_list_ptr: *mut List,
859 data_list_ptr: *mut List,
860 modem_info_ptr: *mut ModemInfo,
861 trace_ptr: *mut Trace,
862 unk: u32,
863
864 modem_info: ModemInfo,
865 trace: Trace,
866
867 // 0 = control, 1 = data
868 lists: [List; 2],
869 msgs: [[Message; LIST_LEN]; 2],
870
871 tx_bufs: [[u8; TX_BUF_SIZE]; TX_BUF_COUNT],
872}
873
874#[repr(C)]
875struct ModemInfo {
876 version: u32,
877 control_list_ptr: *mut List,
878 data_list_ptr: *mut List,
879 padding: [u32; 5],
880}
881
882#[repr(C)]
883struct Trace {
884 size: usize,
885 base: *mut u8,
886 tx_state: u32,
887 tx_ptr: *mut u8,
888 rx_state: u32,
889 rx_ptr: *mut u8,
890 unk1: u32,
891 unk2: u32,
892}
893
894const TRACE_CHANNEL_COUNT: usize = 3;
895
896#[repr(C)]
897#[cfg_attr(feature = "defmt", derive(defmt::Format))]
898struct TraceContext {
899 unk1: u32,
900 unk2: u32,
901 len: u32,
902 chans: [*mut TraceChannel; TRACE_CHANNEL_COUNT],
903}
904
905#[repr(C)]
906#[cfg_attr(feature = "defmt", derive(defmt::Format))]
907struct TraceChannel {
908 id: u8,
909 unk1: u8,
910 unk2: u8,
911 unk3: u8,
912 write_ptr: *mut u8,
913 read_ptr: *mut u8,
914 start: *mut u8,
915 end: *mut u8,
916}
917
918#[repr(C)]
919struct List {
920 len: usize,
921 items: [ListItem; LIST_LEN],
922}
923
924#[repr(C)]
925struct ListItem {
926 /// top 16 bits: seqno
927 /// bottom 8 bits:
928 /// 0x01: sent
929 /// 0x02: held
930 /// 0x03: freed
931 state: u32,
932 message: *mut Message,
933}
934
935#[repr(C)]
936#[derive(defmt::Format, Clone, Copy)]
937struct Message {
938 id: u32,
939
940 /// 1 = control, 2 = data
941 channel: u8,
942 unk1: u8,
943 unk2: u8,
944 unk3: u8,
945
946 data: *mut u8,
947 data_len: usize,
948 param_len: usize,
949 param: [u8; 44],
950}
951
952struct OnDrop<F: FnOnce()> {
953 f: MaybeUninit<F>,
954}
955
956impl<F: FnOnce()> OnDrop<F> {
957 pub fn new(f: F) -> Self {
958 Self { f: MaybeUninit::new(f) }
959 }
960
961 pub fn defuse(self) {
962 mem::forget(self)
963 }
964}
965
966impl<F: FnOnce()> Drop for OnDrop<F> {
967 fn drop(&mut self) {
968 unsafe { self.f.as_ptr().read()() }
969 }
970}