From f9d4a24a91b9dc3c09c569ec4dd9c47a37ed1cd4 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Fri, 22 Sep 2023 14:50:27 +0200 Subject: [PATCH] Add socket callbacks and memory layout --- CHANGELOG.md | 5 ++ Cargo.toml | 8 +-- README.md | 1 - src/error.rs | 8 +++ src/lib.rs | 84 ++++++++++++++++++++++++------ src/lte_link.rs | 1 + src/socket.rs | 135 +++++++++++++++++++++++++++++++++++++++++++----- 7 files changed, 207 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cedf2d..937c3ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 0.4.1 (2023-09-22) + +- Added a new modem init function where the memory layout can be manually specified +- Sockets now use the built-in nrfxlib callbacks instead of waking at every IPC interrupt + ## 0.4.0 (2023-09-07) - Update nrfxlib-sys to 2.4.2, removing the need for the `EGU1` interrupt diff --git a/Cargo.toml b/Cargo.toml index 3417dea..5606baa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nrf-modem" -version = "0.4.0" +version = "0.4.1" edition = "2021" rust-version = "1.64" license = "MIT OR Apache-2.0" @@ -15,8 +15,8 @@ keywords = ["nRF9160", "LTE", "GPS", "NB-IoT", "embedded"] [dependencies] nrfxlib-sys = "2.4.2" futures = { version = "0.3.24", default-features = false, features = ["async-await"] } -num_enum = { version = "0.6.1", default-features = false } -defmt = { version = "0.3.2", optional = true } +num_enum = { version = "0.7.0", default-features = false } +defmt = { version = "0.3", optional = true } cortex-m = "0.7" linked_list_allocator = { version="0.10.1", default-features=false, features=["use_spin"] } nrf9160-pac = "0.12.2" @@ -24,7 +24,7 @@ arrayvec = { version = "0.7", default-features = false } at-commands = "0.5.2" no-std-net = "0.6.0" critical-section = "1.1" -embassy-sync = "0.2.0" +embassy-sync = "0.3.0" [features] default = [] diff --git a/README.md b/README.md index c546d58..87fa408 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,6 @@ The `EGU1` and `IPC` interrupts must be routed to the modem software. #[allow(non_snake_case)] fn IPC() { nrf_modem::ipc_irq_handler(); - cortex_m::asm::sev(); } let mut cp = unwrap!(cortex_m::Peripherals::take()); diff --git a/src/error.rs b/src/error.rs index 343cd5d..dda7cb5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,9 +7,15 @@ use crate::socket::SocketOptionError; #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] +/// The global error type of this crate pub enum Error { + /// An operation was tried for which the modem needs to be initialized, but the modem is not yet initialized ModemNotInitialized, + /// There can only be one Gnss instance, yet a second was requested GnssAlreadyTaken, + /// An unkown error occured. Check [nrf_errno.h](https://github.com/nrfconnect/sdk-nrfxlib/blob/main/nrf_modem/include/nrf_errno.h) to see what it means. + /// + /// Sometimes the sign is flipped, but ignore that and just look at the number. NrfError(isize), BufferTooSmall(Option), OutOfMemory, @@ -30,6 +36,8 @@ pub enum Error { Disconnected, TooManyLteLinks, InternalRuntimeMutexLocked, + /// The given memory layout falls outside of the acceptable range + BadMemoryLayout, } pub trait ErrorSource { diff --git a/src/lib.rs b/src/lib.rs index 286c048..55b0ec1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ use crate::error::ErrorSource; use core::{ cell::RefCell, + ops::Range, sync::atomic::{AtomicBool, Ordering}, }; use cortex_m::interrupt::Mutex; @@ -69,6 +70,31 @@ pub(crate) static MODEM_RUNTIME_STATE: RuntimeState = RuntimeState::new(); /// Start the NRF Modem library pub async fn init(mode: SystemMode) -> Result<(), Error> { + init_with_custom_layout(mode, Default::default()).await +} + +/// Start the NRF Modem library with a manually specified memory layout +pub async fn init_with_custom_layout( + mode: SystemMode, + memory_layout: MemoryLayout, +) -> Result<(), Error> { + const SHARED_MEMORY_RANGE: Range = 0x2000_0000..0x2002_0000; + + if !SHARED_MEMORY_RANGE.contains(&memory_layout.base_address) { + return Err(Error::BadMemoryLayout); + } + if !SHARED_MEMORY_RANGE.contains( + &(memory_layout.base_address + + nrfxlib_sys::NRF_MODEM_SHMEM_CTRL_SIZE + + memory_layout.tx_area_size + + memory_layout.rx_area_size + + memory_layout.trace_area_size + // Minus one, because this check should be inclusive + - 1), + ) { + return Err(Error::BadMemoryLayout); + } + // The modem is only certified when the DC/DC converter is enabled and it isn't by default unsafe { (*nrf9160_pac::REGULATORS_NS::PTR) @@ -90,27 +116,25 @@ pub async fn init(mode: SystemMode) -> Result<(), Error> { let params = nrfxlib_sys::nrf_modem_init_params { shmem: nrfxlib_sys::nrf_modem_shmem_cfg { ctrl: nrfxlib_sys::nrf_modem_shmem_cfg__bindgen_ty_1 { - // At start of shared memory (see memory.x) - base: 0x2001_0000, - // This is the amount specified in the NCS 1.5.1 release. + base: memory_layout.base_address, size: nrfxlib_sys::NRF_MODEM_SHMEM_CTRL_SIZE, }, tx: nrfxlib_sys::nrf_modem_shmem_cfg__bindgen_ty_2 { - // Follows on from control buffer - base: 0x2001_0000 + nrfxlib_sys::NRF_MODEM_SHMEM_CTRL_SIZE, - // This is the amount specified in the NCS 1.5.1 release. - size: 0x0000_2000, + base: memory_layout.base_address + nrfxlib_sys::NRF_MODEM_SHMEM_CTRL_SIZE, + size: memory_layout.tx_area_size, }, rx: nrfxlib_sys::nrf_modem_shmem_cfg__bindgen_ty_3 { - // Follows on from TX buffer - base: 0x2001_0000 + nrfxlib_sys::NRF_MODEM_SHMEM_CTRL_SIZE + 0x2000, - // This is the amount specified in the NCS 1.5.1 release. - size: 0x0000_2000, + base: memory_layout.base_address + + nrfxlib_sys::NRF_MODEM_SHMEM_CTRL_SIZE + + memory_layout.tx_area_size, + size: memory_layout.rx_area_size, }, - // No trace info trace: nrfxlib_sys::nrf_modem_shmem_cfg__bindgen_ty_4 { - base: 0x2001_0000, - size: 0, + base: memory_layout.base_address + + nrfxlib_sys::NRF_MODEM_SHMEM_CTRL_SIZE + + memory_layout.tx_area_size + + memory_layout.rx_area_size, + size: memory_layout.trace_area_size, }, }, ipc_irq_prio: 0, @@ -159,6 +183,35 @@ pub async fn init(mode: SystemMode) -> Result<(), Error> { Ok(()) } +/// The memory layout used by the modem library. +/// +/// The full range needs to be in the lower 128k of ram. +/// This also contains the fixed [nrfxlib_sys::NRF_MODEM_SHMEM_CTRL_SIZE]. +/// +/// Nordic guide: https://developer.nordicsemi.com/nRF_Connect_SDK/doc/2.4.1/nrfxlib/nrf_modem/doc/architecture.html#shared-memory-configuration +pub struct MemoryLayout { + /// The start of the memory area + pub base_address: u32, + /// The buffer size of the socket send operations, as well as sent AT commands and TLS certs + pub tx_area_size: u32, + /// The buffer size of the socket receive operations, as well as received AT commands, gnss messages and TLS certs + pub rx_area_size: u32, + /// The buffer size of the trace logs + pub trace_area_size: u32, +} + +impl Default for MemoryLayout { + fn default() -> Self { + Self { + base_address: 0x2001_0000, + tx_area_size: 0x2000, + rx_area_size: 0x2000, + // Trace is not implemented yet + trace_area_size: 0, + } + } +} + unsafe extern "C" fn modem_fault_handler(_info: *mut nrfxlib_sys::nrf_modem_fault_info) { #[cfg(feature = "defmt")] defmt::error!( @@ -172,9 +225,8 @@ unsafe extern "C" fn modem_fault_handler(_info: *mut nrfxlib_sys::nrf_modem_faul pub fn ipc_irq_handler() { unsafe { crate::ffi::nrf_ipc_irq_handler(); - nrfxlib_sys::nrf_modem_os_event_notify(0); - crate::socket::wake_sockets(); } + cortex_m::asm::sev(); } /// Identifies which radios in the nRF9160 should be active diff --git a/src/lte_link.rs b/src/lte_link.rs index 7b749d9..4e0cd37 100644 --- a/src/lte_link.rs +++ b/src/lte_link.rs @@ -16,6 +16,7 @@ use core::{mem, ops::ControlFlow, task::Poll}; /// /// The user can prevent this by creating his own instance that the user only drops when all network tasks are done. #[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct LteLink(()); impl LteLink { diff --git a/src/socket.rs b/src/socket.rs index 16355a2..edfcf57 100644 --- a/src/socket.rs +++ b/src/socket.rs @@ -3,7 +3,7 @@ use crate::{ }; use core::{ cell::RefCell, - ops::{Deref, Neg}, + ops::{BitOr, BitOrAssign, Deref, Neg}, sync::atomic::{AtomicU8, Ordering}, task::{Poll, Waker}, }; @@ -19,16 +19,22 @@ const WAKER_INIT: Option<(Waker, i32, SocketDirection)> = None; static SOCKET_WAKERS: Mutex; WAKER_SLOTS]>> = Mutex::new(RefCell::new([WAKER_INIT; WAKER_SLOTS])); -pub(crate) fn wake_sockets() { +fn wake_sockets(socket_fd: i32, socket_dir: SocketDirection) { critical_section::with(|cs| { SOCKET_WAKERS .borrow_ref_mut(cs) .iter_mut() - .for_each(|waker| { - if let Some((waker, _, _)) = waker.take() { - waker.wake() + .filter(|slot| { + if let Some((_, fd, dir)) = slot { + *fd == socket_fd && dir.same_direction(socket_dir) + } else { + false } }) + .for_each(|slot| { + let (waker, _, _) = slot.take().unwrap(); + waker.wake(); + }); }); } @@ -60,15 +66,92 @@ fn register_socket_waker(waker: Waker, socket_fd: i32, socket_dir: SocketDirecti }); } +unsafe extern "C" fn socket_poll_callback(pollfd: *mut nrfxlib_sys::nrf_pollfd) { + let pollfd = *pollfd; + + let mut direction = SocketDirection::Neither; + + if pollfd.revents as u32 & nrfxlib_sys::NRF_POLLIN != 0 { + direction |= SocketDirection::In; + } + + if pollfd.revents as u32 & nrfxlib_sys::NRF_POLLOUT != 0 { + direction |= SocketDirection::Out; + } + + if pollfd.revents as u32 + & (nrfxlib_sys::NRF_POLLERR | nrfxlib_sys::NRF_POLLHUP | nrfxlib_sys::NRF_POLLNVAL) + != 0 + { + direction |= SocketDirection::Either; + } + + #[cfg(feature = "defmt")] + defmt::trace!( + "Socket poll callback. fd: {}, revents: {:X}, direction: {}", + pollfd.fd, + pollfd.revents, + direction + ); + + wake_sockets(pollfd.fd, direction); +} + /// Used as a identifier for wakers when a socket is split into RX/TX halves -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] enum SocketDirection { - RX = 0, - TX = 1, + /// Neither option + Neither, + /// RX + In, + /// TX + Out, + /// RX and/or TX + Either, +} + +impl BitOrAssign for SocketDirection { + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs; + } +} + +impl BitOr for SocketDirection { + type Output = SocketDirection; + + fn bitor(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (SocketDirection::Neither, rhs) => rhs, + (lhs, SocketDirection::Neither) => lhs, + (SocketDirection::In, SocketDirection::In) => SocketDirection::In, + (SocketDirection::Out, SocketDirection::Out) => SocketDirection::Out, + (SocketDirection::In, SocketDirection::Out) => SocketDirection::Either, + (SocketDirection::Out, SocketDirection::In) => SocketDirection::Either, + (SocketDirection::Either, _) => SocketDirection::Either, + (_, SocketDirection::Either) => SocketDirection::Either, + } + } +} + +impl SocketDirection { + fn same_direction(&self, other: Self) -> bool { + match (self, other) { + (SocketDirection::Neither, _) => false, + (_, SocketDirection::Neither) => false, + (SocketDirection::In, SocketDirection::In) => true, + (SocketDirection::Out, SocketDirection::Out) => true, + (SocketDirection::In, SocketDirection::Out) => false, + (SocketDirection::Out, SocketDirection::In) => false, + (_, SocketDirection::Either) => true, + (SocketDirection::Either, _) => true, + } + } } /// Internal socket implementation #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Socket { /// The file descriptor given by the modem lib fd: i32, @@ -130,6 +213,27 @@ impl Socket { } } + // Register the callback of the socket. This will be used to wake up the socket waker + let poll_callback = nrfxlib_sys::nrf_modem_pollcb { + callback: Some(socket_poll_callback), + events: (nrfxlib_sys::NRF_POLLIN | nrfxlib_sys::NRF_POLLOUT) as _, // All events + oneshot: false, + }; + + unsafe { + let result = nrfxlib_sys::nrf_setsockopt( + fd, + nrfxlib_sys::NRF_SOL_SOCKET as _, + nrfxlib_sys::NRF_SO_POLLCB as _, + (&poll_callback as *const nrfxlib_sys::nrf_modem_pollcb).cast(), + core::mem::size_of::() as u32, + ); + + if result == -1 { + return Err(Error::NrfError(get_last_error())); + } + } + Ok(Socket { fd, family, @@ -204,7 +308,7 @@ impl Socket { // Cast the address to something the nrf-modem understands let address = NrfSockAddr::from(address); - register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::TX); + register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Either); // Do the connect call, this is non-blocking due to the socket setup let mut connect_result = unsafe { @@ -278,7 +382,7 @@ impl Socket { // Cast the address to something the nrf-modem understands let address = NrfSockAddr::from(address); - register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::TX); + register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Either); // Do the bind call, this is non-blocking due to the socket setup let mut bind_result = @@ -324,7 +428,7 @@ impl Socket { return Poll::Ready(Err(Error::OperationCancelled)); } - register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::TX); + register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Out); let mut send_result = unsafe { nrfxlib_sys::nrf_send(self.fd, buffer.as_ptr() as *const _, buffer.len(), 0) @@ -367,7 +471,7 @@ impl Socket { return Poll::Ready(Err(Error::OperationCancelled)); } - register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::RX); + register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::In); let mut receive_result = unsafe { nrfxlib_sys::nrf_recv(self.fd, buffer.as_mut_ptr() as *mut _, buffer.len(), 0) @@ -416,7 +520,7 @@ impl Socket { let socket_addr_ptr = socket_addr_store.as_mut_ptr() as *mut nrfxlib_sys::nrf_sockaddr; let mut socket_addr_len = 0u32; - register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::RX); + register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::In); let mut receive_result = unsafe { nrfxlib_sys::nrf_recvfrom( @@ -472,7 +576,7 @@ impl Socket { let addr = NrfSockAddr::from(address); - register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::TX); + register_socket_waker(cx.waker().clone(), self.fd, SocketDirection::Out); let mut send_result = unsafe { nrfxlib_sys::nrf_sendto( @@ -555,6 +659,7 @@ impl Eq for Socket {} #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SocketFamily { Unspecified = nrfxlib_sys::NRF_AF_UNSPEC, Ipv4 = nrfxlib_sys::NRF_AF_INET, @@ -564,6 +669,7 @@ pub enum SocketFamily { #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SocketType { Stream = nrfxlib_sys::NRF_SOCK_STREAM, Datagram = nrfxlib_sys::NRF_SOCK_DGRAM, @@ -572,6 +678,7 @@ pub enum SocketType { #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SocketProtocol { IP = nrfxlib_sys::NRF_IPPROTO_IP, Tcp = nrfxlib_sys::NRF_IPPROTO_TCP,