Skip to content

Commit b0d1b94

Browse files
author
Jonathan Pallant (42 Technology)
committed
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
1 parent 0b29d7b commit b0d1b94

File tree

8 files changed

+366
-63
lines changed

8 files changed

+366
-63
lines changed

Cargo.toml

Lines changed: 6 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,9 @@ 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"
19+
nrfxlib-sys = { git = "https://github.com/42-technology-ltd/nrfxlib-sys.git", branch = "update_to_1.5.1" }
20+
# nrfxlib-sys = { path = "../nrfxlib-sys" }

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: 1 addition & 2 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 {

src/ffi.rs

Lines changed: 256 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,102 @@
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 + 0),
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+
/// st error from the library. See `bsd_os_errno_set` and
64+
/// Stores the l
1265
/// `get_last_error`.
1366
static LAST_ERROR: core::sync::atomic::AtomicI32 = core::sync::atomic::AtomicI32::new(0);
1467

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-
}
68+
/// Remembers the IPC interrupt context we were given
69+
static IPC_CONTEXT: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
70+
71+
/// Remembers the IPC handler function we were given
72+
static IPC_HANDLER: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
2073

2174
/// Function required by BSD library. We need to set the EGU1 interrupt.
2275
#[no_mangle]
23-
pub extern "C" fn bsd_os_application_irq_set() {
76+
pub extern "C" fn nrf_modem_os_application_irq_set() {
2477
cortex_m::peripheral::NVIC::pend(crate::cpu::Interrupt::EGU1);
2578
}
2679

2780
/// Function required by BSD library. We need to clear the EGU1 interrupt.
2881
#[no_mangle]
29-
pub extern "C" fn bsd_os_application_irq_clear() {
82+
pub extern "C" fn nrf_modem_os_application_irq_clear() {
3083
cortex_m::peripheral::NVIC::unpend(crate::cpu::Interrupt::EGU1);
3184
}
3285

3386
/// Function required by BSD library. We need to set the EGU2 interrupt.
3487
#[no_mangle]
35-
pub extern "C" fn bsd_os_trace_irq_set() {
88+
pub extern "C" fn nrf_modem_os_trace_irq_set() {
3689
cortex_m::peripheral::NVIC::pend(crate::cpu::Interrupt::EGU2);
3790
}
3891

3992
/// Function required by BSD library. We need to clear the EGU2 interrupt.
4093
#[no_mangle]
41-
pub extern "C" fn bsd_os_trace_irq_clear() {
94+
pub extern "C" fn nrf_modem_os_trace_irq_clear() {
4295
cortex_m::peripheral::NVIC::unpend(crate::cpu::Interrupt::EGU2);
4396
}
4497

4598
/// Function required by BSD library. We have no init to do.
4699
#[no_mangle]
47-
pub extern "C" fn bsd_os_init() {
100+
pub extern "C" fn nrf_modem_os_init() {
48101
// Nothing
49102
}
50103

51104
/// Function required by BSD library. Stores an error code we can read later.
52105
#[no_mangle]
53-
pub extern "C" fn bsd_os_errno_set(errno: i32) {
106+
pub extern "C" fn nrf_modem_os_errno_set(errno: i32) {
54107
LAST_ERROR.store(errno, core::sync::atomic::Ordering::SeqCst);
55108
}
56109

@@ -61,7 +114,7 @@ pub fn get_last_error() -> i32 {
61114

62115
/// Function required by BSD library
63116
#[no_mangle]
64-
pub extern "C" fn bsd_os_timedwait(_context: u32, p_timeout_ms: *const i32) -> i32 {
117+
pub extern "C" fn nrf_modem_os_timedwait(_context: u32, p_timeout_ms: *const i32) -> i32 {
65118
let timeout_ms = unsafe { *p_timeout_ms };
66119
if timeout_ms < 0 {
67120
// With Zephyr, negative timeouts pend on a semaphore with K_FOREVER.
@@ -76,13 +129,201 @@ pub extern "C" fn bsd_os_timedwait(_context: u32, p_timeout_ms: *const i32) -> i
76129

77130
/// Function required by BSD library
78131
#[no_mangle]
79-
pub extern "C" fn bsd_os_trace_put(_data: *const u8, _len: u32) -> i32 {
132+
pub extern "C" fn nrf_modem_os_trace_put(_data: *const u8, _len: u32) -> i32 {
80133
// Do nothing
81134
0
82135
}
83136

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

0 commit comments

Comments
 (0)