mirror of https://github.com/ARMmbed/mbed-os.git
533 lines
19 KiB
C++
533 lines
19 KiB
C++
/*
|
|
* Copyright (c) 2017, Arm Limited and affiliates.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#ifndef AT_HANDLER_H_
|
|
#define AT_HANDLER_H_
|
|
|
|
#include "platform/mbed_retarget.h"
|
|
|
|
#include "EventQueue.h"
|
|
#include "PlatformMutex.h"
|
|
#include "nsapi_types.h"
|
|
|
|
#include "PlatformMutex.h"
|
|
#include "Callback.h"
|
|
#include "EventQueue.h"
|
|
|
|
namespace mbed {
|
|
|
|
class FileHandle;
|
|
|
|
/**
|
|
* If application calls associated FileHandle only from single thread context
|
|
* then locking between AT command and response is not needed. However,
|
|
* note that many cellular functions are called indirectly, for example with the socket API.
|
|
* If you are unsure, then AT_HANDLER_MUTEX must be defined.
|
|
*/
|
|
#define AT_HANDLER_MUTEX
|
|
|
|
extern const char *OK;
|
|
extern const char *CRLF;
|
|
|
|
#define BUFF_SIZE 16
|
|
|
|
/* AT Error types enumeration */
|
|
enum DeviceErrorType {
|
|
DeviceErrorTypeNoError = 0,
|
|
DeviceErrorTypeError, // AT ERROR
|
|
DeviceErrorTypeErrorCMS, // AT ERROR CMS
|
|
DeviceErrorTypeErrorCME // AT ERROR CME
|
|
};
|
|
|
|
/* struct used when getting at response error. Defines error code and type */
|
|
struct device_err_t {
|
|
DeviceErrorType errType;
|
|
int errCode;
|
|
};
|
|
|
|
/** Class ATHandler
|
|
*
|
|
* Class for sending AT commands and parsing AT responses.
|
|
*/
|
|
class ATHandler {
|
|
|
|
public:
|
|
/** Constructor
|
|
*
|
|
* @param fh file handle used for reading AT responses and writing AT commands
|
|
* @param queue Event queue used to transfer sigio events to this thread
|
|
* @param timeout Timeout when reading for AT response
|
|
* @param output_delimiter delimiter used when parsing at responses, "\r" should be used as output_delimiter
|
|
* @param send_delay the minimum delay in ms between the end of last response and the beginning of a new command
|
|
*/
|
|
ATHandler(FileHandle *fh, events::EventQueue &queue, int timeout, const char *output_delimiter, uint16_t send_delay = 0);
|
|
virtual ~ATHandler();
|
|
|
|
/** Return used file handle.
|
|
*
|
|
* @return used file handle
|
|
*/
|
|
FileHandle *get_file_handle();
|
|
|
|
/** Locks the mutex for file handle if AT_HANDLER_MUTEX is defined.
|
|
*/
|
|
void lock();
|
|
|
|
/** Unlocks the mutex for file handle if AT_HANDLER_MUTEX is defined.
|
|
*/
|
|
void unlock();
|
|
|
|
/** Locks the mutex for file handle if AT_HANDLER_MUTEX is defined and returns the last error.
|
|
*
|
|
* @return last error that happened when parsing AT responses
|
|
*/
|
|
nsapi_error_t unlock_return_error();
|
|
|
|
/** Set the urc callback for urc. If urc is found when parsing AT responses, then call if called.
|
|
* If urc is already set then it's not set twice.
|
|
*
|
|
* @param prefix Register urc prefix for callback. Urc could be for example "+CMTI: "
|
|
* @param callback Callback, which is called if urc is found in AT response
|
|
* @return NSAPI_ERROR_OK or NSAPI_ERROR_NO_MEMORY if no memory
|
|
*/
|
|
nsapi_error_t set_urc_handler(const char *prefix, mbed::Callback<void()> callback);
|
|
|
|
/** Remove urc handler from linked list of urc's
|
|
*
|
|
* @param prefix Register urc prefix for callback. Urc could be for example "+CMTI: "
|
|
*/
|
|
void remove_urc_handler(const char *prefix);
|
|
|
|
ATHandler *_nextATHandler; // linked list
|
|
|
|
/** returns the last error while parsing AT responses.
|
|
*
|
|
* @return last error
|
|
*/
|
|
nsapi_error_t get_last_error() const;
|
|
|
|
/** returns the last device error while parsing AT responses. Actually AT error (CME/CMS).
|
|
*
|
|
* @return last error struct device_err_t
|
|
*/
|
|
device_err_t get_last_device_error() const;
|
|
|
|
/** Increase reference count. Used for counting references to this instance.
|
|
*/
|
|
void inc_ref_count();
|
|
|
|
/** Decrease reference count. Used for counting references to this instance.
|
|
*/
|
|
void dec_ref_count();
|
|
|
|
/** Get the current reference count. Used for counting references to this instance.
|
|
*
|
|
* @return current reference count
|
|
*/
|
|
int get_ref_count();
|
|
|
|
/** Set timeout in milliseconds for AT commands
|
|
*
|
|
* @param timeout_milliseconds Timeout in milliseconds
|
|
* @param default_timeout Store as default timeout
|
|
*/
|
|
void set_at_timeout(uint32_t timeout_milliseconds, bool default_timeout = false);
|
|
|
|
/** Restore timeout to previous timeout. Handy if there is a need to change timeout temporarily.
|
|
*/
|
|
void restore_at_timeout();
|
|
|
|
/** Clear pending error flag. By default, error is cleared only in lock().
|
|
*/
|
|
void clear_error();
|
|
|
|
/**
|
|
* Flushes the underlying stream
|
|
*/
|
|
void flush();
|
|
|
|
/** Tries to find oob's from the AT response. Call the urc callback if one is found.
|
|
*/
|
|
void process_oob();
|
|
|
|
/** Set sigio for the current file handle. Sigio event goes through eventqueue so that it's handled in current thread.
|
|
*/
|
|
void set_filehandle_sigio();
|
|
|
|
/** Set file handle, which is used for reading AT responses and writing AT commands
|
|
*
|
|
* @param fh file handle used for reading AT responses and writing AT commands
|
|
*/
|
|
void set_file_handle(FileHandle *fh);
|
|
|
|
/** Set is file handle usable. Some situations like after going to data mode, file handle is not usable anymore.
|
|
* Any items in queue are not to be processed.
|
|
*
|
|
* @param usable true for usable filehandle
|
|
*/
|
|
void set_is_filehandle_usable(bool usable);
|
|
|
|
protected:
|
|
void event();
|
|
#ifdef AT_HANDLER_MUTEX
|
|
PlatformMutex _fileHandleMutex;
|
|
#endif
|
|
FileHandle *_fileHandle;
|
|
private:
|
|
|
|
void set_error(nsapi_error_t err);
|
|
|
|
events::EventQueue &_queue;
|
|
nsapi_error_t _last_err;
|
|
int _last_3gpp_error;
|
|
device_err_t _last_at_err;
|
|
uint16_t _oob_string_max_length;
|
|
char *_output_delimiter;
|
|
|
|
struct oob_t {
|
|
const char *prefix;
|
|
int prefix_len;
|
|
mbed::Callback<void()> cb;
|
|
oob_t *next;
|
|
};
|
|
oob_t *_oobs;
|
|
uint32_t _at_timeout;
|
|
uint32_t _previous_at_timeout;
|
|
|
|
uint16_t _at_send_delay;
|
|
uint64_t _last_response_stop;
|
|
|
|
bool _fh_sigio_set;
|
|
|
|
bool _processing;
|
|
int32_t _ref_count;
|
|
bool _is_fh_usable;
|
|
|
|
//*************************************
|
|
public:
|
|
|
|
/** Starts the command writing by clearing the last error and writing the given command.
|
|
* In case of failure when writing, the last error is set to NSAPI_ERROR_DEVICE_ERROR.
|
|
*
|
|
* @param cmd AT command to be written to modem
|
|
*/
|
|
virtual void cmd_start(const char *cmd);
|
|
|
|
/** Writes integer type AT command subparameter. Starts with the delimiter if not the first param after cmd_start.
|
|
* In case of failure when writing, the last error is set to NSAPI_ERROR_DEVICE_ERROR.
|
|
*
|
|
* @param param int to be written to modem as AT command subparameter
|
|
*/
|
|
void write_int(int32_t param);
|
|
|
|
/** Writes string type AT command subparamater. Quotes are added to surround the given string.
|
|
* Starts with the delimiter if not the first param after cmd_start.
|
|
* In case of failure when writing, the last error is set to NSAPI_ERROR_DEVICE_ERROR.
|
|
*
|
|
* @param param string to be written to modem as AT command subparameter
|
|
* @param useQuotations flag indicating whether the string should be included in quotation marks
|
|
*/
|
|
void write_string(const char *param, bool useQuotations = true);
|
|
|
|
/** Stops the AT command by writing command-line terminator CR to mark command as finished.
|
|
*/
|
|
void cmd_stop();
|
|
|
|
/** Stops the AT command by writing command-line terminator CR to mark command as finished and reads the OK/ERROR response.
|
|
*
|
|
*/
|
|
void cmd_stop_read_resp();
|
|
|
|
/** Write bytes without any subparameter delimiters, such as comma.
|
|
* In case of failure when writing, the last error is set to NSAPI_ERROR_DEVICE_ERROR.
|
|
*
|
|
* @param data bytes to be written to modem
|
|
* @param len length of data string
|
|
*
|
|
* @return number of characters successfully written
|
|
*/
|
|
size_t write_bytes(const uint8_t *data, size_t len);
|
|
|
|
/** Sets the stop tag for the current scope (response/information response/element)
|
|
* Parameter's reading routines will stop the reading when such tag is found and will set the found flag.
|
|
* Consume routines will read everything until such tag is found.
|
|
*
|
|
* @param stop_tag_seq string to be set as stop tag
|
|
*/
|
|
void set_stop_tag(const char *stop_tag_seq);
|
|
|
|
/** Sets the delimiter between parameters or between elements of the information response.
|
|
* Parameter's reading routines will stop when such char is read.
|
|
*
|
|
* @param delimiter char to be set as _delimiter
|
|
*/
|
|
void set_delimiter(char delimiter);
|
|
|
|
/** Sets the delimiter to default value defined by DEFAULT_DELIMITER.
|
|
*/
|
|
void set_default_delimiter();
|
|
|
|
/** Defines behaviour for using or ignoring the delimiter within an AT command
|
|
*
|
|
* @param use_delimiter indicating if delimiter should be used or not
|
|
*/
|
|
void use_delimiter(bool use_delimiter);
|
|
|
|
/** Consumes the reading buffer up to the delimiter or stop_tag
|
|
*
|
|
* @param count number of parameters to be skipped
|
|
*/
|
|
void skip_param(uint32_t count = 1);
|
|
|
|
/** Consumes the given length from the reading buffer
|
|
*
|
|
* @param len length to be consumed from reading buffer
|
|
* @param count number of parameters to be skipped
|
|
*/
|
|
void skip_param(ssize_t len, uint32_t count);
|
|
|
|
/** Reads given number of bytes from receiving buffer without checking any subparameter delimiters, such as comma.
|
|
*
|
|
* @param buf output buffer for the read
|
|
* @param len maximum number of bytes to read
|
|
* @return number of successfully read bytes or -1 in case of error
|
|
*/
|
|
ssize_t read_bytes(uint8_t *buf, size_t len);
|
|
|
|
/** Reads chars from reading buffer. Terminates with null. Skips the quotation marks.
|
|
* Stops on delimiter or stop tag.
|
|
*
|
|
* @param str output buffer for the read
|
|
* @param size maximum number of chars to output including NULL
|
|
* @param read_even_stop_tag if true then try to read even if the stop tag was found previously
|
|
* @return length of output string or -1 in case of read timeout before delimiter or stop tag is found
|
|
*/
|
|
ssize_t read_string(char *str, size_t size, bool read_even_stop_tag = false);
|
|
|
|
/** Reads chars representing hex ascii values and converts them to the corresponding chars.
|
|
* For example: "4156" to "AV".
|
|
* Terminates with null. Skips the quotation marks.
|
|
* Stops on delimiter or stop tag.
|
|
*
|
|
* @param str output buffer for the read
|
|
* @param size maximum number of chars to output
|
|
* @return length of output string or -1 in case of read timeout before delimiter or stop tag is found
|
|
*/
|
|
ssize_t read_hex_string(char *str, size_t size);
|
|
|
|
/** Reads as string and converts result to integer. Supports only positive integers.
|
|
*
|
|
* @return the positive integer or -1 in case of error.
|
|
*/
|
|
int32_t read_int();
|
|
|
|
/** This looks for necessary matches: prefix, OK, ERROR, URCs and sets the correct scope.
|
|
*
|
|
* @param prefix string to be matched from receiving buffer. If not NULL and match succeeds, then scope
|
|
* will be set as information response(info_type)
|
|
* @param stop flag to indicate if we go to information response scope or not.
|
|
* (needed when nothing is expected to be received anymore after the prefix match:
|
|
* sms case: "> ", bc95 reboot case)
|
|
*/
|
|
void resp_start(const char *prefix = NULL, bool stop = false);
|
|
|
|
/** Ends all scopes starting from current scope.
|
|
* Consumes everything until the scope's stop tag is found, then
|
|
* goes to next scope until response scope is ending.
|
|
* Possible sequence:
|
|
* element scope -> information response scope -> response scope
|
|
*/
|
|
void resp_stop();
|
|
|
|
/** Looks for matching the prefix given to resp_start() call.
|
|
* If needed, it ends the scope of a previous information response.
|
|
* Sets the information response scope if new prefix is found and response scope if prefix is not found.
|
|
*
|
|
* @return true if new information response is found, false otherwise
|
|
*/
|
|
bool info_resp();
|
|
|
|
/** Looks for matching the start tag.
|
|
* If needed, it ends the scope of a previous element.
|
|
* Sets the element scope if start tag is found and information response scope if start tag is not found.
|
|
*
|
|
* @param start_tag tag to be matched to begin parsing an element of an information response
|
|
* @return true if new element is found, false otherwise
|
|
*/
|
|
bool info_elem(char start_tag);
|
|
|
|
/** Consumes the received content until current stop tag is found.
|
|
*
|
|
* @return true if stop tag is found, false otherwise
|
|
*/
|
|
bool consume_to_stop_tag();
|
|
|
|
/** Return the last 3GPP error code.
|
|
* @return last 3GPP error code
|
|
*/
|
|
int get_3gpp_error();
|
|
|
|
public: // just for debugging
|
|
/**
|
|
* AT debugging, when enabled will print all data read and written,
|
|
* non-printable chars are printed as "[%d]".
|
|
*
|
|
* AT debug can be enabled at compile time using MBED_CONF_CELLULAR_DEBUG_AT flag or at runtime
|
|
* calling set_debug(). Note that MBED_CONF_MBED_TRACE_ENABLE must also be enabled.
|
|
*
|
|
* @param debug_on Enable/disable debugging
|
|
*/
|
|
void set_debug(bool debug_on);
|
|
|
|
private:
|
|
|
|
// should fit any prefix and int
|
|
char _recv_buff[BUFF_SIZE];
|
|
// reading position
|
|
size_t _recv_len;
|
|
// reading length
|
|
size_t _recv_pos;
|
|
|
|
// resp_type: the part of the response that doesn't include the information response (+CMD1,+CMD2..)
|
|
// ends with OK or (CME)(CMS)ERROR
|
|
// info_type: the information response part of the response: starts with +CMD1 and ends with CRLF
|
|
// information response contains parameters or subsets of parameters (elements), both separated by comma
|
|
// elem_type: subsets of parameters that are part of information response, its parameters are separated by comma
|
|
enum ScopeType {RespType, InfoType, ElemType, NotSet};
|
|
void set_scope(ScopeType scope_type);
|
|
ScopeType _current_scope;
|
|
|
|
struct tag_t {
|
|
char tag[7];
|
|
size_t len;
|
|
bool found;
|
|
};
|
|
|
|
// tag to stop response scope
|
|
tag_t _resp_stop;
|
|
// tag to stop information response scope
|
|
tag_t _info_stop;
|
|
// tag to stop element scope
|
|
tag_t _elem_stop;
|
|
// reference to the stop tag of current scope (resp/info/elem)
|
|
tag_t *_stop_tag;
|
|
|
|
// delimiter between parameters and also used for delimiting elements of information response
|
|
char _delimiter;
|
|
// set true on prefix match -> indicates start of an information response or of an element
|
|
bool _prefix_matched;
|
|
// set true on urc match
|
|
bool _urc_matched;
|
|
// set true on (CME)(CMS)ERROR
|
|
bool _error_found;
|
|
// Max length of OK,(CME)(CMS)ERROR and URCs
|
|
size_t _max_resp_length;
|
|
|
|
// prefix set during resp_start and used to try matching possible information responses
|
|
char _info_resp_prefix[BUFF_SIZE];
|
|
bool _debug_on;
|
|
bool _cmd_start;
|
|
bool _use_delimiter;
|
|
|
|
// time when a command or an URC processing was started
|
|
uint64_t _start_time;
|
|
|
|
// Gets char from receiving buffer.
|
|
// Resets and fills the buffer if all are already read (receiving position equals receiving length).
|
|
// Returns a next char or -1 on failure (also sets error flag)
|
|
int get_char();
|
|
// Sets to 0 the reading position, reading length and the whole buffer content.
|
|
void reset_buffer();
|
|
// Reading position set to 0 and buffer's unread content moved to beginning
|
|
void rewind_buffer();
|
|
// Calculate remaining time for polling based on request start time and AT timeout.
|
|
// Returns 0 or time in ms for polling.
|
|
int poll_timeout(bool wait_for_timeout = true);
|
|
// Reads from serial to receiving buffer.
|
|
// Returns true on successful read OR false on timeout.
|
|
bool fill_buffer(bool wait_for_timeout = true);
|
|
|
|
void set_tag(tag_t *tag_dest, const char *tag_seq);
|
|
|
|
// Rewinds the receiving buffer and compares it against given str.
|
|
bool match(const char *str, size_t size);
|
|
// Iterates URCs and checks if they match the receiving buffer content.
|
|
// If URC match sets the scope to information response and after urc's cb returns
|
|
// finishes the information response scope(consumes to CRLF).
|
|
bool match_urc();
|
|
// Checks if any of the error strings are matching the receiving buffer content.
|
|
bool match_error();
|
|
// Checks if current char in buffer matches ch and consumes it,
|
|
// if no match lets the buffer unchanged.
|
|
bool consume_char(char ch);
|
|
// Consumes the received content until tag is found.
|
|
// Consumes the tag only if consume_tag flag is true.
|
|
bool consume_to_tag(const char *tag, bool consume_tag);
|
|
// Checks if receiving buffer contains OK, ERROR, URC or given prefix.
|
|
void resp(const char *prefix, bool check_urc);
|
|
|
|
|
|
ScopeType get_scope();
|
|
|
|
// Consumes to information response stop tag which is CRLF. Sets scope to response.
|
|
void information_response_stop();
|
|
// Consumes to element stop tag. Sets scope to information response
|
|
void information_response_element_stop();
|
|
|
|
// Reads the error code if expected and sets it as last error.
|
|
void at_error(bool error_code, DeviceErrorType error_type);
|
|
|
|
/** Convert AT error code to 3GPP error codes
|
|
* @param err AT error code read from CME/CMS ERROR responses
|
|
* @param error_type error type (CMS/CME/ERROR)
|
|
*/
|
|
void set_3gpp_error(int err, DeviceErrorType error_type);
|
|
|
|
bool check_cmd_send();
|
|
size_t write(const void *data, size_t len);
|
|
|
|
/** Copy content of one char buffer to another buffer and sets NULL terminator
|
|
*
|
|
* @param dest destination char buffer
|
|
* @param src source char buffer
|
|
* @param src_len number of bytes to copy
|
|
*
|
|
*/
|
|
void set_string(char *dest, const char *src, size_t src_len);
|
|
|
|
/** Finds occurrence of one char buffer inside another char buffer.
|
|
*
|
|
* @param dest destination char buffer
|
|
* @param dest_len length of dest
|
|
* @param src string to be searched for
|
|
* @param src_len length of string to be searched for
|
|
*
|
|
* @return pointer to first occurrence of src in dest
|
|
*/
|
|
const char *mem_str(const char *dest, size_t dest_len, const char *src, size_t src_len);
|
|
|
|
// check is urc is already added
|
|
bool find_urc_handler(const char *prefix);
|
|
|
|
// print contents of a buffer to trace log
|
|
void debug_print(char *p, int len);
|
|
};
|
|
|
|
} // namespace mbed
|
|
|
|
#endif //AT_HANDLER_H_
|