nrf_modem/
ffi.rs

1//! # FFI (Foreign Function Interface) Module
2//!
3//! This module contains implementations of functions that libbsd.a expects to
4//! be able to call.
5//!
6//! Copyright (c) 42 Technology, 2019
7//!
8//! Dual-licensed under MIT and Apache 2.0. See the [README](../README.md) for
9//! more details.
10
11#![allow(clippy::missing_safety_doc)]
12
13use core::mem::MaybeUninit;
14#[cfg(feature = "os-irq")]
15use core::sync::atomic::AtomicU8;
16use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
17
18#[cfg(feature = "nrf9160")]
19use nrf9160_pac as pac;
20
21#[cfg(feature = "nrf9120")]
22use nrf9120_pac as pac;
23
24/// Number of IPC configurations in `NrfxIpcConfig`
25const IPC_CONF_NUM: usize = 8;
26
27// Normally a notify should wake all threads. We don't have threads, so a single bool should be enough
28static NOTIFY_ACTIVE: AtomicBool = AtomicBool::new(false);
29
30#[cfg(feature = "os-irq")]
31pub(crate) static OS_IRQ: AtomicU8 = AtomicU8::new(0);
32
33/// Used by `libmodem` to configure the IPC peripheral. See `nrfx_ipc_config_t`
34/// in `nrfx/drivers/include/nrfx_ipc.h`.
35#[derive(Debug, Clone)]
36pub struct NrfxIpcConfig {
37    /// Configuration of the connection between signals and IPC channels.
38    send_task_config: [u32; IPC_CONF_NUM],
39    /// Configuration of the connection between events and IPC channels.
40    receive_event_config: [u32; IPC_CONF_NUM],
41    /// Bitmask with events to be enabled to generate interrupt.
42    receive_events_enabled: u32,
43}
44
45/// IPC callback function type
46// based on https://github.com/NordicSemiconductor/nrfx/blob/98d6f433313a3d8dcf08dce25e744617b45aa913/drivers/include/nrfx_ipc.h#L56
47type NrfxIpcHandler = extern "C" fn(event_idx: u8, ptr: *mut u8);
48
49/// IPC error type
50#[repr(u32)]
51#[derive(Debug, Copy, Clone)]
52pub enum NrfxErr {
53    ///< Operation performed successfully.
54    Success = 0x0BAD0000,
55    ///< Internal error.
56    ErrorInternal = (0x0BAD0000 + 1),
57    ///< No memory for operation.
58    ErrorNoMem = (0x0BAD0000 + 2),
59    ///< Not supported.
60    ErrorNotSupported = (0x0BAD0000 + 3),
61    ///< Invalid parameter.
62    ErrorInvalidParam = (0x0BAD0000 + 4),
63    ///< Invalid state, operation disallowed in this state.
64    ErrorInvalidState = (0x0BAD0000 + 5),
65    ///< Invalid length.
66    ErrorInvalidLength = (0x0BAD0000 + 6),
67    ///< Operation timed out.
68    ErrorTimeout = (0x0BAD0000 + 7),
69    ///< Operation is forbidden.
70    ErrorForbidden = (0x0BAD0000 + 8),
71    ///< Null pointer.
72    ErrorNull = (0x0BAD0000 + 9),
73    ///< Bad memory address.
74    ErrorInvalidAddr = (0x0BAD0000 + 10),
75    ///< Busy.
76    ErrorBusy = (0x0BAD0000 + 11),
77    ///< Module already initialized.
78    ErrorAlreadyInitialized = (0x0BAD0000 + 12),
79}
80
81/// Stores the last error from the library. See `nrf_modem_os_errno_set` and
82/// `get_last_error`.
83static LAST_ERROR: core::sync::atomic::AtomicIsize = core::sync::atomic::AtomicIsize::new(0);
84
85/// Remembers the IPC interrupt context we were given
86static IPC_CONTEXT: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
87
88/// Remembers the IPC handler function we were given
89static IPC_HANDLER: core::sync::atomic::AtomicUsize = core::sync::atomic::AtomicUsize::new(0);
90
91/// Function required by BSD library. We have no init to do.
92#[no_mangle]
93pub extern "C" fn nrf_modem_os_init() {
94    // Nothing
95}
96
97/// Function required by BSD library. We have no shutdown to do.
98#[no_mangle]
99pub extern "C" fn nrf_modem_os_shutdown() {
100    // Nothing
101}
102
103/// Function required by BSD library. Stores an error code we can read later.
104#[no_mangle]
105pub extern "C" fn nrf_modem_os_errno_set(errno: isize) {
106    LAST_ERROR.store(errno, core::sync::atomic::Ordering::SeqCst);
107}
108
109/// Return the last error stored by the nrfxlib C library.
110pub fn get_last_error() -> isize {
111    LAST_ERROR.load(core::sync::atomic::Ordering::SeqCst)
112}
113
114/// Function required by BSD library
115#[no_mangle]
116pub extern "C" fn nrf_modem_os_busywait(usec: i32) {
117    if usec > 0 {
118        // The nRF91* Arm Cortex-M33 runs at 64 MHz, so this is close enough
119        cortex_m::asm::delay((usec as u32) * 64);
120    }
121}
122
123/// Put a thread to sleep for a specific time or until an event occurs.
124///
125/// All waiting threads shall be woken by nrf_modem_event_notify.
126///
127/// **Parameters**
128/// - context – (in) A unique identifier assigned by the library to identify the context.
129/// - timeout – (inout) Timeout in millisec or -1 for infinite timeout.
130///   Contains the timeout value as input and the remainig time to sleep as output.
131///
132/// **Return values**
133/// - 0 – The thread is woken before the timeout expired.
134/// - -NRF_EAGAIN – The timeout expired.
135/// - -NRF_ESHUTDOWN – Modem is not initialized, or was shut down.
136#[no_mangle]
137pub unsafe extern "C" fn nrf_modem_os_timedwait(_context: u32, timeout: *mut i32) -> i32 {
138    if nrf_modem_os_is_in_isr() {
139        return -(nrfxlib_sys::NRF_EPERM as i32);
140    }
141
142    if !nrfxlib_sys::nrf_modem_is_initialized() {
143        return -(nrfxlib_sys::NRF_ESHUTDOWN as i32);
144    }
145
146    if *timeout < -2 {
147        // With Zephyr, negative timeouts pend on a semaphore with K_FOREVER.
148        // We can't do that here.
149        0i32
150    } else {
151        loop {
152            nrf_modem_os_busywait(1000);
153
154            if NOTIFY_ACTIVE.swap(false, Ordering::Relaxed) {
155                return 0;
156            }
157
158            match *timeout {
159                -1 => continue,
160                0 => return -(nrfxlib_sys::NRF_EAGAIN as i32),
161                _ => *timeout -= 1,
162            }
163        }
164    }
165}
166
167/// Notify the application that an event has occurred.
168///
169/// This function shall wake all threads sleeping in nrf_modem_os_timedwait.
170#[no_mangle]
171pub extern "C" fn nrf_modem_os_event_notify() {
172    NOTIFY_ACTIVE.store(true, Ordering::SeqCst);
173}
174
175/// The Modem library needs to dynamically allocate memory (a heap) for proper
176/// functioning. This memory is used to store the internal data structures that
177/// are used to manage the communication between the application core and the
178/// modem core. This memory is never shared with the modem core and hence, it
179/// can be located anywhere in the application core's RAM instead of the shared
180/// memory regions. This function allocates dynamic memory for the library.
181#[no_mangle]
182pub extern "C" fn nrf_modem_os_alloc(num_bytes_requested: usize) -> *mut u8 {
183    unsafe { generic_alloc(num_bytes_requested, &crate::LIBRARY_ALLOCATOR) }
184}
185
186/// The Modem library needs to dynamically allocate memory (a heap) for proper
187/// functioning. This memory is used to store the internal data structures that
188/// are used to manage the communication between the application core and the
189/// modem core. This memory is never shared with the modem core and hence, it
190/// can be located anywhere in the application core's RAM instead of the shared
191/// memory regions. This function allocates dynamic memory for the library.
192#[no_mangle]
193pub unsafe extern "C" fn nrf_modem_os_free(ptr: *mut u8) {
194    generic_free(ptr, &crate::LIBRARY_ALLOCATOR);
195}
196
197/// Allocate a buffer on the TX area of shared memory.
198///
199/// @param bytes Buffer size.
200/// @return pointer to allocated memory
201#[no_mangle]
202pub extern "C" fn nrf_modem_os_shm_tx_alloc(num_bytes_requested: usize) -> *mut u8 {
203    unsafe { generic_alloc(num_bytes_requested, &crate::TX_ALLOCATOR) }
204}
205
206/// Free a shared memory buffer in the TX area.
207///
208/// @param ptr Th buffer to free.
209#[no_mangle]
210pub unsafe extern "C" fn nrf_modem_os_shm_tx_free(ptr: *mut u8) {
211    generic_free(ptr, &crate::TX_ALLOCATOR);
212}
213
214/// @brief Function for loading configuration directly into IPC peripheral.
215///
216/// @param p_config Pointer to the structure with the initial configuration.
217#[no_mangle]
218pub unsafe extern "C" fn nrfx_ipc_config_load(p_config: *const NrfxIpcConfig) {
219    let config: &NrfxIpcConfig = &*p_config;
220
221    let ipc = &(*pac::IPC_NS::ptr());
222
223    for (i, value) in config.send_task_config.iter().enumerate() {
224        ipc.send_cnf[i].write(|w| w.bits(*value));
225    }
226
227    for (i, value) in config.receive_event_config.iter().enumerate() {
228        ipc.receive_cnf[i].write(|w| w.bits(*value));
229    }
230
231    ipc.intenset
232        .write(|w| w.bits(config.receive_events_enabled));
233}
234
235/// @brief Function for initializing the IPC driver.
236///
237/// @param irq_priority Interrupt priority.
238/// @param handler      Event handler provided by the user. Cannot be NULL.
239/// @param p_context    Context passed to event handler.
240///
241/// @retval NRFX_SUCCESS             Initialization was successful.
242/// @retval NRFX_ERROR_INVALID_STATE Driver is already initialized.
243#[no_mangle]
244pub extern "C" fn nrfx_ipc_init(
245    irq_priority: u8,
246    handler: NrfxIpcHandler,
247    p_context: usize,
248) -> NrfxErr {
249    use cortex_m::interrupt::InterruptNumber;
250    let irq = pac::Interrupt::IPC;
251    let irq_num = usize::from(irq.number());
252    unsafe {
253        cortex_m::peripheral::NVIC::unmask(irq);
254        (*cortex_m::peripheral::NVIC::PTR).ipr[irq_num].write(irq_priority);
255    }
256    IPC_CONTEXT.store(p_context, core::sync::atomic::Ordering::SeqCst);
257    IPC_HANDLER.store(handler as usize, core::sync::atomic::Ordering::SeqCst);
258    // Report success
259    NrfxErr::Success
260}
261
262/// Function for uninitializing the IPC module.
263#[no_mangle]
264pub extern "C" fn nrfx_ipc_uninit() {
265    let ipc = unsafe { &(*pac::IPC_NS::ptr()) };
266
267    for i in 0..IPC_CONF_NUM {
268        ipc.send_cnf[i].reset();
269    }
270
271    for i in 0..IPC_CONF_NUM {
272        ipc.receive_cnf[i].reset();
273    }
274
275    ipc.intenset.reset();
276}
277
278#[no_mangle]
279pub extern "C" fn nrfx_ipc_receive_event_enable(event_index: u8) {
280    let ipc = unsafe { &(*pac::IPC_NS::ptr()) };
281    ipc.inten
282        .modify(|r, w| unsafe { w.bits(r.bits() | 1 << event_index) })
283}
284
285#[no_mangle]
286pub extern "C" fn nrfx_ipc_receive_event_disable(event_index: u8) {
287    let ipc = unsafe { &(*pac::IPC_NS::ptr()) };
288    ipc.inten
289        .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << event_index)) })
290}
291
292/// Allocate some memory from the given heap.
293///
294/// We allocate four extra bytes so that we can store the number of bytes
295/// requested. This will be needed later when the memory is freed.
296///
297/// This function is safe to call from an ISR.
298unsafe fn generic_alloc(num_bytes_requested: usize, heap: &crate::WrappedHeap) -> *mut u8 {
299    let sizeof_usize = core::mem::size_of::<usize>();
300    let mut result = core::ptr::null_mut();
301    critical_section::with(|cs| {
302        let num_bytes_allocated = num_bytes_requested + sizeof_usize;
303        let layout =
304            core::alloc::Layout::from_size_align_unchecked(num_bytes_allocated, sizeof_usize);
305        if let Some(ref mut inner_alloc) = *heap.borrow(cs).borrow_mut() {
306            match inner_alloc.allocate_first_fit(layout) {
307                Ok(real_block) => {
308                    let real_ptr = real_block.as_ptr();
309                    // We need the block size to run the de-allocation. Store it in the first four bytes.
310                    core::ptr::write_volatile::<usize>(real_ptr as *mut usize, num_bytes_allocated);
311                    // Give them the rest of the block
312                    result = real_ptr.add(sizeof_usize);
313                }
314                Err(_e) => {
315                    // Ignore
316                }
317            }
318        }
319    });
320    result
321}
322
323/// Free some memory back on to the given heap.
324///
325/// First we must wind the pointer back four bytes to recover the `usize` we
326/// stashed during the allocation. We use this to recreate the `Layout` required
327/// for the `deallocate` function.
328///
329/// This function is safe to call from an ISR.
330unsafe fn generic_free(ptr: *mut u8, heap: &crate::WrappedHeap) {
331    let sizeof_usize = core::mem::size_of::<usize>() as isize;
332    critical_section::with(|cs| {
333        // Fetch the size from the previous four bytes
334        let real_ptr = ptr.offset(-sizeof_usize);
335        let num_bytes_allocated = core::ptr::read_volatile::<usize>(real_ptr as *const usize);
336        let layout = core::alloc::Layout::from_size_align_unchecked(
337            num_bytes_allocated,
338            sizeof_usize as usize,
339        );
340        if let Some(ref mut inner_alloc) = *heap.borrow(cs).borrow_mut() {
341            inner_alloc.deallocate(core::ptr::NonNull::new_unchecked(real_ptr), layout);
342        }
343    });
344}
345
346/// Call this when we have an IPC IRQ. Not `extern C` as its not called by the
347/// library, only our interrupt handler code.
348// This function seems to be based on this verion in C:
349// https://github.com/NordicSemiconductor/nrfx/blob/98d6f433313a3d8dcf08dce25e744617b45aa913/drivers/src/nrfx_ipc.c#L146-L163
350pub unsafe fn nrf_ipc_irq_handler() {
351    // Get the information about events that fired this interrupt
352    let events_map = (*pac::IPC_NS::ptr()).intpend.read().bits();
353
354    // Fetch interrupt handler and context to use during event resolution
355    let handler_addr = IPC_HANDLER.load(core::sync::atomic::Ordering::SeqCst);
356    let handler = if handler_addr != 0 {
357        let handler = core::mem::transmute::<usize, NrfxIpcHandler>(handler_addr);
358        Some(handler)
359    } else {
360        #[cfg(feature = "defmt")]
361        defmt::warn!("No IPC handler registered");
362        None
363    };
364    let context = IPC_CONTEXT.load(core::sync::atomic::Ordering::SeqCst);
365
366    // Clear these events
367    let mut bitmask = events_map;
368    while bitmask != 0 {
369        let event_idx = bitmask.trailing_zeros();
370        bitmask &= !(1 << event_idx);
371        (*pac::IPC_NS::ptr()).events_receive[event_idx as usize].write(|w| w.bits(0));
372
373        // Execute interrupt handler to provide information about events to app
374        if let Some(handler) = handler {
375            let event_idx = event_idx
376                .try_into()
377                .expect("A u32 has less then 255 trailing zeroes");
378            (handler)(event_idx, context as *mut u8);
379        }
380    }
381}
382
383/// Initialize a semaphore.
384///
385/// The function shall allocate and initialize a semaphore and return its address as an output.
386/// If an address of an already allocated semaphore is provided as an input, the allocation part is skipped and the semaphore is only reinitialized.
387///
388/// **Parameters**:
389/// - sem – (inout) The address of the semaphore.
390/// - initial_count – Initial semaphore count.
391/// - limit – Maximum semaphore count.
392///
393/// **Returns**
394/// - 0 on success, a negative errno otherwise.
395#[no_mangle]
396pub unsafe extern "C" fn nrf_modem_os_sem_init(
397    sem: *mut *mut core::ffi::c_void,
398    initial_count: core::ffi::c_uint,
399    limit: core::ffi::c_uint,
400) -> core::ffi::c_int {
401    if sem.is_null() || initial_count > limit {
402        return -(nrfxlib_sys::NRF_EINVAL as i32);
403    }
404
405    // Allocate if we need to
406    if (*sem).is_null() {
407        // Allocate our semaphore datastructure
408        *sem = nrf_modem_os_alloc(core::mem::size_of::<Semaphore>()) as *mut _;
409
410        if (*sem).is_null() {
411            // We are out of memory
412            return -(nrfxlib_sys::NRF_ENOMEM as i32);
413        }
414    }
415
416    // Initialize the data
417    *((*sem) as *mut Semaphore) = Semaphore {
418        max_value: limit,
419        current_value: AtomicU32::new(initial_count),
420    };
421
422    0
423}
424
425/// Give a semaphore.
426///
427/// *Note*: Can be called from an ISR.
428///
429/// **Parameters**
430/// - sem – The semaphore.
431#[no_mangle]
432pub extern "C" fn nrf_modem_os_sem_give(sem: *mut core::ffi::c_void) {
433    unsafe {
434        if sem.is_null() {
435            return;
436        }
437
438        let max_value = (*(sem as *mut Semaphore)).max_value;
439        (*(sem as *mut Semaphore))
440            .current_value
441            .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| {
442                (val < max_value).then_some(val + 1)
443            })
444            .ok();
445    }
446}
447
448/// Take a semaphore.
449///
450/// *Note*: timeout shall be set to NRF_MODEM_OS_NO_WAIT if called from ISR.
451///
452/// **Parameters**
453/// - sem – The semaphore.
454/// - timeout – Timeout in milliseconds. NRF_MODEM_OS_FOREVER indicates infinite timeout. NRF_MODEM_OS_NO_WAIT indicates no timeout.
455///
456/// **Return values**
457/// - 0 – on success.
458/// - -NRF_EAGAIN – If the semaphore could not be taken.
459#[no_mangle]
460pub extern "C" fn nrf_modem_os_sem_take(
461    sem: *mut core::ffi::c_void,
462    mut timeout: core::ffi::c_int,
463) -> core::ffi::c_int {
464    unsafe {
465        if sem.is_null() {
466            return -(nrfxlib_sys::NRF_EAGAIN as i32);
467        }
468
469        if nrfxlib_sys::nrf_modem_os_is_in_isr() {
470            timeout = nrfxlib_sys::NRF_MODEM_OS_NO_WAIT as i32;
471        }
472
473        loop {
474            if (*(sem as *mut Semaphore))
475                .current_value
476                .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |val| {
477                    if val > 0 {
478                        Some(val - 1)
479                    } else {
480                        None
481                    }
482                })
483                .is_ok()
484            {
485                return 0;
486            }
487
488            match timeout {
489                0 => return -(nrfxlib_sys::NRF_EAGAIN as i32),
490                nrfxlib_sys::NRF_MODEM_OS_FOREVER => {
491                    nrf_modem_os_busywait(1000);
492                }
493                _ => {
494                    timeout -= 1;
495                    nrf_modem_os_busywait(1000);
496                }
497            }
498        }
499    }
500}
501
502/// Get a semaphore’s count.
503///
504/// **Parameters**
505/// - sem – The semaphore.
506///
507/// **Returns**
508/// - Current semaphore count.
509#[no_mangle]
510pub extern "C" fn nrf_modem_os_sem_count_get(sem: *mut core::ffi::c_void) -> core::ffi::c_uint {
511    unsafe {
512        if sem.is_null() {
513            return 0;
514        }
515
516        (*(sem as *mut Semaphore))
517            .current_value
518            .load(Ordering::SeqCst)
519    }
520}
521
522struct Semaphore {
523    max_value: u32,
524    current_value: AtomicU32,
525}
526
527/// Check if executing in interrupt context.
528#[no_mangle]
529pub extern "C" fn nrf_modem_os_is_in_isr() -> bool {
530    #[cfg(feature = "os-irq")]
531    {
532        let os_irq = OS_IRQ.load(Ordering::Relaxed);
533        match cortex_m::peripheral::SCB::vect_active() {
534            cortex_m::peripheral::scb::VectActive::Interrupt { irqn } => irqn != os_irq,
535            _ => true,
536        }
537    }
538
539    #[cfg(not(feature = "os-irq"))]
540    {
541        cortex_m::peripheral::SCB::vect_active()
542            != cortex_m::peripheral::scb::VectActive::ThreadMode
543    }
544}
545
546// A basic mutex lock implementation for the os mutex functions below
547struct MutexLock {
548    lock: AtomicBool,
549}
550
551impl MutexLock {
552    pub fn lock(&self) -> bool {
553        matches!(
554            self.lock
555                .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst),
556            Ok(false)
557        )
558    }
559
560    pub fn unlock(&self) {
561        self.lock.store(false, Ordering::SeqCst);
562    }
563}
564
565/// Initialize a mutex.
566///
567/// The function shall allocate and initialize a mutex and return its address
568/// as an output. If an address of an already allocated mutex is provided as
569/// an input, the allocation part is skipped and the mutex is only reinitialized.
570///
571/// **Parameters**:
572/// - mutex – (inout) The address of the mutex.
573///
574/// **Returns**
575/// - 0 on success, a negative errno otherwise.
576#[no_mangle]
577pub unsafe extern "C" fn nrf_modem_os_mutex_init(
578    mutex: *mut *mut core::ffi::c_void,
579) -> core::ffi::c_int {
580    if mutex.is_null() {
581        return -(nrfxlib_sys::NRF_EINVAL as i32);
582    }
583
584    // Allocate if needed
585    if (*mutex).is_null() {
586        // Allocate memory for the MutexLock
587        let p = nrf_modem_os_alloc(core::mem::size_of::<MaybeUninit<MutexLock>>())
588            as *mut MaybeUninit<MutexLock>;
589
590        if p.is_null() {
591            // We are out of memory
592            return -(nrfxlib_sys::NRF_ENOMEM as i32);
593        }
594
595        // Initialize the MutexLock
596        p.write(MaybeUninit::new(MutexLock {
597            lock: AtomicBool::new(false),
598        }));
599
600        // Assign the mutex
601        *mutex = p as *mut core::ffi::c_void;
602    } else {
603        // Already allocated, so just reinitialize (unlock) the mutex
604        (*(mutex as *mut MutexLock)).unlock();
605    }
606
607    0
608}
609
610/// Lock a mutex.
611///
612/// **Parameters**:
613/// - mutex – (in) The mutex.
614/// - timeout – Timeout in milliseconds. NRF_MODEM_OS_FOREVER indicates infinite timeout. NRF_MODEM_OS_NO_WAIT indicates no timeout.
615///
616/// **Return values**
617/// - 0 – on success.
618/// - -NRF_EAGAIN – If the mutex could not be taken.
619#[no_mangle]
620pub unsafe extern "C" fn nrf_modem_os_mutex_lock(
621    mutex: *mut core::ffi::c_void,
622    timeout: core::ffi::c_int,
623) -> core::ffi::c_int {
624    if mutex.is_null() {
625        return -(nrfxlib_sys::NRF_EINVAL as i32);
626    }
627
628    let mutex = &*(mutex as *mut MutexLock);
629
630    let mut locked = mutex.lock();
631
632    if locked || timeout == nrfxlib_sys::NRF_MODEM_OS_NO_WAIT as i32 {
633        return if locked {
634            0
635        } else {
636            -(nrfxlib_sys::NRF_EAGAIN as i32)
637        };
638    }
639
640    let mut elapsed = 0;
641    const WAIT_US: core::ffi::c_int = 100;
642
643    while !locked {
644        nrf_modem_os_busywait(WAIT_US);
645
646        if timeout != nrfxlib_sys::NRF_MODEM_OS_FOREVER {
647            elapsed += WAIT_US;
648            if (elapsed / 1000) > timeout {
649                return -(nrfxlib_sys::NRF_EAGAIN as i32);
650            }
651        }
652
653        locked = mutex.lock();
654    }
655
656    0
657}
658
659/// Unlock a mutex.
660///
661/// **Parameters**:
662/// - mutex – (in) The mutex.
663///
664/// **Return values**
665/// - 0 – on success.
666/// - -NRF_EPERM – If the current thread does not own this mutex.
667/// - -NRF_EINVAL – If the mutex is not locked.
668#[no_mangle]
669pub unsafe extern "C" fn nrf_modem_os_mutex_unlock(
670    mutex: *mut core::ffi::c_void,
671) -> core::ffi::c_int {
672    if mutex.is_null() {
673        return -(nrfxlib_sys::NRF_EINVAL as i32);
674    }
675    (*(mutex as *mut MutexLock)).unlock();
676    0
677}
678
679/// Generic logging procedure
680///
681/// **Parameters**:
682/// - level – Log level
683/// - msg - Message
684/// - ... – Varargs
685#[no_mangle]
686pub unsafe extern "C" fn nrf_modem_os_log_wrapped(
687    _level: core::ffi::c_int,
688    _msg: *const core::ffi::c_char,
689) {
690    #[cfg(all(feature = "defmt", feature = "modem-log"))]
691    {
692        let msg = core::ffi::CStr::from_ptr(_msg);
693        if let Ok(msg) = msg.to_str() {
694            defmt::trace!("Modem log <{}>: {}", _level, msg);
695        }
696    }
697}
698
699/// Logging procedure for dumping hex representation of object.
700///
701/// **Parameters**:
702/// - level – Log level.
703/// - strdata - String to print in the log.
704/// - data - Data whose hex representation we want to log.
705/// - len - Length of the data to hex dump.
706#[no_mangle]
707pub extern "C" fn nrf_modem_os_logdump(
708    _level: core::ffi::c_int,
709    _strdata: *const core::ffi::c_char,
710    _data: *const core::ffi::c_void,
711    _len: core::ffi::c_int,
712) {
713    // TODO FIXME
714}