1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//! # AT Sockets for nrfxlib
//!
//! AT socket related code. AT commands are sent to the modem down a socket
//! using the Nordic-specific `SOCK_PROTO_AT`.
//!
//! Copyright (c) 42 Technology Ltd 2019
//!
//! Dual-licensed under MIT and Apache 2.0. See the [README](../README.md) for
//! more details.

//******************************************************************************
// Sub-Modules
//******************************************************************************

// None

//******************************************************************************
// Imports
//******************************************************************************

use crate::{raw::*, AtError, Error};

//******************************************************************************
// Types
//******************************************************************************

/// Represents a connection to the modem using AT Commands.
#[derive(Debug)]
pub struct AtSocket(Socket);

//******************************************************************************
// Constants
//******************************************************************************

// None

//******************************************************************************
// Global Variables
//******************************************************************************

// None

//******************************************************************************
// Macros
//******************************************************************************

// None

//******************************************************************************
// Public Functions and Impl on Public Types
//******************************************************************************

impl AtSocket {
	/// Create a new AT socket.
	pub fn new() -> Result<AtSocket, Error> {
		let skt = Socket::new(SocketDomain::Lte, SocketType::Datagram, SocketProtocol::At)?;
		Ok(AtSocket(skt))
	}

	/// Send an AT command to the modem
	pub fn send_command(&self, command: &str) -> Result<(), Error> {
		self.0.write(command.as_bytes()).map(|_count| ())
	}

	/// Read from the AT socket until we get something that indicates the command has completed.
	///
	/// Commands are completed by `OK`, `ERROR`, `+CME ERROR:xxx` or `+CMS
	/// ERROR:xxx`. These are mapped to a Rust `Result` type.
	///
	/// Any other data received is deemed to be a command result and passed to the given fn `callback_function`.
	pub fn poll_response<F>(&mut self, mut callback_function: F) -> Result<(), Error>
	where
		F: FnMut(&str),
	{
		let result;
		'outer: loop {
			let mut buf = [0u8; 256];
			let length = 'inner: loop {
				match self.recv(&mut buf)? {
					None => {
						// EAGAIN
					}
					Some(n) => break 'inner n,
				};
			};
			let s = unsafe { core::str::from_utf8_unchecked(&buf[0..length - 1]) };
			for line in s.lines() {
				let line = line.trim();
				match line {
					"OK" => {
						result = Ok(());
						break 'outer;
					}
					"ERROR" => {
						result = Err(Error::AtError(AtError::Error));
						break 'outer;
					}
					err if err.starts_with("+CME ERROR:") => {
						let num_str = &err[11..];
						let value = num_str.trim().parse().unwrap_or(-1);
						result = Err(Error::AtError(AtError::CmeError(value)));
						break 'outer;
					}
					err if err.starts_with("+CMS ERROR:") => {
						let num_str = &err[11..];
						let value = num_str.trim().parse().unwrap_or(-1);
						result = Err(Error::AtError(AtError::CmsError(value)));
						break 'outer;
					}
					data => {
						callback_function(data);
					}
				}
			}
		}
		result
	}
}

impl Pollable for AtSocket {
	/// Get the underlying socket ID for this socket.
	fn get_fd(&self) -> i32 {
		self.0.fd
	}
}

impl core::ops::DerefMut for AtSocket {
	fn deref_mut(&mut self) -> &mut Socket {
		&mut self.0
	}
}

impl core::ops::Deref for AtSocket {
	type Target = Socket;
	fn deref(&self) -> &Socket {
		&self.0
	}
}

/// Sends an AT command to the modem and calls the given closure with any
/// indications received. Indications have any whitespace or newlines trimmed.
///
/// Creates and destroys a new NRF_AF_LTE/NRF_PROTO_AT socket. Will block
/// until we get 'OK' or some sort of error response from the modem.
pub fn send_at_command<F>(command: &str, function: F) -> Result<(), Error>
where
	F: FnMut(&str),
{
	let mut skt = AtSocket::new()?;
	skt.send_command(command)?;
	skt.poll_response(function)
}

//******************************************************************************
// Private Functions and Impl on Private Types
//******************************************************************************

// None

//******************************************************************************
// End of File
//******************************************************************************