aboutsummaryrefslogtreecommitdiff
path: root/embassy-net-nrf91
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-net-nrf91')
-rw-r--r--embassy-net-nrf91/Cargo.toml1
-rw-r--r--embassy-net-nrf91/src/context.rs208
2 files changed, 107 insertions, 102 deletions
diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml
index e2d596d59..07a0c8886 100644
--- a/embassy-net-nrf91/Cargo.toml
+++ b/embassy-net-nrf91/Cargo.toml
@@ -26,6 +26,7 @@ embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-
26 26
27heapless = "0.8" 27heapless = "0.8"
28embedded-io = "0.6.1" 28embedded-io = "0.6.1"
29at-commands = "0.5.4"
29 30
30[package.metadata.embassy_docs] 31[package.metadata.embassy_docs]
31src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-nrf91-v$VERSION/embassy-net-nrf91/src/" 32src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-nrf91-v$VERSION/embassy-net-nrf91/src/"
diff --git a/embassy-net-nrf91/src/context.rs b/embassy-net-nrf91/src/context.rs
index c47b1ade6..b532dca14 100644
--- a/embassy-net-nrf91/src/context.rs
+++ b/embassy-net-nrf91/src/context.rs
@@ -2,6 +2,8 @@ use core::net::IpAddr;
2use heapless::String; 2use heapless::String;
3use core::str::FromStr; 3use core::str::FromStr;
4use core::fmt::Write; 4use core::fmt::Write;
5use heapless::Vec;
6use at_commands::{builder::CommandBuilder, parser::CommandParser};
5 7
6/// Provides a higher level API for configuring and reading information for a given 8/// Provides a higher level API for configuring and reading information for a given
7/// context id. 9/// context id.
@@ -28,10 +30,17 @@ pub enum AuthProt {
28pub enum Error { 30pub enum Error {
29 BufferTooSmall, 31 BufferTooSmall,
30 AtCommand, 32 AtCommand,
33 AtParseError,
31 AddrParseError, 34 AddrParseError,
32 Format, 35 Format,
33} 36}
34 37
38impl From<at_commands::parser::ParseError> for Error {
39 fn from(_: at_commands::parser::ParseError) -> Self {
40 Self::AtParseError
41 }
42}
43
35impl From<core::fmt::Error> for Error { 44impl From<core::fmt::Error> for Error {
36 fn from(_: core::fmt::Error) -> Self { 45 fn from(_: core::fmt::Error) -> Self {
37 Self::Format 46 Self::Format
@@ -42,6 +51,8 @@ impl From<core::fmt::Error> for Error {
42pub struct Status { 51pub struct Status {
43 pub attached: bool, 52 pub attached: bool,
44 pub ip: Option<IpAddr>, 53 pub ip: Option<IpAddr>,
54 pub gateway: Option<IpAddr>,
55 pub dns: Vec<IpAddr, 2>,
45} 56}
46 57
47#[cfg(feature = "defmt")] 58#[cfg(feature = "defmt")]
@@ -67,129 +78,122 @@ impl<'a> Control<'a> {
67 78
68 /// Configures the modem with the provided config. 79 /// Configures the modem with the provided config.
69 pub async fn configure(&self, config: Config<'_>) -> Result<(), Error> { 80 pub async fn configure(&self, config: Config<'_>) -> Result<(), Error> {
70 let mut cmd: String<128> = String::new(); 81 let mut cmd: [u8; 256] = [0; 256];
71 let mut buf: [u8; 256] = [0; 256]; 82 let mut buf: [u8; 256] = [0; 256];
72 83
73 write!(cmd, "AT+CGDCONT={},\"IP\",\"{}\"", self.cid, config.gateway).map_err(|_| Error::BufferTooSmall)?; 84 let op = CommandBuilder::create_set(&mut cmd, true)
74 let n = self.control.at_command(cmd.as_bytes(), &mut buf).await; 85 .named("+CGDCONT")
75 let mut res = &buf[..n]; 86 .with_int_parameter(self.cid)
76 let res = split_field(&mut res); 87 .with_string_parameter("IP")
77 if res != b"OK" { 88 .with_string_parameter(config.gateway)
78 return Err(Error::AtCommand) 89 .finish().map_err(|_| Error::BufferTooSmall)?;
79 } 90 let n = self.control.at_command(op, &mut buf).await;
80 cmd.clear(); 91 CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
81 92
82 write!(cmd, "AT+CGAUTH={},{}", self.cid, config.auth_prot as u8)?; 93 let mut op = CommandBuilder::create_set(&mut cmd, true)
94 .named("+CGAUTH")
95 .with_int_parameter(self.cid)
96 .with_int_parameter(config.auth_prot as u8);
83 if let Some((username, password)) = config.auth { 97 if let Some((username, password)) = config.auth {
84 write!(cmd, ",\"{}\",\"{}\"", username, password).map_err(|_| Error::BufferTooSmall)?; 98 op = op.with_string_parameter(username).with_string_parameter(password);
85 }
86 let n = self.control.at_command(cmd.as_bytes(), &mut buf).await;
87 let mut res = &buf[..n];
88 let res = split_field(&mut res);
89 if res != b"OK" {
90 return Err(Error::AtCommand)
91 } 99 }
92 cmd.clear(); 100 let op = op.finish().map_err(|_| Error::BufferTooSmall)?;
93 101
94 let n = self.control.at_command(b"AT+CFUN=1", &mut buf).await; 102 let n = self.control.at_command(op, &mut buf).await;
95 let mut res = &buf[..n]; 103 CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
96 let res = split_field(&mut res); 104
97 if res != b"OK" { 105 let op = CommandBuilder::create_set(&mut cmd, true)
98 return Err(Error::AtCommand); 106 .named("+CFUN")
99 } 107 .with_int_parameter(1)
108 .finish().map_err(|_| Error::BufferTooSmall)?;
109 let n = self.control.at_command(op, &mut buf).await;
110 CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?;
100 111
101 Ok(()) 112 Ok(())
102 } 113 }
103 114
104 pub async fn status(&self) -> Result<Status, Error> { 115 pub async fn status(&self) -> Result<Status, Error> {
116 let mut cmd: [u8; 256] = [0; 256];
105 let mut buf: [u8; 256] = [0; 256]; 117 let mut buf: [u8; 256] = [0; 256];
106 let n = self.control.at_command(b"AT+CGATT?", &mut buf).await;
107 let mut res = &buf[..n];
108 pop_prefix(&mut res, b"+CGATT: ");
109 let res = split_field(&mut res);
110 let attached = res == b"1";
111 118
119 let op = CommandBuilder::create_query(&mut cmd, true)
120 .named("+CGATT")
121 .finish().map_err(|_| Error::BufferTooSmall)?;
122 let n = self.control.at_command(op, &mut buf).await;
123 let (res, ) = CommandParser::parse(&buf[..n])
124 .expect_identifier(b"+CGATT: ")
125 .expect_int_parameter()
126 .expect_identifier(b"\r\nOK").finish()?;
127 let attached = res == 1;
112 if !attached { 128 if !attached {
113 return Ok(Status { attached, ip: None }) 129 return Ok(Status { attached, ip: None, gateway: None, dns: Vec::new() })
114 } 130 }
115 131
116 let mut s: String<128> = String::new(); 132 let op = CommandBuilder::create_set(&mut cmd, true)
117 write!(s, "AT+CGPADDR={}", self.cid)?; 133 .named("+CGPADDR")
118 let n = self.control.at_command(s.as_bytes(), &mut buf).await; 134 .with_int_parameter(self.cid)
119 let mut res = &buf[..n]; 135 .finish().map_err(|_| Error::BufferTooSmall)?;
120 s.clear(); 136 let n = self.control.at_command(op, &mut buf).await;
121 137 let (_, ip1, ip2, ) = CommandParser::parse(&buf[..n])
122 write!(s, "+CGPADDR: {},", self.cid)?; 138 .expect_identifier(b"+CGPADDR: ")
123 139 .expect_int_parameter()
124 if s.len() > res.len() { 140 .expect_optional_string_parameter()
125 let res = split_field(&mut res); 141 .expect_optional_string_parameter()
126 if res == b"OK" { 142 .expect_identifier(b"\r\nOK").finish()?;
127 Ok(Status { attached, ip: None }) 143
144 let ip = if let Some(ip) = ip1 {
145 let ip = IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?;
146 self.control.open_raw_socket().await;
147 Some(ip)
148 } else {
149 None
150 };
151
152 let op = CommandBuilder::create_set(&mut cmd, true)
153 .named("+CGCONTRDP")
154 .with_int_parameter(self.cid)
155 .finish().map_err(|_| Error::BufferTooSmall)?;
156 let n = self.control.at_command(op, &mut buf).await;
157 let (_cid, _bid, _apn, _mask, gateway, dns1, dns2, _, _, _, _, mtu) = CommandParser::parse(&buf[..n])
158 .expect_identifier(b"+CGCONTRDP: ")
159 .expect_int_parameter()
160 .expect_optional_int_parameter()
161 .expect_optional_string_parameter()
162 .expect_optional_string_parameter()
163 .expect_optional_string_parameter()
164 .expect_optional_string_parameter()
165 .expect_optional_string_parameter()
166 .expect_optional_int_parameter()
167 .expect_optional_int_parameter()
168 .expect_optional_int_parameter()
169 .expect_optional_int_parameter()
170 .expect_optional_int_parameter()
171 .expect_identifier(b"\r\nOK").finish()?;
172
173 let gateway = if let Some(ip) = gateway {
174 if ip.is_empty() {
175 None
128 } else { 176 } else {
129 Err(Error::AtCommand) 177 Some(IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?)
130 } 178 }
131 } else { 179 } else {
132 pop_prefix(&mut res, s.as_bytes()); 180 None
181 };
133 182
134 let ip = split_field(&mut res); 183 let mut dns = Vec::new();
135 if !ip.is_empty() { 184 if let Some(ip) = dns1 {
136 let ip = IpAddr::from_str(unsafe { core::str::from_utf8_unchecked(ip) }).map_err(|_| Error::AddrParseError)?; 185 dns.push(IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?).unwrap();
137 self.control.open_raw_socket().await;
138 Ok(Status { attached, ip: Some(ip) })
139 } else {
140 Ok(Status { attached, ip: None })
141 }
142 } 186 }
143 }
144}
145 187
146pub(crate) fn is_whitespace(char: u8) -> bool { 188 if let Some(ip) = dns2 {
147 match char { 189 dns.push(IpAddr::from_str(ip).map_err(|_| Error::AddrParseError)?).unwrap();
148 b'\r' | b'\n' | b' ' => true,
149 _ => false,
150 }
151}
152
153pub(crate) fn is_separator(char: u8) -> bool {
154 match char {
155 b',' | b'\r' | b'\n' | b' ' => true,
156 _ => false,
157 }
158}
159
160pub(crate) fn split_field<'a>(data: &mut &'a [u8]) -> &'a [u8] {
161 while !data.is_empty() && is_whitespace(data[0]) {
162 *data = &data[1..];
163 }
164
165 if data.is_empty() {
166 return &[];
167 }
168
169 if data[0] == b'"' {
170 let data2 = &data[1..];
171 let end = data2.iter().position(|&x| x == b'"').unwrap_or(data2.len());
172 let field = &data2[..end];
173 let mut rest = &data2[data2.len().min(end + 1)..];
174 if rest.first() == Some(&b'\"') {
175 rest = &rest[1..];
176 }
177 while !rest.is_empty() && is_separator(rest[0]) {
178 rest = &rest[1..];
179 } 190 }
180 *data = rest;
181 field
182 } else {
183 let end = data.iter().position(|&x| is_separator(x)).unwrap_or(data.len());
184 let field = &data[0..end];
185 let rest = &data[data.len().min(end + 1)..];
186 *data = rest;
187 field
188 }
189}
190 191
191pub(crate) fn pop_prefix(data: &mut &[u8], prefix: &[u8]) { 192 Ok(Status {
192 assert!(data.len() >= prefix.len()); 193 attached,
193 assert!(&data[..prefix.len()] == prefix); 194 ip,
194 *data = &data[prefix.len()..]; 195 gateway,
196 dns,
197 })
198 }
195} 199}