1use crate::{at, at_notifications::AtNotificationStream, error::Error, CancellationToken};
4use core::{mem, ops::ControlFlow, task::Poll};
5
6#[derive(Debug, PartialEq, Eq)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub struct LteLink(());
21
22impl LteLink {
23 pub async fn new() -> Result<Self, Error> {
25 if unsafe { !nrfxlib_sys::nrf_modem_is_initialized() } {
26 return Err(Error::ModemNotInitialized);
27 }
28
29 crate::MODEM_RUNTIME_STATE.activate_lte().await?;
30
31 Ok(LteLink(()))
32 }
33
34 pub async fn wait_for_link(&self) -> Result<(), Error> {
39 self.wait_for_link_with_cancellation(&Default::default())
40 .await
41 }
42
43 pub async fn wait_for_link_with_cancellation(
48 &self,
49 token: &CancellationToken,
50 ) -> Result<(), Error> {
51 use futures::StreamExt;
52
53 token.bind_to_current_task().await;
54
55 let notification_stream = AtNotificationStream::<64, 4>::new().await;
58 futures::pin_mut!(notification_stream);
59 notification_stream.as_mut().register().await;
60
61 at::send_at::<0>("AT+CEREG=1").await?;
63
64 token.as_result()?;
65
66 match Self::get_cereg_stat_control_flow(Self::parse_cereg(
69 at::send_at::<64>("AT+CEREG?").await?.as_str(),
70 )) {
71 ControlFlow::Continue(_) => {}
72 ControlFlow::Break(result) => return result,
73 }
74
75 token.as_result()?;
76
77 let mut stream = notification_stream
79 .map(|notif| Self::get_cereg_stat_control_flow(Self::parse_cereg(notif.as_str())));
80
81 while let Some(cereg) = core::future::poll_fn(|cx| {
82 if token.is_cancelled() {
83 Poll::Ready(None)
84 } else {
85 stream.poll_next_unpin(cx)
86 }
87 })
88 .await
89 {
90 match cereg {
91 ControlFlow::Continue(_) => {
92 token.as_result()?;
93 }
94 ControlFlow::Break(result) => return result,
95 }
96 }
97
98 token.as_result()?;
99
100 unreachable!()
101 }
102
103 fn parse_cereg(string: &str) -> Result<i32, Error> {
104 let cereg = at_commands::parser::CommandParser::parse(string.as_bytes())
109 .expect_identifier(b"+CEREG:")
110 .expect_int_parameter()
111 .expect_int_parameter()
112 .expect_identifier(b"\r\nOK\r\n")
113 .finish()
114 .map(|(_, stat)| stat);
115
116 cereg
117 .or_else(|_| {
118 at_commands::parser::CommandParser::parse(string.as_bytes())
119 .expect_identifier(b"+CEREG:")
120 .expect_int_parameter()
121 .expect_identifier(b"\r\n")
122 .finish()
123 .map(|(stat,)| stat)
124 })
125 .map_err(|e| e.into())
126 }
127
128 fn get_cereg_stat_control_flow(stat: Result<i32, Error>) -> ControlFlow<Result<(), Error>, ()> {
129 match stat {
131 Err(_) => ControlFlow::Continue(()),
132 Ok(1) | Ok(5) => ControlFlow::Break(Ok(())),
133 Ok(0) | Ok(2) | Ok(4) => ControlFlow::Continue(()),
134 Ok(3) => ControlFlow::Break(Err(Error::LteRegistrationDenied)),
135 Ok(90) => ControlFlow::Break(Err(Error::SimFailure)),
136 _ => ControlFlow::Break(Err(Error::UnexpectedAtResponse)),
137 }
138 }
139
140 pub async fn deactivate(self) -> Result<(), Error> {
142 mem::forget(self);
143 let result = crate::MODEM_RUNTIME_STATE.deactivate_lte().await;
144
145 if result.is_err() {
146 crate::MODEM_RUNTIME_STATE.set_error_active();
147 }
148
149 result
150 }
151}
152
153impl Drop for LteLink {
154 fn drop(&mut self) {
155 #[cfg(feature = "defmt")]
156 defmt::warn!(
157 "Turning off LTE synchronously. Use async function `deactivate` to avoid blocking and to get more guarantees that the modem is actually shut off."
158 );
159
160 if let Err(_e) = crate::MODEM_RUNTIME_STATE.deactivate_lte_blocking() {
161 #[cfg(feature = "defmt")]
162 defmt::error!("Could not turn off the lte: {}", _e);
163 crate::MODEM_RUNTIME_STATE.set_error_active();
164 }
165 }
166}