Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nrf9151 preview #23

Merged
merged 11 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ jobs:
targets: thumbv8m.main-none-eabihf

- run: cargo fmt -- --check
- run: cargo clippy --target thumbv8m.main-none-eabihf
- run: cargo clippy --features nrf9160 --target thumbv8m.main-none-eabihf
- run: cargo clippy --features nrf9151 --target thumbv8m.main-none-eabihf
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## 0.5.0 (2024-08-09)

- *Breaking:* Updated to nrfxlib-sys 2.7.1
- *Breaking:* Added support for nrf9151. You must now select a feature flag for your chip

## 0.4.3 (2024-06-18)

- Added extra check that prevents initializing the modem multiple times
Expand Down
15 changes: 11 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
[package]
name = "nrf-modem"
version = "0.4.3"
version = "0.5.0"
edition = "2021"
rust-version = "1.64"
license = "MIT OR Apache-2.0"
description = "Async modem api for the nRF9160"
homepage = "https://github.com/diondokter/nrf-modem"
repository = "https://github.com/diondokter/nrf-modem"
readme = "README.md"
keywords = ["nRF9160", "LTE", "GPS", "NB-IoT", "embedded"]
keywords = ["nRF9160", "nRF9151", "LTE", "GPS", "NB-IoT", "embedded"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
nrfxlib-sys = "2.4.2"
nrfxlib-sys = "=2.7.1"

futures = { version = "0.3.24", default-features = false, features = ["async-await"] }
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"
arrayvec = { version = "0.7", default-features = false }
nrf9160-pac = { version = "0.12.2", optional = true }
nrf9120-pac = { version = "0.12.2", optional = true }
at-commands = "0.5.2"
no-std-net = "0.6.0"
critical-section = "1.1"
Expand All @@ -30,3 +32,8 @@ grounded = "0.2.0"
[features]
default = []
defmt = ["dep:defmt", "at-commands/defmt"]

nrf9160 = ["nrfxlib-sys/nrf9160", "dep:nrf9160-pac"]
nrf9151 = ["nrf9120"]
nrf9161 = ["nrf9120"]
nrf9120 = ["nrfxlib-sys/nrf9120", "dep:nrf9120-pac"]
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,29 @@

[![crates.io](https://img.shields.io/crates/v/nrf-modem.svg)](https://crates.io/crates/nrf-modem) [![Documentation](https://docs.rs/nrf-modem/badge.svg)](https://docs.rs/nrf-modem)

This is a library that provides a high-level async API for the nRF9160 modem.
This is a library that provides a high-level async API for the modem on the Nordic nRF91* series chips (System-in-Packages). Supported chips are the following:

* nRF9160
* nRF9151
* nRF9161

It can be used with any executor.

## Using

In your own program or library, you can depend on this crate in the usual fashion:

```toml
[dependencies]
nrf-modem = "0.5.0"
```
The `nrf9160` feature is enabled by default for legacy support. To use this library on another supported chip (e.g., `nrf9151`), select that feature and disable default feautes.

```toml
[dependencies]
nrf-modem = { version = "0.5.0", default-featues = false, features = ["nrf9151"] }
```

## Errors and recovery

Dropping LteLink and Gnss (which also include all sockets and GnssStream) *can* lead to the modem staying active.
Expand Down Expand Up @@ -36,7 +55,7 @@ But it's easy to miss something, so this is a 'best effort' guarantee only.
### Nonsecure

Nordic has made it so that the modem can only be used when in the nonsecure context.
Make sure you are in that context by using e.g. the SPM.
Make sure you are in that context by using e.g. the SPM or TF-M.

### Interrupts

Expand Down
4 changes: 2 additions & 2 deletions src/dns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,13 @@ pub async fn get_host_by_name_with_cancellation(
while !result_iter.is_null() && found_ip.is_none() {
let address = (*result_iter).ai_addr;

if (*address).sa_family == nrfxlib_sys::NRF_AF_INET as i32 {
if (*address).sa_family == nrfxlib_sys::NRF_AF_INET as u16 {
let dns_addr: &nrfxlib_sys::nrf_sockaddr_in =
&*(address as *const nrfxlib_sys::nrf_sockaddr_in);

let socket_addr: SocketAddr = NrfSockAddr::from(*dns_addr).into();
found_ip = Some(socket_addr.ip());
} else if (*address).sa_family == nrfxlib_sys::NRF_AF_INET6 as i32 {
} else if (*address).sa_family == nrfxlib_sys::NRF_AF_INET6 as u16 {
let dns_addr: &nrfxlib_sys::nrf_sockaddr_in6 =
&*(address as *const nrfxlib_sys::nrf_sockaddr_in6);

Expand Down
186 changes: 177 additions & 9 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@

#![allow(clippy::missing_safety_doc)]

use core::mem::MaybeUninit;
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};

#[cfg(feature = "nrf9160")]
use nrf9160_pac as pac;

#[cfg(feature = "nrf9120")]
use nrf9120_pac as pac;

/// Number of IPC configurations in `NrfxIpcConfig`
const IPC_CONF_NUM: usize = 8;

Expand Down Expand Up @@ -103,7 +110,7 @@ pub fn get_last_error() -> isize {
#[no_mangle]
pub extern "C" fn nrf_modem_os_busywait(usec: i32) {
if usec > 0 {
// NRF9160 runs at 64 MHz, so this is close enough
// The nRF91* Arm Cortex-M33 runs at 64 MHz, so this is close enough
cortex_m::asm::delay((usec as u32) * 64);
}
}
Expand All @@ -115,7 +122,7 @@ pub extern "C" fn nrf_modem_os_busywait(usec: i32) {
/// **Parameters**
/// - context – (in) A unique identifier assigned by the library to identify the context.
/// - timeout – (inout) Timeout in millisec or -1 for infinite timeout.
/// Contains the timeout value as input and the remainig time to sleep as output.
/// Contains the timeout value as input and the remainig time to sleep as output.
///
/// **Return values**
/// - 0 – The thread is woken before the timeout expired.
Expand Down Expand Up @@ -206,7 +213,7 @@ pub unsafe extern "C" fn nrf_modem_os_shm_tx_free(ptr: *mut u8) {
pub unsafe extern "C" fn nrfx_ipc_config_load(p_config: *const NrfxIpcConfig) {
let config: &NrfxIpcConfig = &*p_config;

let ipc = &(*nrf9160_pac::IPC_NS::ptr());
let ipc = &(*pac::IPC_NS::ptr());

for (i, value) in config.send_task_config.iter().enumerate() {
ipc.send_cnf[i].write(|w| w.bits(*value));
Expand Down Expand Up @@ -235,7 +242,7 @@ pub extern "C" fn nrfx_ipc_init(
p_context: usize,
) -> NrfxErr {
use cortex_m::interrupt::InterruptNumber;
let irq = nrf9160_pac::Interrupt::IPC;
let irq = pac::Interrupt::IPC;
let irq_num = usize::from(irq.number());
unsafe {
cortex_m::peripheral::NVIC::unmask(irq);
Expand All @@ -250,7 +257,7 @@ pub extern "C" fn nrfx_ipc_init(
/// Function for uninitializing the IPC module.
#[no_mangle]
pub extern "C" fn nrfx_ipc_uninit() {
let ipc = unsafe { &(*nrf9160_pac::IPC_NS::ptr()) };
let ipc = unsafe { &(*pac::IPC_NS::ptr()) };

for i in 0..IPC_CONF_NUM {
ipc.send_cnf[i].reset();
Expand All @@ -265,14 +272,14 @@ pub extern "C" fn nrfx_ipc_uninit() {

#[no_mangle]
pub extern "C" fn nrfx_ipc_receive_event_enable(event_index: u8) {
let ipc = unsafe { &(*nrf9160_pac::IPC_NS::ptr()) };
let ipc = unsafe { &(*pac::IPC_NS::ptr()) };
ipc.inten
.modify(|r, w| unsafe { w.bits(r.bits() | 1 << event_index) })
}

#[no_mangle]
pub extern "C" fn nrfx_ipc_receive_event_disable(event_index: u8) {
let ipc = unsafe { &(*nrf9160_pac::IPC_NS::ptr()) };
let ipc = unsafe { &(*pac::IPC_NS::ptr()) };
ipc.inten
.modify(|r, w| unsafe { w.bits(r.bits() & !(1 << event_index)) })
}
Expand Down Expand Up @@ -337,7 +344,7 @@ unsafe fn generic_free(ptr: *mut u8, heap: &crate::WrappedHeap) {
// https://github.com/NordicSemiconductor/nrfx/blob/98d6f433313a3d8dcf08dce25e744617b45aa913/drivers/src/nrfx_ipc.c#L146-L163
pub unsafe fn nrf_ipc_irq_handler() {
// Get the information about events that fired this interrupt
let events_map = (*nrf9160_pac::IPC_NS::ptr()).intpend.read().bits();
let events_map = (*pac::IPC_NS::ptr()).intpend.read().bits();

// Fetch interrupt handler and context to use during event resolution
let handler_addr = IPC_HANDLER.load(core::sync::atomic::Ordering::SeqCst);
Expand All @@ -356,7 +363,7 @@ pub unsafe fn nrf_ipc_irq_handler() {
while bitmask != 0 {
let event_idx = bitmask.trailing_zeros();
bitmask &= !(1 << event_idx);
(*nrf9160_pac::IPC_NS::ptr()).events_receive[event_idx as usize].write(|w| w.bits(0));
(*pac::IPC_NS::ptr()).events_receive[event_idx as usize].write(|w| w.bits(0));

// Execute interrupt handler to provide information about events to app
if let Some(handler) = handler {
Expand Down Expand Up @@ -517,3 +524,164 @@ struct Semaphore {
pub extern "C" fn nrf_modem_os_is_in_isr() -> bool {
cortex_m::peripheral::SCB::vect_active() != cortex_m::peripheral::scb::VectActive::ThreadMode
}

// A basic mutex lock implementation for the os mutex functions below
struct MutexLock {
lock: AtomicBool,
}

impl MutexLock {
pub fn lock(&self) -> bool {
matches!(
self.lock
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst),
Ok(false)
)
}

pub fn unlock(&self) {
self.lock.store(false, Ordering::SeqCst);
}
}

/// Initialize a mutex.
///
/// The function shall allocate and initialize a mutex and return its address
/// as an output. If an address of an already allocated mutex is provided as
/// an input, the allocation part is skipped and the mutex is only reinitialized.
///
/// **Parameters**:
/// - mutex – (inout) The address of the mutex.
///
/// **Returns**
/// - 0 on success, a negative errno otherwise.
#[no_mangle]
pub unsafe extern "C" fn nrf_modem_os_mutex_init(
mutex: *mut *mut core::ffi::c_void,
) -> core::ffi::c_int {
if mutex.is_null() {
return -(nrfxlib_sys::NRF_EINVAL as i32);
}

// Allocate if needed
if (*mutex).is_null() {
// Allocate memory for the MutexLock
let p = nrf_modem_os_alloc(core::mem::size_of::<MaybeUninit<MutexLock>>())
as *mut MaybeUninit<MutexLock>;

if p.is_null() {
// We are out of memory
return -(nrfxlib_sys::NRF_ENOMEM as i32);
}

// Initialize the MutexLock
p.write(MaybeUninit::new(MutexLock {
lock: AtomicBool::new(false),
}));

// Assign the mutex
*mutex = p as *mut core::ffi::c_void;
} else {
// Already allocated, so just reinitialize (unlock) the mutex
(*(mutex as *mut MutexLock)).unlock();
}

0
}

/// Lock a mutex.
///
/// **Parameters**:
/// - mutex – (in) The mutex.
/// - timeout – Timeout in milliseconds. NRF_MODEM_OS_FOREVER indicates infinite timeout. NRF_MODEM_OS_NO_WAIT indicates no timeout.
///
/// **Return values**
/// - 0 – on success.
/// - -NRF_EAGAIN – If the mutex could not be taken.
#[no_mangle]
pub unsafe extern "C" fn nrf_modem_os_mutex_lock(
mutex: *mut core::ffi::c_void,
timeout: core::ffi::c_int,
) -> core::ffi::c_int {
if mutex.is_null() {
return -(nrfxlib_sys::NRF_EINVAL as i32);
}

let mutex = &*(mutex as *mut MutexLock);

let mut locked = mutex.lock();

if locked || timeout == nrfxlib_sys::NRF_MODEM_OS_NO_WAIT as i32 {
return if locked {
0
} else {
-(nrfxlib_sys::NRF_EAGAIN as i32)
};
}

let mut elapsed = 0;
const WAIT_US: core::ffi::c_int = 100;

while !locked {
nrf_modem_os_busywait(WAIT_US);

if timeout != nrfxlib_sys::NRF_MODEM_OS_FOREVER {
elapsed += WAIT_US;
if (elapsed / 1000) > timeout {
return -(nrfxlib_sys::NRF_EAGAIN as i32);
}
}

locked = mutex.lock();
}

0
}

/// Unlock a mutex.
///
/// **Parameters**:
/// - mutex – (in) The mutex.
///
/// **Return values**
/// - 0 – on success.
/// - -NRF_EPERM – If the current thread does not own this mutex.
/// - -NRF_EINVAL – If the mutex is not locked.
#[no_mangle]
pub unsafe extern "C" fn nrf_modem_os_mutex_unlock(
mutex: *mut core::ffi::c_void,
) -> core::ffi::c_int {
if mutex.is_null() {
return -(nrfxlib_sys::NRF_EINVAL as i32);
}
(*(mutex as *mut MutexLock)).unlock();
0
}

/// Generic logging procedure
///
/// **Parameters**:
/// - level – Log level
/// - fmt – Format string
/// - ... – Varargs
#[no_mangle]
pub extern "C" fn nrf_modem_os_log(_level: core::ffi::c_int, _fmt: *const core::ffi::c_char) {
// TODO FIXME
}

/// Logging procedure for dumping hex representation of object.
///
/// **Parameters**:
/// - level – Log level.
/// - strdata - String to print in the log.
/// - data - Data whose hex representation we want to log.
/// - len - Length of the data to hex dump.
#[no_mangle]
pub extern "C" fn nrf_modem_os_logdump(
_level: core::ffi::c_int,
_strdata: *const core::ffi::c_char,
_data: *const core::ffi::c_void,
_len: core::ffi::c_int,
) {
// TODO FIXME
}
Loading
Loading