diff options
Diffstat (limited to 'embassy-net-nrf91/src/lib.rs')
| -rw-r--r-- | embassy-net-nrf91/src/lib.rs | 970 |
1 files changed, 970 insertions, 0 deletions
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 | ||
| 6 | mod fmt; | ||
| 7 | |||
| 8 | use core::cell::RefCell; | ||
| 9 | use core::future::poll_fn; | ||
| 10 | use core::marker::PhantomData; | ||
| 11 | use core::mem::{self, MaybeUninit}; | ||
| 12 | use core::ptr::{self, addr_of, addr_of_mut, copy_nonoverlapping}; | ||
| 13 | use core::slice; | ||
| 14 | use core::sync::atomic::{compiler_fence, fence, Ordering}; | ||
| 15 | use core::task::{Poll, Waker}; | ||
| 16 | |||
| 17 | use embassy_net_driver_channel as ch; | ||
| 18 | use embassy_sync::waitqueue::{AtomicWaker, WakerRegistration}; | ||
| 19 | use heapless::Vec; | ||
| 20 | use nrf9160_pac as pac; | ||
| 21 | use pac::NVIC; | ||
| 22 | |||
| 23 | const RX_SIZE: usize = 8 * 1024; | ||
| 24 | const TRACE_SIZE: usize = 16 * 1024; | ||
| 25 | const 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. | ||
| 30 | pub type NetDriver<'a> = ch::Device<'a, MTU>; | ||
| 31 | |||
| 32 | static WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 33 | |||
| 34 | /// Call this function on IPC IRQ | ||
| 35 | pub 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 | |||
| 44 | struct Allocator<'a> { | ||
| 45 | start: *mut u8, | ||
| 46 | end: *mut u8, | ||
| 47 | _phantom: PhantomData<&'a mut u8>, | ||
| 48 | } | ||
| 49 | |||
| 50 | impl<'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. | ||
| 91 | pub 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. | ||
| 215 | pub struct State { | ||
| 216 | ch: ch::State<MTU, 4, 4>, | ||
| 217 | inner: MaybeUninit<RefCell<StateInner>>, | ||
| 218 | } | ||
| 219 | |||
| 220 | impl 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 | |||
| 230 | const TX_BUF_COUNT: usize = 4; | ||
| 231 | const TX_BUF_SIZE: usize = 1024; | ||
| 232 | |||
| 233 | struct TraceChannelInfo { | ||
| 234 | ptr: *mut TraceChannel, | ||
| 235 | start: *mut u8, | ||
| 236 | end: *mut u8, | ||
| 237 | } | ||
| 238 | |||
| 239 | const REQ_COUNT: usize = 4; | ||
| 240 | |||
| 241 | struct PendingRequest { | ||
| 242 | req_serial: u32, | ||
| 243 | resp_msg: *mut Message, | ||
| 244 | waker: Waker, | ||
| 245 | } | ||
| 246 | |||
| 247 | struct 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 | |||
| 267 | impl 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 | |||
| 633 | struct PointerChecker { | ||
| 634 | start: *mut u8, | ||
| 635 | end: *mut u8, | ||
| 636 | } | ||
| 637 | |||
| 638 | impl 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. | ||
| 667 | pub struct Control<'a> { | ||
| 668 | state: &'a RefCell<StateInner>, | ||
| 669 | } | ||
| 670 | |||
| 671 | impl<'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(¶m); | ||
| 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. | ||
| 817 | pub struct Runner<'a, TW: embedded_io::Write> { | ||
| 818 | ch: ch::Runner<'a, MTU>, | ||
| 819 | state: &'a RefCell<StateInner>, | ||
| 820 | trace_writer: TW, | ||
| 821 | } | ||
| 822 | |||
| 823 | impl<'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 | |||
| 851 | const LIST_LEN: usize = 16; | ||
| 852 | |||
| 853 | #[repr(C)] | ||
| 854 | struct 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)] | ||
| 875 | struct 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)] | ||
| 883 | struct 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 | |||
| 894 | const TRACE_CHANNEL_COUNT: usize = 3; | ||
| 895 | |||
| 896 | #[repr(C)] | ||
| 897 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 898 | struct 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))] | ||
| 907 | struct 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)] | ||
| 919 | struct List { | ||
| 920 | len: usize, | ||
| 921 | items: [ListItem; LIST_LEN], | ||
| 922 | } | ||
| 923 | |||
| 924 | #[repr(C)] | ||
| 925 | struct 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)] | ||
| 937 | struct 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 | |||
| 952 | struct OnDrop<F: FnOnce()> { | ||
| 953 | f: MaybeUninit<F>, | ||
| 954 | } | ||
| 955 | |||
| 956 | impl<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 | |||
| 966 | impl<F: FnOnce()> Drop for OnDrop<F> { | ||
| 967 | fn drop(&mut self) { | ||
| 968 | unsafe { self.f.as_ptr().read()() } | ||
| 969 | } | ||
| 970 | } | ||
