1use crate::error::{Error, ErrorSource};
4use arrayvec::ArrayString;
5use core::{
6 cell::RefCell,
7 future::Future,
8 ops::DerefMut,
9 sync::atomic::{AtomicBool, AtomicPtr, Ordering},
10 task::Poll,
11};
12use critical_section::Mutex;
13use futures::task::AtomicWaker;
14
15static AT_PROGRESS: AtomicBool = AtomicBool::new(false);
22static AT_PROGRESS_WAKER: AtomicWaker = AtomicWaker::new();
25
26static AT_DATA: Mutex<RefCell<(AtomicPtr<u8>, usize)>> =
28 Mutex::new(RefCell::new((AtomicPtr::new(core::ptr::null_mut()), 0)));
29static AT_DATA_WAKER: AtomicWaker = AtomicWaker::new();
31
32unsafe extern "C" fn at_callback(resp: *const core::ffi::c_char) {
35 #[cfg(feature = "defmt")]
36 defmt::trace!(
37 "AT <- {}",
38 core::ffi::CStr::from_ptr(resp as _).to_str().unwrap()
39 );
40
41 critical_section::with(|cs| {
43 let mut data = AT_DATA.borrow_ref_mut(cs);
44 let (ptr, size) = data.deref_mut();
45
46 if ptr.get_mut().is_null() {
47 return;
48 }
49
50 let mut index = 0;
52 while index < *size && *resp.add(index) != 0 {
53 *ptr.get_mut().add(index) = *resp.add(index) as _;
54 index += 1;
55 }
56
57 *ptr = AtomicPtr::default();
59 *size = 0;
60 });
61 AT_DATA_WAKER.wake();
62}
63
64pub async fn send_at<const CAP: usize>(command: &str) -> Result<ArrayString<CAP>, Error> {
71 SendATFuture {
72 state: Default::default(),
73 command: command.as_bytes(),
74 response: [0; CAP],
75 }
76 .await
77}
78
79pub async fn send_at_bytes<const CAP: usize>(command: &[u8]) -> Result<ArrayString<CAP>, Error> {
81 SendATFuture {
82 state: Default::default(),
83 command,
84 response: [0; CAP],
85 }
86 .await
87}
88
89pub fn send_at_blocking<const CAP: usize>(command: &str) -> Result<ArrayString<CAP>, Error> {
96 #[cfg(feature = "defmt")]
97 defmt::trace!("AT -> {}", command);
98
99 let string = if CAP > 0 {
100 let mut buffer = [0; CAP];
101 unsafe {
102 nrfxlib_sys::nrf_modem_at_cmd(
103 buffer.as_mut_ptr() as _,
104 buffer.len(),
105 c"%.*s".as_ptr() as *const core::ffi::c_char,
106 command.len(),
107 command.as_ptr(),
108 )
109 .into_result()?;
110 }
111
112 let mut return_string = ArrayString::from_byte_string(&buffer).unwrap();
113 strip_null_bytes(&mut return_string);
114 return_string
115 } else {
116 unsafe {
117 nrfxlib_sys::nrf_modem_at_printf(
118 c"%.*s".as_ptr() as *const core::ffi::c_char,
119 command.len(),
120 command.as_ptr(),
121 )
122 .into_result()?;
123 }
124
125 ArrayString::new()
126 };
127
128 #[cfg(feature = "defmt")]
129 defmt::trace!("AT <- {}", string.as_str());
130
131 Ok(string)
132}
133
134struct SendATFuture<'c, const CAP: usize> {
135 state: SendATState,
136 command: &'c [u8],
137 response: [u8; CAP],
138}
139
140impl<const CAP: usize> Future for SendATFuture<'_, CAP> {
141 type Output = Result<ArrayString<CAP>, Error>;
142
143 fn poll(
144 mut self: core::pin::Pin<&mut Self>,
145 cx: &mut core::task::Context<'_>,
146 ) -> core::task::Poll<Self::Output> {
147 match self.state {
148 SendATState::WaitingOnAccess => {
149 if AT_PROGRESS.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
150 == Ok(false)
151 {
152 self.state = SendATState::AccessGranted;
153 cx.waker().wake_by_ref();
154 Poll::Pending
155 } else {
156 AT_PROGRESS_WAKER.register(cx.waker());
157 Poll::Pending
158 }
159 }
160 SendATState::AccessGranted => {
161 critical_section::with(|cs| {
163 *AT_DATA.borrow_ref_mut(cs) = (AtomicPtr::new(self.response.as_mut_ptr()), CAP)
164 });
165 AT_DATA_WAKER.register(cx.waker());
166
167 #[cfg(feature = "defmt")]
168 defmt::trace!(
169 "AT -> {}",
170 defmt::unwrap!(core::str::from_utf8(self.command).ok())
171 );
172
173 let result = unsafe {
174 nrfxlib_sys::nrf_modem_at_cmd_async(
175 Some(at_callback),
176 c"%.*s".as_ptr() as *const core::ffi::c_char,
177 self.command.len(),
178 self.command.as_ptr(),
179 )
180 .into_result()
181 };
182
183 match result {
184 Ok(_) => {
185 self.state = SendATState::WaitingOnData;
186 Poll::Pending
187 }
188 Err(e) => Poll::Ready(Err(e)),
189 }
190 }
191 SendATState::WaitingOnData => critical_section::with(|cs| {
192 let mut data = AT_DATA.borrow_ref_mut(cs);
193
194 if data.0.get_mut().is_null() {
195 if let Some(last) = self.response.last_mut() {
199 *last = 0
200 }
201
202 let mut return_string = ArrayString::from_byte_string(&self.response).unwrap();
203 strip_null_bytes(&mut return_string);
204
205 Poll::Ready(Ok(return_string))
206 } else {
207 AT_DATA_WAKER.register(cx.waker());
208 Poll::Pending
209 }
210 }),
211 }
212 }
213}
214
215impl<const CAP: usize> Drop for SendATFuture<'_, CAP> {
216 fn drop(&mut self) {
217 match self.state {
218 SendATState::WaitingOnAccess => {}
219 SendATState::AccessGranted | SendATState::WaitingOnData => {
220 critical_section::with(|cs| {
224 *AT_DATA.borrow_ref_mut(cs) = (AtomicPtr::default(), 0)
225 });
226 AT_PROGRESS.store(false, Ordering::SeqCst);
227 AT_PROGRESS_WAKER.wake();
228 }
229 }
230 }
231}
232
233#[derive(Default)]
234enum SendATState {
235 #[default]
236 WaitingOnAccess,
237 AccessGranted,
238 WaitingOnData,
239}
240
241fn strip_null_bytes<const CAP: usize>(string: &mut ArrayString<CAP>) {
242 if let Some((reverse_index, _)) = string
243 .bytes()
244 .rev()
245 .enumerate()
246 .find(|(_, byte)| *byte != 0)
247 {
248 let index = string.len() - reverse_index;
249 string.truncate(index);
250 }
251}