2018-02-09 11:24:27 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2018-05-02 04:55:06 +00:00
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdio.h>
|
2018-05-03 11:42:26 +00:00
|
|
|
#include <limits.h>
|
2018-02-09 11:24:27 +00:00
|
|
|
#include "ATHandler.h"
|
|
|
|
#include "mbed_poll.h"
|
|
|
|
#include "FileHandle.h"
|
|
|
|
#include "mbed_wait_api.h"
|
|
|
|
#include "mbed_debug.h"
|
2018-02-10 10:17:22 +00:00
|
|
|
#ifdef MBED_CONF_RTOS_PRESENT
|
|
|
|
#include "rtos/Thread.h"
|
|
|
|
#endif
|
2018-04-09 10:46:40 +00:00
|
|
|
#include "Kernel.h"
|
2018-04-19 10:02:36 +00:00
|
|
|
#include "CellularUtil.h"
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
using namespace mbed;
|
|
|
|
using namespace events;
|
2018-04-19 10:02:36 +00:00
|
|
|
using namespace mbed_cellular_util;
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
#include "CellularLog.h"
|
|
|
|
|
2018-02-28 14:32:41 +00:00
|
|
|
#if MBED_CONF_MBED_TRACE_ENABLE
|
2018-02-09 11:24:27 +00:00
|
|
|
#define at_debug(format, ...) do { if (_debug_on) debug(format, ## __VA_ARGS__); } while (0)
|
|
|
|
#else
|
|
|
|
#define at_debug(...)
|
|
|
|
#endif
|
|
|
|
|
2018-05-02 04:55:06 +00:00
|
|
|
// URCs should be handled fast, if you add debug traces within URC processing then you also need to increase this time
|
|
|
|
#define PROCESS_URC_TIME 20
|
|
|
|
|
2018-02-09 11:24:27 +00:00
|
|
|
const char *mbed::OK = "OK\r\n";
|
|
|
|
const uint8_t OK_LENGTH = 4;
|
|
|
|
const char *mbed::CRLF = "\r\n";
|
|
|
|
const uint8_t CRLF_LENGTH = 2;
|
|
|
|
const char *CME_ERROR = "+CME ERROR:";
|
|
|
|
const uint8_t CME_ERROR_LENGTH = 11;
|
|
|
|
const char *CMS_ERROR = "+CMS ERROR:";
|
|
|
|
const uint8_t CMS_ERROR_LENGTH = 11;
|
|
|
|
const char *ERROR_ = "ERROR\r\n";
|
|
|
|
const uint8_t ERROR_LENGTH = 7;
|
|
|
|
const uint8_t MAX_RESP_LENGTH = CMS_ERROR_LENGTH;
|
2018-02-20 08:37:38 +00:00
|
|
|
const char DEFAULT_DELIMITER = ',';
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
static const uint8_t map_3gpp_errors[][2] = {
|
|
|
|
{ 103, 3 }, { 106, 6 }, { 107, 7 }, { 108, 8 }, { 111, 11 }, { 112, 12 }, { 113, 13 }, { 114, 14 },
|
|
|
|
{ 115, 15 }, { 122, 22 }, { 125, 25 }, { 172, 95 }, { 173, 96 }, { 174, 97 }, { 175, 99 }, { 176, 111 },
|
|
|
|
{ 177, 8 }, { 126, 26 }, { 127, 27 }, { 128, 28 }, { 129, 29 }, { 130, 30 }, { 131, 31 }, { 132, 32 },
|
|
|
|
{ 133, 33 }, { 134, 34 }, { 140, 40 }, { 141, 41 }, { 142, 42 }, { 143, 43 }, { 144, 44 }, { 145, 45 },
|
|
|
|
{ 146, 46 }, { 178, 65 }, { 179, 66 }, { 180, 48 }, { 181, 83 }, { 171, 49 },
|
|
|
|
};
|
|
|
|
|
2018-04-09 10:46:40 +00:00
|
|
|
ATHandler::ATHandler(FileHandle *fh, EventQueue &queue, int timeout, const char *output_delimiter, uint16_t send_delay) :
|
2018-02-09 11:24:27 +00:00
|
|
|
_nextATHandler(0),
|
2018-05-02 04:55:06 +00:00
|
|
|
_fileHandle(fh),
|
2018-02-09 11:24:27 +00:00
|
|
|
_queue(queue),
|
|
|
|
_last_err(NSAPI_ERROR_OK),
|
|
|
|
_last_3gpp_error(0),
|
|
|
|
_oob_string_max_length(0),
|
|
|
|
_oobs(NULL),
|
|
|
|
_at_timeout(timeout),
|
|
|
|
_previous_at_timeout(timeout),
|
2018-04-09 10:46:40 +00:00
|
|
|
_at_send_delay(send_delay),
|
|
|
|
_last_response_stop(0),
|
2018-02-09 11:24:27 +00:00
|
|
|
_fh_sigio_set(false),
|
|
|
|
_processing(false),
|
|
|
|
_ref_count(1),
|
|
|
|
_stop_tag(NULL),
|
2018-02-20 08:37:38 +00:00
|
|
|
_delimiter(DEFAULT_DELIMITER),
|
2018-02-09 11:24:27 +00:00
|
|
|
_prefix_matched(false),
|
|
|
|
_urc_matched(false),
|
|
|
|
_error_found(false),
|
|
|
|
_max_resp_length(MAX_RESP_LENGTH),
|
|
|
|
_debug_on(false),
|
2018-05-02 04:55:06 +00:00
|
|
|
_cmd_start(false),
|
|
|
|
_start_time(0)
|
2018-02-09 11:24:27 +00:00
|
|
|
{
|
|
|
|
//enable_debug(true);
|
|
|
|
|
2018-02-11 21:04:28 +00:00
|
|
|
clear_error();
|
|
|
|
|
2018-02-09 11:24:27 +00:00
|
|
|
if (output_delimiter) {
|
2018-03-21 12:37:18 +00:00
|
|
|
_output_delimiter = new char[strlen(output_delimiter) + 1];
|
|
|
|
if (!_output_delimiter) {
|
|
|
|
MBED_ASSERT(0);
|
|
|
|
} else {
|
|
|
|
memcpy(_output_delimiter, output_delimiter, strlen(output_delimiter) + 1);
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
} else {
|
2018-05-03 11:42:26 +00:00
|
|
|
_output_delimiter = NULL;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
reset_buffer();
|
2018-03-21 12:37:18 +00:00
|
|
|
memset(_recv_buff, 0, sizeof(_recv_buff));
|
2018-02-09 11:24:27 +00:00
|
|
|
memset(_info_resp_prefix, 0, sizeof(_info_resp_prefix));
|
|
|
|
|
|
|
|
_current_scope = NotSet;
|
|
|
|
set_tag(&_resp_stop, OK);
|
|
|
|
set_tag(&_info_stop, CRLF);
|
|
|
|
set_tag(&_elem_stop, ")");
|
|
|
|
|
2018-05-02 04:55:06 +00:00
|
|
|
_fileHandle->set_blocking(false);
|
|
|
|
|
|
|
|
set_filehandle_sigio();
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::enable_debug(bool enable)
|
|
|
|
{
|
|
|
|
_debug_on = enable;
|
|
|
|
}
|
|
|
|
|
|
|
|
ATHandler::~ATHandler()
|
|
|
|
{
|
|
|
|
while (_oobs) {
|
|
|
|
struct oob_t *oob = _oobs;
|
|
|
|
_oobs = oob->next;
|
|
|
|
delete oob;
|
|
|
|
}
|
|
|
|
if (_output_delimiter) {
|
|
|
|
delete [] _output_delimiter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::inc_ref_count()
|
|
|
|
{
|
|
|
|
_ref_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::dec_ref_count()
|
|
|
|
{
|
|
|
|
_ref_count--;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ATHandler::get_ref_count()
|
|
|
|
{
|
|
|
|
return _ref_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
FileHandle *ATHandler::get_file_handle()
|
|
|
|
{
|
|
|
|
return _fileHandle;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::set_file_handle(FileHandle *fh)
|
|
|
|
{
|
|
|
|
_fileHandle = fh;
|
|
|
|
}
|
|
|
|
|
2018-03-14 13:24:29 +00:00
|
|
|
nsapi_error_t ATHandler::set_urc_handler(const char *prefix, mbed::Callback<void()> callback)
|
2018-02-09 11:24:27 +00:00
|
|
|
{
|
2018-03-16 11:05:42 +00:00
|
|
|
if (find_urc_handler(prefix, callback)) {
|
2018-03-13 10:38:35 +00:00
|
|
|
tr_warn("URC already added with prefix: %s", prefix);
|
2018-03-14 13:24:29 +00:00
|
|
|
return NSAPI_ERROR_OK;
|
2018-03-13 10:38:35 +00:00
|
|
|
}
|
|
|
|
|
2018-02-09 11:24:27 +00:00
|
|
|
struct oob_t *oob = new struct oob_t;
|
2018-03-14 13:24:29 +00:00
|
|
|
if (!oob) {
|
|
|
|
return NSAPI_ERROR_NO_MEMORY;
|
|
|
|
} else {
|
2018-03-13 10:38:35 +00:00
|
|
|
size_t prefix_len = strlen(prefix);
|
|
|
|
if (prefix_len > _oob_string_max_length) {
|
|
|
|
_oob_string_max_length = prefix_len;
|
|
|
|
if (_oob_string_max_length > _max_resp_length) {
|
|
|
|
_max_resp_length = _oob_string_max_length;
|
|
|
|
}
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
2018-03-13 10:38:35 +00:00
|
|
|
|
|
|
|
oob->prefix = prefix;
|
2018-03-23 12:31:41 +00:00
|
|
|
oob->prefix_len = prefix_len;
|
2018-03-13 10:38:35 +00:00
|
|
|
oob->cb = callback;
|
|
|
|
oob->next = _oobs;
|
|
|
|
_oobs = oob;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
2018-03-14 13:24:29 +00:00
|
|
|
|
|
|
|
return NSAPI_ERROR_OK;
|
2018-03-13 10:38:35 +00:00
|
|
|
}
|
|
|
|
|
2018-03-16 09:10:04 +00:00
|
|
|
void ATHandler::remove_urc_handler(const char *prefix, mbed::Callback<void()> callback)
|
|
|
|
{
|
|
|
|
struct oob_t *current = _oobs;
|
|
|
|
struct oob_t *prev = NULL;
|
|
|
|
while (current) {
|
|
|
|
if (strcmp(prefix, current->prefix) == 0 && current->cb == callback) {
|
|
|
|
if (prev) {
|
|
|
|
prev->next = current->next;
|
|
|
|
} else {
|
|
|
|
_oobs = current->next;
|
|
|
|
}
|
|
|
|
delete current;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
prev = current;
|
|
|
|
current = prev->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-16 11:05:42 +00:00
|
|
|
bool ATHandler::find_urc_handler(const char *prefix, mbed::Callback<void()> callback)
|
2018-03-13 10:38:35 +00:00
|
|
|
{
|
2018-03-13 13:49:24 +00:00
|
|
|
struct oob_t *oob = _oobs;
|
|
|
|
while (oob) {
|
|
|
|
if (strcmp(prefix, oob->prefix) == 0 && oob->cb == callback) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
oob = oob->next;
|
|
|
|
}
|
2018-03-13 10:38:35 +00:00
|
|
|
|
2018-03-13 13:49:24 +00:00
|
|
|
return false;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::event()
|
|
|
|
{
|
|
|
|
// _processing must be set before filehandle write/read to avoid repetitive sigio events
|
|
|
|
if (!_processing) {
|
|
|
|
_processing = true;
|
|
|
|
(void) _queue.call(Callback<void(void)>(this, &ATHandler::process_oob));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::lock()
|
|
|
|
{
|
|
|
|
#ifdef AT_HANDLER_MUTEX
|
|
|
|
_fileHandleMutex.lock();
|
|
|
|
#endif
|
|
|
|
_processing = true;
|
|
|
|
clear_error();
|
2018-05-02 04:55:06 +00:00
|
|
|
_start_time = rtos::Kernel::get_ms_count();
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::unlock()
|
|
|
|
{
|
|
|
|
_processing = false;
|
|
|
|
#ifdef AT_HANDLER_MUTEX
|
|
|
|
_fileHandleMutex.unlock();
|
|
|
|
#endif
|
|
|
|
if (_fileHandle->readable() || (_recv_pos < _recv_len)) {
|
|
|
|
(void) _queue.call(Callback<void(void)>(this, &ATHandler::process_oob));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsapi_error_t ATHandler::unlock_return_error()
|
|
|
|
{
|
|
|
|
nsapi_error_t err = _last_err;
|
|
|
|
unlock();
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::set_at_timeout(uint32_t timeout_milliseconds, bool default_timeout)
|
|
|
|
{
|
|
|
|
if (default_timeout) {
|
|
|
|
_previous_at_timeout = timeout_milliseconds;
|
|
|
|
_at_timeout = timeout_milliseconds;
|
|
|
|
} else if (timeout_milliseconds != _at_timeout) {
|
|
|
|
_previous_at_timeout = _at_timeout;
|
|
|
|
_at_timeout = timeout_milliseconds;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::restore_at_timeout()
|
|
|
|
{
|
|
|
|
if (_previous_at_timeout != _at_timeout) {
|
|
|
|
_at_timeout =_previous_at_timeout;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::process_oob()
|
|
|
|
{
|
|
|
|
lock();
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("process_oob %d", (_fileHandle->readable() || (_recv_pos < _recv_len)));
|
2018-02-09 11:24:27 +00:00
|
|
|
if (_fileHandle->readable() || (_recv_pos < _recv_len)) {
|
|
|
|
_current_scope = NotSet;
|
2018-05-02 04:55:06 +00:00
|
|
|
uint32_t timeout = _at_timeout;
|
|
|
|
_at_timeout = PROCESS_URC_TIME;
|
|
|
|
while (true) {
|
2018-02-09 11:24:27 +00:00
|
|
|
if (match_urc()) {
|
2018-05-02 04:55:06 +00:00
|
|
|
if (!(_fileHandle->readable() || (_recv_pos < _recv_len))) {
|
|
|
|
break; // we have nothing to read anymore
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
2018-05-02 04:55:06 +00:00
|
|
|
_start_time = rtos::Kernel::get_ms_count(); // time to process next (potential) URC
|
2018-05-03 11:42:26 +00:00
|
|
|
} else if (mem_str(_recv_buff, _recv_len, CRLF, CRLF_LENGTH)) { // If no match found, look for CRLF and consume everything up to CRLF
|
2018-02-09 11:24:27 +00:00
|
|
|
consume_to_tag(CRLF, true);
|
|
|
|
} else {
|
2018-05-02 04:55:06 +00:00
|
|
|
if (!fill_buffer()) {
|
|
|
|
break;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
2018-05-02 04:55:06 +00:00
|
|
|
}
|
|
|
|
_at_timeout = timeout;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("process_oob exit");
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
flush(); // consume anything that could not be handled
|
|
|
|
|
|
|
|
unlock();
|
|
|
|
}
|
|
|
|
|
2018-05-02 04:55:06 +00:00
|
|
|
void ATHandler::set_filehandle_sigio()
|
|
|
|
{
|
|
|
|
if (_fh_sigio_set) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_fileHandle->sigio(mbed::Callback<void()>(this, &ATHandler::event));
|
|
|
|
_fh_sigio_set = true;
|
|
|
|
}
|
|
|
|
|
2018-02-09 11:24:27 +00:00
|
|
|
void ATHandler::reset_buffer()
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-03-23 12:31:41 +00:00
|
|
|
_recv_pos = 0;
|
|
|
|
_recv_len = 0;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::rewind_buffer()
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
if (_recv_pos > 0 && _recv_len >= _recv_pos) {
|
|
|
|
_recv_len -= _recv_pos;
|
|
|
|
// move what is not read to beginning of buffer
|
|
|
|
memmove(_recv_buff, _recv_buff + _recv_pos, _recv_len);
|
|
|
|
_recv_pos = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-03 12:43:48 +00:00
|
|
|
int ATHandler::poll_timeout(bool wait_for_timeout)
|
2018-02-09 11:24:27 +00:00
|
|
|
{
|
2018-05-03 11:42:26 +00:00
|
|
|
int timeout;
|
|
|
|
if (wait_for_timeout) {
|
|
|
|
uint64_t now = rtos::Kernel::get_ms_count();
|
|
|
|
if (now >= _start_time + _at_timeout) {
|
|
|
|
timeout = 0;
|
|
|
|
} else if ( _start_time + _at_timeout - now > INT_MAX) {
|
|
|
|
timeout = INT_MAX;
|
|
|
|
} else {
|
|
|
|
timeout = _start_time + _at_timeout - now;
|
2018-04-25 11:39:27 +00:00
|
|
|
}
|
2018-05-03 11:42:26 +00:00
|
|
|
} else {
|
|
|
|
timeout = 0;
|
2018-05-02 04:55:06 +00:00
|
|
|
}
|
2018-05-03 12:43:48 +00:00
|
|
|
return timeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ATHandler::fill_buffer(bool wait_for_timeout)
|
|
|
|
{
|
|
|
|
tr_debug("%s", __func__);
|
|
|
|
// Reset buffer when full
|
|
|
|
if (sizeof(_recv_buff) == _recv_len) {
|
|
|
|
reset_buffer();
|
|
|
|
}
|
2018-02-09 11:24:27 +00:00
|
|
|
|
2018-05-03 11:42:26 +00:00
|
|
|
pollfh fhs;
|
|
|
|
fhs.fh = _fileHandle;
|
|
|
|
fhs.events = POLLIN;
|
2018-05-03 12:43:48 +00:00
|
|
|
int timeout = 0;
|
|
|
|
int count = poll(&fhs, 1, poll_timeout(wait_for_timeout));
|
2018-05-03 11:42:26 +00:00
|
|
|
if (count > 0 && (fhs.revents & POLLIN)) {
|
|
|
|
ssize_t len = _fileHandle->read(_recv_buff + _recv_len, sizeof(_recv_buff) - _recv_len);
|
|
|
|
if (len > 0) {
|
|
|
|
_recv_len += len;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wait_for_timeout) {
|
|
|
|
set_error(NSAPI_ERROR_DEVICE_ERROR);
|
|
|
|
}
|
2018-05-02 04:55:06 +00:00
|
|
|
|
|
|
|
return false;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
2018-02-19 08:02:37 +00:00
|
|
|
int ATHandler::get_char()
|
2018-02-09 11:24:27 +00:00
|
|
|
{
|
|
|
|
if (_recv_pos == _recv_len) {
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
reset_buffer(); // try to read as much as possible
|
2018-05-02 04:55:06 +00:00
|
|
|
if (!fill_buffer()) {
|
|
|
|
tr_warn("AT TIMEOUT");
|
2018-02-09 11:24:27 +00:00
|
|
|
return -1; // timeout to read
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s: %c", __func__, _recv_buff[_recv_pos]);
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
return _recv_buff[_recv_pos++];
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::skip_param(uint32_t count)
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-11 21:04:28 +00:00
|
|
|
if (_last_err || !_stop_tag || _stop_tag->found) {
|
2018-02-09 11:24:27 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-19 08:02:37 +00:00
|
|
|
for (uint32_t i = 0; (i < count && !_stop_tag->found); i++) {
|
2018-02-09 11:24:27 +00:00
|
|
|
size_t match_pos = 0;
|
|
|
|
while (true) {
|
2018-02-19 08:02:37 +00:00
|
|
|
int c = get_char();
|
2018-02-09 11:24:27 +00:00
|
|
|
if (c == -1) {
|
|
|
|
set_error(NSAPI_ERROR_DEVICE_ERROR);
|
|
|
|
return;
|
|
|
|
} else if (c == _delimiter) {
|
|
|
|
break;
|
|
|
|
} else if (_stop_tag->len && c == _stop_tag->tag[match_pos]) {
|
|
|
|
match_pos++;
|
|
|
|
if (match_pos == _stop_tag->len) {
|
|
|
|
_stop_tag->found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (match_pos) {
|
|
|
|
match_pos = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::skip_param(ssize_t len, uint32_t count)
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-11 21:04:28 +00:00
|
|
|
if (_last_err || !_stop_tag || _stop_tag->found) {
|
2018-02-09 11:24:27 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-19 08:02:37 +00:00
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
2018-02-09 11:24:27 +00:00
|
|
|
ssize_t read_len = 0;
|
|
|
|
while (read_len < len) {
|
2018-02-19 08:02:37 +00:00
|
|
|
int c = get_char();
|
2018-02-09 11:24:27 +00:00
|
|
|
if (c == -1) {
|
|
|
|
set_error(NSAPI_ERROR_DEVICE_ERROR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
read_len++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t ATHandler::read_bytes(uint8_t *buf, size_t len)
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
if (_last_err) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t read_len = 0;
|
2018-02-19 08:02:37 +00:00
|
|
|
for (; read_len < len; read_len++) {
|
|
|
|
int c = get_char();
|
2018-02-09 11:24:27 +00:00
|
|
|
if (c == -1) {
|
|
|
|
set_error(NSAPI_ERROR_DEVICE_ERROR);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
buf[read_len] = c;
|
|
|
|
}
|
|
|
|
return read_len;
|
|
|
|
}
|
|
|
|
|
2018-04-24 09:14:54 +00:00
|
|
|
ssize_t ATHandler::read(char *buf, size_t size, bool read_even_stop_tag, bool hex)
|
2018-02-09 11:24:27 +00:00
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-04-24 09:14:54 +00:00
|
|
|
at_debug("\n----------read buff:----------\n");
|
2018-02-09 11:24:27 +00:00
|
|
|
for (size_t i = _recv_pos; i < _recv_len; i++) {
|
|
|
|
at_debug("%c", _recv_buff[i]);
|
|
|
|
}
|
2018-04-24 09:14:54 +00:00
|
|
|
at_debug("\n----------read end----------\n");
|
2018-02-09 11:24:27 +00:00
|
|
|
|
2018-02-11 21:04:28 +00:00
|
|
|
if (_last_err || !_stop_tag || (_stop_tag->found && read_even_stop_tag == false)) {
|
2018-02-09 11:24:27 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t match_pos = 0;
|
2018-04-24 09:14:54 +00:00
|
|
|
size_t read_size = hex ? size*2 : size;
|
2018-04-19 10:02:36 +00:00
|
|
|
|
|
|
|
consume_char('\"');
|
|
|
|
|
2018-04-24 09:14:54 +00:00
|
|
|
size_t read_idx = 0;
|
|
|
|
size_t buf_idx = 0;
|
2018-04-30 10:25:15 +00:00
|
|
|
char hexbuf[2];
|
2018-04-24 09:14:54 +00:00
|
|
|
|
|
|
|
for (; read_idx < (read_size + match_pos); read_idx++) {
|
2018-04-27 11:38:14 +00:00
|
|
|
int c = get_char();
|
2018-04-24 09:14:54 +00:00
|
|
|
buf_idx = hex ? read_idx/2 : read_idx;
|
2018-04-19 10:02:36 +00:00
|
|
|
if (c == -1) {
|
2018-04-30 10:25:15 +00:00
|
|
|
buf[buf_idx] = '\0';
|
2018-04-19 10:02:36 +00:00
|
|
|
set_error(NSAPI_ERROR_DEVICE_ERROR);
|
|
|
|
return -1;
|
|
|
|
} else if (c == _delimiter) {
|
2018-04-30 10:25:15 +00:00
|
|
|
buf[buf_idx] = '\0';
|
2018-04-19 10:02:36 +00:00
|
|
|
break;
|
|
|
|
} else if (c == '\"') {
|
|
|
|
match_pos = 0;
|
2018-04-24 09:14:54 +00:00
|
|
|
if (read_idx > 0) {
|
|
|
|
read_idx--;
|
2018-04-19 10:02:36 +00:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
} else if (_stop_tag->len && c == _stop_tag->tag[match_pos]) {
|
|
|
|
match_pos++;
|
|
|
|
if (match_pos == _stop_tag->len) {
|
|
|
|
_stop_tag->found = true;
|
|
|
|
// remove tag from string if it was matched
|
2018-04-24 09:14:54 +00:00
|
|
|
buf_idx -= (_stop_tag->len - 1);
|
2018-04-30 10:25:15 +00:00
|
|
|
buf[buf_idx] = '\0';
|
2018-04-19 10:02:36 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (match_pos) {
|
|
|
|
match_pos = 0;
|
|
|
|
}
|
|
|
|
|
2018-04-24 09:14:54 +00:00
|
|
|
if (!hex) {
|
2018-05-03 11:42:26 +00:00
|
|
|
buf[buf_idx] = c;
|
2018-04-19 10:02:36 +00:00
|
|
|
} else {
|
2018-04-30 10:25:15 +00:00
|
|
|
hexbuf[read_idx % 2] = c;
|
|
|
|
if (read_idx % 2 == 1) {
|
|
|
|
hex_str_to_char_str(hexbuf, 2, buf+buf_idx);
|
2018-04-24 09:14:54 +00:00
|
|
|
}
|
2018-04-19 10:02:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-27 11:38:14 +00:00
|
|
|
return buf_idx;
|
2018-04-24 09:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t ATHandler::read_string(char *buf, size_t size, bool read_even_stop_tag)
|
|
|
|
{
|
|
|
|
return read(buf, size, read_even_stop_tag, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t ATHandler::read_hex_string(char *buf, size_t size)
|
|
|
|
{
|
|
|
|
return read(buf, size, false, true);
|
2018-04-19 10:02:36 +00:00
|
|
|
}
|
|
|
|
|
2018-02-09 11:24:27 +00:00
|
|
|
int32_t ATHandler::read_int()
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
|
2018-05-03 11:42:26 +00:00
|
|
|
if (_last_err || !_stop_tag || _stop_tag->found) {
|
|
|
|
return -1;
|
|
|
|
}
|
2018-02-09 11:24:27 +00:00
|
|
|
|
2018-02-19 08:02:37 +00:00
|
|
|
char buff[BUFF_SIZE];
|
2018-02-09 11:24:27 +00:00
|
|
|
char *first_no_digit;
|
|
|
|
|
|
|
|
if (read_string(buff, (size_t)sizeof(buff)) == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return std::strtol(buff, &first_no_digit, 10);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::set_delimiter(char delimiter)
|
|
|
|
{
|
|
|
|
_delimiter = delimiter;
|
|
|
|
}
|
|
|
|
|
2018-02-20 08:37:38 +00:00
|
|
|
void ATHandler::set_default_delimiter()
|
|
|
|
{
|
|
|
|
_delimiter = DEFAULT_DELIMITER;
|
|
|
|
}
|
|
|
|
|
2018-02-09 11:24:27 +00:00
|
|
|
void ATHandler::set_tag(tag_t* tag_dst, const char *tag_seq)
|
|
|
|
{
|
|
|
|
if (tag_seq) {
|
|
|
|
size_t tag_len = strlen(tag_seq);
|
|
|
|
set_string(tag_dst->tag, tag_seq, tag_len);
|
|
|
|
tag_dst->len = tag_len;
|
|
|
|
tag_dst->found = false;
|
|
|
|
} else {
|
|
|
|
_stop_tag = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::set_stop_tag(const char *stop_tag_seq)
|
|
|
|
{
|
|
|
|
if (_last_err || !_stop_tag) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_tag(_stop_tag, stop_tag_seq);
|
|
|
|
}
|
|
|
|
|
2018-02-12 13:56:31 +00:00
|
|
|
void ATHandler::set_scope(ScopeType scope_type)
|
2018-02-09 11:24:27 +00:00
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s: %d", __func__, scope_type);
|
2018-02-09 11:24:27 +00:00
|
|
|
if (_current_scope != scope_type) {
|
|
|
|
_current_scope = scope_type;
|
|
|
|
switch (_current_scope) {
|
2018-05-03 11:42:26 +00:00
|
|
|
case RespType:
|
|
|
|
_stop_tag = &_resp_stop;
|
|
|
|
_stop_tag->found = false;
|
|
|
|
break;
|
|
|
|
case InfoType:
|
|
|
|
_stop_tag = &_info_stop;
|
|
|
|
_stop_tag->found = false;
|
|
|
|
consume_char(' ');
|
|
|
|
break;
|
|
|
|
case ElemType:
|
|
|
|
_stop_tag = &_elem_stop;
|
|
|
|
_stop_tag->found = false;
|
|
|
|
break;
|
|
|
|
case NotSet:
|
|
|
|
_stop_tag = NULL;
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
break;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// should match from recv_pos?
|
|
|
|
bool ATHandler::match(const char* str, size_t size)
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s: %s", __func__, str);
|
2018-02-09 11:24:27 +00:00
|
|
|
rewind_buffer();
|
|
|
|
|
|
|
|
if ((_recv_len - _recv_pos) < size) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (str && memcmp(_recv_buff + _recv_pos, str, size) == 0) {
|
|
|
|
// consume matching part
|
|
|
|
_recv_pos += size;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ATHandler::match_urc()
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
rewind_buffer();
|
|
|
|
size_t prefix_len = 0;
|
|
|
|
for (struct oob_t *oob = _oobs; oob; oob = oob->next) {
|
2018-03-23 12:31:41 +00:00
|
|
|
prefix_len = oob->prefix_len;
|
2018-02-09 11:24:27 +00:00
|
|
|
if (_recv_len >= prefix_len) {
|
|
|
|
if (match(oob->prefix, prefix_len)) {
|
2018-03-23 12:31:41 +00:00
|
|
|
tr_debug("URC! %s\n", oob->prefix);
|
2018-02-09 11:24:27 +00:00
|
|
|
set_scope(InfoType);
|
2018-02-12 13:56:31 +00:00
|
|
|
if (oob->cb) {
|
2018-02-11 21:04:28 +00:00
|
|
|
oob->cb();
|
|
|
|
}
|
2018-02-09 11:24:27 +00:00
|
|
|
information_response_stop();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ATHandler::match_error()
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
if (match(CME_ERROR, CME_ERROR_LENGTH)) {
|
|
|
|
at_error(true, DeviceErrorTypeErrorCME);
|
|
|
|
return true;
|
|
|
|
} else if (match(CMS_ERROR, CMS_ERROR_LENGTH)) {
|
|
|
|
at_error(true, DeviceErrorTypeErrorCMS);
|
|
|
|
return true;
|
|
|
|
} else if (match(ERROR_, ERROR_LENGTH)) {
|
|
|
|
at_error(false, DeviceErrorTypeNoError);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::clear_error()
|
|
|
|
{
|
|
|
|
_last_err = NSAPI_ERROR_OK;
|
|
|
|
_last_at_err.errCode = 0;
|
|
|
|
_last_at_err.errType = DeviceErrorTypeNoError;
|
|
|
|
_last_3gpp_error = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsapi_error_t ATHandler::get_last_error() const
|
|
|
|
{
|
|
|
|
return _last_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
device_err_t ATHandler::get_last_device_error() const
|
|
|
|
{
|
|
|
|
return _last_at_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::set_error(nsapi_error_t err)
|
|
|
|
{
|
|
|
|
if (_last_err == NSAPI_ERROR_OK) {
|
|
|
|
_last_err = err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_last_err != err) {
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_warn("AT error code changed from %d to %d!", _last_err, err);
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-19 08:02:37 +00:00
|
|
|
int ATHandler::get_3gpp_error()
|
2018-02-09 11:24:27 +00:00
|
|
|
{
|
|
|
|
return _last_3gpp_error;
|
|
|
|
}
|
|
|
|
|
2018-02-19 08:02:37 +00:00
|
|
|
void ATHandler::set_3gpp_error(int err, DeviceErrorType error_type)
|
2018-02-09 11:24:27 +00:00
|
|
|
{
|
|
|
|
if (_last_3gpp_error) { // don't overwrite likely root cause error
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error_type == DeviceErrorTypeErrorCMS && err < 128) {
|
|
|
|
// CMS errors 0-127 maps straight to 3GPP errors
|
|
|
|
_last_3gpp_error = err;
|
|
|
|
} else {
|
2018-02-19 08:02:37 +00:00
|
|
|
for (size_t i = 0; i<sizeof(map_3gpp_errors)/sizeof(map_3gpp_errors[0]); i++) {
|
2018-02-09 11:24:27 +00:00
|
|
|
if (map_3gpp_errors[i][0] == err) {
|
|
|
|
_last_3gpp_error = map_3gpp_errors[i][1];
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("AT3GPP error code %d", get_3gpp_error());
|
2018-02-09 11:24:27 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::at_error(bool error_code_expected, DeviceErrorType error_type)
|
|
|
|
{
|
|
|
|
if (error_code_expected && (error_type == DeviceErrorTypeErrorCMS || error_type == DeviceErrorTypeErrorCME)) {
|
|
|
|
set_scope(InfoType);
|
2018-03-21 12:37:18 +00:00
|
|
|
int32_t err = read_int();
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
if (err != -1) {
|
|
|
|
set_3gpp_error(err, error_type);
|
|
|
|
_last_at_err.errCode = err;
|
|
|
|
_last_at_err.errType = error_type;
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("ATHandler ERROR: %d", err);
|
2018-02-09 11:24:27 +00:00
|
|
|
} else {
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("ATHandler ERROR reading failed");
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_last_err = NSAPI_ERROR_DEVICE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::resp(const char *prefix, bool check_urc)
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s: %s", __func__, prefix);
|
2018-02-09 11:24:27 +00:00
|
|
|
|
2018-02-20 08:37:38 +00:00
|
|
|
at_debug("\n----------resp buff:----------\n");
|
2018-02-09 11:24:27 +00:00
|
|
|
for (size_t i = _recv_pos; i < _recv_len; i++) {
|
|
|
|
at_debug("%c", _recv_buff[i]);
|
|
|
|
}
|
|
|
|
at_debug("\n----------buff----------\n");
|
|
|
|
|
|
|
|
_prefix_matched = false;
|
|
|
|
_urc_matched = false;
|
|
|
|
_error_found = false;
|
|
|
|
|
|
|
|
while (!get_last_error()) {
|
|
|
|
|
|
|
|
match(CRLF, CRLF_LENGTH);
|
|
|
|
|
|
|
|
if (match(OK, OK_LENGTH)) {
|
|
|
|
set_scope(RespType);
|
|
|
|
_stop_tag->found = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-02-19 08:02:37 +00:00
|
|
|
if (match_error()) {
|
2018-02-09 11:24:27 +00:00
|
|
|
_error_found = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prefix && match(prefix, strlen(prefix))) {
|
|
|
|
_prefix_matched = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check_urc && match_urc()) {
|
|
|
|
_urc_matched = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no match found, look for CRLF and consume everything up to and including CRLF
|
2018-02-16 09:13:31 +00:00
|
|
|
if (mem_str(_recv_buff, _recv_len, CRLF, CRLF_LENGTH)) {
|
2018-02-09 11:24:27 +00:00
|
|
|
// If no prefix, return on CRLF - means data to read
|
|
|
|
if (!prefix) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
consume_to_tag(CRLF, true);
|
|
|
|
} else {
|
|
|
|
// If no prefix, no CRLF and no more chance to match for OK, ERROR or URC(since max resp length is already in buffer)
|
|
|
|
// return so data could be read
|
|
|
|
if (!prefix && ((_recv_len-_recv_pos) >= _max_resp_length)) {
|
|
|
|
return;
|
|
|
|
}
|
2018-05-02 04:55:06 +00:00
|
|
|
(void)fill_buffer();
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
// something went wrong so application need to recover and retry
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::resp_start(const char *prefix, bool stop)
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s: %s", __func__, prefix);
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
if (_last_err) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try get as much data as possible
|
|
|
|
rewind_buffer();
|
2018-05-03 11:42:26 +00:00
|
|
|
(void)fill_buffer(false);
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
if (prefix) {
|
2018-05-03 11:42:26 +00:00
|
|
|
MBED_ASSERT(strlen(prefix) < BUFF_SIZE);
|
|
|
|
strcpy(_info_resp_prefix, prefix); // copy prefix so we can later use it without having to provide again for info_resp
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
set_scope(RespType);
|
|
|
|
|
|
|
|
resp(prefix, true);
|
|
|
|
|
|
|
|
if (!stop && prefix && _prefix_matched) {
|
|
|
|
set_scope(InfoType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check urc because of error as urc
|
|
|
|
bool ATHandler::info_resp()
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
if (_last_err || _resp_stop.found) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_prefix_matched) {
|
|
|
|
_prefix_matched = false;
|
2018-05-03 11:42:26 +00:00
|
|
|
return true;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If coming here after another info response was started(looping), stop the previous one.
|
|
|
|
// Trying to handle stopping in this level instead of doing it in upper level.
|
|
|
|
if (get_scope() == InfoType) {
|
|
|
|
information_response_stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
resp(_info_resp_prefix, false);
|
|
|
|
|
|
|
|
if (_prefix_matched) {
|
2018-05-03 11:42:26 +00:00
|
|
|
set_scope(InfoType);
|
|
|
|
_prefix_matched = false;
|
|
|
|
return true;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// On mismatch go to response scope
|
|
|
|
set_scope(RespType);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ATHandler::info_elem(char start_tag)
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s: %c", __func__, start_tag);
|
2018-02-09 11:24:27 +00:00
|
|
|
if (_last_err) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If coming here after another info response element was started(looping), stop the previous one.
|
|
|
|
// Trying to handle stopping in this level instead of doing it in upper level.
|
|
|
|
if (get_scope() == ElemType) {
|
|
|
|
information_response_element_stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
consume_char(_delimiter);
|
|
|
|
|
|
|
|
if (consume_char(start_tag)) {
|
|
|
|
_prefix_matched = true;
|
|
|
|
set_scope(ElemType);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// On mismatch go to information response scope
|
|
|
|
set_scope(InfoType);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ATHandler::consume_char(char ch)
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s: %c", __func__, ch);
|
2018-02-19 08:02:37 +00:00
|
|
|
int read_char = get_char();
|
2018-05-02 04:55:06 +00:00
|
|
|
if (read_char == -1) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-02-09 11:24:27 +00:00
|
|
|
// If we read something else than ch, recover it
|
2018-05-02 04:55:06 +00:00
|
|
|
if (read_char != ch) {
|
2018-02-09 11:24:27 +00:00
|
|
|
_recv_pos--;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ATHandler::consume_to_tag(const char *tag, bool consume_tag)
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s: %s", __func__, tag);
|
2018-02-09 11:24:27 +00:00
|
|
|
size_t match_pos = 0;
|
|
|
|
|
|
|
|
while (true) {
|
2018-02-19 08:02:37 +00:00
|
|
|
int c = get_char();
|
2018-02-09 11:24:27 +00:00
|
|
|
if (c == -1) {
|
|
|
|
break;
|
|
|
|
} else if (c == tag[match_pos]) {
|
|
|
|
match_pos++;
|
|
|
|
if (match_pos == strlen(tag)) {
|
|
|
|
if (!consume_tag) {
|
|
|
|
_recv_pos -= strlen(tag);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} else if (match_pos) {
|
|
|
|
match_pos = 0;
|
|
|
|
}
|
|
|
|
}
|
2018-03-09 12:03:17 +00:00
|
|
|
tr_debug("consume_to_tag not found");
|
2018-02-09 11:24:27 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ATHandler::consume_to_stop_tag()
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
if (!_stop_tag || (_stop_tag && _stop_tag->found) || _error_found) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (consume_to_tag((const char*)_stop_tag->tag, true)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-09 12:03:17 +00:00
|
|
|
tr_debug("consume_to_stop_tag not found");
|
2018-02-09 11:24:27 +00:00
|
|
|
set_error(NSAPI_ERROR_DEVICE_ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// consume by size needed?
|
|
|
|
|
|
|
|
void ATHandler::resp_stop()
|
|
|
|
{
|
|
|
|
// Do not return on error so that we can consume whatever there is in the buffer
|
|
|
|
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
if (_current_scope == ElemType) {
|
|
|
|
information_response_element_stop();
|
|
|
|
set_scope(InfoType);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_current_scope == InfoType) {
|
|
|
|
information_response_stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go for response stop_tag
|
|
|
|
if (consume_to_stop_tag()) {
|
|
|
|
set_scope(NotSet);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore stop tag to OK
|
|
|
|
set_tag(&_resp_stop, OK);
|
|
|
|
// Reset info resp prefix
|
|
|
|
memset(_info_resp_prefix, 0, sizeof(_info_resp_prefix));
|
2018-04-09 10:46:40 +00:00
|
|
|
|
|
|
|
_last_response_stop = rtos::Kernel::get_ms_count();
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::information_response_stop()
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
if (consume_to_stop_tag()) {
|
|
|
|
set_scope(RespType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::information_response_element_stop()
|
|
|
|
{
|
2018-02-19 10:44:24 +00:00
|
|
|
tr_debug("%s", __func__);
|
2018-02-09 11:24:27 +00:00
|
|
|
if (consume_to_stop_tag()) {
|
|
|
|
set_scope(InfoType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ATHandler::ScopeType ATHandler::get_scope()
|
|
|
|
{
|
|
|
|
return _current_scope;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::set_string(char *dest, const char *src, size_t src_len)
|
|
|
|
{
|
|
|
|
memcpy(dest, src, src_len);
|
|
|
|
dest[src_len] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* ATHandler::mem_str(const char* dest, size_t dest_len, const char* src, size_t src_len)
|
|
|
|
{
|
2018-02-16 09:13:31 +00:00
|
|
|
if (dest_len > src_len) {
|
2018-02-21 15:23:09 +00:00
|
|
|
for(size_t i = 0; i < dest_len-src_len+1; ++i) {
|
2018-02-16 09:13:31 +00:00
|
|
|
if(memcmp(dest+i, src, src_len) == 0) {
|
|
|
|
return dest+i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::cmd_start(const char* cmd)
|
|
|
|
{
|
2018-04-09 10:46:40 +00:00
|
|
|
|
|
|
|
if (_at_send_delay) {
|
2018-05-02 04:55:06 +00:00
|
|
|
rtos::Thread::wait_until(_last_response_stop + _at_send_delay);
|
2018-05-03 11:42:26 +00:00
|
|
|
}
|
2018-04-09 10:46:40 +00:00
|
|
|
|
2018-03-27 12:16:37 +00:00
|
|
|
at_debug("AT cmd %s (err %d)\n", cmd, _last_err);
|
2018-04-09 10:46:40 +00:00
|
|
|
|
2018-02-09 11:24:27 +00:00
|
|
|
if (_last_err != NSAPI_ERROR_OK) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-09 12:03:17 +00:00
|
|
|
(void)write(cmd, strlen(cmd));
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
_cmd_start = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::write_int(int32_t param)
|
|
|
|
{
|
2018-03-27 12:16:37 +00:00
|
|
|
at_debug("AT int %d\n", param);
|
2018-02-09 11:24:27 +00:00
|
|
|
// do common checks before sending subparameter
|
|
|
|
if (check_cmd_send() == false) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write the integer subparameter
|
2018-02-19 08:02:37 +00:00
|
|
|
const int32_t str_len = 12;
|
2018-02-09 11:24:27 +00:00
|
|
|
char number_string[str_len];
|
2018-03-01 10:05:49 +00:00
|
|
|
int32_t result = sprintf(number_string, "%ld", param);
|
2018-02-09 11:24:27 +00:00
|
|
|
if (result > 0 && result < str_len) {
|
2018-03-09 12:03:17 +00:00
|
|
|
(void)write(number_string, strlen(number_string));
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::write_string(const char* param, bool useQuotations)
|
|
|
|
{
|
2018-03-27 12:16:37 +00:00
|
|
|
at_debug("AT str %s (with quotes %d)\n", param, useQuotations);
|
2018-02-09 11:24:27 +00:00
|
|
|
// do common checks before sending subparameter
|
|
|
|
if (check_cmd_send() == false) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// we are writing string, surround it with quotes
|
2018-03-09 12:03:17 +00:00
|
|
|
if (useQuotations && write("\"", 1) != 1) {
|
2018-02-09 11:24:27 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-09 12:03:17 +00:00
|
|
|
(void)write(param, strlen(param));
|
2018-02-09 11:24:27 +00:00
|
|
|
|
|
|
|
if (useQuotations) {
|
|
|
|
// we are writing string, surround it with quotes
|
2018-03-09 12:03:17 +00:00
|
|
|
(void)write("\"", 1);
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::cmd_stop()
|
|
|
|
{
|
2018-03-27 12:16:37 +00:00
|
|
|
at_debug("AT stop %s (err %d)\n", _output_delimiter, _last_err);
|
2018-02-09 11:24:27 +00:00
|
|
|
if (_last_err != NSAPI_ERROR_OK) {
|
|
|
|
return;
|
|
|
|
}
|
2018-05-03 11:42:26 +00:00
|
|
|
// Finish with CR
|
2018-03-21 12:37:18 +00:00
|
|
|
(void)write(_output_delimiter, strlen(_output_delimiter));
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
2018-02-19 08:02:37 +00:00
|
|
|
size_t ATHandler::write_bytes(const uint8_t *data, size_t len)
|
2018-02-09 11:24:27 +00:00
|
|
|
{
|
2018-03-27 12:16:37 +00:00
|
|
|
at_debug("AT write bytes %d (err %d)\n", len, _last_err);
|
2018-05-03 11:42:26 +00:00
|
|
|
|
2018-02-09 11:24:27 +00:00
|
|
|
if (_last_err != NSAPI_ERROR_OK) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-03-27 12:16:37 +00:00
|
|
|
return write(data, len);
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
2018-03-27 12:16:37 +00:00
|
|
|
size_t ATHandler::write(const void *data, size_t len)
|
2018-02-09 11:24:27 +00:00
|
|
|
{
|
|
|
|
pollfh fhs;
|
|
|
|
fhs.fh = _fileHandle;
|
|
|
|
fhs.events = POLLOUT;
|
2018-03-27 12:16:37 +00:00
|
|
|
size_t write_len = 0;
|
|
|
|
for (; write_len < len; ) {
|
2018-05-03 12:43:48 +00:00
|
|
|
int count = poll(&fhs, 1, poll_timeout());
|
2018-03-27 12:16:37 +00:00
|
|
|
if (count <= 0 || !(fhs.revents & POLLOUT)) {
|
|
|
|
set_error(NSAPI_ERROR_DEVICE_ERROR);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ssize_t ret = _fileHandle->write((uint8_t*)data + write_len, len - write_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
set_error(NSAPI_ERROR_DEVICE_ERROR);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
write_len += (size_t)ret;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
2018-03-09 12:03:17 +00:00
|
|
|
return write_len;
|
2018-02-09 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// do common checks before sending subparameters
|
|
|
|
bool ATHandler::check_cmd_send()
|
|
|
|
{
|
|
|
|
if (_last_err != NSAPI_ERROR_OK) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't write delimiter if this is the first subparameter
|
|
|
|
if (_cmd_start) {
|
|
|
|
_cmd_start = false;
|
|
|
|
} else {
|
2018-03-09 12:03:17 +00:00
|
|
|
if (write(&_delimiter, 1) != 1) {
|
|
|
|
// writing of delimiter failed, return. write() already have set the _last_err
|
2018-02-09 11:24:27 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ATHandler::flush()
|
|
|
|
{
|
2018-05-03 11:42:26 +00:00
|
|
|
reset_buffer();
|
|
|
|
while (fill_buffer(false)) {
|
2018-02-09 11:24:27 +00:00
|
|
|
reset_buffer();
|
|
|
|
}
|
|
|
|
}
|