1#![no_std]
2#![doc = include_str!("../README.md")]
3use crate::error::ErrorSource;
6use core::{
7    cell::RefCell,
8    ops::Range,
9    sync::atomic::{AtomicBool, Ordering},
10};
11use critical_section::Mutex;
12use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
13use linked_list_allocator::Heap;
14
15mod at;
16mod at_notifications;
17mod cancellation;
18mod dns;
19mod dtls_socket;
20#[cfg(feature = "embassy-net")]
21pub mod embassy_net_modem;
22pub(crate) mod embedded_io_macros;
23#[cfg(feature = "embedded-nal-async")]
24mod embedded_nal_async;
25mod error;
26pub mod ffi;
27mod gnss;
28pub(crate) mod ip;
29mod lte_link;
30mod sms;
31pub mod socket;
33mod tcp_stream;
34mod tls_stream;
35mod udp_socket;
36mod uicc_link;
37pub(crate) mod waker_node_list;
38
39pub use nrfxlib_sys;
40
41pub use at::*;
42pub use at_notifications::AtNotificationStream;
43pub use cancellation::CancellationToken;
44pub use dns::*;
45pub use dtls_socket::*;
46#[cfg(feature = "embedded-nal-async")]
47pub use embedded_nal_async::*;
48pub use error::Error;
49pub use gnss::*;
50pub use lte_link::LteLink;
51pub use sms::*;
52pub use socket::CipherSuite;
53pub use socket::PeerVerification;
54pub use tcp_stream::*;
55pub use tls_stream::*;
56pub use udp_socket::*;
57pub use uicc_link::UiccLink;
58
59#[cfg(feature = "nrf9160")]
60use nrf9160_pac as pac;
61
62#[cfg(feature = "nrf9120")]
63use nrf9120_pac as pac;
64
65type WrappedHeap = Mutex<RefCell<Option<Heap>>>;
76
77static LIBRARY_ALLOCATOR: WrappedHeap = Mutex::new(RefCell::new(None));
81
82static TX_ALLOCATOR: WrappedHeap = Mutex::new(RefCell::new(None));
87
88pub(crate) static MODEM_RUNTIME_STATE: RuntimeState = RuntimeState::new();
89static INITIALIZED: AtomicBool = AtomicBool::new(false);
90
91pub async fn init(mode: SystemMode, #[cfg(feature = "os-irq")] os_irq: u8) -> Result<(), Error> {
96    init_with_custom_layout(
97        mode,
98        Default::default(),
99        #[cfg(feature = "os-irq")]
100        os_irq,
101    )
102    .await
103}
104
105pub async fn init_with_custom_layout(
110    mode: SystemMode,
111    memory_layout: MemoryLayout,
112    #[cfg(feature = "os-irq")] os_irq: u8,
113) -> Result<(), Error> {
114    if INITIALIZED.fetch_or(true, Ordering::SeqCst) {
115        return Err(Error::ModemAlreadyInitialized);
116    }
117
118    #[cfg(feature = "os-irq")]
119    ffi::OS_IRQ.store(os_irq, Ordering::Relaxed);
120
121    const SHARED_MEMORY_RANGE: Range<u32> = 0x2000_0000..0x2002_0000;
122
123    if !SHARED_MEMORY_RANGE.contains(&memory_layout.base_address) {
124        return Err(Error::BadMemoryLayout);
125    }
126
127    if !SHARED_MEMORY_RANGE.contains(
128        &(memory_layout.base_address
129                + nrfxlib_sys::NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE
130                + memory_layout.tx_area_size
131                + memory_layout.rx_area_size
132                + memory_layout.trace_area_size
133                - 1),
135    ) {
136        return Err(Error::BadMemoryLayout);
137    }
138
139    #[cfg(feature = "modem-trace")]
140    if memory_layout.trace_area_size == 0 {
141        return Err(Error::BadMemoryLayout);
142    }
143
144    unsafe {
146        (*pac::REGULATORS_NS::PTR)
147            .dcdcen
148            .modify(|_, w| w.dcdcen().enabled());
149    }
150
151    unsafe {
152        const HEAP_SIZE: usize = 1024;
153        static mut HEAP_MEMORY: [u32; HEAP_SIZE] = [0u32; HEAP_SIZE];
155        let heap_start = &raw mut HEAP_MEMORY;
156        let heap_size = HEAP_SIZE * core::mem::size_of::<u32>();
157        critical_section::with(|cs| {
158            *LIBRARY_ALLOCATOR.borrow(cs).borrow_mut() =
159                Some(Heap::new(heap_start.cast::<u8>(), heap_size))
160        });
161    }
162
163    static PARAMS: grounded::uninit::GroundedCell<nrfxlib_sys::nrf_modem_init_params> =
165        grounded::uninit::GroundedCell::uninit();
166
167    let params = nrfxlib_sys::nrf_modem_init_params {
168        shmem: nrfxlib_sys::nrf_modem_shmem_cfg {
169            ctrl: nrfxlib_sys::nrf_modem_shmem_cfg__bindgen_ty_1 {
170                base: memory_layout.base_address,
171                size: nrfxlib_sys::NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE,
172            },
173            tx: nrfxlib_sys::nrf_modem_shmem_cfg__bindgen_ty_2 {
174                base: memory_layout.base_address + nrfxlib_sys::NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE,
175                size: memory_layout.tx_area_size,
176            },
177            rx: nrfxlib_sys::nrf_modem_shmem_cfg__bindgen_ty_3 {
178                base: memory_layout.base_address
179                    + nrfxlib_sys::NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE
180                    + memory_layout.tx_area_size,
181                size: memory_layout.rx_area_size,
182            },
183            trace: nrfxlib_sys::nrf_modem_shmem_cfg__bindgen_ty_4 {
184                base: memory_layout.base_address
185                    + nrfxlib_sys::NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE
186                    + memory_layout.tx_area_size
187                    + memory_layout.rx_area_size,
188                size: memory_layout.trace_area_size,
189            },
190        },
191        ipc_irq_prio: 0,
192        fault_handler: Some(modem_fault_handler),
193        dfu_handler: Some(modem_dfu_handler),
194    };
195
196    critical_section::with(|_| unsafe { PARAMS.get().write(params) });
197
198    unsafe {
199        critical_section::with(|cs| {
201            *TX_ALLOCATOR.borrow(cs).borrow_mut() = Some(Heap::new(
202                params.shmem.tx.base as usize as *mut u8,
203                params.shmem.tx.size as usize,
204            ))
205        });
206    }
207
208    unsafe { nrfxlib_sys::nrf_modem_init(PARAMS.get()) }.into_result()?;
210
211    #[cfg(feature = "modem-trace")]
213    at::send_at::<0>("AT%XMODEMTRACE=1,2").await?;
214
215    at_notifications::initialize()?;
217
218    let (modem_state,) =
220        at_commands::parser::CommandParser::parse(at::send_at::<32>("AT+CFUN?").await?.as_bytes())
221            .expect_identifier(b"+CFUN: ")
222            .expect_int_parameter()
223            .expect_identifier(b"\r\nOK\r\n")
224            .finish()?;
225
226    if modem_state != 0 {
227        at::send_at::<0>("AT+CFUN=0").await?;
229    }
230
231    if !mode.is_valid_config() {
232        return Err(Error::InvalidSystemModeConfig);
233    }
234
235    let mut buffer = [0; 64];
236    let command = mode.create_at_command(&mut buffer)?;
237    at::send_at_bytes::<0>(command).await?;
238
239    mode.setup_psm().await?;
240
241    Ok(())
242}
243
244#[cfg(feature = "modem-trace")]
251pub async fn fetch_traces(cb: impl AsyncFn(&[u8])) -> Result<(), Error> {
252    let mut frags: *mut nrfxlib_sys::nrf_modem_trace_data = core::ptr::null_mut();
253    let mut nfrags = 0;
254
255    let res = unsafe {
256        nrfxlib_sys::nrf_modem_trace_get(
257            &mut frags,
258            &mut nfrags,
259            nrfxlib_sys::NRF_MODEM_OS_NO_WAIT as i32,
260        )
261    };
262
263    if res != 0 {
264        return Err(Error::NrfError(res as isize));
265    }
266
267    let frags = unsafe { core::slice::from_raw_parts(frags, nfrags) };
269    for nrfxlib_sys::nrf_modem_trace_data { data, len } in frags {
270        let data = unsafe { core::slice::from_raw_parts(*data as *mut u8, *len) };
271        cb(data).await;
272        unsafe {
273            nrfxlib_sys::nrf_modem_trace_processed(*len);
274        }
275    }
276
277    Ok(())
278}
279
280pub struct MemoryLayout {
287    pub base_address: u32,
289    pub tx_area_size: u32,
291    pub rx_area_size: u32,
293    pub trace_area_size: u32,
295}
296
297impl Default for MemoryLayout {
298    fn default() -> Self {
299        Self {
300            base_address: 0x2001_0000,
301            tx_area_size: 0x2000,
302            rx_area_size: 0x2000,
303            trace_area_size: if cfg!(feature = "modem-trace") {
304                0x2000
305            } else {
306                0
307            },
308        }
309    }
310}
311
312unsafe extern "C" fn modem_fault_handler(_info: *mut nrfxlib_sys::nrf_modem_fault_info) {
313    #[cfg(feature = "defmt")]
314    defmt::error!(
315        "Modem fault - reason: {}, pc: {}",
316        (*_info).reason,
317        (*_info).program_counter
318    );
319}
320
321unsafe extern "C" fn modem_dfu_handler(_val: u32) {
322    #[cfg(feature = "defmt")]
323    defmt::trace!("Modem DFU handler");
324}
325
326pub fn ipc_irq_handler() {
328    unsafe {
329        crate::ffi::nrf_ipc_irq_handler();
330    }
331    cortex_m::asm::sev();
332}
333
334#[derive(Debug, Copy, Clone)]
338pub struct SystemMode {
339    pub lte_support: bool,
341    pub lte_psm_support: bool,
343    pub nbiot_support: bool,
345    pub gnss_support: bool,
347    pub preference: ConnectionPreference,
349}
350
351#[derive(Debug, Copy, Clone)]
353pub enum ConnectionPreference {
354    None = 0,
356    Lte = 1,
358    Nbiot = 2,
360    NetworkPreferenceWithLteFallback = 3,
362    NetworkPreferenceWithNbiotFallback = 4,
364}
365
366impl SystemMode {
367    fn is_valid_config(&self) -> bool {
368        if self.lte_psm_support && !self.lte_support {
369            return false;
370        }
371        match self.preference {
372            ConnectionPreference::None => true,
373            ConnectionPreference::Lte => self.lte_support,
374            ConnectionPreference::Nbiot => self.nbiot_support,
375            ConnectionPreference::NetworkPreferenceWithLteFallback => {
376                self.lte_support && self.nbiot_support
377            }
378            ConnectionPreference::NetworkPreferenceWithNbiotFallback => {
379                self.lte_support && self.nbiot_support
380            }
381        }
382    }
383
384    fn create_at_command<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a [u8], Error> {
385        at_commands::builder::CommandBuilder::create_set(buffer, true)
386            .named("%XSYSTEMMODE")
387            .with_int_parameter(self.lte_support as u8)
388            .with_int_parameter(self.nbiot_support as u8)
389            .with_int_parameter(self.gnss_support as u8)
390            .with_int_parameter(self.preference as u8)
391            .finish()
392            .map_err(|e| Error::BufferTooSmall(Some(e)))
393    }
394
395    async fn setup_psm(&self) -> Result<(), Error> {
396        if self.lte_support {
397            if self.lte_psm_support {
398                at::send_at::<0>("AT+CPSMS=1").await?;
400            } else {
401                at::send_at::<0>("AT+CPSMS=0").await?;
403            }
404        }
405        Ok(())
406    }
407}
408
409#[cfg(feature = "nrf9160")]
417pub async fn configure_gnss_on_pca10090ns() -> Result<(), Error> {
418    #[cfg(feature = "defmt")]
419    defmt::debug!("Configuring XMAGPIO pins for 1574-1577 MHz");
420
421    crate::at::send_at::<0>("AT%XMAGPIO=1,0,0,1,1,1574,1577").await?;
423    Ok(())
424}
425
426struct RuntimeState {
427    state: embassy_sync::mutex::Mutex<CriticalSectionRawMutex, RuntimeStateInner>,
428    error: AtomicBool,
429}
430
431struct RuntimeStateInner {
432    gps_active: bool,
433    lte_link_count: u16,
434    uicc_link_count: u16,
435}
436
437impl RuntimeState {
438    const fn new() -> Self {
439        Self {
440            state: embassy_sync::mutex::Mutex::new(RuntimeStateInner {
441                gps_active: false,
442                lte_link_count: 0,
443                uicc_link_count: 0,
444            }),
445            error: AtomicBool::new(false),
446        }
447    }
448
449    pub(crate) async fn activate_gps(&self) -> Result<(), Error> {
450        let mut state = self.state.lock().await;
451
452        if state.gps_active {
453            return Err(Error::GnssAlreadyTaken);
454        }
455
456        ModemActivation::Gnss.act_on_modem().await?;
457
458        state.gps_active = true;
459
460        Ok(())
461    }
462
463    pub(crate) async fn deactivate_gps(&self) -> Result<(), Error> {
464        let mut state = self.state.lock().await;
465
466        if !state.gps_active {
467            panic!("Can't deactivate an inactive gps");
468        }
469
470        if state.lte_link_count == 0 && state.uicc_link_count == 0 {
471            ModemDeactivation::Everything.act_on_modem().await?;
472        } else {
473            ModemDeactivation::OnlyGnss.act_on_modem().await?;
474        }
475
476        state.gps_active = false;
477
478        Ok(())
479    }
480
481    pub(crate) fn deactivate_gps_blocking(&self) -> Result<(), Error> {
482        let mut state = self
483            .state
484            .try_lock()
485            .map_err(|_| Error::InternalRuntimeMutexLocked)?;
486
487        if !state.gps_active {
488            panic!("Can't deactivate an inactive gps");
489        }
490
491        if state.lte_link_count == 0 && state.uicc_link_count == 0 {
492            ModemDeactivation::Everything.act_on_modem_blocking()?;
493        } else {
494            ModemDeactivation::OnlyGnss.act_on_modem_blocking()?;
495        }
496
497        state.gps_active = false;
498
499        Ok(())
500    }
501
502    pub(crate) async fn activate_lte(&self) -> Result<(), Error> {
503        let mut state = self.state.lock().await;
504
505        if state.lte_link_count == u16::MAX {
506            return Err(Error::TooManyLteLinks);
507        }
508
509        if state.lte_link_count == 0 {
510            ModemActivation::Lte.act_on_modem().await?;
511        }
512
513        state.lte_link_count += 1;
514
515        Ok(())
516    }
517
518    pub(crate) async fn deactivate_lte(&self) -> Result<(), Error> {
519        let mut state = self.state.lock().await;
520
521        if state.lte_link_count == 0 {
522            panic!("Can't deactivate an inactive lte");
523        }
524
525        if state.lte_link_count == 1 {
526            if !state.gps_active && state.uicc_link_count == 0 {
527                ModemDeactivation::Everything.act_on_modem().await?;
528            } else {
529                ModemDeactivation::OnlyLte.act_on_modem().await?;
530                if state.uicc_link_count == 0 {
531                    ModemDeactivation::OnlyUicc.act_on_modem().await?;
532                }
533            }
534        }
535
536        state.lte_link_count -= 1;
537
538        Ok(())
539    }
540
541    pub(crate) fn deactivate_lte_blocking(&self) -> Result<(), Error> {
542        let mut state = self
543            .state
544            .try_lock()
545            .map_err(|_| Error::InternalRuntimeMutexLocked)?;
546
547        if state.lte_link_count == 0 {
548            panic!("Can't deactivate an inactive lte");
549        }
550
551        if state.lte_link_count == 1 {
552            if !state.gps_active && state.uicc_link_count == 0 {
553                ModemDeactivation::Everything.act_on_modem_blocking()?;
554            } else {
555                ModemDeactivation::OnlyLte.act_on_modem_blocking()?;
556                if state.uicc_link_count == 0 {
557                    ModemDeactivation::OnlyUicc.act_on_modem_blocking()?;
558                }
559            }
560        }
561
562        state.lte_link_count -= 1;
563
564        Ok(())
565    }
566
567    pub(crate) async fn activate_uicc(&self) -> Result<(), Error> {
568        let mut state = self.state.lock().await;
569
570        if state.uicc_link_count == u16::MAX {
571            return Err(Error::TooManyUiccLinks);
572        }
573
574        if state.uicc_link_count == 0 {
575            ModemActivation::Uicc.act_on_modem().await?;
576        }
577
578        state.uicc_link_count += 1;
579
580        Ok(())
581    }
582
583    pub(crate) async fn deactivate_uicc(&self) -> Result<(), Error> {
584        let mut state = self.state.lock().await;
585
586        if state.uicc_link_count == 0 {
587            panic!("Can't deactivate an inactive UICC");
588        }
589
590        if state.uicc_link_count == 1 {
591            if state.gps_active || state.lte_link_count > 0 {
592                ModemDeactivation::OnlyUicc
593            } else {
594                ModemDeactivation::Everything
595            }
596        } else {
597            ModemDeactivation::Nothing
598        }
599        .act_on_modem()
600        .await?;
601
602        state.uicc_link_count -= 1;
603
604        Ok(())
605    }
606
607    pub(crate) fn deactivate_uicc_blocking(&self) -> Result<(), Error> {
608        let mut state = self
609            .state
610            .try_lock()
611            .map_err(|_| Error::InternalRuntimeMutexLocked)?;
612
613        if state.uicc_link_count == 0 {
614            panic!("Can't deactivate an inactive UICC");
615        }
616
617        if state.uicc_link_count == 1 {
618            if state.gps_active || state.lte_link_count > 0 {
619                ModemDeactivation::OnlyUicc
620            } else {
621                ModemDeactivation::Everything
622            }
623        } else {
624            ModemDeactivation::Nothing
625        }
626        .act_on_modem_blocking()?;
627
628        state.uicc_link_count -= 1;
629
630        Ok(())
631    }
632
633    pub(crate) fn set_error_active(&self) {
634        self.error.store(true, Ordering::SeqCst);
635    }
636
637    pub(crate) fn get_error_active(&self) -> bool {
638        self.error.load(Ordering::SeqCst)
639    }
640
641    pub(crate) async unsafe fn reset_runtime_state(&self) -> Result<(), Error> {
642        let mut state = self.state.lock().await;
643
644        if self
645            .error
646            .compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
647            .is_ok()
648        {
649            ModemDeactivation::Everything.act_on_modem().await?;
650            state.gps_active = false;
651            state.lte_link_count = 0;
652            state.uicc_link_count = 0;
653        }
654
655        self.error.store(false, Ordering::SeqCst);
656
657        Ok(())
658    }
659}
660
661pub fn has_runtime_state_error() -> bool {
666    MODEM_RUNTIME_STATE.get_error_active()
667}
668
669pub async unsafe fn reset_runtime_state() -> Result<(), Error> {
675    MODEM_RUNTIME_STATE.reset_runtime_state().await
676}
677
678enum ModemDeactivation {
679    OnlyGnss,
680    OnlyLte,
681    OnlyUicc,
682    Nothing,
683    Everything,
684}
685
686impl ModemDeactivation {
687    async fn act_on_modem(&self) -> Result<(), Error> {
688        match self {
689            ModemDeactivation::OnlyGnss => {
690                #[cfg(feature = "defmt")]
691                defmt::debug!("Disabling modem GNSS");
692
693                at::send_at::<0>("AT+CFUN=30").await?;
694            }
695            ModemDeactivation::OnlyLte => {
696                #[cfg(feature = "defmt")]
697                defmt::debug!("Disabling modem LTE");
698
699                at::send_at::<0>("AT+CFUN=20").await?;
701                }
703            ModemDeactivation::OnlyUicc => {
704                #[cfg(feature = "defmt")]
705                defmt::debug!("Disabling UICC");
706                at::send_at::<0>("AT+CFUN=40").await?;
708            }
709            ModemDeactivation::Nothing => {}
710            ModemDeactivation::Everything => {
711                #[cfg(feature = "defmt")]
712                defmt::debug!("Disabling full modem");
713
714                at::send_at::<0>("AT+CFUN=0").await?;
715            }
716        }
717
718        Ok(())
719    }
720
721    fn act_on_modem_blocking(&self) -> Result<(), Error> {
722        match self {
723            ModemDeactivation::OnlyGnss => {
724                #[cfg(feature = "defmt")]
725                defmt::debug!("Disabling modem GNSS");
726
727                at::send_at_blocking::<0>("AT+CFUN=30")?;
728            }
729            ModemDeactivation::OnlyLte => {
730                #[cfg(feature = "defmt")]
731                defmt::debug!("Disabling modem LTE");
732
733                at::send_at_blocking::<0>("AT+CFUN=20")?;
735                }
737            ModemDeactivation::OnlyUicc => {
738                #[cfg(feature = "defmt")]
739                defmt::debug!("Disabling UICC");
740                at::send_at_blocking::<0>("AT+CFUN=40")?;
742            }
743            ModemDeactivation::Nothing => {}
744            ModemDeactivation::Everything => {
745                #[cfg(feature = "defmt")]
746                defmt::debug!("Disabling full modem");
747
748                at::send_at_blocking::<0>("AT+CFUN=0")?;
749            }
750        }
751
752        Ok(())
753    }
754}
755
756enum ModemActivation {
757    Lte,
758    Gnss,
759    Uicc,
760}
761
762impl ModemActivation {
763    async fn act_on_modem(&self) -> Result<(), Error> {
764        match self {
765            ModemActivation::Gnss => {
766                #[cfg(feature = "defmt")]
767                defmt::debug!("Enabling modem GNSS");
768
769                at::send_at::<0>("AT+CFUN=31").await?;
770            }
771            ModemActivation::Lte => {
772                #[cfg(feature = "defmt")]
773                defmt::debug!("Enabling modem LTE");
774
775                at::send_at::<0>("AT%XDATAPRFL=0").await?;
777                at::send_at::<0>("AT+CEPPI=1").await?;
779                at::send_at::<0>("AT+CFUN=21").await?;
781            }
782            ModemActivation::Uicc => {
783                #[cfg(feature = "defmt")]
784                defmt::debug!("Enabling UICC");
785
786                at::send_at::<0>("AT+CEPPI=1").await?;
788                at::send_at::<0>("AT+CFUN=41").await?;
790            }
791        }
792
793        Ok(())
794    }
795}