ublox_cellular/
network.rs

1use crate::{
2    command::{
3        error::UbloxError,
4        mobile_control::{
5            types::{Functionality, ResetMode},
6            SetModuleFunctionality,
7        },
8        network_service::{
9            types::OperatorSelectionMode, GetNetworkRegistrationStatus, SetOperatorSelection,
10        },
11        psn::{
12            self, types::PDPContextStatus, GetEPSNetworkRegistrationStatus,
13            GetGPRSNetworkRegistrationStatus, GetPDPContextState, SetPDPContextState,
14        },
15        Urc,
16    },
17    error::GenericError,
18    registration::{self, ConnectionState, RegistrationParams, RegistrationState},
19    services::data::ContextState,
20};
21use atat::{atat_derive::AtatLen, AtatClient};
22use core::convert::TryInto;
23use embedded_time::{duration::*, Clock, TimeError};
24use hash32_derive::Hash32;
25use serde::{Deserialize, Serialize};
26
27const REGISTRATION_CHECK_INTERVAL: Seconds<u32> = Seconds::<u32>(15);
28const REGISTRATION_TIMEOUT: Minutes<u32> = Minutes::<u32>(5);
29
30#[derive(Debug, PartialEq)]
31pub enum Error {
32    Generic(GenericError),
33    AT(atat::Error<UbloxError>),
34    RegistrationDenied,
35    UnknownProfile,
36    ActivationFailed,
37    _Unknown,
38}
39
40impl From<TimeError> for Error {
41    fn from(e: TimeError) -> Self {
42        Error::Generic(e.into())
43    }
44}
45
46#[derive(
47    Debug, Clone, Copy, Eq, PartialEq, Hash32, Serialize, Deserialize, AtatLen, defmt::Format,
48)]
49pub struct ProfileId(pub u8);
50
51#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, AtatLen, defmt::Format)]
52pub struct ContextId(pub u8);
53
54pub struct AtTx<C> {
55    urc_attempts: u8,
56    max_urc_attempts: u8,
57    consecutive_timeouts: u8,
58    client: C,
59}
60
61impl<C: AtatClient> AtTx<C> {
62    pub fn new(client: C, max_urc_attempts: u8) -> Self {
63        Self {
64            urc_attempts: 0,
65            consecutive_timeouts: 0,
66            max_urc_attempts,
67            client,
68        }
69    }
70
71    pub fn reset(&mut self) -> Result<(), Error> {
72        self.client.reset();
73        Ok(())
74    }
75
76    pub fn send_ignore_timeout<A, const LEN: usize>(
77        &mut self,
78        req: &A,
79    ) -> Result<A::Response, Error>
80    where
81        A: atat::AtatCmd<LEN>,
82        A::Error: Into<UbloxError>,
83    {
84        self.client
85            .send(req)
86            .map_err(|e| match e {
87                nb::Error::Other(ate) => {
88                    // let request = req.as_bytes();
89
90                    if !matches!(ate, atat::Error::Timeout) {
91                        // defmt::error!("{}: [{=[u8]:a}]", ate, request[..request.len() - 2]);
92                    }
93
94                    match ate {
95                        atat::Error::Error(ubx) => {
96                            let u: UbloxError = ubx.into();
97                            Error::AT(atat::Error::Error(u))
98                        }
99                        atat::Error::Timeout => {
100                            self.consecutive_timeouts += 1;
101                            Error::AT(atat::Error::Timeout)
102                        }
103                        atat::Error::Read => Error::AT(atat::Error::Read),
104                        atat::Error::Write => Error::AT(atat::Error::Write),
105                        atat::Error::InvalidResponse => Error::AT(atat::Error::InvalidResponse),
106                        atat::Error::Aborted => Error::AT(atat::Error::Aborted),
107                        atat::Error::Overflow => Error::AT(atat::Error::Overflow),
108                        atat::Error::Parse => Error::AT(atat::Error::Parse),
109                    }
110                }
111                nb::Error::WouldBlock => Error::_Unknown,
112            })
113            .map(|res| {
114                self.consecutive_timeouts = 0;
115                res
116            })
117    }
118
119    pub fn send<A, const LEN: usize>(&mut self, req: &A) -> Result<A::Response, Error>
120    where
121        A: atat::AtatCmd<LEN>,
122        A::Error: Into<UbloxError>,
123    {
124        self.client
125            .send(req)
126            .map_err(|e| match e {
127                nb::Error::Other(ate) => {
128                    // let request = req.as_bytes();
129                    // defmt::error!("{}: [{=[u8]:a}]", ate, request[..request.len() - 2]);
130
131                    match ate {
132                        atat::Error::Error(ubx) => {
133                            let u: UbloxError = ubx.into();
134                            Error::AT(atat::Error::Error(u))
135                        }
136                        atat::Error::Timeout => {
137                            self.consecutive_timeouts += 1;
138                            Error::AT(atat::Error::Timeout)
139                        }
140                        atat::Error::Read => Error::AT(atat::Error::Read),
141                        atat::Error::Write => Error::AT(atat::Error::Write),
142                        atat::Error::InvalidResponse => Error::AT(atat::Error::InvalidResponse),
143                        atat::Error::Aborted => Error::AT(atat::Error::Aborted),
144                        atat::Error::Overflow => Error::AT(atat::Error::Overflow),
145                        atat::Error::Parse => Error::AT(atat::Error::Parse),
146                    }
147                }
148                nb::Error::WouldBlock => Error::_Unknown,
149            })
150            .map(|res| {
151                self.consecutive_timeouts = 0;
152                res
153            })
154    }
155
156    pub fn handle_urc<F: FnOnce(Urc) -> bool>(&mut self, f: F) -> Result<(), Error> {
157        let mut a = self.urc_attempts;
158        let max = self.max_urc_attempts;
159
160        self.client.peek_urc_with::<Urc, _>(|urc| {
161            if !f(urc.clone()) {
162                if a < max {
163                    a += 1;
164                    return false;
165                    // } else {
166                    // defmt::warn!("Dropping stale URC! {}", defmt::Debug2Format(&urc));
167                }
168            }
169            a = 0;
170            true
171        });
172        self.urc_attempts = a;
173        Ok(())
174    }
175}
176
177pub struct Network<C, CLK>
178where
179    CLK: Clock,
180{
181    pub(crate) status: RegistrationState<CLK>,
182    pub(crate) context_state: ContextState,
183    pub(crate) at_tx: AtTx<C>,
184}
185
186impl<C, CLK> Network<C, CLK>
187where
188    C: AtatClient,
189    CLK: Clock,
190{
191    pub(crate) fn new(at_tx: AtTx<C>, timer: CLK) -> Self {
192        Network {
193            status: RegistrationState::new(timer),
194            context_state: ContextState::Setup,
195            at_tx,
196        }
197    }
198
199    pub fn is_connected(&self) -> Result<bool, Error> {
200        Ok(matches!(self.status.conn_state, ConnectionState::Connected))
201    }
202
203    pub fn reset_reg_time(&mut self) -> Result<(), Error> {
204        let now = self.status.timer.try_now().map_err(TimeError::from)?;
205
206        self.status.reg_start_time.replace(now);
207        self.status.reg_check_time = self.status.reg_start_time;
208        Ok(())
209    }
210
211    pub fn process_events(&mut self) -> Result<(), Error>
212    where
213        Generic<CLK::T>: TryInto<Milliseconds>,
214    {
215        if self.at_tx.consecutive_timeouts > 10 {
216            defmt::warn!("Resetting the modem due to consecutive AT timeouts");
217            return Err(Error::Generic(GenericError::Timeout));
218        }
219
220        self.handle_urc()?;
221        self.check_registration_state()?;
222        self.intervene_registration()?;
223        // self.check_running_imsi();
224
225        let now = self.status.timer.try_now().map_err(TimeError::from)?;
226        let should_check = self
227            .status
228            .reg_check_time
229            .and_then(|ref reg_check_time| {
230                now.checked_duration_since(reg_check_time)
231                    .and_then(|dur| dur.try_into().ok())
232                    .map(|dur| dur >= REGISTRATION_CHECK_INTERVAL)
233            })
234            .unwrap_or(true);
235
236        if self.status.conn_state != ConnectionState::Connecting || !should_check {
237            return Ok(());
238        }
239
240        self.status.reg_check_time.replace(now);
241
242        self.update_registration()?;
243
244        let now = self.status.timer.try_now().map_err(TimeError::from)?;
245        let is_timeout = self
246            .status
247            .reg_start_time
248            .and_then(|ref reg_start_time| {
249                now.checked_duration_since(reg_start_time)
250                    .and_then(|dur| dur.try_into().ok())
251                    .map(|dur| dur >= REGISTRATION_TIMEOUT)
252            })
253            .unwrap_or(false);
254
255        if self.status.conn_state == ConnectionState::Connecting && is_timeout {
256            defmt::warn!("Resetting the modem due to the network registration timeout");
257
258            return Err(Error::Generic(GenericError::Timeout));
259        }
260        Ok(())
261    }
262
263    pub fn check_registration_state(&mut self) -> Result<(), Error> {
264        // Don't do anything if we are actually disconnected by choice
265        if self.status.conn_state == ConnectionState::Disconnected {
266            return Ok(());
267        }
268
269        // If both (CSD + PSD) is registered, or EPS is registered, we are connected!
270        if (self.status.csd.registered() && self.status.psd.registered())
271            || self.status.eps.registered()
272        {
273            self.status.set_connection_state(ConnectionState::Connected);
274        } else if self.status.conn_state == ConnectionState::Connected {
275            // FIXME: potentially go back into connecting state only when getting into
276            // a 'sticky' non-registered state
277            self.status.reset();
278            self.status
279                .set_connection_state(ConnectionState::Connecting);
280        }
281
282        Ok(())
283    }
284
285    pub fn intervene_registration(&mut self) -> Result<(), Error>
286    where
287        Generic<CLK::T>: TryInto<Milliseconds>,
288    {
289        if self.status.conn_state != ConnectionState::Connecting {
290            return Ok(());
291        }
292
293        let now = self.status.timer.try_now().map_err(TimeError::from)?;
294
295        // If EPS has been sticky for longer than `timeout`
296        let timeout = Seconds(self.status.registration_interventions * 15);
297        if self.status.eps.sticky() && self.status.eps.duration(now) >= timeout {
298            // If (EPS + CSD) is not attempting registration
299            if self.status.eps.get_status() == registration::Status::NotRegistering
300                && self.status.csd.get_status() == registration::Status::NotRegistering
301            {
302                defmt::debug!(
303                    "Sticky not registering state for {} s, PLMN reselection",
304                    Seconds::<u32>::from(self.status.eps.duration(now)).integer()
305                );
306
307                self.status.csd.reset();
308                self.status.psd.reset();
309                self.status.eps.reset();
310                self.status.registration_interventions += 1;
311                self.send_internal(
312                    &SetOperatorSelection {
313                        mode: OperatorSelectionMode::Automatic,
314                        format: Some(2),
315                    },
316                    false,
317                )
318                .ok();
319                return Ok(());
320
321            // If (EPS + CSD) is denied registration
322            } else if self.status.eps.get_status() == registration::Status::Denied
323                && self.status.csd.get_status() == registration::Status::Denied
324            {
325                defmt::debug!(
326                    "Sticky denied state for {} s, RF reset",
327                    Seconds::<u32>::from(self.status.eps.duration(now)).integer()
328                );
329                self.status.csd.reset();
330                self.status.psd.reset();
331                self.status.eps.reset();
332                self.status.registration_interventions += 1;
333                self.send_internal(
334                    &SetModuleFunctionality {
335                        fun: Functionality::Minimum,
336                        rst: Some(ResetMode::DontReset),
337                    },
338                    false,
339                )?;
340                self.send_internal(
341                    &SetModuleFunctionality {
342                        fun: Functionality::Full,
343                        rst: Some(ResetMode::DontReset),
344                    },
345                    false,
346                )?;
347                return Ok(());
348            }
349        }
350
351        // If CSD has been sticky for longer than `timeout`,
352        // and (CSD + PSD) is denied registration.
353        if self.status.csd.sticky()
354            && self.status.csd.duration(now) >= timeout
355            && self.status.csd.get_status() == registration::Status::Denied
356            && self.status.psd.get_status() == registration::Status::Denied
357        {
358            defmt::debug!(
359                "Sticky CSD and PSD denied state for {} s, RF reset",
360                Seconds::<u32>::from(self.status.csd.duration(now)).integer()
361            );
362            self.status.csd.reset();
363            self.status.psd.reset();
364            self.status.eps.reset();
365            self.status.registration_interventions += 1;
366            self.send_internal(
367                &SetModuleFunctionality {
368                    fun: Functionality::Minimum,
369                    rst: Some(ResetMode::DontReset),
370                },
371                false,
372            )?;
373            self.send_internal(
374                &SetModuleFunctionality {
375                    fun: Functionality::Full,
376                    rst: Some(ResetMode::DontReset),
377                },
378                false,
379            )?;
380            return Ok(());
381        }
382
383        // If CSD is registered, but PSD has been sticky for longer than `timeout`,
384        // and (PSD + EPS) is not attempting registration.
385        if self.status.csd.registered()
386            && self.status.psd.sticky()
387            && self.status.psd.duration(now) >= timeout
388            && self.status.psd.get_status() == registration::Status::NotRegistering
389            && self.status.eps.get_status() == registration::Status::NotRegistering
390        {
391            defmt::debug!(
392                "Sticky not registering PSD state for {} s, force GPRS attach",
393                Seconds::<u32>::from(self.status.psd.duration(now)).integer()
394            );
395            self.status.psd.reset();
396            self.status.registration_interventions += 1;
397            self.send_internal(&GetPDPContextState, true)?;
398
399            if self
400                .send_internal(
401                    &SetPDPContextState {
402                        status: PDPContextStatus::Activated,
403                        cid: None,
404                    },
405                    true,
406                )
407                .is_err()
408            {
409                self.status.csd.reset();
410                self.status.psd.reset();
411                self.status.eps.reset();
412                defmt::warn!("GPRS attach failed, try PLMN reselection");
413                self.send_internal(
414                    &SetOperatorSelection {
415                        mode: OperatorSelectionMode::Automatic,
416                        format: Some(2),
417                    },
418                    true,
419                )?;
420            }
421        }
422
423        Ok(())
424    }
425
426    pub fn update_registration(&mut self) -> Result<(), Error> {
427        let ts = self.status.timer.try_now().map_err(TimeError::from)?;
428
429        if let Ok(reg) = self.send_internal(&GetNetworkRegistrationStatus, false) {
430            self.status.compare_and_set(reg.into(), ts);
431        }
432
433        if let Ok(reg) = self.send_internal(&GetGPRSNetworkRegistrationStatus, false) {
434            self.status.compare_and_set(reg.into(), ts);
435        }
436
437        if let Ok(reg) = self.send_internal(&GetEPSNetworkRegistrationStatus, false) {
438            self.status.compare_and_set(reg.into(), ts);
439        }
440
441        Ok(())
442    }
443
444    pub(crate) fn handle_urc(&mut self) -> Result<(), Error> {
445        // TODO: How to do this cleaner?
446        let mut ctx_state = self.context_state;
447        let mut new_reg_params: Option<RegistrationParams> = None;
448
449        self.at_tx.handle_urc(|urc| {
450            match urc {
451                Urc::NetworkDetach => {
452                    defmt::warn!("Network Detach URC!");
453                }
454                Urc::MobileStationDetach => {
455                    defmt::warn!("ME Detach URC!");
456                }
457                Urc::NetworkDeactivate => {
458                    defmt::warn!("Network Deactivate URC!");
459                }
460                Urc::MobileStationDeactivate => {
461                    defmt::warn!("ME Deactivate URC!");
462                }
463                Urc::NetworkPDNDeactivate => {
464                    defmt::warn!("Network PDN Deactivate URC!");
465                }
466                Urc::MobileStationPDNDeactivate => {
467                    defmt::warn!("ME PDN Deactivate URC!");
468                }
469                Urc::ExtendedPSNetworkRegistration(psn::urc::ExtendedPSNetworkRegistration {
470                    state,
471                }) => {
472                    defmt::info!("[URC] ExtendedPSNetworkRegistration {}", state);
473                }
474                Urc::GPRSNetworkRegistration(reg_params) => {
475                    new_reg_params.replace(reg_params.into());
476                }
477                Urc::EPSNetworkRegistration(reg_params) => {
478                    new_reg_params.replace(reg_params.into());
479                }
480                Urc::NetworkRegistration(reg_params) => {
481                    new_reg_params.replace(reg_params.into());
482                }
483                Urc::DataConnectionActivated(psn::urc::DataConnectionActivated {
484                    result,
485                    ip_addr: _,
486                }) => {
487                    defmt::info!("[URC] DataConnectionActivated {=u8}", result);
488                    ctx_state = ContextState::Active;
489                }
490                Urc::DataConnectionDeactivated(psn::urc::DataConnectionDeactivated {
491                    profile_id,
492                }) => {
493                    defmt::info!("[URC] DataConnectionDeactivated {}", profile_id);
494                    ctx_state = ContextState::Activating;
495                }
496                Urc::MessageWaitingIndication(_) => {
497                    defmt::info!("[URC] MessageWaitingIndication");
498                }
499                _ => return false,
500            };
501            true
502        })?;
503
504        if let Some(reg_params) = new_reg_params {
505            let ts = self.status.timer.try_now().map_err(TimeError::from)?;
506            self.status.compare_and_set(reg_params, ts)
507        }
508
509        self.context_state = ctx_state;
510        Ok(())
511    }
512
513    pub(crate) fn send_internal<A, const LEN: usize>(
514        &mut self,
515        req: &A,
516        check_urc: bool,
517    ) -> Result<A::Response, Error>
518    where
519        A: atat::AtatCmd<LEN>,
520        A::Error: Into<UbloxError>,
521    {
522        if check_urc {
523            if let Err(e) = self.handle_urc() {
524                defmt::error!("Failed handle URC  {}", defmt::Debug2Format(&e));
525            }
526        }
527
528        self.at_tx.send(req)
529    }
530}
531
532#[cfg(test)]
533mod tests {
534    use embedded_time::{duration::*, Instant};
535
536    use crate::{
537        registration::Status,
538        test_helpers::{MockAtClient, MockTimer},
539    };
540
541    use super::*;
542
543    #[test]
544    #[ignore]
545    fn intervene_registration() {
546        // Setup
547        let tx = AtTx::new(MockAtClient::new(0), 5);
548        let timer = MockTimer::new(Some(25_234));
549        let mut network = Network::new(tx, timer);
550        network.status.conn_state = ConnectionState::Connecting;
551        // Update both started & updated
552        network
553            .status
554            .eps
555            .set_status(Status::NotRegistering, Instant::new(1234));
556        // Update only updated
557        network
558            .status
559            .eps
560            .set_status(Status::NotRegistering, Instant::new(1534));
561        network
562            .status
563            .csd
564            .set_status(Status::NotRegistering, Instant::new(1534));
565
566        assert_eq!(network.status.eps.updated(), Some(Instant::new(1534)));
567        assert_eq!(network.status.eps.started(), Some(Instant::new(1234)));
568        assert!(network.status.eps.sticky());
569
570        let ts = network.status.timer.try_now().unwrap();
571        assert_eq!(network.status.eps.duration(ts), Milliseconds(24_000_u32));
572
573        assert!(network.intervene_registration().is_ok());
574
575        assert_eq!(network.status.registration_interventions, 2);
576    }
577
578    #[test]
579    fn reset_reg_time() {
580        let tx = AtTx::new(MockAtClient::new(0), 5);
581        let timer = MockTimer::new(Some(1234));
582        let mut network = Network::new(tx, timer);
583
584        assert!(network.reset_reg_time().is_ok());
585
586        assert_eq!(network.status.reg_start_time, Some(Instant::new(1234)));
587        assert_eq!(network.status.reg_check_time, Some(Instant::new(1234)));
588    }
589
590    #[test]
591    fn check_registration_state() {
592        let tx = AtTx::new(MockAtClient::new(0), 5);
593        let timer = MockTimer::new(Some(1234));
594        let mut network = Network::new(tx, timer);
595
596        // Check that `ConnectionState` will change from `Connected` to `Connecting`
597        // with a state reset, if neither (csd + psd) || eps is actually registered
598        network.status.conn_state = ConnectionState::Connected;
599        network.status.registration_interventions = 3;
600        network
601            .status
602            .csd
603            .set_status(Status::Denied, Instant::new(1));
604        network
605            .status
606            .eps
607            .set_status(Status::NotRegistering, Instant::new(5));
608
609        assert!(network.check_registration_state().is_ok());
610
611        assert_eq!(network.status.conn_state, ConnectionState::Connecting);
612        assert_eq!(network.status.reg_start_time, Some(Instant::new(1234)));
613        assert_eq!(network.status.reg_check_time, Some(Instant::new(1234)));
614        assert_eq!(network.status.csd.get_status(), Status::None);
615        assert_eq!(network.status.csd.updated(), None);
616        assert_eq!(network.status.csd.started(), None);
617        assert_eq!(network.status.psd.get_status(), Status::None);
618        assert_eq!(network.status.psd.updated(), None);
619        assert_eq!(network.status.psd.started(), None);
620        assert_eq!(network.status.eps.get_status(), Status::None);
621        assert_eq!(network.status.eps.updated(), None);
622        assert_eq!(network.status.eps.started(), None);
623
624        // Check that `ConnectionState` will change from `Connecting` to `Connected`
625        // if eps is actually registered
626        network
627            .status
628            .eps
629            .set_status(Status::Roaming, Instant::new(5));
630
631        assert!(network.check_registration_state().is_ok());
632
633        assert_eq!(network.status.conn_state, ConnectionState::Connected);
634
635        // Check that `ConnectionState` will change from `Connecting` to `Connected`
636        // if (csd + psd) is actually registered
637        network.status.conn_state = ConnectionState::Connecting;
638        network.status.reset();
639        network
640            .status
641            .eps
642            .set_status(Status::Denied, Instant::new(5));
643        network
644            .status
645            .csd
646            .set_status(Status::Roaming, Instant::new(5));
647        network.status.psd.set_status(Status::Home, Instant::new(5));
648
649        assert!(network.check_registration_state().is_ok());
650
651        assert_eq!(network.status.conn_state, ConnectionState::Connected);
652    }
653
654    #[test]
655    fn unhandled_urcs() {
656        let mut tx = AtTx::new(MockAtClient::new(0), 5);
657
658        tx.handle_urc(|_| false).unwrap();
659        assert_eq!(tx.client.n_urcs_dequeued, 0);
660        tx.handle_urc(|_| false).unwrap();
661        tx.handle_urc(|_| false).unwrap();
662        tx.handle_urc(|_| false).unwrap();
663        tx.handle_urc(|_| false).unwrap();
664        tx.handle_urc(|_| false).unwrap();
665        assert_eq!(tx.client.n_urcs_dequeued, 1);
666        tx.handle_urc(|_| false).unwrap();
667        tx.handle_urc(|_| true).unwrap();
668        tx.handle_urc(|_| false).unwrap();
669        assert_eq!(tx.client.n_urcs_dequeued, 2);
670    }
671}