Skip to content

Commit a15764e

Browse files
author
Jonathan Pallant
authored
Work with NCS 1.5.1 (#22)
* Work with NCS 1.5.1. We had to: * Add two heaps (a main heap and a TX only heap) which are interrupt-safe. * Implement the IPC peripheral driver * Minor changes for the new heapless crate * Build on stable, with HF * Use nrfxlib-sys 1.5.1-rc1 * Fix comment mis-format. * Fix memory allocations. Now we can start the modem and bring up multiple sockets. * Bump to -sys 1.5.1 final. * Clean up allocators. They were basically the same, so now there's one function which is given the specific heap to use. * Cleaning up clippy lints.
1 parent 0b29d7b commit a15764e

File tree

11 files changed

+376
-92
lines changed

11 files changed

+376
-92
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ jobs:
1111
strategy:
1212
matrix:
1313
target:
14-
- thumbv8m.main-none-eabi
14+
- thumbv8m.main-none-eabihf
1515
steps:
1616
- uses: actions/checkout@v1
1717
- uses: actions-rs/toolchain@v1
1818
with:
19-
toolchain: nightly
19+
toolchain: stable
2020
target: ${{ matrix.target }}
2121
override: true
2222
- uses: actions-rs/cargo@v1

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "nrfxlib"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
authors = ["Jonathan Pallant (42 Technology) <[email protected]>"]
55
edition = "2018"
66
license = "MIT OR Apache-2.0"
@@ -12,6 +12,7 @@ resolver = "2"
1212
[dependencies]
1313
nrf9160-pac = "0.2.1"
1414
cortex-m = "0.6"
15-
heapless = "0.5"
15+
heapless = "0.7"
16+
linked_list_allocator = { version="0.9.0", default-features=false, features=["use_spin"] }
1617
log = "0.4"
17-
nrfxlib-sys = "1.4.2"
18+
nrfxlib-sys = "1.5.1"

src/api.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,22 @@ use nrfxlib_sys as sys;
5151
/// You must call this when an EGU1 interrupt occurs.
5252
pub fn application_irq_handler() {
5353
unsafe {
54-
sys::bsd_os_application_irq_handler();
54+
sys::nrf_modem_os_application_irq_handler();
5555
}
5656
}
5757

5858
/// Trampoline into the BSD library function `bsd_os_trace_irq_handler`. You
5959
/// must call this when an EGU2 interrupt occurs.
6060
pub fn trace_irq_handler() {
6161
unsafe {
62-
sys::bsd_os_trace_irq_handler();
62+
sys::nrf_modem_os_trace_irq_handler();
6363
}
6464
}
6565

66-
/// Trampoline into the BSD library function `IPC_IRQHandler`. You must call
67-
/// this when an IPC interrupt occurs.
66+
/// IPC code now lives outside `lib_modem`, so call our IPC handler function.
6867
pub fn ipc_irq_handler() {
6968
unsafe {
70-
crate::ffi::IPC_IRQHandler();
69+
crate::ffi::ipc_irq_handler();
7170
}
7271
}
7372

src/dtls.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,7 @@ impl DtlsSocket {
121121

122122
let mut result;
123123
// Now, make a null-terminated hostname
124-
let mut hostname_smallstring: heapless::String<heapless::consts::U64> =
125-
heapless::String::new();
124+
let mut hostname_smallstring: heapless::String<64> = heapless::String::new();
126125
write!(hostname_smallstring, "{}\0", hostname).map_err(|_| Error::HostnameTooLong)?;
127126
// Now call getaddrinfo with some hints
128127
let hints = sys::nrf_addrinfo {
@@ -161,7 +160,7 @@ impl DtlsSocket {
161160
sin_len: core::mem::size_of::<sys::nrf_sockaddr_in>() as u8,
162161
sin_family: sys::NRF_AF_INET as i32,
163162
sin_port: htons(port),
164-
sin_addr: dns_addr.sin_addr.clone(),
163+
sin_addr: dns_addr.sin_addr,
165164
};
166165

167166
debug!("Trying IP address {}", &crate::NrfSockAddrIn(connect_addr));

src/ffi.rs

Lines changed: 243 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,101 @@
88
//! Dual-licensed under MIT and Apache 2.0. See the [README](../README.md) for
99
//! more details.
1010
11-
/// Stores the last error from the library. See `bsd_os_errno_set` and
11+
use log::debug;
12+
13+
/// Number of IPC configurations in `NrfxIpcConfig`
14+
const IPC_CONF_NUM: usize = 8;
15+
16+
/// Used by `libmodem` to configure the IPC peripheral. See `nrfx_ipc_config_t`
17+
/// in `nrfx/drivers/include/nrfx_ipc.h`.
18+
#[derive(Debug, Clone)]
19+
pub struct NrfxIpcConfig {
20+
/// Configuration of the connection between signals and IPC channels.
21+
send_task_config: [u32; IPC_CONF_NUM],
22+
/// Configuration of the connection between events and IPC channels.
23+
receive_event_config: [u32; IPC_CONF_NUM],
24+
/// Bitmask with events to be enabled to generate interrupt.
25+
receive_events_enabled: u32,
26+
}
27+
28+
/// IPC callback function type
29+
type NrfxIpcHandler = extern "C" fn(event_mask: u32, ptr: *mut u8);
30+
31+
/// IPC error type
32+
#[repr(u32)]
33+
#[derive(Debug, Copy, Clone)]
34+
pub enum NrfxErr {
35+
///< Operation performed successfully.
36+
Success = 0x0BAD0000,
37+
///< Internal error.
38+
ErrorInternal = (0x0BAD0000 + 1),
39+
///< No memory for operation.
40+
ErrorNoMem = (0x0BAD0000 + 2),
41+
///< Not supported.
42+
ErrorNotSupported = (0x0BAD0000 + 3),
43+
///< Invalid parameter.
44+
ErrorInvalidParam = (0x0BAD0000 + 4),
45+
///< Invalid state, operation disallowed in this state.
46+
ErrorInvalidState = (0x0BAD0000 + 5),
47+
///< Invalid length.
48+
ErrorInvalidLength = (0x0BAD0000 + 6),
49+
///< Operation timed out.
50+
ErrorTimeout = (0x0BAD0000 + 7),
51+
///< Operation is forbidden.
52+
ErrorForbidden = (0x0BAD0000 + 8),
53+
///< Null pointer.
54+
ErrorNull = (0x0BAD0000 + 9),
55+
///< Bad memory address.
56+
ErrorInvalidAddr = (0x0BAD0000 + 10),
57+
///< Busy.
58+
ErrorBusy = (0x0BAD0000 + 11),
59+
///< Module already initialized.
60+
ErrorAlreadyInitialized = (0x0BAD0000 + 12),
61+
}
62+
63+
/// Stores the last error from the library. See `nrf_modem_os_errno_set` and
1264
/// `get_last_error`.
1365
static LAST_ERROR: core::sync::atomic::AtomicI32 = core::sync::atomic::AtomicI32::new(0);
1466

15-
extern "C" {
16-
// This function is in the C library but not in the headers generated by
17-
// nrfxlib-sys.
18-
pub fn IPC_IRQHandler();
19-
}
67+
/// Remembers the IPC interrupt context we were given
68+
static IPC_CONTEXT: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
69+
70+
/// Remembers the IPC handler function we were given
71+
static IPC_HANDLER: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
2072

2173
/// Function required by BSD library. We need to set the EGU1 interrupt.
2274
#[no_mangle]
23-
pub extern "C" fn bsd_os_application_irq_set() {
75+
pub extern "C" fn nrf_modem_os_application_irq_set() {
2476
cortex_m::peripheral::NVIC::pend(crate::cpu::Interrupt::EGU1);
2577
}
2678

2779
/// Function required by BSD library. We need to clear the EGU1 interrupt.
2880
#[no_mangle]
29-
pub extern "C" fn bsd_os_application_irq_clear() {
81+
pub extern "C" fn nrf_modem_os_application_irq_clear() {
3082
cortex_m::peripheral::NVIC::unpend(crate::cpu::Interrupt::EGU1);
3183
}
3284

3385
/// Function required by BSD library. We need to set the EGU2 interrupt.
3486
#[no_mangle]
35-
pub extern "C" fn bsd_os_trace_irq_set() {
87+
pub extern "C" fn nrf_modem_os_trace_irq_set() {
3688
cortex_m::peripheral::NVIC::pend(crate::cpu::Interrupt::EGU2);
3789
}
3890

3991
/// Function required by BSD library. We need to clear the EGU2 interrupt.
4092
#[no_mangle]
41-
pub extern "C" fn bsd_os_trace_irq_clear() {
93+
pub extern "C" fn nrf_modem_os_trace_irq_clear() {
4294
cortex_m::peripheral::NVIC::unpend(crate::cpu::Interrupt::EGU2);
4395
}
4496

4597
/// Function required by BSD library. We have no init to do.
4698
#[no_mangle]
47-
pub extern "C" fn bsd_os_init() {
99+
pub extern "C" fn nrf_modem_os_init() {
48100
// Nothing
49101
}
50102

51103
/// Function required by BSD library. Stores an error code we can read later.
52104
#[no_mangle]
53-
pub extern "C" fn bsd_os_errno_set(errno: i32) {
105+
pub extern "C" fn nrf_modem_os_errno_set(errno: i32) {
54106
LAST_ERROR.store(errno, core::sync::atomic::Ordering::SeqCst);
55107
}
56108

@@ -61,7 +113,7 @@ pub fn get_last_error() -> i32 {
61113

62114
/// Function required by BSD library
63115
#[no_mangle]
64-
pub extern "C" fn bsd_os_timedwait(_context: u32, p_timeout_ms: *const i32) -> i32 {
116+
pub extern "C" fn nrf_modem_os_timedwait(_context: u32, p_timeout_ms: *const i32) -> i32 {
65117
let timeout_ms = unsafe { *p_timeout_ms };
66118
if timeout_ms < 0 {
67119
// With Zephyr, negative timeouts pend on a semaphore with K_FOREVER.
@@ -76,13 +128,189 @@ pub extern "C" fn bsd_os_timedwait(_context: u32, p_timeout_ms: *const i32) -> i
76128

77129
/// Function required by BSD library
78130
#[no_mangle]
79-
pub extern "C" fn bsd_os_trace_put(_data: *const u8, _len: u32) -> i32 {
131+
pub extern "C" fn nrf_modem_os_trace_put(_data: *const u8, _len: u32) -> i32 {
80132
// Do nothing
81133
0
82134
}
83135

84136
/// Function required by BSD library
85137
#[no_mangle]
86-
pub extern "C" fn bsd_irrecoverable_error_handler(err: u32) -> ! {
138+
pub extern "C" fn nrf_modem_irrecoverable_error_handler(err: u32) -> ! {
87139
panic!("bsd_irrecoverable_error_handler({})", err);
88140
}
141+
142+
/// The Modem library needs to dynamically allocate memory (a heap) for proper
143+
/// functioning. This memory is used to store the internal data structures that
144+
/// are used to manage the communication between the application core and the
145+
/// modem core. This memory is never shared with the modem core and hence, it
146+
/// can be located anywhere in the application core's RAM instead of the shared
147+
/// memory regions. This function allocates dynamic memory for the library.
148+
#[no_mangle]
149+
pub extern "C" fn nrf_modem_os_alloc(num_bytes_requested: usize) -> *mut u8 {
150+
unsafe { generic_alloc(num_bytes_requested, &crate::LIBRARY_ALLOCATOR) }
151+
}
152+
153+
/// The Modem library needs to dynamically allocate memory (a heap) for proper
154+
/// functioning. This memory is used to store the internal data structures that
155+
/// are used to manage the communication between the application core and the
156+
/// modem core. This memory is never shared with the modem core and hence, it
157+
/// can be located anywhere in the application core's RAM instead of the shared
158+
/// memory regions. This function allocates dynamic memory for the library.
159+
#[no_mangle]
160+
pub extern "C" fn nrf_modem_os_free(ptr: *mut u8) {
161+
unsafe {
162+
generic_free(ptr, &crate::LIBRARY_ALLOCATOR);
163+
}
164+
}
165+
166+
/// Allocate a buffer on the TX area of shared memory.
167+
///
168+
/// @param bytes Buffer size.
169+
/// @return pointer to allocated memory
170+
#[no_mangle]
171+
pub extern "C" fn nrf_modem_os_shm_tx_alloc(num_bytes_requested: usize) -> *mut u8 {
172+
unsafe { generic_alloc(num_bytes_requested, &crate::TX_ALLOCATOR) }
173+
}
174+
175+
/// Free a shared memory buffer in the TX area.
176+
///
177+
/// @param ptr Th buffer to free.
178+
#[no_mangle]
179+
pub extern "C" fn nrf_modem_os_shm_tx_free(ptr: *mut u8) {
180+
unsafe {
181+
generic_free(ptr, &crate::TX_ALLOCATOR);
182+
}
183+
}
184+
185+
/// @brief Function for loading configuration directly into IPC peripheral.
186+
///
187+
/// @param p_config Pointer to the structure with the initial configuration.
188+
#[no_mangle]
189+
pub extern "C" fn nrfx_ipc_config_load(p_config: *const NrfxIpcConfig) {
190+
unsafe {
191+
let config: &NrfxIpcConfig = &*p_config;
192+
debug!("nrfx_ipc_config_load({:?})", config);
193+
194+
let ipc = &(*nrf9160_pac::IPC_NS::ptr());
195+
196+
for (i, value) in config.send_task_config.iter().enumerate() {
197+
ipc.send_cnf[i as usize].write(|w| w.bits(*value));
198+
}
199+
200+
for (i, value) in config.receive_event_config.iter().enumerate() {
201+
ipc.receive_cnf[i as usize].write(|w| w.bits(*value));
202+
}
203+
204+
ipc.intenset
205+
.write(|w| w.bits(config.receive_events_enabled));
206+
}
207+
}
208+
209+
///
210+
/// @brief Function for initializing the IPC driver.
211+
///
212+
/// @param irq_priority Interrupt priority.
213+
/// @param handler Event handler provided by the user. Cannot be NULL.
214+
/// @param p_context Context passed to event handler.
215+
///
216+
/// @retval NRFX_SUCCESS Initialization was successful.
217+
/// @retval NRFX_ERROR_INVALID_STATE Driver is already initialized.
218+
#[no_mangle]
219+
pub extern "C" fn nrfx_ipc_init(
220+
irq_priority: u8,
221+
handler: NrfxIpcHandler,
222+
p_context: usize,
223+
) -> NrfxErr {
224+
use cortex_m::interrupt::InterruptNumber;
225+
let irq = nrf9160_pac::Interrupt::IPC;
226+
let irq_num = usize::from(irq.number());
227+
unsafe {
228+
cortex_m::peripheral::NVIC::unmask(irq);
229+
(*cortex_m::peripheral::NVIC::ptr()).ipr[irq_num].write(irq_priority);
230+
}
231+
IPC_CONTEXT.store(p_context, core::sync::atomic::Ordering::SeqCst);
232+
IPC_HANDLER.store(handler as usize, core::sync::atomic::Ordering::SeqCst);
233+
// Report success
234+
NrfxErr::Success
235+
}
236+
237+
/// Function for uninitializing the IPC module.
238+
#[no_mangle]
239+
pub extern "C" fn nrfx_ipc_uninit() {
240+
unimplemented!();
241+
}
242+
243+
/// Allocate some memory from the given heap.
244+
///
245+
/// We allocate four extra bytes so that we can store the number of bytes
246+
/// requested. This will be needed later when the memory is freed.
247+
///
248+
/// This function is safe to call from an ISR.
249+
unsafe fn generic_alloc(num_bytes_requested: usize, heap: &crate::WrappedHeap) -> *mut u8 {
250+
let sizeof_usize = core::mem::size_of::<usize>();
251+
let mut result = core::ptr::null_mut();
252+
cortex_m::interrupt::free(|cs| {
253+
let num_bytes_allocated = num_bytes_requested + sizeof_usize;
254+
let layout =
255+
core::alloc::Layout::from_size_align_unchecked(num_bytes_allocated, sizeof_usize);
256+
if let Some(ref mut inner_alloc) = *heap.borrow(cs).borrow_mut() {
257+
match inner_alloc.allocate_first_fit(layout) {
258+
Ok(real_block) => {
259+
let real_ptr = real_block.as_ptr();
260+
// We need the block size to run the de-allocation. Store it in the first four bytes.
261+
core::ptr::write_volatile::<usize>(real_ptr as *mut usize, num_bytes_allocated);
262+
// Give them the rest of the block
263+
result = real_ptr.add(sizeof_usize);
264+
}
265+
Err(_e) => {
266+
// Ignore
267+
}
268+
}
269+
}
270+
});
271+
result
272+
}
273+
274+
/// Free some memory back on to the given heap.
275+
///
276+
/// First we must wind the pointer back four bytes to recover the `usize` we
277+
/// stashed during the allocation. We use this to recreate the `Layout` required
278+
/// for the `deallocate` function.
279+
///
280+
/// This function is safe to call from an ISR.
281+
unsafe fn generic_free(ptr: *mut u8, heap: &crate::WrappedHeap) {
282+
let sizeof_usize = core::mem::size_of::<usize>() as isize;
283+
cortex_m::interrupt::free(|cs| {
284+
// Fetch the size from the previous four bytes
285+
let real_ptr = ptr.offset(-sizeof_usize);
286+
let num_bytes_allocated = core::ptr::read_volatile::<usize>(real_ptr as *const usize);
287+
let layout = core::alloc::Layout::from_size_align_unchecked(
288+
num_bytes_allocated,
289+
sizeof_usize as usize,
290+
);
291+
if let Some(ref mut inner_alloc) = *heap.borrow(cs).borrow_mut() {
292+
inner_alloc.deallocate(core::ptr::NonNull::new_unchecked(real_ptr), layout);
293+
}
294+
});
295+
}
296+
297+
/// Call this when we have an IPC IRQ. Not `extern C` as its not called by the
298+
/// library, only our interrupt handler code.
299+
pub unsafe fn ipc_irq_handler() {
300+
// Get the information about events that fired this interrupt
301+
let events_map = (*nrf9160_pac::IPC_NS::ptr()).intpend.read().bits() as u32;
302+
303+
// Clear these events
304+
let mut bitmask = events_map;
305+
while bitmask != 0 {
306+
let event_idx = bitmask.trailing_zeros();
307+
bitmask ^= 1 << event_idx;
308+
(*nrf9160_pac::IPC_NS::ptr()).events_receive[event_idx as usize].write(|w| w.bits(0));
309+
}
310+
311+
// Execute interrupt handler to provide information about events to app
312+
let handler_addr = IPC_HANDLER.load(core::sync::atomic::Ordering::SeqCst);
313+
let handler = core::mem::transmute::<usize, NrfxIpcHandler>(handler_addr);
314+
let context = IPC_CONTEXT.load(core::sync::atomic::Ordering::SeqCst);
315+
(handler)(events_map, context as *mut u8);
316+
}

0 commit comments

Comments
 (0)