blob: db5744163220c7359807d1c5de98051bb3021f12 [file] [log] [blame]
/******************************************************************************
*(C) Copyright 2008 Marvell International Ltd.
* All Rights Reserved
******************************************************************************/
/*****************************************************************************
* Utility Library
*
* DESCRIPTION
* ITU-T V.250 AT-command parser. Parses a stream of text looking for AT
* commands to process. When AT-commands are found they are parsed, a table
* lookup is performed, and application-supplied call-back functions are
* invoked.
*
* This parser supports both basic and extended AT commands. For basic AT
* commands this parser does not distinguish between "action", "set" or
* "get" requests, since the basic AT-command syntax does not specify which
* result is desired (instead this attribute is intrinsic to each AT-command).
* For extended AT commands this parser distinguishes between "action", "set"
* "get", and "query syntax" requests.
*
* OVERALL STRUCTURE
*
* .--------------. .--------------.
* | AT Manager * | | DTE Client * |
* | | `--------------'
* `--------------' ^
* | ^ |AT-commands
* | |utlAtGetParameterFunction_P v
* | |utlAtSetParameterFunction_P .-------------------------. .-------------------.
* | |utlAtSParameterFunction_P | DCE I/O Interface * | | Sound Interface * |
* | | `-------------------------' `-------------------'
* | | ^ | ^ ^
* | | utlAtDceIoConfigFunction_P| |utlAtParse() |utlAtReplyFunction_P |
* | | | v | |utlAtSoundConfigFunction_P
* | | .-------------------------. |
* | | | |-------------------'
* | `-----------------------------| AT PARSER (DCE) |<----------------------------.
* `------------------------------>| |<---------------. |
* utlAtParserOp() | |--------------. | v
* `-------------------------' | | ---------------------
* ^ | | | AT-commands Table *
* utlAtDriverEvent()| |utlAtDriverRequestFunction_P | | ---------------------
* utlAtCommandResponse()| |utlAtTxLineDataFunction_P | |
* | v | |
* .------------------------. | |
* | Raw Modem Adaptation * | | |
* `------------------------' | |utlAtRetrieveDialStringFunction_P
* ^ | |
* | utlAtSaveDialStringFunction_P| |
* v | |
* .--------------. v |
* | Raw Modem * | ---------------------
* `--------------' Dial String Storage
* ---------------------
* * == must be supplied by application using this AT parser
*******************************************************************************************/
#include <ctype.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "utlTypes.h"
#include "utlConvert.h"
#include "utlError.h"
#include "utlLinkedList.h"
#include "utlMalloc.h"
#include "utlTime.h"
#include "utlTimer.h"
#include "utlTrace.h"
#include "utlVString.h"
#include "utilities.h"
#include "utlAtParser.h"
#ifdef utlTEST
# include <termios.h>
# include "utlEventHandler.h"
#endif
#include "atcmd_mult.h"
/*--- Configuration ---------------------------------------------------------*/
/*--- version info ---*/
#define utlAT_VERSION "2.04"
/*--- maximum length of an AT-command response (in characters) ---*/
#define utlAT_MAX_RESPONSE_LENGTH ((size_t)400)
/*--- maximum length of connect-text string (in characters) ---*/
#define utlAT_MAX_CONNECT_TEXT_LENGTH ((size_t)16)
/*--- maximum possible number of parameters in an extended-AT command ---*/
#define utlAT_MAX_PARAMETERS ((size_t)95)
/*--- array of supported basic-AT command prefix characters ---*/
#define utlAT_BASIC_COMMAND_PREFIXES "&"
/*--- array of supported extended-AT command prefix characters ---*/
#define utlAT_EXTENDED_COMMAND_PREFIXES "+*$%^"
/*--- array of characters allowed in dial strings ---*/
#define utlAT_DIALING_CHARACTERS "0123456789aAbBcCdD#*+,/\">gGiIlLpPsStTwW@!$"
/*--- for tracing AT parsers ---*/
#define utlTRACE_AT_PARSER "AT parsers"
char revisionId[128] = "unknown";
/*--- Data Types ------------------------------------------------------------*/
typedef enum {
utlAT_RESULT_CODE_OK = 0,
utlAT_RESULT_CODE_CONNECT = 1,
utlAT_RESULT_CODE_RING = 2,
utlAT_RESULT_CODE_NO_CARRIER = 3,
utlAT_RESULT_CODE_ERROR = 4,
utlAT_RESULT_CODE_NO_DIAL_TONE = 6,
utlAT_RESULT_CODE_BUSY = 7,
utlAT_RESULT_CODE_NO_ANSWER = 8,
utlAT_RESULT_CODE_SUPPRESS = 100,
utlAT_RESULT_CODE_NULL
} utlAtResultCode_T;
typedef enum {
utlAT_STRING_STATE_PASS_THROUGH,
utlAT_STRING_STATE_EXTRACT_HEX_DIGIT1,
utlAT_STRING_STATE_EXTRACT_HEX_DIGIT2,
} utlAtStringState_T;
/*--- Global Variables ------------------------------------------------------*/
/*--- Local Variables -------------------------------------------------------*/
static utlLinkedList_T parsers = utlEMPTY_LINKED_LIST;
/*****************************************************************************
* FORWARD DECLARATIONS
*****************************************************************************/
static utlReturnCode_T utlAtReset(const utlAtParser_P parser_p);
static utlReturnCode_T utlAtDceIoConfigEvent(const utlAtParser_P parser_p);
static utlReturnCode_T utlAtSoundConfigEvent(const utlAtParser_P parser_p);
static utlAtResultCode_T utlProcessSParameter(const utlAtParser_P parser_p,
const unsigned int parameter_num,
const unsigned int parameter_value);
static utlReturnCode_T utlSendBasicSyntaxResultCode(const utlAtParser_P2c parser_p,
const utlAtResultCode_T code,
const bool ignore_state);
static utlReturnCode_T utlGenerateFormattedGetResponse(const utlAtParser_P parser_p,
const char *command_name_p,
const utlAtParameterValue_P2c parameters_p,
const size_t num_parameters);
static void utlAbandonAllPendingAsyncResponse(utlAtParser_P parser_p);
unsigned int utlSendToProxy(char * cmdName,utlAtRequestType_T reqType ,const utlAtParser_P parser_p);
/*****************************************************************************
* APPLICATION-REPLACEABLE STUBS
*****************************************************************************/
/*--- dial-string storage ---*/
static struct {
char location_name[utlAT_MAX_STORED_LOCATION_NAME_LENGTH + 1];
char dial_string[utlAT_MAX_STORED_DIAL_STRING_LENGTH + 1];
} dial_string_storage[utlAT_MAX_STORED_DIAL_STRINGS];
static bool dial_string_storage_initialized = false;
char prxy_command_name[18] = {0};
// monitor LPP failure caused by NULL parser_p
#define LPP_DEBUG
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAtParserInitDialStringStorage()
* INPUT
* none
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Initializes the AT Parser's internal dial string storage.
*---------------------------------------------------------------------------*/
static void utlAtParserInitDialStringStore(void)
{
size_t i;
if (dial_string_storage_initialized == true)
return;
/*--- initialize internal static dial-string storage ---*/
for (i = 0; i < utlAT_MAX_STORED_DIAL_STRINGS; i++)
{
dial_string_storage[i].location_name[0] = '\0';
dial_string_storage[i].dial_string[0] = '\0';
}
dial_string_storage_initialized = true;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAtParserStoreSaveDialString(location_name_p, dial_string_p, arg_p)
* INPUT
* location_name_p == location to store dial-string to
* dial_string_p == NULL pointer, or a pointer to the dial string to be
* saved expressed as a null-terminated string
* arg_p == pointer to user-defined data
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Saves the dial string specified by `dial_string_p' to the AT Parser's
* internal dial-string store. If `dial_string_p' is NULL, clears any
* dial string previously saved to the specified location.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlAtParserStoreSaveDialString(const char *location_name_p,
const char *dial_string_p,
void *arg_p UNUSED)
{
size_t i;
size_t insertion_i;
utlAssert(location_name_p != NULL);
if (dial_string_storage_initialized != true)
utlAtParserInitDialStringStore();
/*--- search for specified dial string (ignoring character case) ---*/
insertion_i = -1;
for (i = 0; i < utlAT_MAX_STORED_DIAL_STRINGS; i++)
{
int cmp;
/*--- no more filled entries? ---*/
if (dial_string_storage[i].location_name[0] == '\0')
break;
cmp = strcasecmp(location_name_p, dial_string_storage[i].location_name);
/*--- found an existing entry with the same location name? ---*/
if (cmp == 0)
{
/*--- if no dial-string was supplied, clear/remove the specified entry ---*/
if ((dial_string_p == NULL) || (strlen(dial_string_p) == (size_t)0))
{
for (; i < (utlAT_MAX_STORED_DIAL_STRINGS - 1); i++)
{
(void)strcpy(dial_string_storage[i].location_name, dial_string_storage[i + 1].location_name);
(void)strcpy(dial_string_storage[i].dial_string, dial_string_storage[i + 1].dial_string);
}
dial_string_storage[i].location_name[0] = '\0';
dial_string_storage[i].dial_string[0] = '\0';
return utlSUCCESS;
}
/*--- check that dial-string fits into static storage ---*/
if (strlen(dial_string_p) > utlAT_MAX_STORED_DIAL_STRING_LENGTH)
{
utlError(utlAtParserStoreSaveDialString, "Dial-string too long to save.");
return utlFAILED;
}
/*--- save dial string--- */
(void)strcpy(dial_string_storage[i].dial_string, dial_string_p);
return utlSUCCESS;
}
/*--- found where to insert new dial string? ---*/
if (cmp < 0)
{
insertion_i = i;
for (i++; i < utlAT_MAX_STORED_DIAL_STRINGS; i++)
if (dial_string_storage[i].location_name[0] == '\0')
break;
break;
}
}
/*--- was specified dial-string not found and is there no room for another dial string? ---*/
if (i >= utlAT_MAX_STORED_DIAL_STRINGS)
{
/*--- were we just going to clear the location anyway? ---*/
if ((dial_string_p == NULL) || (strlen(dial_string_p) == (size_t)0))
return utlSUCCESS;
utlError(utlAtParserStoreSaveDialString1, "Dial-string storage full.");
return utlFAILED;
}
/*--- check that dial-string fits into static storage ---*/
if ((strlen(location_name_p) > utlAT_MAX_STORED_LOCATION_NAME_LENGTH) ||
(strlen(dial_string_p) > utlAT_MAX_STORED_DIAL_STRING_LENGTH))
{
utlError(utlAtParserStoreSaveDialString2, "Location name and/or Dial-string too long to save.");
return utlFAILED;
}
/*--- insertion sort ---*/
if (insertion_i != (size_t)-1)
for (; i > insertion_i; i--)
{
(void)strcpy(dial_string_storage[i].location_name, dial_string_storage[i - 1].location_name);
(void)strcpy(dial_string_storage[i].dial_string, dial_string_storage[i - 1].dial_string);
}
(void)strcpy(dial_string_storage[i].location_name, location_name_p);
(void)strcpy(dial_string_storage[i].dial_string, dial_string_p);
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAtParserStoreRetrieveDialString(location_name_pp, dial_string_pp, arg_p)
* INPUT
* location_name_pp == pointer to where a location-name pointer resides
* or a pointer to a NULL pointer
* dial_string_pp == pointer to where a pointer to the retrieved dial
* string should be placed
* arg_p == pointer to user-defined data
* OUTPUT
* *location_name_pp == next unread location-name, or NULL
* *dial_string_pp == pointer to the retrieved dial string, where the
* dial string is expressed as a null-terminated
* character string
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Retrieves a previously saved dial string from the AT Parser's internal
* dial-string store. To allow dial strings to be seqentially retrieved
* when the dial string location names are unknown, the first passed in
* `location_name_pp' should be set to NULL. Upon return
* `*location_name_pp' will be set to the next unread location name, and
* `*dial_string_pp' will be null. All subsequent calls will always set
* `*location_name_pp' to the next unread location name, and
* `*dial_string_pp' to the dial string associated with the passed-in
* location name. When this function returns from reading the last dial
* string, `*location_name_pp' will be set to NULL.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlAtParserStoreRetrieveDialString(const char **location_name_pp,
const char **dial_string_pp,
void *arg_p UNUSED)
{
size_t i;
utlAssert(location_name_pp != NULL);
if (dial_string_storage_initialized != true)
utlAtParserInitDialStringStore();
/*--- fetch location name for first filled slot? ---*/
if (*location_name_pp == NULL)
{
/*--- search for first filled location ---*/
for (i = 0; i < utlAT_MAX_STORED_DIAL_STRINGS; i++)
if (dial_string_storage[i].location_name[0] != '\0')
{
*location_name_pp = dial_string_storage[i].location_name;
break;
}
if ( dial_string_pp != NULL)
*dial_string_pp = NULL;
return utlSUCCESS;
}
utlAssert(dial_string_pp != NULL);
/*--- search for specified dial string (ignoring character case) ---*/
for (i = 0; i < utlAT_MAX_STORED_DIAL_STRINGS; i++)
if (strcasecmp(*location_name_pp, dial_string_storage[i].location_name) == 0)
break;
/*--- nothing found? ---*/
if (i >= utlAT_MAX_STORED_DIAL_STRINGS)
{
*location_name_pp = NULL;
*dial_string_pp = NULL;
return utlSUCCESS;
}
*location_name_pp = NULL;
*dial_string_pp = dial_string_storage[i].dial_string;
/*--- search for next unread dial string (ignoring character case) ---*/
for (i++; i < utlAT_MAX_STORED_DIAL_STRINGS; i++)
if (dial_string_storage[i].location_name[0] != '\0')
{
*location_name_pp = dial_string_storage[i].location_name;
break;
}
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlNextAvailableXID()
* INPUT
* none
* OUTPUT
* none
* RETURNS
* a transaction ID
* DESCRIPTION
* Fetches the next available (unused) transaction ID. The transaction
* ID is a 32-bit integer that we allow to wrap. We assume that by the
* time the integer has wrapped, the oldest values are no longer in use.
* Assuming a consumption rate of 50 transaction ID's per second, the
* value would wrap in about 990 days.
*
* Transaction ID's 0 and utlFAILED are reserved and thus never used.
*---------------------------------------------------------------------------*/
static unsigned int utlNextAvailableXID(void)
{
static unsigned int next_free_xid = 1;
static pthread_mutex_t xid_mutex = PTHREAD_MUTEX_INITIALIZER;
unsigned int xid;
bool unique;
do {
utlAtParser_P parser_p;
pthread_mutex_lock(&xid_mutex);
next_free_xid++;
/*--- fetch next free transaction ID ---*/
xid = next_free_xid;
pthread_mutex_unlock(&xid_mutex);
unique = (next_free_xid != (unsigned int)0) &&
(next_free_xid != 0xFFFFFFFF);
/*--- for each parser... ---*/
for (parser_p = (utlAtParser_P)parsers.head_p; parser_p != NULL;
parser_p = parser_p->next_p)
{
utlAtAsyncResponse_P async_response_p;
/*--- for each async response this parser is waiting for... ---*/
async_response_p = (utlAtAsyncResponse_P)parser_p->states.async_responses.pending.head_p;
for (; async_response_p != NULL; async_response_p = async_response_p->next_p)
{
if (async_response_p->xid == xid)
{
unique = false;
break;
}
}
if (unique == false)
break;
}
} while (unique == false);
#ifdef LPP_DEBUG
ERRMSG(utlNextAvailableXID477,"%s: xid %u\n", __FUNCTION__, xid);
#endif
return xid;
}
/*****************************************************************************
* AT-PARSER
*****************************************************************************/
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAtReset(parser_p)
* INPUT
* parser_p == an open AT-command parser
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Initializes AT-command parser parameters.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlAtReset(const utlAtParser_P parser_p)
{
utlAssert(parser_p != NULL);
#ifdef LPP_DEBUG
ERRMSG(utlAtReset502, "%s: enter\n", __FUNCTION__);
#endif
/*--- S-parameters ---*/
{
size_t i;
parser_p->parameters.S[utlAT_AUTO_ANSWER] = 0; /* disable auto-answer */
parser_p->parameters.S[utlAT_RING_COUNTER] = 0; /* cleared */
parser_p->parameters.S[utlAT_ESCAPE_CHAR] = '+';
parser_p->parameters.S[utlAT_LINE_TERM_CHAR] = '\015'; /* <carriage-return> */
parser_p->parameters.S[utlAT_FORMATTING_CHAR] = '\012'; /* <new-line> */
parser_p->parameters.S[utlAT_LINE_EDIT_CHAR] = '\010'; /* <back-space> */
parser_p->parameters.S[utlAT_BLIND_DIAL_PAUSE_TIME] = 2; /* 2 seconds */
parser_p->parameters.S[utlAT_CONN_COMPLETE_TIMEOUT] = 1; /* 1 second */
parser_p->parameters.S[utlAT_DIALING_PAUSE_TIME] = 2; /* 2 seconds */
parser_p->parameters.S[utlAT_CARRIER_DETECT_TIME] = 6; /* 0.6 seconds */
parser_p->parameters.S[utlAT_CARRIER_LOSS_TIME] = 7; /* 0.7 seconds */
parser_p->parameters.S[utlAT_DTMF_TONE_DURATION] = 63; /* 0.63 seconds */
parser_p->parameters.S[utlAT_ESCAPE_GUARD_TIME] = 50; /* 1 second */
parser_p->parameters.S[utlAT_SEPARATER_CHAR] = ',';
parser_p->parameters.S[utlAT_TERMINATER_CHAR] = ';';
parser_p->parameters.S[utlAT_XON_CHAR] = 0x11; /* <DC1> */
parser_p->parameters.S[utlAT_XOFF_CHAR] = 0x13; /* <DC3> */
parser_p->parameters.S[utlAT_SLEEP_TIME] = 0; /* 0 seconds */
parser_p->parameters.S[utlAT_DTR_DELAY_TIME] = 10; /* 0.1 seconds */
parser_p->parameters.S[utlAT_HOOK_FLASH_TIME] = 5; /* 0.5 seconds */
parser_p->parameters.S[utlAT_INACTIVITY_TIME] = 10; /* 100 seconds */
parser_p->parameters.S[utlAT_DISCONNECT_WAIT_TIME] = 0; /* 0 seconds */
/*--- notify application of S-parameter value changes ---*/
if (parser_p->call_backs.s_parameter_function_p != NULL)
{
for (i = 0; i < utlAT_NUM_S_PARAMETERS; i++)
if ((parser_p->call_backs.s_parameter_function_p)(i, parser_p->parameters.S[i], parser_p->call_backs.arg_p) != utlSUCCESS)
return utlFAILED;
}
}
/*--- parameters ---*/
{
parser_p->parameters.echo_text = false;
parser_p->parameters.verbose_results = true;
parser_p->parameters.suppress_results = false;
parser_p->parameters.include_connect_text = false;
parser_p->parameters.detect_dial_tone = false;
parser_p->parameters.detect_busy_signal = false;
parser_p->parameters.dialing_mode = utlAT_DIALING_MODE_TONE;
parser_p->parameters.raw_framing = 3;
parser_p->parameters.raw_parity = 3;
}
/*--- buffers ---*/
{
(void)memset(parser_p->buffers.queue, 0, sizeof(parser_p->buffers.queue));
parser_p->buffers.echo_p = parser_p->buffers.queue;
parser_p->buffers.head_p = parser_p->buffers.queue;
parser_p->buffers.tail_p = parser_p->buffers.queue;
parser_p->buffers.term_p = parser_p->buffers.queue + utlNumberOf(parser_p->buffers.queue);
parser_p->buffers.overflow = false;
(void)strcpy(parser_p->buffers.previous_line, "AT");
}
/*--- DCE I/O config ---*/
{
parser_p->dce_io_config.data_rate = 0; /* auto-detect */
parser_p->dce_io_config.data_bits = 8;
parser_p->dce_io_config.parity_bits = 0; /* no parity */
parser_p->dce_io_config.stop_bits = 1;
parser_p->dce_io_config.parity = utlDATA_PARITY_SPACE;
parser_p->dce_io_config.flow_control.dce_by_dte = 2;
parser_p->dce_io_config.flow_control.dte_by_dce = 2;
parser_p->dce_io_config.flow_control.xon_char = parser_p->parameters.S[utlAT_XON_CHAR];
parser_p->dce_io_config.flow_control.xoff_char = parser_p->parameters.S[utlAT_XOFF_CHAR];
parser_p->dce_io_config.modes.dcd_always_on = false;
parser_p->dce_io_config.modes.ignore_dtr = false;
parser_p->dce_io_config.modes.drop_call_on_dtr_loss = false;
parser_p->dce_io_config.modes.dsr_mode = 0;
parser_p->states.dce_io_config_pending_mask = ~0u; /* all config-changes are pending */
}
/*--- sound config ---*/
{
parser_p->sound_config.monitor_speaker.mode = 1;
parser_p->sound_config.monitor_speaker.level = 0;
parser_p->states.sound_config_pending_mask = ~0u; /* all config-changes are pending */
}
/*--- peg counts ---*/
{
parser_p->peg_counts.basic_commands = 0;
parser_p->peg_counts.extended_commands = 0;
parser_p->peg_counts.undefined_commands = 0;
parser_p->peg_counts.bad_commands = 0;
}
/*--- parser states ---*/
{
parser_p->states.go_on_line = false;
parser_p->states.discard_current_line = false;
parser_p->states.bypass_mode = false;
{
utlAtAsyncResponse_P async_response_p;
if (parser_p->queue_semaphore.initialized && utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlAtReset, "Cannot exclusively acquire semaphore!\n");
return utlFAILED;
}
/*--- discard any pending async responses ---*/
while ((async_response_p = utlGetHeadNode(&(parser_p->states.async_responses.pending), utlAtAsyncResponse_T)) != NULL)
{
if (parser_p->queue_semaphore.initialized) utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
/*--- stop timer ---*/
if ( async_response_p->timer_id != (utlTimerId_T)utlFAILED)
{
if (utlStopTimer(async_response_p->timer_id) != utlSUCCESS)
return utlFAILED;
async_response_p->timer_id = utlFAILED;
}
if (parser_p->queue_semaphore.initialized && utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlAtReset1, "Cannot exclusively acquire semaphore!\n");
return utlFAILED;
}
utlPutTailNode(&(parser_p->states.async_responses.unused), utlAtAsyncResponse_T, async_response_p);
}
/*--- re-initialize nodes ---*/
for (async_response_p = (utlAtAsyncResponse_P)parser_p->states.async_responses.unused.head_p; async_response_p != NULL; async_response_p = async_response_p->next_p)
{
async_response_p->parser_p = parser_p;
async_response_p->xid = utlFAILED;
async_response_p->op = utlAT_ASYNC_OP_UNKNOWN;
async_response_p->command_p = NULL;
}
if (parser_p->queue_semaphore.initialized) utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
}
parser_p->states.sync_response.complete_reported = false;
parser_p->states.sync_response.abort_reported = false;
parser_p->states.sync_response.error_reported = false;
parser_p->states.sync_response.parameter_values_p = NULL;
parser_p->states.sync_response.custom_p = NULL;
parser_p->states.sync_response.info_text_p = NULL;
parser_p->states.sync_response.abort_response_p = NULL;
parser_p->states.sync_response.command_syntax_p = NULL;
parser_p->states.dce_io_config_pending_mask = ~0;
parser_p->states.sound_config_pending_mask = ~0;
parser_p->states.line_off = 0;
parser_p->states.escape.last_tx_time.seconds = 0;
parser_p->states.escape.last_tx_time.nanoseconds = 0;
parser_p->states.escape.state = utlAT_PARSER_ESCAPE_STATE_BEGIN;
parser_p->states.dial_string.active = false;
parser_p->states.dial_string.dial_string_delay = 0; /* no delay */
(void)memset(parser_p->states.dial_string.last, 0, sizeof(parser_p->states.dial_string.last));
(void)memset(parser_p->states.dial_string.buf, 0, sizeof(parser_p->states.dial_string.buf));
parser_p->states.dial_string.c_p = parser_p->states.dial_string.buf; /* reset */
parser_p->states.dial_string.options.international = false;
parser_p->states.dial_string.options.do_call_origination = true;
parser_p->states.dial_string.options.use_CCUG_SS_info = false;
parser_p->states.dial_string.options.CLI_presentation = '\0';
parser_p->states.delay_dialing_time.seconds = 0;
parser_p->states.delay_dialing_time.nanoseconds = 125000000;
parser_p->states.min_silence_time.seconds = 5;
parser_p->states.min_silence_time.nanoseconds = 0;
}
/*--- set values slaved from s-parameter settings ---*/
if ((utlProcessSParameter(parser_p, utlAT_BLIND_DIAL_PAUSE_TIME, parser_p->parameters.S[utlAT_BLIND_DIAL_PAUSE_TIME]) != utlSUCCESS) ||
(utlProcessSParameter(parser_p, utlAT_CONN_COMPLETE_TIMEOUT, parser_p->parameters.S[utlAT_CONN_COMPLETE_TIMEOUT]) != utlSUCCESS) ||
(utlProcessSParameter(parser_p, utlAT_DIALING_PAUSE_TIME, parser_p->parameters.S[utlAT_DIALING_PAUSE_TIME]) != utlSUCCESS) ||
(utlProcessSParameter(parser_p, utlAT_CARRIER_DETECT_TIME, parser_p->parameters.S[utlAT_CARRIER_DETECT_TIME]) != utlSUCCESS) ||
(utlProcessSParameter(parser_p, utlAT_CARRIER_LOSS_TIME, parser_p->parameters.S[utlAT_CARRIER_LOSS_TIME]) != utlSUCCESS) ||
(utlProcessSParameter(parser_p, utlAT_XON_CHAR, parser_p->parameters.S[utlAT_XON_CHAR]) != utlSUCCESS) ||
(utlProcessSParameter(parser_p, utlAT_XOFF_CHAR, parser_p->parameters.S[utlAT_XOFF_CHAR]) != utlSUCCESS) ||
(utlProcessSParameter(parser_p, utlAT_DTR_DELAY_TIME, parser_p->parameters.S[utlAT_DTR_DELAY_TIME]) != utlSUCCESS) ||
(utlProcessSParameter(parser_p, utlAT_HOOK_FLASH_TIME, parser_p->parameters.S[utlAT_HOOK_FLASH_TIME]) != utlSUCCESS) ||
(utlProcessSParameter(parser_p, utlAT_INACTIVITY_TIME, parser_p->parameters.S[utlAT_INACTIVITY_TIME]) != utlSUCCESS) ||
(utlProcessSParameter(parser_p, utlAT_DISCONNECT_WAIT_TIME, parser_p->parameters.S[utlAT_DISCONNECT_WAIT_TIME]) != utlSUCCESS))
return utlFAILED;
if (parser_p->cmd_cnt_semaphore.initialized && utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlAtReset2, "Cannot exclusively acquire semaphore!\n");
return utlFAILED;
}
parser_p->commands_in_line = 0;
if (parser_p->cmd_cnt_semaphore.initialized) utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
if( utlInitSemaphore(&parser_p->queue_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE |
utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS )
return utlFAILED;
if( utlInitSemaphore(&parser_p->cmd_cnt_semaphore, utlSEMAPHORE_ATTR_SHARING_ENABLE |
utlSEMAPHORE_ATTR_NESTING_ENABLE) != utlSUCCESS )
return utlFAILED;
//reset proxy configuration
parser_p->isProxy = false;
return utlSUCCESS;
}
/*------------------------------------------------------------------------------
* Function that check parameters for utlOpenAtParser
*------------------------------------------------------------------------------*/
static int CheckParamForOpenAtParser(const utlAtCommand_P2c commands_p,
const size_t num_commands,
void *arg_p)
{
#ifdef utlDEBUG
utlAtCommand_P2c command_p;
utlAtCommand_P2c term_command_p;
/*--- for each command... ---*/
term_command_p = commands_p + num_commands;
for (command_p = commands_p; command_p < term_command_p; command_p++)
{
/*--- name missing? ---*/
if (command_p->name_p == NULL)
{
utlError(CheckParamForOpenAtParser, "Command at %d has no name.", command_p - commands_p);
return -1;
}
if (command_p->type == utlAT_COMMAND_TYPE_BASIC)
{
/*--- check command name syntax (must be a single character,
or a single character preceded by one of the pre-defined
basic-AT-command prefixes) */
{
const char *c_p;
c_p = command_p->name_p;
if (strchr(utlAT_BASIC_COMMAND_PREFIXES, *c_p) != NULL)
c_p++;
if (((*c_p < 'A') || (*c_p > 'Z')) &&
((*c_p < 'a') || (*c_p > 'z')))
{
utlError(CheckParamForOpenAtParser1, "Invalid basic AT-command-name character '%c'.", *c_p);
return -1;
}
c_p++;
if (*c_p != '\0')
{
utlError(CheckParamForOpenAtParser2, "Basic AT-command name '%s' too long.",
command_p->name_p);
return -1;
}
}
/*--- check command parameter info (can either be zero or one parameter) ---*/
{
/*--- no parameters? ---*/
if (command_p->parameters_p == NULL)
{
if (command_p->num_parameters != (size_t)0)
{
utlError(CheckParamForOpenAtParser3, "Command '%s' has an invalid number of parameters (%d).", command_p->name_p, command_p->num_parameters);
return -1;
}
}
else
{
utlAtParameter_P2c parameter_p;
if (command_p->num_parameters != (size_t)1)
{
utlError(CheckParamForOpenAtParser4, "Command '%s' has an invalid number of parameters (%d).", command_p->name_p, command_p->num_parameters);
return -1;
}
parameter_p = command_p->parameters_p;
/*--- parameter (if present) must be decimal, except for the D and &Z commands ---*/
if ((((strcasecmp(command_p->name_p, "D") == 0) || (strcasecmp(command_p->name_p, "&Z") == 0)) && (parameter_p->type != utlAT_DATA_TYPE_DIAL_STRING)) ||
(((strcasecmp(command_p->name_p, "D") != 0) && (strcasecmp(command_p->name_p, "&Z") != 0)) && (parameter_p->type != utlAT_DATA_TYPE_DECIMAL)))
{
utlError(CheckParamForOpenAtParser5, "Command '%s' has an invalid parameter type.", command_p->name_p);
return -1;
}
if (parameter_p->access != utlAT_PARAMETER_ACCESS_READ_WRITE)
{
utlError(CheckParamForOpenAtParser6, "Command '%s' specifies an invalid parameter access.", command_p->name_p);
return -1;
}
}
}
}
else if ((command_p->type == utlAT_COMMAND_TYPE_EXTENDED) ||
(command_p->type == utlAT_COMMAND_TYPE_EXACTION)) ||
(command_p->type == utlAT_COMMAND_TYPE_EXTENDED_EXACTION))
{
/*--- check command name syntax ---*/
{
const char *c_p;
if (strlen(command_p->name_p) > 17)
{
utlError(CheckParamForOpenAtParser7, "Command name %s too long.", command_p->name_p);
return -1;
}
c_p = command_p->name_p;
/*--- first character must be a valid extended command prefix, second must
be alphabetic, rest must belong to a specific set of characters */
if (strchr(utlAT_EXTENDED_COMMAND_PREFIXES, *c_p) == NULL)
{
utlError(CheckParamForOpenAtParser8, "Invalid extended AT-command-name character '%c' in command '%s'.", *c_p, command_p->name_p);
return -1;
}
c_p++;
if (((*c_p < 'A') || (*c_p > 'Z')) &&
((*c_p < 'a') || (*c_p > 'z')) && (*c_p != ' '))
{
utlError(CheckParamForOpenAtParser9, "Invalid extended AT-command-name character '%c' in command '%s'.", *c_p, command_p->name_p);
return -1;
}
c_p++;
for (; *c_p != '\0'; c_p++)
if (((*c_p < 'A') || (*c_p > 'Z')) &&
((*c_p < 'a') || (*c_p > 'z')) &&
((*c_p < '0') || (*c_p > '9')) &&
(*c_p != '!') && (*c_p != '%') &&
(*c_p != '-') && (*c_p != '.') &&
(*c_p != '/') && (*c_p != ':') && (*c_p != '_'))
{
utlError(CheckParamForOpenAtParser10, "Invalid extended AT-command-name character '%c' in command '%s'.", *c_p, command_p->name_p);
return -1;
}
}
/*--- check command parameter(s) info ---*/
{
/*--- no parameters? ---*/
if (command_p->parameters_p == NULL)
{
/* FIXME: The definition of utlDEFINE_EXACTION_AT_COMMAND has problem and will result in wrong parameter; "telcontroller.c" cannot initialize AT parser because of this error.
if (command_p->num_parameters != (size_t) 0) {
utlError("Command '%s' has an invalid number of parameters (%d).", command_p->name_p, command_p->num_parameters);
return -1;
}
*/
}
else
{
utlAtParameter_P2c parameter_p;
utlAtParameter_P2c term_parameter_p;
if (command_p->num_parameters > utlAT_MAX_PARAMETERS)
{
utlError(CheckParamForOpenAtParser11, "Command '%s' has too many parameters (%d).", command_p->name_p, command_p->num_parameters);
return -1;
}
/*--- for each parameter... ---*/
term_parameter_p = command_p->parameters_p + command_p->num_parameters;
for ( parameter_p = command_p->parameters_p; parameter_p < term_parameter_p; parameter_p++)
{
if ((parameter_p->type != utlAT_DATA_TYPE_DECIMAL) &&
(parameter_p->type != utlAT_DATA_TYPE_HEXADECIMAL) &&
(parameter_p->type != utlAT_DATA_TYPE_BINARY) &&
(parameter_p->type != utlAT_DATA_TYPE_STRING) &&
(parameter_p->type != utlAT_DATA_TYPE_QSTRING) &&
(parameter_p->type != utlAT_DATA_TYPE_DIAL_STRING))
{
utlError(CheckParamForOpenAtParser12, "Command '%s' has an invalid parameter type.", command_p->name_p);
return -1;
}
if ((parameter_p->access != utlAT_PARAMETER_ACCESS_READ_WRITE) &&
(parameter_p->access != utlAT_PARAMETER_ACCESS_READ) &&
(parameter_p->access != utlAT_PARAMETER_ACCESS_READ_ONLY) &&
(parameter_p->access != utlAT_PARAMETER_ACCESS_WRITE_ONLY))
{
utlError(CheckParamForOpenAtParser13, "Command '%s' specifies an invalid parameter access.", command_p->name_p);
return -1;
}
/*--- check that dial-string parameter is the very last parameter ---*/
if (( parameter_p->type == utlAT_DATA_TYPE_DIAL_STRING) &&
((parameter_p + 1) != term_parameter_p))
{
utlError(CheckParamForOpenAtParser14, "Dial-string parameter is not the last parameter in command '%s'.", command_p->name_p);
return -1;
}
}
}
}
}
}
#endif
UNUSEDPARAM(commands_p)
UNUSEDPARAM(num_commands)
UNUSEDPARAM(arg_p)
return 0;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utOpenAtParser(commands_p, num_commands, arg_p)
* INPUT
* commands_p == pointer to an array of utlAtCommand_T structures
* num_commands == number of commands pointed to by `commands_p'
* arg_p == user-defined argument for call-backs
* OUTPUT
* none
* RETURNS
* pointer to an initialized utlAtParser_T structure.
* DESCRIPTION
* Opens a new AT command parser.
*---------------------------------------------------------------------------*/
utlAtParser_P utlOpenAtParser(const utlAtCommand_P2c commands_p,
const size_t num_commands,
void *arg_p)
{
utlAtParser_P parser_p;
utlAssert(commands_p != NULL);
utlAssert(num_commands > (size_t)0);
atcmd_mult_init();
if (CheckParamForOpenAtParser(commands_p, num_commands, arg_p) < 0)
return NULL;
/*--- allocate and initialize a new AT command parser ---*/
{
size_t parser_size;
parser_size = sizeof(utlAtParser_T);
if ((parser_p = (utlAtParser_P)utlMalloc(parser_size)) == NULL)
return NULL;
(void)memset(parser_p, 0, sizeof(*parser_p));
parser_p->next_p = NULL;
/*--- info ---*/
{
parser_p->info.id_p = "ASR";
parser_p->info.manufacturer_p = "ASR";
parser_p->info.model_p = "Linux";
parser_p->info.revision_p = "1.0";
parser_p->info.serial_number_p = NULL;
parser_p->info.object_id_p = NULL;
parser_p->info.country_code_p = NULL;
parser_p->info.connect_text_p = NULL;
parser_p->info.mt_capabilities_p = "+GCAP: +CGSM";
}
/*--- options ---*/
{
parser_p->options.allow_default_S_parameter_values = true;
parser_p->options.allow_string_escapes = true;
parser_p->options.use_carrier_result_code = false;
parser_p->options.report_each_ring = false;
}
/*--- call backs ---*/
{
parser_p->call_backs.dce_io_config_function_p = NULL;
parser_p->call_backs.sound_config_function_p = NULL;
parser_p->call_backs.s_parameter_function_p = NULL;
parser_p->call_backs.save_dial_string_function_p = utlAtParserStoreSaveDialString;
parser_p->call_backs.retrieve_dial_string_function_p = utlAtParserStoreRetrieveDialString;
parser_p->call_backs.reply_function_p = NULL;
parser_p->call_backs.tx_line_data_function_p = NULL;
parser_p->call_backs.driver_request_function_p = NULL;
parser_p->call_backs.arg_p = arg_p;
}
parser_p->commands_p = commands_p;
parser_p->num_commands = num_commands;
/*--- parser states ---*/
{
utlAtAsyncResponse_P async_response_p;
utlAtAsyncResponse_P term_async_response_p;
utlInitLinkedList(&(parser_p->states.async_responses.unused));
utlInitLinkedList(&(parser_p->states.async_responses.pending));
term_async_response_p = parser_p->states.async_responses.nodes +
utlNumberOf(parser_p->states.async_responses.nodes);
for (async_response_p = parser_p->states.async_responses.nodes;
async_response_p < term_async_response_p; async_response_p++)
{
async_response_p->parser_p = parser_p;
async_response_p->timer_id = utlFAILED;
utlPutTailNode(&(parser_p->states.async_responses.unused),
utlAtAsyncResponse_T, async_response_p);
}
}
if (utlAtReset(parser_p) != utlSUCCESS)
{
/*--- clean ---*/
utlFree(parser_p);
return NULL;
}
}
/*--- apply any pending configuration changes ---*/
if ((utlAtDceIoConfigEvent(parser_p) != utlSUCCESS) ||
(utlAtSoundConfigEvent(parser_p) != utlSUCCESS))
{
/*--- clean ---*/
utlFree(parser_p);
return NULL;
}
/*--- add to list of known AT parsers ---*/
utlPutTailNode(&parsers, utlAtParser_T, parser_p);
return parser_p;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlCloseAtParser(parser_p)
* INPUT
* parser_p == an open AT-command parser
* OUTPUT
* none
* RETURNS
* utlSUCCESS
* DESCRIPTION
* Closes an open AT command parser.
*---------------------------------------------------------------------------*/
utlReturnCode_T utlCloseAtParser(const utlAtParser_P parser_p)
{
utlAssert(parser_p != NULL);
#ifdef LPP_DEBUG
ERRMSG(utlCloseAtParser1059, "%s: enter\n", __FUNCTION__);
#endif
/*--- parser states ---*/
{
utlAtAsyncResponse_P async_response_p;
/*--- discard any pending responsess ---*/
while ((async_response_p = utlGetHeadNode(&(parser_p->states.async_responses.pending), utlAtAsyncResponse_T)) != NULL)
if ( async_response_p->timer_id != (utlTimerId_T) utlFAILED)
{
if (utlStopTimer(async_response_p->timer_id) != utlSUCCESS)
return utlFAILED;
async_response_p->timer_id = utlFAILED;
}
}
/*--- remove AT parser from list of known parsers ---*/
if (utlGetNode(&parsers, utlAtParser_T, NULL, parser_p) != parser_p)
return utlFAILED;
/*--- destroy parser ---*/
utlFreeConst(parser_p);
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAtParserOp(parser_p, op, ...)
*
* utlAtParserOp(parser_p, utlAT_PARSER_OP_HANG_UP)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_RESET)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_DCE_IO_CONFIG_HANDLER, dce_io_config_function_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_SOUND_CONFIG_HANDLER, sound_config_function_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_S_PARAMETER_HANDLER, s_parameter_function_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_SAVE_DIAL_STRING_HANLDER, save_dial_string_function_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_RETRIEVE_DIAL_STRING_HANLDER, retrieve_dial_string_function_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_REPLY_HANDLER, reply_function_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_TX_LINE_DATA_HANDLER, tx_line_data_function_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_DRIVER_REQUEST_HANDLER, driver_request_function_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_ECHO_ON)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_ECHO_OFF)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SELECT_CARRIER_RESULT_CODE)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SELECT_CONNECT_RESULT_CODE)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_S_PARAMETER_VALUE, parameter_num, parameter_value)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_GET_S_PARAMETER_VALUE, parameter_num, parameter_value_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_ID, id_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_MANUFACTURER, manufacturer_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_MODEL, model_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_REVISION, revision_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_SERIAL_NUMBER, serial_number_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_OBJECT_ID, object_id_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_COUNTRY_CODE, country_code_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_SET_CONNECT_TEXT, connect_text_p)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_ENABLE_BYPASS_MODE)
* utlAtParserOp(parser_p, utlAT_PARSER_OP_DISABLE_BYPASS_MODE)
* INPUT
* parser_p == an open AT-command parser
* op == the operation to perform
* ... == zero or more operation-specific parameters
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Performs the requested operation on an open At-command parser.
*---------------------------------------------------------------------------*/
utlReturnCode_T utlAtParserOp(const utlAtParser_P parser_p,
const utlAtParserOp_T op,
...)
{
va_list va_arg_p;
const char *s_p = NULL;
unsigned int parameter_num = 0;
unsigned int parameter_value = 0;
unsigned int *parameter_value_p = NULL;
utlAssert(parser_p != NULL);
va_start(va_arg_p, op);
/*--- check parameter(s) (where possible) ---*/
switch (op)
{
case utlAT_PARSER_OP_SET_S_PARAMETER_VALUE:
parameter_num = va_arg(va_arg_p, unsigned int);
parameter_value = va_arg(va_arg_p, unsigned int);
/*--- parameter-number too large? ---*/
if (parameter_num >= utlNumberOf(parser_p->parameters.S))
{
utlError(utlAtParserOp, "Invalid S-parameter number %d", parameter_num);
va_end(va_arg_p);
return utlFAILED;
}
break;
case utlAT_PARSER_OP_GET_S_PARAMETER_VALUE:
parameter_num = va_arg(va_arg_p, unsigned int);
parameter_value_p = va_arg(va_arg_p, unsigned int *);
/*--- parameter-number too large? ---*/
if (parameter_num >= utlNumberOf(parser_p->parameters.S))
{
utlError(utlAtParserOp1, "Invalid S-parameter number %d", parameter_num);
va_end(va_arg_p);
return utlFAILED;
}
/*--- bad pointer ---*/
if (parameter_value_p == NULL)
{
utlError(utlAtParserOp2, "Invalid S-parameter value pointer");
va_end(va_arg_p);
return utlFAILED;
}
break;
case utlAT_PARSER_OP_SET_ID:
case utlAT_PARSER_OP_SET_MANUFACTURER:
case utlAT_PARSER_OP_SET_MODEL:
case utlAT_PARSER_OP_SET_REVISION:
case utlAT_PARSER_OP_SET_SERIAL_NUMBER:
case utlAT_PARSER_OP_SET_OBJECT_ID:
case utlAT_PARSER_OP_SET_CAPABILITIES:
s_p = va_arg(va_arg_p, const char *);
/*--- is string parameter too long? ---*/
if (strlen(s_p) > (size_t)2048)
{
utlError(utlAtParserOp3, "Character string parameter is too long (> 2048 characters).");
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
/*--- does string parameter contain reserved character sequences? ---*/
if (( strstr(s_p, "0\r")) || (strcasestr(s_p, "OK\r")))
{
utlError(utlAtParserOp4, "Character string contains reserved character sequences (0<cr> or OK<cr>).");
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
break;
case utlAT_PARSER_OP_SET_COUNTRY_CODE:
s_p = va_arg(va_arg_p, const char *);
/*--- Country codes are encoded using T.35 8-bit country codes, and
consist of a single 2-digit hexadecimal value, or multple comma-
separated 2-digit hexadecimal values all enclosed in parenthesis.
Some sample country code strings are shown below:
"20"
"(20,B5,00)"
Some T.35 country codes:
20 == Canada
3C == Finland
3D == France
42 == Germany
50 == Hongkong
58 == Israel
00 == Japan
61 == Korea
FE == Taiwan
A9 == Thailand
B4 == UK
B5 == USA */
/*--- is string parameter too long? ---*/
if (strlen(s_p) > (size_t)2048)
{
utlError(utlAtParserOp5, "Character string parameter is too long (> 2048 characters).");
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
/*--- check that syntax is of the form: (hh,hh,hh,..,hh) ---*/
if (s_p[0] == '(')
{
const char *c_p;
c_p = s_p;
/*--- ensure there's an opening parenthesis ---*/
if (*c_p != '(')
{
utlError(utlAtParserOp6, "Invalid country-code syntax.");
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
for (c_p++; *c_p != '\0'; c_p++)
{
/*--- check 2-digit hexadecimal value ---*/
if ((((c_p[0] < '0') || (c_p[0] > '9')) &&
((c_p[0] < 'A') || (c_p[0] > 'F')) &&
((c_p[0] < 'a') || (c_p[0] > 'f'))) ||
(((c_p[1] < '0') || (c_p[1] > '9')) &&
((c_p[1] < 'A') || (c_p[1] > 'F')) &&
((c_p[1] < 'a') || (c_p[1] > 'f'))))
{
utlError(utlAtParserOp7, "Invalid country-code syntax.");
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
c_p += 2;
if (*c_p == ')')
break;
else if (*c_p != ',')
{
utlError(utlAtParserOp8, "Invalid country-code syntax.");
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
}
/*--- ensure there's a closing parenthesis ---*/
if (*c_p != ')')
{
utlError(utlAtParserOp9, "Invalid country-code syntax.");
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
/*--- check that syntax of single values have the form: hh ---*/
}
else
{
/*--- check 2-digit hexadecimal value ---*/
if ((((s_p[0] < '0') || (s_p[0] > '9')) &&
((s_p[0] < 'A') || (s_p[0] > 'F')) &&
((s_p[0] < 'a') || (s_p[0] > 'f'))) ||
(((s_p[1] < '0') || (s_p[1] > '9')) &&
((s_p[1] < 'A') || (s_p[1] > 'F')) &&
((s_p[1] < 'a') || (s_p[1] > 'f'))) ||
( s_p[2] != '\0'))
{
utlError(utlAtParserOp10, "Invalid country-code syntax.");
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
}
break;
case utlAT_PARSER_OP_SET_CONNECT_TEXT:
s_p = va_arg(va_arg_p, const char *);
/*--- is string parameter too long? ---*/
if (strlen(s_p) > (size_t)utlAT_MAX_CONNECT_TEXT_LENGTH)
{
utlError(utlAtParserOp11, "Character string parameter is too long (> %d characters).", utlAT_MAX_CONNECT_TEXT_LENGTH);
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
break;
default:
break;
}
/*--- execute operation ---*/
switch (op)
{
case utlAT_PARSER_OP_HANG_UP:
break;
case utlAT_PARSER_OP_RESET:
break;
case utlAT_PARSER_OP_SET_DCE_IO_CONFIG_HANDLER:
parser_p->call_backs.dce_io_config_function_p = va_arg(va_arg_p, utlAtDceIoConfigFunction_P);
/*--- notify application of current I/O config settings ---*/
if (parser_p->call_backs.dce_io_config_function_p != NULL)
{
if (utlAtDceIoConfigEvent(parser_p) != utlSUCCESS)
{
/*--- clean ---*/
va_end(va_arg_p);
return utlAT_RESULT_CODE_ERROR;
}
}
break;
case utlAT_PARSER_OP_SET_SOUND_CONFIG_HANDLER:
parser_p->call_backs.sound_config_function_p = va_arg(va_arg_p, utlAtSoundConfigFunction_P);
/*--- notify application of current sound config settings ---*/
if (parser_p->call_backs.sound_config_function_p != NULL)
{
if (utlAtSoundConfigEvent(parser_p) != utlSUCCESS)
{
/*--- clean ---*/
va_end(va_arg_p);
return utlAT_RESULT_CODE_ERROR;
}
}
break;
case utlAT_PARSER_OP_SET_S_PARAMETER_HANDLER:
parser_p->call_backs.s_parameter_function_p = va_arg(va_arg_p, utlAtSParameterFunction_P);
/*--- notify application of current S-parameter settings ---*/
if (parser_p->call_backs.s_parameter_function_p != NULL)
{
size_t i;
for (i = 0; i < utlAT_NUM_S_PARAMETERS; i++)
if ((parser_p->call_backs.s_parameter_function_p)(i, parser_p->parameters.S[i], parser_p->call_backs.arg_p) != utlSUCCESS)
{
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
}
break;
case utlAT_PARSER_OP_SET_REPLY_HANDLER:
{
utlAtReplyFunction_P reply_function_p;
reply_function_p = va_arg(va_arg_p, utlAtReplyFunction_P);
/*--- when the first handler is set, emit an "OK" message ---*/
if ((parser_p->call_backs.reply_function_p == NULL) && (reply_function_p != NULL))
{
parser_p->call_backs.reply_function_p = reply_function_p;
/* Fix me: We don't need to emit "OK" here */
//if (utlSendBasicSyntaxResultCode(parser_p, utlAT_RESULT_CODE_OK, false) != utlSUCCESS)
// return utlFAILED;
}
else
parser_p->call_backs.reply_function_p = reply_function_p;
}
break;
case utlAT_PARSER_OP_SET_GET_ATCMD_TIMEOUT_VALUE_HANDLER:
{
utlAtGetAtcmdTimeoutValueFunction_P getAtcmdTimeoutValue_function_p;
getAtcmdTimeoutValue_function_p = va_arg(va_arg_p, utlAtGetAtcmdTimeoutValueFunction_P);
parser_p->call_backs.getAtcmdTimeoutValue_function_p = getAtcmdTimeoutValue_function_p;
}
break;
case utlAT_PARSER_OP_SET_ATCMD_TIMEOUT_ERROR_HANDLER:
{
utlAtcmdTimeoutErrorFunction_P atcmdTimeoutError_function_P;
atcmdTimeoutError_function_P = va_arg(va_arg_p, utlAtcmdTimeoutErrorFunction_P);
parser_p->call_backs.atcmdTimeoutError_function_P = atcmdTimeoutError_function_P;
}
break;
case utlAT_PARSER_OP_SET_ATCMD_CONTINUOUS_TIMEOUT_HANDLER:
{
utlAtcmdContinuousTimeoutFunction_P atcmdContinuousTimeout_function_p;
atcmdContinuousTimeout_function_p = va_arg(va_arg_p, utlAtcmdContinuousTimeoutFunction_P);
parser_p->call_backs.atcmdContinuousTimeout_function_p = atcmdContinuousTimeout_function_p;
}
break;
case utlAT_PARSER_OP_SET_AT_PARSER_TRIGGER_HANDLER:
{
utlAtParserTriggerFunction_P atParserTrigger_function_p;
atParserTrigger_function_p = va_arg(va_arg_p, utlAtParserTriggerFunction_P);
parser_p->call_backs.atParserTrigger_function_p = atParserTrigger_function_p;
}
break;
case utlAT_PARSER_OP_SET_SAVE_DIAL_STRING_HANDLER: parser_p->call_backs.save_dial_string_function_p = va_arg(va_arg_p, utlAtSaveDialStringFunction_P); break;
case utlAT_PARSER_OP_SET_RETRIEVE_DIAL_STRING_HANDLER: parser_p->call_backs.retrieve_dial_string_function_p = va_arg(va_arg_p, utlAtRetrieveDialStringFunction_P); break;
case utlAT_PARSER_OP_SET_TX_LINE_DATA_HANDLER: parser_p->call_backs.tx_line_data_function_p = va_arg(va_arg_p, utlAtTxLineDataFunction_P); break;
case utlAT_PARSER_OP_SET_DRIVER_REQUEST_HANDLER: parser_p->call_backs.driver_request_function_p = va_arg(va_arg_p, utlAtDriverRequestFunction_P); break;
case utlAT_PARSER_OP_ECHO_ON: parser_p->parameters.echo_text = true; break;
case utlAT_PARSER_OP_ECHO_OFF: parser_p->parameters.echo_text = false; break;
case utlAT_PARSER_OP_SELECT_CARRIER_RESULT_CODE: parser_p->options.use_carrier_result_code = true; break;
case utlAT_PARSER_OP_SELECT_CONNECT_RESULT_CODE: parser_p->options.use_carrier_result_code = false; break;
case utlAT_PARSER_OP_SET_S_PARAMETER_VALUE:
/*--- apply S-parameter value change ---*/
if (utlProcessSParameter(parser_p, parameter_num, parameter_value) != utlSUCCESS)
{
va_end(va_arg_p);
return utlAT_RESULT_CODE_ERROR;
}
/*--- notify application of S-parameter value change ---*/
if ( parser_p->call_backs.s_parameter_function_p != NULL)
if ((parser_p->call_backs.s_parameter_function_p)(parameter_num,
parameter_value,
parser_p->call_backs.arg_p) != utlSUCCESS)
{
va_end(va_arg_p);
return utlAT_RESULT_CODE_ERROR;
}
/*--- save new S-parameter value ---*/
parser_p->parameters.S[parameter_num] = parameter_value;
break;
case utlAT_PARSER_OP_GET_S_PARAMETER_VALUE:
/*--- fetch value ---*/
*parameter_value_p = parser_p->parameters.S[parameter_num];
break;
case utlAT_PARSER_OP_REPORT_EACH_RING: parser_p->options.report_each_ring = true; break;
case utlAT_PARSER_OP_ONLY_REPORT_FIRST_RING: parser_p->options.report_each_ring = false; break;
case utlAT_PARSER_OP_SET_ID: parser_p->info.id_p = s_p; break;
case utlAT_PARSER_OP_SET_MANUFACTURER: parser_p->info.manufacturer_p = s_p; break;
case utlAT_PARSER_OP_SET_MODEL: parser_p->info.model_p = s_p; break;
case utlAT_PARSER_OP_SET_REVISION: parser_p->info.revision_p = s_p; break;
case utlAT_PARSER_OP_SET_SERIAL_NUMBER: parser_p->info.serial_number_p = s_p; break;
case utlAT_PARSER_OP_SET_OBJECT_ID: parser_p->info.object_id_p = s_p; break;
case utlAT_PARSER_OP_SET_COUNTRY_CODE: parser_p->info.country_code_p = s_p; break;
case utlAT_PARSER_OP_SET_CONNECT_TEXT: parser_p->info.connect_text_p = s_p; break;
case utlAT_PARSER_OP_SET_CAPABILITIES: parser_p->info.mt_capabilities_p= s_p; break;
case utlAT_PARSER_OP_ENABLE_BYPASS_MODE: parser_p->states.bypass_mode = true; break;
case utlAT_PARSER_OP_DISABLE_BYPASS_MODE: parser_p->states.bypass_mode = false; break;
case utlAT_PARSER_OP_SET_AUTO_ANSWER_DELAY:
{
utlSetAutoAnswerDelay_P setAutoAnswerDelay_function_p;
setAutoAnswerDelay_function_p = va_arg(va_arg_p, utlSetAutoAnswerDelay_P);
parser_p->call_backs.setAutoAnswerDelay_function_p = setAutoAnswerDelay_function_p;
break;
}
case utlAT_PARSER_OP_GET_AUTO_ANSWER_DELAY:
{
utlGetAutoAnswerDelay_P getAutoAnswerDelay_function_p;
getAutoAnswerDelay_function_p = va_arg(va_arg_p, utlGetAutoAnswerDelay_P);
parser_p->call_backs.getAutoAnswerDelay_function_p = getAutoAnswerDelay_function_p;
break;
}
case utlAT_PARSER_OP_SET_AT_PARSER_PROXY_CB:
{
utlSendToProxy_P sendToProxy_function_p;
sendToProxy_function_p = va_arg(va_arg_p, utlSendToProxy_P);
parser_p->call_backs.sendToProxy_function_p = sendToProxy_function_p;
break;
}
case utlAT_PARSER_OP_SET_IS_PROXY_REQ_CB:
{
utlIsProxyReq_P isProxyReq_function_p;
isProxyReq_function_p = va_arg(va_arg_p, utlIsProxyReq_P);
parser_p->call_backs.isProxyReq_function_p = isProxyReq_function_p;
break;
}
case utlAT_PARSER_OP_SET_INC_PROXY_TO_COUNTER_CB:
{
utlIncProxyTOCounter_P incProxyTOCounter_function_p;
incProxyTOCounter_function_p = va_arg(va_arg_p, utlIncProxyTOCounter_P);
parser_p->call_backs.incProxyTOCounter_p = incProxyTOCounter_function_p;
break;
}
case utlAT_PARSER_OP_SET_PRXY_ONLY_CMD_PTR:
{
utlAtCommand_P pProxyOnlyCmd;
pProxyOnlyCmd = va_arg(va_arg_p, utlAtCommand_P);
parser_p->pProxyOnlyCmd = pProxyOnlyCmd;
break;
}
case utlAT_PARSER_OP_SET_PRXY_ESC_CB:
{
utlProxyEsc_P pProxyEsc;
pProxyEsc = va_arg(va_arg_p, utlProxyEsc_P);
parser_p->call_backs.proxyEsc_p = pProxyEsc;
break;
}
case utlAT_PARSER_OP_CLEAR_SMS_DATDAMODE_CB:
{
utlClearSmsDataMode_P pClearSmsDataMode;
pClearSmsDataMode = va_arg(va_arg_p, utlClearSmsDataMode_P);
parser_p->call_backs.clearSmsDataMode_p = pClearSmsDataMode;
break;
}
case utlAT_PARSER_OP_CLEAR_SMS_OVER_NAS_CB:
{
utlClearSmsoverNas_P pClearSmsoverNas;
pClearSmsoverNas = va_arg(va_arg_p, utlClearSmsoverNas_P);
parser_p->call_backs.clearSmsoverNas_P = pClearSmsoverNas;
break;
}
case utlAT_PARSER_OP_SET_CHECK_SMS_PARA_CB:
{
utlCheckSmsPara_P checkSmsPara_function_p;
checkSmsPara_function_p = va_arg(va_arg_p, utlCheckSmsPara_P);
parser_p->call_backs.checkSmsPara_function_p = checkSmsPara_function_p;
break;
}
case utlAT_PARSER_OP_CONVERT_ATCMD_STR_CB:
{
utlConvertAtcmdStr_P convert_atcmd_str_p;
convert_atcmd_str_p = va_arg(va_arg_p, utlConvertAtcmdStr_P);
parser_p->call_backs.convert_atcmd_str_p = convert_atcmd_str_p;
break;
}
default:
utlError(utlAtParserOp12, "Invalid op-code %d.", op);
/*--- clean ---*/
va_end(va_arg_p);
return utlFAILED;
}
va_end(va_arg_p);
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlSendString(parser_p, string_p)
* INPUT
* parser_p == an open AT-command parser
* string_p == pointer to the string to send expressed as a null-
* terminated character string
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Sends the specified null-terminated reply string to the DTE.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlSendString(const utlAtParser_P2c parser_p,
const char *string_p)
{
utlAssert(parser_p != NULL);
utlAssert(string_p != NULL);
/*--- is bypass mode enabled? ---*/
if (parser_p->states.bypass_mode)
return utlSUCCESS;
/*--- nothing to send? ---*/
if (*string_p == '\0')
return utlSUCCESS;
/*--- invoke reply call-back (if any) ---*/
if ( parser_p->call_backs.reply_function_p != NULL)
return (parser_p->call_backs.reply_function_p)(string_p, parser_p->call_backs.arg_p);
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlSendSubstring(parser_p, string_p, len)
* INPUT
* parser_p == an open AT-command parser
* string_p == pointer to the string to send
* len == number of characters to send, starting from the character
* pointed to by `string_p'
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Sends the specified reply sub-string to the DTE.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlSendSubstring(const utlAtParser_P2c parser_p,
const char *string_p,
size_t len)
{
char buf[utlAT_MAX_RESPONSE_LENGTH];
const char *c_p;
utlAssert(parser_p != NULL);
utlAssert(string_p != NULL);
/*--- is bypass mode enabled? ---*/
if (parser_p->states.bypass_mode)
return utlSUCCESS;
/*--- no reply call-back defined? ---*/
if (parser_p->call_backs.reply_function_p == NULL)
return utlSUCCESS;
c_p = string_p;
/*--- while there's more data to send... ---*/
while (len > (size_t)0)
{
size_t n;
if (len >= utlNumberOf(buf)) n = utlNumberOf(buf) - 1;
else n = len;
/*--- copy data into buffer, filtering out illegal characters ---*/
{
const char *src_p;
const char *term_src_p;
char *dest_p;
src_p = c_p;
term_src_p = c_p + n;
for (dest_p = buf; src_p < term_src_p; src_p++)
/*--- only process characters that are valid in AT-commands... ---*/
if ((*src_p >= '\00') ||
(*src_p == parser_p->parameters.S[utlAT_ESCAPE_CHAR]) ||
(*src_p == parser_p->parameters.S[utlAT_LINE_TERM_CHAR]) ||
(*src_p == parser_p->parameters.S[utlAT_FORMATTING_CHAR]) ||
(*src_p == parser_p->parameters.S[utlAT_LINE_EDIT_CHAR]) ||
(*src_p == parser_p->parameters.S[utlAT_SEPARATER_CHAR]) ||
(*src_p == parser_p->parameters.S[utlAT_TERMINATER_CHAR]))
*dest_p++ = *src_p;
*dest_p = '\0';
}
/*--- invoke reply call-back ---*/
if ((parser_p->call_backs.reply_function_p)(buf, parser_p->call_backs.arg_p) != utlSUCCESS)
return utlFAILED;
len -= n;
c_p += n;
}
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlSendInfoResponse(parser_p, string_p)
* INPUT
* parser_p == an open AT-command parser
* string_p == pointer to the info-text response expressed as a null-
* terminated character string
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Sends the specified info-response string to the DTE.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlSendInfoResponse(const utlAtParser_P2c parser_p,
const char *string_p)
{
char buf[utlAT_MAX_RESPONSE_LENGTH];
size_t len;
size_t i;
utlAssert(parser_p != NULL);
utlAssert(string_p != NULL);
len = strlen(string_p);
/*--- nothing to send? ---*/
if (len == (size_t)0)
return utlSUCCESS;
/*--- no room? ---*/
if (len >= (utlNumberOf(buf) - 5))
{
utlError(utlSendInfoResponse, "Response-buffer overflow.");
return utlFAILED;
}
i = 0;
/*--- prefix ---*/
if (parser_p->parameters.verbose_results == true)
{
buf[i++] = parser_p->parameters.S[utlAT_LINE_TERM_CHAR];
buf[i++] = parser_p->parameters.S[utlAT_FORMATTING_CHAR];
}
/*--- text ---*/
(void)strcpy(buf + i, string_p);
i += len;
/*--- suffix ---*/
buf[i++] = parser_p->parameters.S[utlAT_LINE_TERM_CHAR];
buf[i++] = parser_p->parameters.S[utlAT_FORMATTING_CHAR];
buf[i] = '\0';
return utlSendString(parser_p, buf);
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlSendBasicSyntaxResultCode(parser_p, code, ignore_state)
* INPUT
* parser_p == an open AT-command parser
* code == a result code
* ignore_state == ignore current state?
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Sends the specified basic-AT-Command result code to the DTE.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlSendBasicSyntaxResultCode(const utlAtParser_P2c parser_p,
const utlAtResultCode_T code,
const bool ignore_state)
{
UNUSEDPARAM(ignore_state)
char buf[40];
size_t i;
utlAssert(parser_p != NULL);
/*--- suppress result code? ---*/
if ((parser_p->parameters.suppress_results == true) ||
(code == utlAT_RESULT_CODE_SUPPRESS))
return utlSUCCESS;
if (utlAcquireExclusiveAccess(&((utlAtParser_P)parser_p)->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlSendBasicSyntaxResultCode, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlFAILED;
}
/* For multiple AT commands in one line, only for the last AT command, we can return OK */
if (code == utlAT_RESULT_CODE_OK && parser_p->commands_in_line != 0)
{
utlReleaseExclusiveAccess(&((utlAtParser_P)parser_p)->cmd_cnt_semaphore);
return utlSUCCESS;
}
utlReleaseExclusiveAccess(&((utlAtParser_P)parser_p)->cmd_cnt_semaphore);
if (parser_p->parameters.verbose_results == true)
{
const char *s_p;
switch (code)
{
case utlAT_RESULT_CODE_CONNECT:
if (parser_p->options.use_carrier_result_code == true) s_p = "CARRIER";
else s_p = "CONNECT";
break;
case utlAT_RESULT_CODE_OK: s_p = "OK"; break;
case utlAT_RESULT_CODE_RING: s_p = "RING"; break;
case utlAT_RESULT_CODE_NO_CARRIER: s_p = "NO CARRIER"; break;
case utlAT_RESULT_CODE_ERROR: s_p = "ERROR"; break;
case utlAT_RESULT_CODE_NO_DIAL_TONE: s_p = "NO DIAL TONE"; break;
case utlAT_RESULT_CODE_BUSY: s_p = "BUSY"; break;
case utlAT_RESULT_CODE_NO_ANSWER: s_p = "NO ANSWER"; break;
default:
utlError(utlSendBasicSyntaxResultCode1, "Invalid result code %d.", code);
return utlFAILED;
}
i = 0;
/*--- prefix ---*/
buf[i++] = parser_p->parameters.S[utlAT_LINE_TERM_CHAR];
buf[i++] = parser_p->parameters.S[utlAT_FORMATTING_CHAR];
/*--- text ---*/
{
(void)strcpy(buf + i, s_p);
i = strlen(buf);
/*--- if we're connecting, insert connect text (if configured) ---*/
if ((parser_p->parameters.include_connect_text == true) &&
(code == utlAT_RESULT_CODE_CONNECT) &&
(parser_p->info.connect_text_p != NULL))
{
buf[i++] = ' ';
buf[i++] = '(';
(void)strncpy(buf + i, parser_p->info.connect_text_p, utlNumberOf(buf) - 8);
i += strlen(parser_p->info.connect_text_p);
buf[i++] = ')';
}
}
/*--- suffix ---*/
buf[i++] = parser_p->parameters.S[utlAT_LINE_TERM_CHAR];
buf[i++] = parser_p->parameters.S[utlAT_FORMATTING_CHAR];
buf[i] = '\0';
}
else
{
i = utlDecimalToString(buf, code, utlNumberOf(buf) - 3);
buf[i++] = parser_p->parameters.S[utlAT_LINE_TERM_CHAR];
buf[i++] = parser_p->parameters.S[utlAT_FORMATTING_CHAR];
buf[i] = '\0';
}
return utlSendString(parser_p, buf);
}
static utlReturnCode_T __utlAtCommandResponse(utlAtParser_P parser_p,
utlAtCommand_P2c command_p,
const utlAtCommandResponseType_T response_type,
va_list va_arg_p)
{
utlAtParameterValue_P2c parameter_values_p = NULL;
size_t num_parameters = 0;
const char *custom_p = NULL;
const char *info_text_p = NULL;
const char *abort_response_p = NULL;
const char *command_syntax_p = NULL;
/*--- fetch remaining parameters... ---*/
switch (response_type)
{
case utlAT_COMMAND_RESPONSE_TYPE_PARAMETERS:
parameter_values_p = va_arg(va_arg_p, utlAtParameterValue_P2c);
num_parameters = va_arg(va_arg_p, size_t);
/*--- verify parameter count ---*/
if ((num_parameters != command_p->num_parameters) ||
(num_parameters > utlAT_MAX_PARAMETERS))
{
utlError(__utlAtCommandResponse, "Invalid number of AT command parameters: %d", num_parameters);
return utlFAILED;
}
/*--- verify parameter types ---*/
{
utlAtParameter_P parameter_p;
utlAtParameterValue_P2c parameter_value_p;
parameter_p = command_p->parameters_p;
parameter_value_p = parameter_values_p;
/*--- for each of this AT-Command's known parameters... ---*/
while (parameter_p < (command_p->parameters_p + command_p->num_parameters))
{
if (parameter_p->type != parameter_value_p->type)
{
utlError(__utlAtCommandResponse1, "Invalid data type for parameter %d", parameter_p - command_p->parameters_p);
return utlFAILED;
}
parameter_p++;
parameter_value_p++;
}
}
break;
case utlAT_COMMAND_RESPONSE_TYPE_CUSTOM:
custom_p = va_arg(va_arg_p, const char *);
break;
case utlAT_COMMAND_RESPONSE_TYPE_INFO_TEXT:
info_text_p = va_arg(va_arg_p, const char *);
break;
case utlAT_COMMAND_RESPONSE_TYPE_COMMAND_SYNTAX:
command_syntax_p = va_arg(va_arg_p, char *);
break;
case utlAT_COMMAND_RESPONSE_TYPE_REQUEST_COMPLETED:
break;
case utlAT_COMMAND_RESPONSE_TYPE_ABORT:
abort_response_p = va_arg(va_arg_p, const char *);
break;
case utlAT_COMMAND_RESPONSE_TYPE_ERROR:
/* do nothing */
break;
default:
utlError(__utlAtCommandResponse2, "Invalid response-type %d.", response_type);
return utlFAILED;
}
/*--- generate reply ---*/
switch (response_type)
{
case utlAT_COMMAND_RESPONSE_TYPE_PARAMETERS:
if (utlGenerateFormattedGetResponse(parser_p, command_p->name_p, parameter_values_p, num_parameters) != utlSUCCESS)
{
return utlFAILED;
}
if (utlSendBasicSyntaxResultCode(parser_p, utlAT_RESULT_CODE_OK, false) != utlSUCCESS)
return utlFAILED;
break;
case utlAT_COMMAND_RESPONSE_TYPE_CUSTOM:
if (utlSendInfoResponse(parser_p, custom_p) != utlSUCCESS)
{
return utlFAILED;
}
if (utlSendBasicSyntaxResultCode(parser_p, utlAT_RESULT_CODE_SUPPRESS, false) != utlSUCCESS)
return utlFAILED;
break;
case utlAT_COMMAND_RESPONSE_TYPE_INFO_TEXT:
if (utlSendInfoResponse(parser_p, info_text_p) != utlSUCCESS)
{
return utlFAILED;
}
if (utlSendBasicSyntaxResultCode(parser_p, utlAT_RESULT_CODE_OK, false) != utlSUCCESS)
return utlFAILED;
break;
case utlAT_COMMAND_RESPONSE_TYPE_COMMAND_SYNTAX:
if (utlSendInfoResponse(parser_p, command_syntax_p) != utlSUCCESS)
{
return utlAT_RESULT_CODE_ERROR;
}
if (utlSendBasicSyntaxResultCode(parser_p, utlAT_RESULT_CODE_OK, false) != utlSUCCESS)
return utlFAILED;
break;
case utlAT_COMMAND_RESPONSE_TYPE_REQUEST_COMPLETED:
if (utlSendBasicSyntaxResultCode(parser_p, utlAT_RESULT_CODE_OK, false) != utlSUCCESS)
return utlFAILED;
break;
case utlAT_COMMAND_RESPONSE_TYPE_ABORT:
case utlAT_COMMAND_RESPONSE_TYPE_ERROR:
/*--- if we are in a state that requires state-machine intervention ---*/
/*--- just echo failure string ---*/
if (response_type == utlAT_COMMAND_RESPONSE_TYPE_ABORT)
{
if (utlSendInfoResponse(parser_p, abort_response_p) != utlSUCCESS)
{
return utlFAILED;
}
}
else
{
if (utlSendBasicSyntaxResultCode(parser_p, utlAT_RESULT_CODE_ERROR, false) != utlSUCCESS)
{
return utlFAILED;
}
}
break;
/* dead code detected here. comment here
default:
utlError("Invalid response-type %d.", response_type);
return utlFAILED;
*/
}
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAtCommandResponse(xid, response_type, ...)
*
* utlAtCommandResponse(xid, utlAT_COMMAND_RESPONSE_TYPE_PARAMETERS, parameter_values_p, num_parameters)
* utlAtCommandResponse(xid, utlAT_COMMAND_RESPONSE_TYPE_CUSTOM, custom_p)
* utlAtCommandResponse(xid, utlAT_COMMAND_RESPONSE_TYPE_INFO_TEXT, info_text_p)
* utlAtCommandResponse(xid, utlAT_COMMAND_RESPONSE_TYPE_COMMAND_SYNTAX, command_syntax_p)
* utlAtCommandResponse(xid, utlAT_COMMAND_RESPONSE_TYPE_REQUEST_COMPLETED)
* utlAtCommandResponse(xid, utlAT_COMMAND_RESPONSE_TYPE_ABORT, abort_response_p)
* utlAtCommandResponse(xid, utlAT_COMMAND_RESPONSE_TYPE_SYNC_ERROR)
* INPUT
* xid == transaction ID
* response_type == the type of response being reported
* ... == type-specific parameters
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Forwards an AT-command response to the At-command parser waiting for
* `xid'.
*---------------------------------------------------------------------------*/
utlReturnCode_T utlAtCommandResponse(const unsigned int xid,
const utlAtCommandResponseType_T response_type,
...)
{
va_list va_arg_p;
utlAtParser_P parser_p;
utlAtAsyncResponse_P async_response_p;
utlAtCommand_P2c command_p;
utlReturnCode_T ret = utlFAILED;
#ifdef LPP_DEBUG
ERRMSG(utlAtCommandResponse2086, "%s: xid %u, response_type %d\n", __FUNCTION__,xid, response_type);
#endif
/*--- ignore command response if transaction ID is 0 or 1 (both are reserved) ---*/
if ((xid == (unsigned int)0) ||
(xid == (unsigned int)1))
return utlSUCCESS;
/*FUTURE
oxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo
Interleaving requests...
Currently:
- We get characters and place them into a circular queue (line accumulation).
Once the circular queue contains a complete AT-command, we echo it, and then invoke the call-back
function for that command (if any). If call-back is synchronous, we immediately emit the response.
if call-back is asynchronous, we wait until we get the response, at which time we emit the response.
if another AT-command arrives, we place the characters into the circular queue and wait until we've
echoed the asynchronous response from the current command.
Want:
- The ability to have multiple asynchronous AT command call-backs outstanding at any given time.
Thoughts:
- We already have the technology to queue AT-commands, I would like to keep that feature in order to
limit the max number of active AT commands.
- AT-command echoing is done very early. I some how need to defer this.
- Typical sequence:
AT-command 1
echo "AT-command 1"
AT-command 2
queue "AT-command 2
AT-command 1 response"
echo "AT-command 1 response"
echo "AT-command 2
AT-command 2 response"
echo "AT-command 2 response"
AT-command 3
echo "AT-command 3"
- "parser_p->states.async_response" must be converted into an array AT-commands awaiting an async response.
There is no longer a single await-async timer. Instead there is one for each outstanding async command.
I need to keep track of what is pending an echo. DONE
- Need to keep track of echoed back-space chars too.
- Right now echoing and processing are done at the same time. I need to separate these. Instead of echoing
via "utlSendSubstring()", I could queue it for future echoing. But how to demark what gets echoed from what
gets emitted due to replies?
What if each "await" node had a pointer to where to echo up to (where that command ends). There would then
be two head pointers, one indicating how far we've processed, and another indicating where we should stop
echoing.
- when issuing new commands while existing ones are not done, don't allow new ones with the same OP to
be issued (to prevent abiguity)
----------------------------------
Solution 1: Receive AT command and issue call-back
immediately, queue echos and responses.
Synchronous: Call-back function returns and specifies the contents of
the response, we just format the response and then echo it.
We need to hold-back the echo'ed text too.
Asynchronous: Call-back function starts the command rolling. We then
wait for the driver event containing the contents of the
response, wich we format and echo.
We need to hold-back the echo'ed text too.
Solution 2:
Receive AT command and issue call-back immediately. Only echo command
once we've received the response. We'll need to preserve command ordering somehow.
Solution 3:
Every time we issue a command, create a node and add to pending-commands list.
Every time we receive a response, match the response to a pending command in the
pending commands list. Every time we receive a response, check the pending-commands
list for finished commands--echo any that are found. Note that the pending-commands
list must also contain the command-string to be echo'ed.
Could have two types of command nodes: waiting for an AT-command reply, strings
that need to be echoed. Nodes must be ordered. Can echo strings at the head
of the list immediately. Can only echo AT-command reply nodes once the AT command
reply has been received.
Notes:
- We want a command like ATH to be able to abort a pending ATD command.
- Can all commands be issued while a current one is pending?
- How many commands will we allow to be pending at the same time?
- I will need a separate async_response structure for each pending command.
- Timeout function needs to know which async_response structure has timed out.
"arg_p" structure should point to async_response function, not parser structure.
-
oxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo
Notes:
- Echoed text will always be in the correct order, we just need to insert the responses into the right places
- Responses are a problem. They can arrive in any order.
- I need to match up the responses to the associated requests. When the xid is assigned (first by this library,
then later optionally by the call-back), I need to save it with the text that needs to be echoed
- Sequence:
receive text, echo into a text-fragment node
assign xid, invoke call-back (which may modify the xid)
save xid to text-fragment node
receive reply text, place into a new text-fragment node
insertion sort new text-fragment node into list of waiting text fragments
typedef struct utlTextFragment_S *utlTextFragment_P;
typedef struct utlTextFragment_S {
utlTextFragment_P next_p;
unsigned int xid;
char text[100];
} utlTextFragment_T;
struct {
utlLinkedList_T text_fragments;
} pending_text;
oxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo
*/
/*--- determine the parser and async_response node associated with the given transaction ID ---*/
{
#ifdef LPP_DEBUG
ERRMSG(utlAtCommandResponse2203,"%s: input xid %u\n", __FUNCTION__,xid);
#endif
/*--- for each parser... ---*/
for (parser_p = (utlAtParser_P)parsers.head_p; parser_p != NULL; parser_p = parser_p->next_p)
{
#ifdef LPP_DEBUG
ERRMSG(utlAtCommandResponse2207,"%s: parser_p 0x%X\n", __FUNCTION__, parser_p);
#endif
/*--- for each async response this parser is waiting for... ---*/
for (async_response_p = (utlAtAsyncResponse_P)parser_p->states.async_responses.pending.head_p; async_response_p != NULL; async_response_p = async_response_p->next_p)
{
#ifdef LPP_DEBUG
ERRMSG(utlAtCommandResponse2212,"%s: async_response_p->xid %u\n", __FUNCTION__, async_response_p->xid);
#endif
if (async_response_p->xid == xid)
break;
}
/*--- found it? ---*/
if (async_response_p != NULL)
break;
}
/*--- is XID invalid/unknown? ---*/
if (parser_p == NULL)
{
utlError(utlAtCommandResponse0,"Invalid/unknown transaction ID %d\n", xid);
return utlFAILED;
}
}
utlAssert(async_response_p != NULL);
/*--- if we're not expecting this response, we'll ignore it ---*/
switch (response_type)
{
case utlAT_COMMAND_RESPONSE_TYPE_PARAMETERS:
if (async_response_p->op != utlAT_ASYNC_OP_GET)
return utlSUCCESS;
break;
case utlAT_COMMAND_RESPONSE_TYPE_CUSTOM:
case utlAT_COMMAND_RESPONSE_TYPE_INFO_TEXT:
if ((async_response_p->op != utlAT_ASYNC_OP_EXEC) &&
(async_response_p->op != utlAT_ASYNC_OP_GET) &&
(async_response_p->op != utlAT_ASYNC_OP_SET) &&
(async_response_p->op != utlAT_ASYNC_OP_ACTION) &&
(async_response_p->op != utlAT_ASYNC_OP_SYNTAX))
return utlSUCCESS;
break;
case utlAT_COMMAND_RESPONSE_TYPE_COMMAND_SYNTAX:
if (async_response_p->op != utlAT_ASYNC_OP_SYNTAX)
return utlSUCCESS;
break;
case utlAT_COMMAND_RESPONSE_TYPE_REQUEST_COMPLETED:
if ((async_response_p->op != utlAT_ASYNC_OP_EXEC) &&
(async_response_p->op != utlAT_ASYNC_OP_SET) &&
(async_response_p->op != utlAT_ASYNC_OP_ACTION))
return utlSUCCESS;
break;
case utlAT_COMMAND_RESPONSE_TYPE_ABORT:
case utlAT_COMMAND_RESPONSE_TYPE_ERROR:
if (async_response_p->op == utlAT_ASYNC_OP_UNKNOWN)
return utlSUCCESS;
break;
default:
utlError(utlAtCommandResponse, "Invalid response type %d.", response_type);
return utlFAILED;
}
/*--- now that we've received an expected asynchronous response, stop timer first ---*/
{
/*--- stop timer ---*/
if (async_response_p->timer_id != (utlTimerId_T)utlFAILED)
{
if (utlStopTimer(async_response_p->timer_id) != utlSUCCESS)
return utlFAILED;
async_response_p->timer_id = utlFAILED;
}
}
//clear SMS datamode
parser_p->call_backs.clearSmsDataMode_p(async_response_p);
/*--- release multiple at commands mutex ---*/
atmcd_mult_unlock(async_response_p->parser_p, async_response_p->command_p, op2type(async_response_p->op));
/*--- update commands_in_line ---*/
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlAtCommandResponse1, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
ret = utlFAILED;
goto exit;
}
if (parser_p->commands_in_line == 0)
{
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
ret = utlSUCCESS;
goto exit;
}
#ifndef AT_CMD_ASYNC_MODE
if(response_type == utlAT_COMMAND_RESPONSE_TYPE_ABORT
|| response_type == utlAT_COMMAND_RESPONSE_TYPE_ERROR)
parser_p->commands_in_line = 0;
else
parser_p->commands_in_line--;
#else
parser_p->commands_in_line--;
#endif
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
/*--- ensure we actually reference a command ---*/
if(async_response_p->command_p == NULL)
{
utlError(utlAtCommandResponse3, "%s: command already timeout and has been handled, return directly\n", __FUNCTION__);
return utlFAILED;
}
//check if the command was proxied, and reset the TO counter if it was proxied.
if(async_response_p->isProxy) {
DBGMSG(utlAtCommandResponse4, "PROXY_DEBUG -%s,%d - cmd %s is proxy , and was answered, reset counter\n ",
__FUNCTION__, __LINE__, async_response_p->command_p->name_p);
#ifdef OPT_DSDS
parser_p->call_backs.incProxyTOCounter_p(async_response_p->xid, 0);
#else
parser_p->call_backs.incProxyTOCounter_p(0);
#endif
}
command_p = async_response_p->command_p;
async_response_p->command_p = NULL;
/*--- fetch parameter and send reply ---*/
va_start(va_arg_p, response_type);
ret = __utlAtCommandResponse(parser_p, command_p, response_type, va_arg_p);
va_end(va_arg_p);
exit:
/*--- update pending list ---*/
#ifndef AT_CMD_ASYNC_MODE
if(response_type == utlAT_COMMAND_RESPONSE_TYPE_ABORT
|| response_type == utlAT_COMMAND_RESPONSE_TYPE_ERROR)
{
utlAbandonAllPendingAsyncResponse(parser_p);
}
else
#endif
{
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlAtCommandResponse5, "Cannot exclusively acquire semaphore!\n");
return utlFAILED;
}
if (utlGetNode( &(parser_p->states.async_responses.pending), utlAtAsyncResponse_T, NULL, async_response_p) != NULL)
{
utlPutTailNode(&(parser_p->states.async_responses.unused), utlAtAsyncResponse_T, async_response_p);
}
else
{
utlError(utlAtCommandResponse6, "%s:!!!!!!!!!!!!!!!!!!!!!get node error\n", __FUNCTION__);
}
#ifdef LPP_DEBUG
ERRMSG(utlAtCommandResponse2374, "%s: remove xid, parser_p 0x%X, counter %d\n", __FUNCTION__,parser_p, parser_p->states.async_responses.pending.node_count);
#endif
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
}
async_response_p->xid = utlFAILED;
/*--- trigger AT parser again ---*/
{
if(parser_p->call_backs.atParserTrigger_function_p == NULL)
{
utlError(utlAtCommandResponse7, "Not have AT parser trigger handler\n");
return utlFAILED;
}
if (parser_p->call_backs.atParserTrigger_function_p(parser_p) < 0)
return utlFAILED;
}
return ret;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAtDceIoConfigEvent(parser_p)
* INPUT
* parser_p == an open AT-command parser
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Generates notifications of DCE I/O configuration-changes.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlAtDceIoConfigEvent(const utlAtParser_P parser_p)
{
utlAssert(parser_p != NULL);
/*--- nothing to report? ---*/
if (parser_p->states.dce_io_config_pending_mask == (unsigned int)0)
return utlSUCCESS;
/*--- update dependent fields ---*/
switch (parser_p->parameters.raw_framing)
{
case 0: parser_p->dce_io_config.data_bits = 0;
parser_p->dce_io_config.parity_bits = 0;
parser_p->dce_io_config.stop_bits = 0; break;
case 1: parser_p->dce_io_config.data_bits = 8;
parser_p->dce_io_config.parity_bits = 0;
parser_p->dce_io_config.stop_bits = 2; break;
case 2: parser_p->dce_io_config.data_bits = 8;
parser_p->dce_io_config.parity_bits = 1;
parser_p->dce_io_config.stop_bits = 1; break;
case 3: parser_p->dce_io_config.data_bits = 8;
parser_p->dce_io_config.parity_bits = 0;
parser_p->dce_io_config.stop_bits = 1; break;
case 4: parser_p->dce_io_config.data_bits = 7;
parser_p->dce_io_config.parity_bits = 0;
parser_p->dce_io_config.stop_bits = 2; break;
case 5: parser_p->dce_io_config.data_bits = 7;
parser_p->dce_io_config.parity_bits = 1;
parser_p->dce_io_config.stop_bits = 1; break;
case 6: parser_p->dce_io_config.data_bits = 7;
parser_p->dce_io_config.parity_bits = 0;
parser_p->dce_io_config.stop_bits = 1; break;
default:
return utlFAILED;
}
switch (parser_p->parameters.raw_parity)
{
case 0: parser_p->dce_io_config.parity = utlDATA_PARITY_ODD; break;
case 1: parser_p->dce_io_config.parity = utlDATA_PARITY_EVEN; break;
case 2: parser_p->dce_io_config.parity = utlDATA_PARITY_MARK; break;
case 3: parser_p->dce_io_config.parity = utlDATA_PARITY_SPACE;
break;
default:
return utlFAILED;
}
/*--- notify I/O subsystem of DCE I/O configuration change ---*/
if ( parser_p->call_backs.dce_io_config_function_p != NULL)
{
if ((parser_p->call_backs.dce_io_config_function_p)(&(parser_p->dce_io_config), parser_p->call_backs.arg_p) != utlSUCCESS)
return utlFAILED;
parser_p->states.dce_io_config_pending_mask = 0;
}
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAtSoundConfigEvent(parser_p)
* INPUT
* parser_p == an open AT-command parser
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Generates notifications of sound configuration-changes.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlAtSoundConfigEvent(const utlAtParser_P parser_p)
{
utlAssert(parser_p != NULL);
/*--- nothing to report? ---*/
if (parser_p->states.sound_config_pending_mask == (unsigned int)0)
return utlSUCCESS;
/*--- notify sound subsystem of DCE sound configuration change ---*/
if ( parser_p->call_backs.sound_config_function_p != NULL)
{
if ((parser_p->call_backs.sound_config_function_p)(&(parser_p->sound_config), parser_p->call_backs.arg_p) != utlSUCCESS)
return utlFAILED;
parser_p->states.sound_config_pending_mask = 0;
}
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlProcessSParameter(parser_p, parameter_num, parameter_value)
* INPUT
* parser_p == an open AT-command parser
* parameter_num == Number of S-parameter to process
* parameter_value == S-parameter's value
* OUTPUT
* none
* RETURNS
* returns utlSUCCESS for success, otherwise utlFAILED
* DESCRIPTION
* Processes the specified S-parameter: update dependent fields.
*---------------------------------------------------------------------------*/
static utlAtResultCode_T utlProcessSParameter(const utlAtParser_P parser_p,
const unsigned int parameter_num,
const unsigned int parameter_value)
{
utlAssert(parser_p != NULL);
/*--- check value ---*/
{
bool bad_value;
bad_value = false;
switch (parameter_num)
{
case utlAT_ESCAPE_CHAR:
case utlAT_SEPARATER_CHAR:
case utlAT_TERMINATER_CHAR:
case utlAT_XON_CHAR:
case utlAT_XOFF_CHAR:
if (parameter_value > (unsigned int)127)
bad_value = true;
break;
/* for S3, only 13 is valid */
case utlAT_LINE_TERM_CHAR:
if (parameter_value != (unsigned int)13)
bad_value = true;
break;
/* for S4, only 10 is valid */
case utlAT_FORMATTING_CHAR:
if (parameter_value != (unsigned int)10)
bad_value = true;
break;
/* for S5, only 8 is valid */
case utlAT_LINE_EDIT_CHAR:
if (parameter_value != (unsigned int)8)
bad_value = true;
break;
case utlAT_AUTO_ANSWER:
case utlAT_RING_COUNTER:
case utlAT_DIALING_PAUSE_TIME:
case utlAT_ESCAPE_GUARD_TIME:
case utlAT_DTR_DELAY_TIME:
case utlAT_HOOK_FLASH_TIME:
case utlAT_INACTIVITY_TIME:
case utlAT_DISCONNECT_WAIT_TIME:
if (parameter_value > (unsigned int)255)
bad_value = true;
break;
case utlAT_BLIND_DIAL_PAUSE_TIME:
if ((parameter_value < (unsigned int)2) ||
(parameter_value > (unsigned int)10))
bad_value = true;
break;
case utlAT_CONN_COMPLETE_TIMEOUT:
case utlAT_CARRIER_DETECT_TIME:
if ((parameter_value < (unsigned int)1) ||
(parameter_value > (unsigned int)255))
bad_value = true;
break;
/* the range of S11 is 50 - 255 */
case utlAT_DTMF_TONE_DURATION:
if ((parameter_value < (unsigned int)50) ||
(parameter_value > (unsigned int)255))
bad_value = true;
break;
case utlAT_CARRIER_LOSS_TIME:
if ((parameter_value < (unsigned int)1) ||
(parameter_value > (unsigned int)254))
bad_value = true;
break;
default:
/*--- will S-parameter value not fit into S-parameter data type? ---*/
if (parameter_value > ( 1u << ((sizeof(parser_p->parameters.S[0]) * 8u) - 1u)) +
((1u << ((sizeof(parser_p->parameters.S[0]) * 8u) - 1u)) - 1u))
bad_value = true;
break;
}
if (bad_value == true)
{
utlError(utlProcessSParameter, "Invalid S-parameter value (%d) for S-parameter %d.", parameter_value, parameter_num);
return utlAT_RESULT_CODE_ERROR;
}
}
/*--- update dependencies (if any) ---*/
switch (parameter_num)
{
case utlAT_AUTO_ANSWER:
if(parser_p->call_backs.setAutoAnswerDelay_function_p == NULL)
{
utlError(utlProcessSParameter1, "No auto answer delay set call back function!");
return utlAT_RESULT_CODE_ERROR;
}
parser_p->call_backs.setAutoAnswerDelay_function_p(parser_p->call_backs.arg_p, parameter_value);
break;
case utlAT_BLIND_DIAL_PAUSE_TIME: parser_p->states.blind_dial_pause_time.seconds = parameter_value;
parser_p->states.blind_dial_pause_time.nanoseconds = 0; break;
case utlAT_CONN_COMPLETE_TIMEOUT: parser_p->states.conn_complete_timeout.seconds = parameter_value;
parser_p->states.conn_complete_timeout.nanoseconds = 0; break;
case utlAT_DIALING_PAUSE_TIME: parser_p->states.dialing_pause_time.seconds = parameter_value;
parser_p->states.dialing_pause_time.nanoseconds = 0; break;
case utlAT_CARRIER_DETECT_TIME: parser_p->states.carrier_detect_time.seconds = parameter_value / 10;
parser_p->states.carrier_detect_time.nanoseconds = (parameter_value % 10) * (1000000000 / 10); break;
case utlAT_CARRIER_LOSS_TIME: parser_p->states.carrier_loss_time.seconds = parameter_value / 10;
parser_p->states.carrier_loss_time.nanoseconds = (parameter_value % 10) * (1000000000 / 10); break;
case utlAT_XON_CHAR: parser_p->dce_io_config.flow_control.xon_char = parameter_value;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_FLOW_CONTROL; break;
case utlAT_XOFF_CHAR: parser_p->dce_io_config.flow_control.xoff_char = parameter_value;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_FLOW_CONTROL; break;
case utlAT_DTR_DELAY_TIME: parser_p->dce_io_config.dtr_delay_time.seconds = parameter_value / 100;
parser_p->dce_io_config.dtr_delay_time.nanoseconds = parameter_value % 100 * (1000000000 / 100);
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DTR_DELAY; break;
case utlAT_HOOK_FLASH_TIME: parser_p->states.hook_flash_time.seconds = parameter_value / 10;
parser_p->states.hook_flash_time.nanoseconds = (parameter_value % 10) * (1000000000 / 10); break;
case utlAT_INACTIVITY_TIME: parser_p->states.inactivity_time.seconds = parameter_value * 10;
parser_p->states.inactivity_time.nanoseconds = 0; break;
case utlAT_DISCONNECT_WAIT_TIME: parser_p->states.disconnect_wait_time.seconds = parameter_value;
parser_p->states.disconnect_wait_time.nanoseconds = 0; break;
default:
break;
}
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlProcessBasicAtCommand(parser_p, command_p, parameter_value_p,
* response_p, response_siz, syntax_off)
* INPUT
* parser_p == an open AT-command parser
* command_p == the AT command to process
* parameter_value_p == the found parameter value (there's only ever one)
* response_p == pointer to where the response (if any) should be placed
* response_siz == length of array pointed to by `response_p'
* syntax_off == offset from start of line to the AT-command (for
* error reporting)
* OUTPUT
* *response_p == the response (if any)
* RETURNS
* utlAT_RESULT_CODE_ERROR for failure, other values for success
* DESCRIPTION
* Processes a basic AT-command.
*---------------------------------------------------------------------------*/
static utlAtResultCode_T utlProcessBasicAtCommand(const utlAtParser_P parser_p,
const utlAtCommand_P2c command_p,
const utlAtParameterValue_P2c parameter_value_p,
char *response_p,
const size_t response_siz,
const size_t syntax_off)
{
utlAtResultCode_T rc;
utlAssert(parser_p != NULL);
utlAssert(command_p != NULL);
utlAssert(parameter_value_p != NULL);
utlAssert(response_p != NULL);
utlError(utlProcessBasicAtCommand100, "utlProcessBasicAtCommand\n");
rc = utlAT_RESULT_CODE_OK;
switch (command_p->name_p[0])
{
case 'e': /*--- command echo ---*/
case 'E': if (parameter_value_p->value.decimal == (unsigned int)0) parser_p->parameters.echo_text = false;
else if (parameter_value_p->value.decimal == (unsigned int)1) parser_p->parameters.echo_text = true;
else
rc = utlAT_RESULT_CODE_ERROR;
break;
case 'i': /*--- request identification information ---*/
case 'I': if (parameter_value_p->value.decimal != (unsigned int)0)
{
rc = utlAT_RESULT_CODE_ERROR;
break;
}
(void)strncpy(response_p, parser_p->info.id_p, response_siz);
break;
case 'l': /*--- set monitor-speaker level ---*/
case 'L': switch (parameter_value_p->value.decimal)
{
case 0:
case 1:
case 2:
case 3: parser_p->sound_config.monitor_speaker.level = parameter_value_p->value.decimal;
parser_p->states.sound_config_pending_mask |= utlAT_SOUND_CONFIG_PENDING_LEVEL;
break;
default:
rc = utlAT_RESULT_CODE_ERROR;
break;
}
break;
case 'm': /*--- set monitor-speaker mode ---*/
case 'M': switch (parameter_value_p->value.decimal)
{
case 0:
case 1:
case 2: parser_p->sound_config.monitor_speaker.mode = parameter_value_p->value.decimal;
parser_p->states.sound_config_pending_mask |= utlAT_SOUND_CONFIG_PENDING_MODE;
break;
default:
rc = utlAT_RESULT_CODE_ERROR;
break;
}
break;
case 'o': /*--- enter on-line mode ---*/
case 'O': if ((parameter_value_p->value.decimal == (unsigned int)0) ||
(parameter_value_p->value.decimal == (unsigned int)1))
{
parser_p->states.go_on_line = true;
rc = utlAT_RESULT_CODE_CONNECT;
}
else
{
rc = utlAT_RESULT_CODE_ERROR;
break;
}
break;
case 'p': /*--- select pulse dialing ---*/
case 'P': if (parameter_value_p->value.decimal != (unsigned int)0)
{
rc = utlAT_RESULT_CODE_ERROR;
break;
}
parser_p->parameters.dialing_mode = utlAT_DIALING_MODE_PULSE;
break;
case 'q': /*--- result-code suppression ---*/
case 'Q': if (parameter_value_p->value.decimal == (unsigned int)0) parser_p->parameters.suppress_results = false;
else if (parameter_value_p->value.decimal == (unsigned int)1) parser_p->parameters.suppress_results = true;
else
{
rc = utlAT_RESULT_CODE_ERROR;
break;
}
break;
case 't': /*--- select DTMF tone dialing ---*/
case 'T': if (parameter_value_p->value.decimal != (unsigned int)0)
{
rc = utlAT_RESULT_CODE_ERROR;
break;
}
parser_p->parameters.dialing_mode = utlAT_DIALING_MODE_TONE;
break;
case 'v': /*--- DCE response format ---*/
case 'V': if (parameter_value_p->value.decimal == (unsigned int)0) parser_p->parameters.verbose_results = false;
else if (parameter_value_p->value.decimal == (unsigned int)1) parser_p->parameters.verbose_results = true;
else
{
rc = utlAT_RESULT_CODE_ERROR;
break;
}
break;
case 'x': /*--- result code selection and call progress monitoring control ---*/
case 'X': switch (parameter_value_p->value.decimal)
{
case 0: parser_p->parameters.include_connect_text = false;
parser_p->parameters.detect_dial_tone = false;
parser_p->parameters.detect_busy_signal = false;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DIAL_TONE |
utlAT_DCE_IO_CONFIG_PENDING_BUSY_SIGNAL; break;
case 1: parser_p->parameters.include_connect_text = true;
parser_p->parameters.detect_dial_tone = false;
parser_p->parameters.detect_busy_signal = false;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DIAL_TONE |
utlAT_DCE_IO_CONFIG_PENDING_BUSY_SIGNAL; break;
case 2: parser_p->parameters.include_connect_text = true;
parser_p->parameters.detect_dial_tone = true;
parser_p->parameters.detect_busy_signal = false;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DIAL_TONE |
utlAT_DCE_IO_CONFIG_PENDING_BUSY_SIGNAL; break;
case 3: parser_p->parameters.include_connect_text = true;
parser_p->parameters.detect_dial_tone = false;
parser_p->parameters.detect_busy_signal = true;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DIAL_TONE |
utlAT_DCE_IO_CONFIG_PENDING_BUSY_SIGNAL; break;
case 4: parser_p->parameters.include_connect_text = true;
parser_p->parameters.detect_dial_tone = true;
parser_p->parameters.detect_busy_signal = true;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DIAL_TONE |
utlAT_DCE_IO_CONFIG_PENDING_BUSY_SIGNAL; break;
default:
rc = utlAT_RESULT_CODE_ERROR;
break;
}
break;
case 'z': /*--- Reset to default configuration ---*/
case 'Z': if (parameter_value_p->value.decimal != (unsigned int)0)
{
rc = utlAT_RESULT_CODE_ERROR;
break;
}
{
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlProcessBasicAtCommand, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlAT_RESULT_CODE_ERROR;
}
size_t commands_in_line = parser_p->commands_in_line;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
if (utlAtReset(parser_p) != utlSUCCESS)
rc = utlAT_RESULT_CODE_ERROR;
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlProcessBasicAtCommand1, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlAT_RESULT_CODE_ERROR;
}
parser_p->commands_in_line = commands_in_line;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
}
break;
case '&': /*--- basic-command prefix character ---*/
switch (command_p->name_p[1])
{
case 'c': /*--- DCD (output) control ---*/
case 'C': switch (parameter_value_p->value.decimal)
{
case 0: parser_p->dce_io_config.modes.dcd_always_on = true;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DCD; break;
case 1: parser_p->dce_io_config.modes.dcd_always_on = false;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DCD; break;
default:
rc = utlAT_RESULT_CODE_ERROR;
break;
}
break;
case 'd': /*--- DTR (input) monitoring ---*/
case 'D': switch (parameter_value_p->value.decimal)
{
case 0: parser_p->dce_io_config.modes.ignore_dtr = true;
parser_p->dce_io_config.modes.drop_call_on_dtr_loss = false;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DTR; break;
case 1: parser_p->dce_io_config.modes.ignore_dtr = false;
parser_p->dce_io_config.modes.drop_call_on_dtr_loss = false;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DTR; break;
case 2: parser_p->dce_io_config.modes.ignore_dtr = false;
parser_p->dce_io_config.modes.drop_call_on_dtr_loss = true;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DTR; break;
default:
rc = utlAT_RESULT_CODE_ERROR;
break;
}
break;
case 'f': /*--- set to factory configuration ---*/
case 'F': if (parameter_value_p->value.decimal != (unsigned int)0)
{
rc = utlAT_RESULT_CODE_ERROR;
break;
}
{
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlProcessBasicAtCommand2, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlAT_RESULT_CODE_ERROR;
}
size_t commands_in_line = parser_p->commands_in_line;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
if (utlAtReset(parser_p) != utlSUCCESS)
rc = utlAT_RESULT_CODE_ERROR;
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlProcessBasicAtCommand3, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlAT_RESULT_CODE_ERROR;
}
parser_p->commands_in_line = commands_in_line;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
}
break;
case 's': /*--- DSR (output) control ---*/
case 'S': switch (parameter_value_p->value.decimal)
{
case 0: parser_p->dce_io_config.modes.dsr_mode = 0;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DSR; break;
case 1: parser_p->dce_io_config.modes.dsr_mode = 0;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DSR; break;
default:
rc = utlAT_RESULT_CODE_ERROR;
break;
}
break;
default: break;
}
default: break;
}
/*--- if execution of the basic build-in AT-command failed ---*/
if (rc == utlAT_RESULT_CODE_ERROR)
{
switch (parameter_value_p->type)
{
case utlAT_DATA_TYPE_DECIMAL: utlError(utlProcessBasicAtCommand4, "Invalid parameter value (%d) for `%s' AT command at col=%d.", parameter_value_p->value.decimal, command_p->name_p, syntax_off); break;
case utlAT_DATA_TYPE_DIAL_STRING: utlError(utlProcessBasicAtCommand5, "Invalid parameter value (%s) for `%s' AT command at col=%d.", parameter_value_p->value.dial_string_p, command_p->name_p, syntax_off); break;
default: utlError(utlProcessBasicAtCommand6, "Invalid parameter value for `%s' AT command at col=%d.", command_p->name_p, syntax_off); break;
}
}
return rc;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAsyncResponseTimeoutHandler(id, timeout_count, arg_p, curr_time_p)
* INPUT
* id == timer ID
* timeout_count == the time-out count
* arg_p == pointer to the response we timed-out on
* curr_time_p == current time since the epoch
* OUTPUT
* modifies the data pointed to by `arg_p'
* RETURNS
* utlSUCCESS for success, utlFAILED for failure.
* DESCRIPTION
* Handles async response time-out events: terminates the wait by
* generating an error.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlAsyncResponseTimeoutHandler(const utlTimerId_T id,
const unsigned long timeout_count,
void *arg_p,
const utlAbsoluteTime_P2c curr_time_p)
{
UNUSEDPARAM(id)
UNUSEDPARAM(timeout_count)
UNUSEDPARAM(curr_time_p)
utlAtAsyncResponse_P async_response_p;
utlReturnCode_T rc;
utlAtParser_P parser_p;
utlAssert(arg_p != NULL);
async_response_p = (utlAtAsyncResponse_P)arg_p;
parser_p = async_response_p->parser_p;
utlAssert(parser_p != NULL);
if(async_response_p->command_p != NULL)
{
utlError(utlAsyncResponseTimeoutHandler, "AT Command %s timeout\n", async_response_p->command_p->name_p);
}
//clear SMS_over_nas
parser_p->call_backs.clearSmsoverNas_P(async_response_p);
//check if this command was proxied
if(async_response_p->isProxy) {
//clear the proxy flag for the command, to avoid double handling
async_response_p->isProxy = 0;
utlError(utlAsyncResponseTimeoutHandler3, "ready to proxyEsc\n");
//proxy esc to ims
parser_p->call_backs.proxyEsc_p(arg_p);
//increment the command counter
if(async_response_p->command_p) {
DBGMSG(utlAsyncResponseTimeoutHandler1, "PROXY_DEBUG -%s,%d - cmd %s is proxy , and failed, inc counter\n ",
__FUNCTION__, __LINE__, async_response_p->command_p->name_p);
}
#ifdef OPT_DSDS
parser_p->call_backs.incProxyTOCounter_p(async_response_p->xid,1);
#else
parser_p->call_backs.incProxyTOCounter_p(1);
#endif
}
if(parser_p->call_backs.atcmdTimeoutError_function_P == NULL)
{
utlError(utlAsyncResponseTimeoutHandler2, "Not have AT command timeout error handler\n");
return utlFAILED;
}
rc = parser_p->call_backs.atcmdTimeoutError_function_P(async_response_p->xid);
async_response_p->op = utlAT_ASYNC_OP_UNKNOWN;
async_response_p->command_p = NULL;
async_response_p->timer_id = utlFAILED;
parser_p->states.sync_response.complete_reported = false;
parser_p->states.sync_response.abort_reported = false;
parser_p->states.sync_response.error_reported = false;
parser_p->states.sync_response.parameter_values_p = NULL;
parser_p->states.sync_response.custom_p = NULL;
parser_p->states.sync_response.info_text_p = NULL;
parser_p->states.sync_response.abort_response_p = NULL;
parser_p->states.sync_response.command_syntax_p = NULL;
if(parser_p->call_backs.atcmdContinuousTimeout_function_p != NULL)
{
#ifdef OPT_DSDS
parser_p->call_backs.atcmdContinuousTimeout_function_p(arg_p);
#else
parser_p->call_backs.atcmdContinuousTimeout_function_p();
#endif
}
return rc;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlSetupPendingAsyncResponse(parser_p, op, command_p)
* INPUT
* parser_p == an open AT-command parser
* op == a pending asyncchronous operation
* command_p == pointer to the AT-command about to be executed
* OUTPUT
* none
* RETURNS
* a pointer to a new async-response node success, NULL for failure.
* DESCRIPTION
* Sets up for an async response.
*---------------------------------------------------------------------------*/
static utlAtAsyncResponse_P utlSetupPendingAsyncResponse(const utlAtParser_P parser_p,
const utlAtAsyncOp_T op,
const utlAtCommand_P2c command_p,
unsigned int proxyFlag)
{
utlAtAsyncResponse_P async_response_p;
utlAssert(parser_p != NULL);
utlError(utlSetupPendingAsyncResponse100, "utlSetupPendingAsyncResponse\n");
{
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlSetupPendingAsyncResponse, "Cannot exclusively acquire semaphore!\n");
return NULL;
}
if ((async_response_p = utlGetHeadNode(&(parser_p->states.async_responses.unused), utlAtAsyncResponse_T)) == NULL)
{
utlError(utlSetupPendingAsyncResponse1, "There is no unused response node!\n");
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
return NULL;
}
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
async_response_p->parser_p = parser_p;
async_response_p->xid = utlNextAvailableXID();
async_response_p->op = op;
async_response_p->command_p = command_p;
async_response_p->text_response_p = NULL;
async_response_p->isProxy = proxyFlag; //if 1 - command send to proxy
/*--- start timer ---*/
{
utlRelativeTime_T period;
if(parser_p->call_backs.getAtcmdTimeoutValue_function_p == NULL)
{
utlError(utlSetupPendingAsyncResponse2, "Not have the at command timeout value getting handler\n");
return NULL;
}
if(strcasecmp(command_p->name_p, "*PRXYUNKOWN") != 0)
period.seconds = parser_p->call_backs.getAtcmdTimeoutValue_function_p(command_p->name_p, op);
else
period.seconds = parser_p->call_backs.getAtcmdTimeoutValue_function_p(prxy_command_name, op);
if(proxyFlag)
period.seconds += 180; //to avoid AT with long timeout blocks following AT
ERRMSG(utlSetupPendingAsyncResponse101, "[%s] name_p %s, period.seconds %d\n", __FUNCTION__, command_p->name_p, period.seconds);
period.nanoseconds = 0;
if ((async_response_p->timer_id = utlStartTimer(&period, 1, &utlAsyncResponseTimeoutHandler, async_response_p)) == (utlTimerId_T)utlFAILED)
{
utlError(utlSetupPendingAsyncResponse3, "Fail to start the timer!\n");
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlSetupPendingAsyncResponse4, "Cannot exclusively acquire semaphore!\n");
return NULL;
}
/*--- clean ---*/
(void)utlPutTailNode(&(parser_p->states.async_responses.unused), utlAtAsyncResponse_T, async_response_p);
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
return NULL;
}
}
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlSetupPendingAsyncResponse5, "Cannot exclusively acquire semaphore!\n");
return NULL;
}
utlPutTailNode(&(parser_p->states.async_responses.pending), utlAtAsyncResponse_T, async_response_p);
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
}
parser_p->states.sync_response.complete_reported = false;
parser_p->states.sync_response.abort_reported = false;
parser_p->states.sync_response.error_reported = false;
parser_p->states.sync_response.parameter_values_p = NULL;
parser_p->states.sync_response.custom_p = NULL;
parser_p->states.sync_response.info_text_p = NULL;
parser_p->states.sync_response.abort_response_p = NULL;
parser_p->states.sync_response.command_syntax_p = NULL;
#ifdef LPP_DEBUG
utlAtAsyncResponse_P async_response_p_bak;
utlAtParser_P parser_p_bak;
for (parser_p_bak = (utlAtParser_P)parsers.head_p; parser_p_bak != NULL; parser_p_bak = parser_p_bak->next_p)
{
ERRMSG(utlSetupPendingAsyncResponse3157,"%s: parser_p 0x%X\n", __FUNCTION__, parser_p_bak);
/*--- for each async response this parser is waiting for... ---*/
for (async_response_p_bak = (utlAtAsyncResponse_P)parser_p_bak->states.async_responses.pending.head_p; async_response_p_bak != NULL; async_response_p_bak = async_response_p_bak->next_p)
{
ERRMSG(utlSetupPendingAsyncResponse3162,"%s: async_response_p->xid %u\n", __FUNCTION__, async_response_p_bak->xid);
}
}
#endif
return async_response_p;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlModifyPendingAsyncResponse(period_p, xid)
* INPUT
* period_p == pointer to the timeout time you want to set
* xid == transaction ID
* OUTPUT
* none
* RETURNS
* utlFAILED for failure, utlSUCCESS for success
* DESCRIPTION
* Modify the Pending time for an async response.
*---------------------------------------------------------------------------*/
utlReturnCode_T utlModifyPendingAsyncResponse( const utlRelativeTime_P period_p,
const unsigned int xid)
{
utlAtParser_P parser_p = NULL;
utlAtAsyncResponse_P async_response_p = NULL;
utlAssert(period_p != NULL);
for (parser_p = (utlAtParser_P)parsers.head_p; parser_p != NULL; parser_p = parser_p->next_p)
{
async_response_p = (utlAtAsyncResponse_P)parser_p->states.async_responses.pending.head_p;
for (; async_response_p != NULL; async_response_p = async_response_p->next_p)
{
if (async_response_p->xid == xid)
{
break;
}
}
if (async_response_p != NULL)
break;
}
if(parser_p == NULL)
{
utlError(utlModifyPendingAsyncResponse, "Invalid/unknown transaction ID %d\n", xid);
return utlFAILED;
}
utlAssert(async_response_p != NULL);
if (async_response_p->timer_id != (utlTimerId_T)utlFAILED)
{
if (utlStopTimer(async_response_p->timer_id) != utlSUCCESS)
return utlFAILED;
async_response_p->timer_id = utlFAILED;
}
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlModifyPendingAsyncResponse1, "Cannot exclusively acquire semaphore!\n");
return utlFAILED;
}
if (utlGetNode(&(parser_p->states.async_responses.pending), utlAtAsyncResponse_T, NULL, async_response_p) == NULL)
{
utlError(utlModifyPendingAsyncResponse2, "There is no such pending response node!\n");
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
return utlFAILED;
}
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
if ((async_response_p->timer_id = utlStartTimer(period_p, 1, &utlAsyncResponseTimeoutHandler, async_response_p)) == (utlTimerId_T)utlFAILED)
{
utlError(utlModifyPendingAsyncResponse3, "Fail to start the timer!\n");
/*--- clean ---*/
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlModifyPendingAsyncResponse4, "Cannot exclusively acquire semaphore!\n");
return utlFAILED;
}
utlPutTailNode(&(parser_p->states.async_responses.unused), utlAtAsyncResponse_T, async_response_p);
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
return utlFAILED;
}
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlModifyPendingAsyncResponse5, "Cannot exclusively acquire semaphore!\n");
return utlFAILED;
}
utlPutTailNode(&(parser_p->states.async_responses.pending), utlAtAsyncResponse_T, async_response_p);
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAbandonPendingAsyncResponse(async_response_p)
* INPUT
* async_response_p == the response to abandon
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Abandons the setup for a pending async response.
*---------------------------------------------------------------------------*/
static void utlAbandonPendingAsyncResponse(const utlAtAsyncResponse_P async_response_p)
{
utlAtParser_P parser_p;
utlAssert(async_response_p != NULL);
parser_p = async_response_p->parser_p;
utlAssert(parser_p != NULL);
/*--- release ---*/
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlAbandonPendingAsyncResponse, "Cannot exclusively acquire semaphore!\n");
return;
}
if (utlGetNode( &(parser_p->states.async_responses.pending), utlAtAsyncResponse_T, NULL, async_response_p) != NULL)
{
utlPutTailNode(&(parser_p->states.async_responses.unused), utlAtAsyncResponse_T, async_response_p);
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
/*--- stop timer (as necessary) ---*/
if ( async_response_p->timer_id != (utlTimerId_T)utlFAILED)
{
(void)utlStopTimer(async_response_p->timer_id);
async_response_p->timer_id = utlFAILED;
}
}
else
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
#ifdef LPP_DEBUG
ERRMSG(utlAbandonPendingAsyncResponse3307, "%s: remove xid, parser_p 0x%X, counter %d\n", __FUNCTION__,parser_p, parser_p->states.async_responses.pending.node_count);
#endif
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAbandonAllPendingAsyncResponse(parser_p)
* INPUT
* parser_p
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Abandons all the pending async responses for one parser.
*---------------------------------------------------------------------------*/
static void utlAbandonAllPendingAsyncResponse(utlAtParser_P parser_p)
{
utlAtAsyncResponse_P async_response_p;
utlAssert(parser_p != NULL);
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlAbandonAllPendingAsyncResponse, "Cannot exclusively acquire semaphore!\n");
return;
}
/* just fetch the head node without removing it from pending list */
async_response_p = (utlAtAsyncResponse_P)parser_p->states.async_responses.pending.head_p;
if (async_response_p != NULL)
{
while (async_response_p != NULL)
{
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
/*--- stop timer (as necessary) ---*/
if (async_response_p->timer_id != (utlTimerId_T)utlFAILED)
{
(void)utlStopTimer(async_response_p->timer_id);
async_response_p->timer_id = utlFAILED;
}
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlAbandonAllPendingAsyncResponse1, "Cannot exclusively acquire semaphore!\n");
return;
}
/* remove node from pending list, and append it to unused list */
if (utlGetNode( &(parser_p->states.async_responses.pending), utlAtAsyncResponse_T, NULL, async_response_p) != NULL)
utlPutTailNode(&(parser_p->states.async_responses.unused), utlAtAsyncResponse_T, async_response_p);
#ifdef LPP_DEBUG
ERRMSG(utlAbandonAllPendingAsyncResponse3360, "%s: remove xid, parser_p 0x%X,counter %d\n", __FUNCTION__,parser_p, parser_p->states.async_responses.pending.node_count);
#endif
/* fetch next head node from pending list */
async_response_p = (utlAtAsyncResponse_P)parser_p->states.async_responses.pending.head_p;
}
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
}
else
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlClearPendingAsyncResponse()
* INPUT
* parser_p
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Clear all the pending async responses for all parsers
*---------------------------------------------------------------------------*/
void utlClearPendingAsyncResponse(void)
{
utlAtParser_P parser_p;
utlAtAsyncResponse_P async_response_p;
#ifdef LPP_DEBUG
ERRMSG(utlClearPendingAsyncResponse3367,"%s: clear xid\n", __FUNCTION__);
#endif
/*--- iterate all parsers and send ERROR for pending async response ---*/
{
/*--- for each parser... ---*/
for (parser_p = (utlAtParser_P)parsers.head_p; parser_p != NULL; parser_p = parser_p->next_p)
{
/*--- fetch the first pending async response for this parser,
other pending async response will be cleared in utlAtCommandResponse */
async_response_p = (utlAtAsyncResponse_P)parser_p->states.async_responses.pending.head_p;
if (async_response_p != NULL)
utlAtCommandResponse(async_response_p->xid, utlAT_COMMAND_RESPONSE_TYPE_ERROR, "");
parser_p->states.sync_response.complete_reported = false;
parser_p->states.sync_response.abort_reported = false;
parser_p->states.sync_response.error_reported = false;
parser_p->states.sync_response.parameter_values_p = NULL;
parser_p->states.sync_response.custom_p = NULL;
parser_p->states.sync_response.info_text_p = NULL;
parser_p->states.sync_response.abort_response_p = NULL;
parser_p->states.sync_response.command_syntax_p = NULL;
}
}
}
#if 0
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAtResultCode_T utlHandleLateEvent(const utlAtParser_P parser_p,
* utlAtAsyncResponse_P async_response_p)
* INPUT
* parser_p == an open AT-command parser
* async_response_p == name of the AT command to execute
*
* OUTPUT
* none
* RETURNS
* utlAT_RESULT_CODE_ERROR for failure, other values for success
* DESCRIPTION
* Handle driver events that occur during the invocation of an application-supplied call-back function
*---------------------------------------------------------------------------*/
static utlAtResultCode_T utlHandleLateEvent(const utlAtParser_P parser_p,
utlAtAsyncResponse_P async_response_p)
{
utlAtResultCode_T rc = utlAT_RESULT_CODE_NULL;
/*--- utlAT_COMMAND_RESPONSE_TYPE_ERROR response ---*/
if (parser_p->states.sync_response.error_reported)
{
parser_p->states.sync_response.error_reported = false;
utlAbandonPendingAsyncResponse(async_response_p);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
/*--- utlAT_COMMAND_RESPONSE_TYPE_ABORT response ---*/
if (parser_p->states.sync_response.abort_reported)
{
parser_p->states.sync_response.abort_reported = false;
utlAbandonPendingAsyncResponse(async_response_p);
if (utlSendInfoResponse(parser_p, parser_p->states.sync_response.abort_response_p) != utlSUCCESS)
return utlAT_RESULT_CODE_ERROR;
parser_p->states.sync_response.abort_response_p = NULL;
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_SUPPRESS;
}
/*--- utlAT_COMMAND_RESPONSE_TYPE_REQUEST_COMPLETED response ---*/
if (parser_p->states.sync_response.complete_reported)
{
utlAbandonPendingAsyncResponse(async_response_p);
parser_p->peg_counts.basic_commands++;
return utlAT_RESULT_CODE_OK;
}
/*--- utlAT_COMMAND_RESPONSE_TYPE_CUSTOM response ---*/
if (parser_p->states.sync_response.custom_p != NULL)
{
utlAbandonPendingAsyncResponse(async_response_p);
if (utlSendInfoResponse(parser_p, parser_p->states.sync_response.custom_p) != utlSUCCESS)
return utlAT_RESULT_CODE_ERROR;
parser_p->states.sync_response.custom_p = NULL;
parser_p->peg_counts.basic_commands++;
return utlAT_RESULT_CODE_SUPPRESS;
}
/*--- utlAT_COMMAND_RESPONSE_TYPE_INFO_TEXT response ---*/
if (parser_p->states.sync_response.info_text_p != NULL)
{
utlAbandonPendingAsyncResponse(async_response_p);
if (utlSendInfoResponse(parser_p, parser_p->states.sync_response.info_text_p) != utlSUCCESS)
return utlAT_RESULT_CODE_ERROR;
parser_p->states.sync_response.info_text_p = NULL;
parser_p->peg_counts.basic_commands++;
return utlAT_RESULT_CODE_OK;
}
/*--- utlAT_COMMAND_RESPONSE_TYPE_COMMAND_SYNTAX events ---*/
if (parser_p->states.sync_response.command_syntax_p != NULL)
{
utlAbandonPendingAsyncResponse(async_response_p);
if (utlSendInfoResponse(parser_p, parser_p->states.sync_response.command_syntax_p) != utlSUCCESS)
return utlAT_RESULT_CODE_ERROR;
parser_p->states.sync_response.command_syntax_p = NULL;
parser_p->peg_counts.extended_commands++;
return utlAT_RESULT_CODE_OK;
}
return rc;
}
#endif
unsigned int utlCheckIfSendToProxy(const char * cmdName,utlAtRequestType_T reqType ,const utlAtParser_P parser_p, const char* parameters_string_p)
{
utlAtParameterOp_T cmdOp = utlAT_PARAMETER_OP_SYNTAX;
unsigned int parserIdForProxy;
unsigned int proxyFlag = 0;
VDBGMSG(utlCheckIfSendToProxy, "PROXY_DEBUG -%s,%d - check if comand %s with op %d is proxy\n",
__FUNCTION__, __LINE__, cmdName, (int)reqType);
//TODO the two special case used to trigger cp assert , should not foward to proxy channel
if(strcasecmp(cmdName,"D")==0 && strcasecmp(parser_p->states.dial_string.buf,"##3424*9#")==0){
return proxyFlag;
}
if(strcasecmp(cmdName,"D")==0 && parser_p->states.dial_string.buf[strlen(parser_p->states.dial_string.buf)-1]!=';'){
return proxyFlag;
}
if(strcasecmp(cmdName,"+CUSD")==0 && strcasecmp(parser_p->basic_command_param,"1,##3424*9#")==0){
return proxyFlag;
}
#if 0
if(strcasecmp(cmdName,"+CFUN")==0){
return proxyFlag;
}
#endif
if(strcasecmp(cmdName,"+CLCK")==0){
if(parameters_string_p != NULL) {
char* pos0 = NULL;
char* pos1 = NULL;
char fac[16]={'\0'};
pos0 = strstr(parameters_string_p, "\"");
if (pos0 != NULL) {
pos1 = strstr(pos0 + 1, "\"");
if (pos1 != NULL)
strncpy(fac, pos0 + 1, pos1 - pos0 -1);
}
if (strcasecmp(fac, "AO") != 0 && strcasecmp(fac, "OI") != 0 && strcasecmp(fac, "OX") != 0 && strcasecmp(fac, "AI") != 0 &&
strcasecmp(fac, "IR") != 0 && strcasecmp(fac, "AB") != 0 && strcasecmp(fac, "AG") != 0 && strcasecmp(fac, "AC") != 0) {
VDBGMSG(utlCheckIfSendToProxy_3438, "PROXY_DEBUG -%s,%d - comand %s will not proxy\n",
__FUNCTION__, __LINE__, cmdName);
return proxyFlag;
}
} else
return proxyFlag;
}
//translate command req to operation
switch(reqType) {
case utlAT_REQUEST_TYPE_SYNTAX:
cmdOp = utlAT_PARAMETER_OP_SYNTAX;
break;
case utlAT_REQUEST_TYPE_SET:
cmdOp = utlAT_PARAMETER_OP_SET;
break;
case utlAT_REQUEST_TYPE_GET:
cmdOp = utlAT_PARAMETER_OP_GET;
break;
}
//check if the command appears in the proxy list
if(parser_p->call_backs.isProxyReq_function_p != NULL) {
parserIdForProxy = *(unsigned int*)(parser_p->call_backs.arg_p);
proxyFlag = (parser_p->call_backs.isProxyReq_function_p)(cmdName,cmdOp, parserIdForProxy);
}
return proxyFlag;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlExecuteBasicAtCommand(parser_p, command_name_p, parameter_found,
* parameter_value_p, syntax_off)
* INPUT
* parser_p == an open AT-command parser
* command_name_p == name of the AT command to execute
* parameter_found == was the parameter found?
* parameter_value_p == a pointer to the parameter value
* syntax_off == offset from start of line to the AT-command (for
* error reporting)
* OUTPUT
* none
* RETURNS
* utlAT_RESULT_CODE_ERROR for failure, other values for success
* DESCRIPTION
* Executes the basic AT-command specified by `command_name_p'.
*---------------------------------------------------------------------------*/
static utlAtResultCode_T utlExecuteBasicAtCommand(const utlAtParser_P parser_p,
const char *command_name_p,
const bool parameter_found,
const utlAtParameterValue_P2c parameter_value_p,
const size_t syntax_off)
{
utlAtCommand_P2c command_p;
char text_response[utlAT_MAX_RESPONSE_LENGTH];
utlAtResultCode_T rc;
unsigned int proxyFlag = 0;
utlAssert(parser_p != NULL);
utlAssert(parameter_value_p != NULL);
utlAssert(command_name_p != NULL);
utlError(utlExecuteBasicAtCommand100, "utlExecuteBasicAtCommand\n");
/*--- search for AT-command info ---*/
{
utlAtCommand_P2c term_command_p;
term_command_p = parser_p->commands_p + parser_p->num_commands;
for (command_p = parser_p->commands_p; command_p < term_command_p; command_p++)
if ((command_p->type == utlAT_COMMAND_TYPE_BASIC) &&
(tolower( command_p->name_p[0]) == tolower(command_name_p[0])) &&
(strcasecmp(command_p->name_p, command_name_p) == 0))
break;
if (command_p >= term_command_p)
{
parser_p->peg_counts.undefined_commands++;
return utlAT_RESULT_CODE_ERROR;
}
}
/*-- complain if command should not have a parameter, yet a parameter was found ---*/
if ((parameter_found == true) && (command_p->parameters_p == NULL))
{
utlError(utlExecuteBasicAtCommand, "Unexpected parameter supplied to AT command at col=%d.", syntax_off);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
//check if the command is proxy
proxyFlag = utlCheckIfSendToProxy(command_name_p, utlAT_REQUEST_TYPE_SET, parser_p, NULL);//the handled basic commands are considered as SET
utlError(utlExecuteBasicAtCommand2, "PRXY_DEBUG -%s,%d- proxy flag for comand %s with op %d is %d\n",
__FUNCTION__, __LINE__, command_name_p, (int)utlAT_REQUEST_TYPE_SET,proxyFlag);
utlTrace(utlTRACE_AT_PARSER,
switch (parameter_value_p->type)
{
case utlAT_DATA_TYPE_DECIMAL: utlPrintTrace("AT%s (set: %d)\n", command_name_p, parameter_value_p->value.decimal); break;
case utlAT_DATA_TYPE_HEXADECIMAL: utlPrintTrace("AT%s (set: 0x%x)\n", command_name_p, parameter_value_p->value.hexadecimal); break;
case utlAT_DATA_TYPE_BINARY: utlPrintTrace("AT%s (set: 0x%x)\n", command_name_p, parameter_value_p->value.binary); break;
case utlAT_DATA_TYPE_STRING: utlPrintTrace("AT%s (set: \"%s\")\n", command_name_p, parameter_value_p->value.string_p); break;
case utlAT_DATA_TYPE_QSTRING: utlPrintTrace("AT%s (set: \"%s\")\n", command_name_p, parameter_value_p->value.qstring_p); break;
case utlAT_DATA_TYPE_DIAL_STRING: utlPrintTrace("AT%s (set: %s)\n", command_name_p, parameter_value_p->value.dial_string_p); break;
}
);
atmcd_mult_lock(parser_p, command_p, 0);
/*--- process built-in AT commands (as necessary) ---*/
{
/*--- initially assume no text response is required ---*/
text_response[0] = '\0';
if ((rc = utlProcessBasicAtCommand(parser_p,
command_p,
parameter_value_p,
text_response,
utlNumberOf(text_response),
syntax_off)) == utlAT_RESULT_CODE_ERROR)
{
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
utlError(utlExecuteBasicAtCommand11, "utlExecuteBasicAtCommand: utlProcessBasicAtCommand fail\n");
goto out_err;
}
}
/*--- invoke AT-command call-back (as appropriate) ---*/
if ((command_p->call_backs.set_parameter_function_p != NULL)||(proxyFlag))
{
utlAtAsyncResponse_P async_response_p;
/*--- setup for pending asynchronous response ---*/
if ((async_response_p = utlSetupPendingAsyncResponse(parser_p, utlAT_ASYNC_OP_EXEC, command_p,proxyFlag)) == NULL) {
rc = utlAT_RESULT_CODE_ERROR;
utlError(utlExecuteBasicAtCommand12, "utlExecuteBasicAtCommand: utlSetupPendingAsyncResponse fail\n");
goto out_err;
}
if(proxyFlag) {
//handle ATD specificly
if(!strcasecmp(command_name_p,"d")) {
//handle D specificly
utlError(utlExecuteBasicAtCommand3, "PROXY_DEBUG -%s,%d- for ATD - proxy flag is on, sent string is %s\n",
__FUNCTION__, __LINE__, parser_p->states.dial_string.buf);
if( (parser_p->call_backs.sendToProxy_function_p)(command_name_p,
utlAT_PARAMETER_OP_EXEC, //need to send the command with =
parser_p->states.dial_string.buf,
&(async_response_p->xid),
parser_p->call_backs.arg_p)!= utlSUCCESS)
{
/*--- clean ---*/
utlAbandonPendingAsyncResponse(async_response_p);
utlError(utlExecuteBasicAtCommand13, "utlExecuteBasicAtCommand: sendToProxy ATD fail\n");
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_err;
}
}
else {
//send the command with the proxy string that was created before
utlError(utlExecuteBasicAtCommand4, "PROXY_DEBUG -%s,%d- for cmd %s- with param %s\n",
__FUNCTION__, __LINE__, command_name_p,parser_p->basic_command_param);
if( (parser_p->call_backs.sendToProxy_function_p)(command_name_p,
utlAT_PARAMETER_OP_EXEC,
parser_p->basic_command_param,
&(async_response_p->xid),
parser_p->call_backs.arg_p)!= utlSUCCESS)
{
/*--- clean ---*/
utlAbandonPendingAsyncResponse(async_response_p);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
utlError(utlExecuteBasicAtCommand14, "utlExecuteBasicAtCommand: sendToProxy fail\n");
goto out_err;
}
}
}
else{
if ((command_p->call_backs.set_parameter_function_p)(utlAT_PARAMETER_OP_EXEC,
command_name_p,
(command_p->num_parameters == (size_t)0) ? NULL : parameter_value_p,
command_p->num_parameters,
text_response,
&(async_response_p->xid),
parser_p->call_backs.arg_p) != utlSUCCESS)
{
/*--- clean ---*/
utlAbandonPendingAsyncResponse(async_response_p);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
utlError(utlExecuteBasicAtCommand15, "utlExecuteBasicAtCommand: set_parameter_function fail\n");
goto out_err;
}
}
rc = utlAT_RESULT_CODE_SUPPRESS;
}
else
{
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
utlError(utlExecuteBasicAtCommand16, "utlExecuteBasicAtCommand: bad_commands\n");
goto out_err;
}
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlExecuteBasicAtCommand5, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
rc = utlAT_RESULT_CODE_ERROR;
goto out_err;
}
/* dead code detected by converty comment here
if (rc == utlAT_RESULT_CODE_OK)
parser_p->commands_in_line--;
*/
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
parser_p->peg_counts.basic_commands++;
return rc;
out_err:
utlError(utlExecuteBasicAtCommand6, "TOMER @%s:%d\n", __FUNCTION__, __LINE__);
atmcd_mult_unlock(parser_p, command_p, 0);
return rc;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlFormatExtendedResultString(parser_p, result_p, result_siz, name_p, string_p)
* INPUT
* parser_p == an open AT-command parser
* result_p == pointer to where to place the formated result
* result_siz == length of array pointed to by `result_p'
* name_p == pointer to an AT-Command name expressed as a null-terminated
* character string
* string_p == pointer to a null-terminated character string
* OUTPUT
* *result_p == the formated result
* RETURNS
* the number of characters written to `string_p'
* DESCRIPTION
* Formats the specified extended-AT-Command result string.
*---------------------------------------------------------------------------*/
static size_t utlFormatExtendedResultString(const utlAtParser_P parser_p,
char *result_p,
const size_t result_siz,
const char *name_p,
const char *string_p)
{
size_t name_len;
size_t string_len;
size_t i;
utlAssert(parser_p != NULL);
utlAssert(result_p != NULL);
utlAssert(name_p != NULL);
utlAssert(string_p != NULL);
/*--- are results being suppressed? ---*/
if (parser_p->parameters.suppress_results == true)
return 0;
name_len = strlen( name_p);
string_len = strlen(string_p);
/*--- no room for result? ---*/
if ((name_len + string_len) >= (result_siz - 3))
{
utlError(utlFormatExtendedResultString, "Buffer overflow.");
return 0;
}
/*--- format for result is: "<name_p>: <string_p>" ---*/
(void)strcpy(result_p, name_p);
i = name_len;
result_p[i++] = ':';
result_p[i++] = ' ';
(void)strcpy(result_p + i, string_p);
i += string_len;
result_p[i] = '\0';
return i;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlProcessExtendedAtCommand(parser_p, request_type, command_p,
* parameter_values_p, response_p,
* response_siz, syntax_off)
* INPUT
* parser_p == an open AT-command parser
* request_type == type of request
* command_p == pointer to the AT command to process
* parameter_values_p == command's associated parameter values
* response_p == where to place the response (if any)
* response_siz == length of array pointed to by `response_p'
* syntax_off == offset from start of line to AT-command (only
* used for error reporting)
* OUTPUT
* response_p == where the response was placed (if any)
* RETURNS
* utlAT_RESULT_CODE_ERROR for failure, other values for success
* DESCRIPTION
* Processes the extended built-in AT command specified by `command_p'.
*---------------------------------------------------------------------------*/
static utlAtResultCode_T utlProcessExtendedAtCommand(const utlAtParser_P parser_p,
const utlAtRequestType_T request_type,
const utlAtCommand_P2c command_p,
utlAtParameterValue_P parameter_values_p,
char *response_p,
const size_t response_siz,
const size_t syntax_off)
{
utlAtResultCode_T rc;
utlAssert(parser_p != NULL);
utlAssert(command_p != NULL);
utlAssert(response_p != NULL);
utlAssert(response_siz > (size_t)2);
utlAssert(command_p->name_p != NULL);
utlError(utlProcessExtendedAtCommand100, "utlProcessExtendedAtCommand\n");
/*--- nothing to do (we only process commands starting with "+A", "+G", "+I", or "+C")? ---*/
if ((command_p->name_p[0] != '+') || ((command_p->name_p[1] != 'a') &&
(command_p->name_p[1] != 'A') &&
(command_p->name_p[1] != 'c') &&
(command_p->name_p[1] != 'C') &&
(command_p->name_p[1] != 'g') &&
(command_p->name_p[1] != 'G') &&
(command_p->name_p[1] != 'i') &&
(command_p->name_p[1] != 'I')))
return utlAT_RESULT_CODE_OK;
rc = utlAT_RESULT_CODE_OK;
/*--- save/fetch dial-string ---*/
if (strcasecmp(command_p->name_p, "+ASTO") == 0)
{
utlAssert((parameter_values_p[0].type == utlAT_DATA_TYPE_DECIMAL) ||
(parameter_values_p[0].type == utlAT_DATA_TYPE_STRING) ||
(parameter_values_p[0].type == utlAT_DATA_TYPE_QSTRING));
utlAssert(parameter_values_p[1].type == utlAT_DATA_TYPE_DIAL_STRING);
/*--- fetch? ---*/
if (request_type == utlAT_REQUEST_TYPE_GET)
{
char *c_p;
char *term_c_p;
const char *next_location_name_p;
bool need_cr_lf;
c_p = response_p;
term_c_p = response_p + response_siz;
next_location_name_p = NULL;
/*--- retrieve name of first location (if any) ---*/
if ( parser_p->call_backs.retrieve_dial_string_function_p != NULL)
if ((parser_p->call_backs.retrieve_dial_string_function_p)(&next_location_name_p, NULL, parser_p->call_backs.arg_p) != utlSUCCESS)
{
rc = utlAT_RESULT_CODE_ERROR;
next_location_name_p = NULL;
}
/*--- for each stored dial-string... ---*/
need_cr_lf = false;
while (next_location_name_p != NULL)
{
const char *location_name_p;
const char *dial_string_p;
/*--- save location name reference ---*/
location_name_p = next_location_name_p;
dial_string_p = NULL;
/*--- retrieve previously saved dial string (if any) ---*/
if ( parser_p->call_backs.retrieve_dial_string_function_p != NULL)
if ((parser_p->call_backs.retrieve_dial_string_function_p)(&next_location_name_p, &dial_string_p, parser_p->call_backs.arg_p) != utlSUCCESS)
{
rc = utlAT_RESULT_CODE_ERROR;
break;
}
/*--- found a previously-saved dial string? ---*/
if ((dial_string_p != NULL) && (strlen(dial_string_p) > (size_t)0))
{
char buf[utlAT_MAX_RESPONSE_LENGTH];
/*--- generate dial-string info: <location>,<dial-string><cr><lf> ---*/
{
size_t i;
(void)strncpy(buf, location_name_p, utlNumberOf(buf));
i = strlen(location_name_p);
buf[i++] = parser_p->parameters.S[utlAT_SEPARATER_CHAR];
(void)strncpy(buf + i, dial_string_p, utlNumberOf(buf) - i - 4);
i += strlen(dial_string_p);
buf[i] = '\0';
}
/*--- append dial-string info ---*/
{
size_t rv;
/*--- separate multiple entries with line-termination sequences (if there's room) ---*/
if (need_cr_lf == true)
{
if (response_siz > (size_t)(c_p - response_p + 2))
{
*c_p++ = parser_p->parameters.S[utlAT_LINE_TERM_CHAR];
*c_p++ = parser_p->parameters.S[utlAT_FORMATTING_CHAR];
}
}
else
need_cr_lf = true;
if ((rv = utlFormatExtendedResultString(parser_p, c_p,
response_siz - (c_p - response_p),
command_p->name_p,
buf)) == (size_t)0)
{
rc = utlAT_RESULT_CODE_ERROR;
break;
}
c_p += rv;
}
}
}
/*--- generate empty response if there's nothing to report ---*/
if (c_p == response_p)
{
*c_p++ = parser_p->parameters.S[utlAT_LINE_TERM_CHAR];
*c_p++ = parser_p->parameters.S[utlAT_FORMATTING_CHAR];
}
*c_p = '\0';
/*--- else save ---*/
}
else if (request_type == utlAT_REQUEST_TYPE_SET)
{
const char *location_name_p;
const char *dial_string_p;
char location_name[20]; /* plenty big for a 64-bit unsigned integer */
/*--- we'll accept either a decimal or string for "location". According
to V.250 this parameter should be a decimal number, but our dial-string
storage implementation also supports storing phone numbers under
names. So allowing ASTO to also store under names is convenient for
testing. */
if (parameter_values_p[0].type == utlAT_DATA_TYPE_DECIMAL)
{
unsigned int location;
location = parameter_values_p[0].value.decimal;
if (utlDecimalToString(location_name, location, utlNumberOf(location_name)) == (size_t)0)
rc = utlAT_RESULT_CODE_ERROR;
location_name_p = location_name;
}
else if (parameter_values_p[0].type == utlAT_DATA_TYPE_STRING)
location_name_p = parameter_values_p[0].value.string_p;
else if (parameter_values_p[0].type == utlAT_DATA_TYPE_STRING)
location_name_p = parameter_values_p[0].value.qstring_p;
else
location_name_p = "";
if (rc == utlAT_RESULT_CODE_OK)
{
dial_string_p = parameter_values_p[1].value.dial_string_p;
/*--- invoke save-dial-string call-back (if any) ---*/
if ( parser_p->call_backs.save_dial_string_function_p != NULL)
{
if ((parser_p->call_backs.save_dial_string_function_p)(location_name_p, dial_string_p, parser_p->call_backs.arg_p) != utlSUCCESS)
rc = utlAT_RESULT_CODE_ERROR;
}
}
}
else
rc = utlAT_RESULT_CODE_ERROR;
/*--- request manufacturer identification ---*/
}
else if ((strcasecmp(command_p->name_p, "+GMI") == 0) ||
(strcasecmp(command_p->name_p, "+CGMI") == 0))
{
if ((request_type == utlAT_REQUEST_TYPE_GET) ||
(request_type == utlAT_REQUEST_TYPE_SET))
// (void) strncpy(response_p, parser_p->info.manufacturer_p, response_siz);
sprintf(response_p, "%s: \"%s\"", command_p->name_p, parser_p->info.manufacturer_p);
else
rc = utlAT_RESULT_CODE_ERROR;
/*--- request model identification ---*/
}
else if ((strcasecmp(command_p->name_p, "+GMM") == 0) ||
(strcasecmp(command_p->name_p, "+CGMM") == 0))
{
if ((request_type == utlAT_REQUEST_TYPE_GET) ||
(request_type == utlAT_REQUEST_TYPE_SET))
// (void) strncpy(response_p, parser_p->info.model_p, response_siz);
sprintf(response_p, "%s: \"%s\"", command_p->name_p, parser_p->info.model_p);
else
rc = utlAT_RESULT_CODE_ERROR;
/*--- request revision identification ---*/
}
else if ((strcasecmp(command_p->name_p, "+GMR") == 0) ||
(strcasecmp(command_p->name_p, "+CGMR") == 0))
{
if ((request_type == utlAT_REQUEST_TYPE_GET) ||
(request_type == utlAT_REQUEST_TYPE_SET))
// (void) strncpy(response_p, parser_p->info.revision_p, response_siz);
sprintf(response_p, "%s: \"%s\"", command_p->name_p, revisionId);
else
rc = utlAT_RESULT_CODE_ERROR;
/*--- request product serial number (optional command) ---*/
}
else if ((strcasecmp(command_p->name_p, "+GSN") == 0) ||
(strcasecmp(command_p->name_p, "+CGSN") == 0))
{
if (parser_p->info.serial_number_p == NULL)
rc = utlAT_RESULT_CODE_ERROR;
else if ((request_type == utlAT_REQUEST_TYPE_GET) ||
(request_type == utlAT_REQUEST_TYPE_SET))
(void)strncpy(response_p, parser_p->info.serial_number_p, response_siz);
else
rc = utlAT_RESULT_CODE_ERROR;
/*--- request global object identification (optional command) ---*/
}
else if ((strcasecmp(command_p->name_p, "+GOI") == 0) ||
(strcasecmp(command_p->name_p, "+CGOI") == 0))
{
if (parser_p->info.object_id_p == NULL)
rc = utlAT_RESULT_CODE_ERROR;
else if ((request_type == utlAT_REQUEST_TYPE_GET) ||
(request_type == utlAT_REQUEST_TYPE_SET))
(void)strncpy(response_p, parser_p->info.object_id_p, response_siz);
else
rc = utlAT_RESULT_CODE_ERROR;
/*--- request complete capabilities list ---*/
}
else if (strcasecmp(command_p->name_p, "+GCAP") == 0)
{
if ((request_type == utlAT_REQUEST_TYPE_GET) ||
(request_type == utlAT_REQUEST_TYPE_SET))
{
(void)strncpy(response_p, parser_p->info.mt_capabilities_p, response_siz);//according to System Definition
} else
rc = utlAT_RESULT_CODE_ERROR;
/*--- country of installation (optional command) ---*/
}
else if ((strcasecmp(command_p->name_p, "+GCI") == 0) ||
(strcasecmp(command_p->name_p, "+CGCI") == 0))
{
if ((request_type == utlAT_REQUEST_TYPE_GET) ||
(request_type == utlAT_REQUEST_TYPE_SET))
{
if (utlFormatExtendedResultString(parser_p, response_p, response_siz, command_p->name_p, parser_p->info.country_code_p) == (size_t)0)
rc = utlAT_RESULT_CODE_ERROR;
}
else
rc = utlAT_RESULT_CODE_ERROR;
/*--- fixed DTE rate (optional command) ---*/
}
else if (strcasecmp(command_p->name_p, "+IPR") == 0)
{
if (request_type == utlAT_REQUEST_TYPE_GET)
{
char buf[20];
if (utlDecimalToString(buf, parser_p->dce_io_config.data_rate, utlNumberOf(buf)) == (size_t)0)
rc = utlAT_RESULT_CODE_ERROR;
else if (utlFormatExtendedResultString(parser_p, response_p, response_siz, command_p->name_p, buf) == (size_t)0)
rc = utlAT_RESULT_CODE_ERROR;
}
else if (request_type == utlAT_REQUEST_TYPE_SET)
{
/*--- check parameter value and save if OK ---*/
switch (parameter_values_p[0].value.decimal)
{
case 0: case 134: case 600: case 4800: case 19200: case 76800:
case 50: case 150: case 1200: case 7200: case 28800: case 115200:
case 75: case 200: case 1800: case 9600: case 38400: case 230400:
case 110: case 300: case 2400: case 14400: case 57600:
parser_p->dce_io_config.data_rate = parameter_values_p[0].value.decimal;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_BIT_RATE; break;
break;
default:
rc = utlAT_RESULT_CODE_ERROR;
}
}
else
rc = utlAT_RESULT_CODE_ERROR;
/*--- DTE-DCE character framing (optional command) ---*/
}
else if (strcasecmp(command_p->name_p, "+ICF") == 0)
{
if (request_type == utlAT_REQUEST_TYPE_GET)
{
char buf[40];
size_t i;
i = utlDecimalToString(buf, parser_p->parameters.raw_framing, utlNumberOf(buf) - 1);
buf[i++] = parser_p->parameters.S[utlAT_SEPARATER_CHAR];
i += utlDecimalToString(buf + i, parser_p->parameters.raw_parity, utlNumberOf(buf) - i);
if (utlFormatExtendedResultString(parser_p, response_p, response_siz, command_p->name_p, buf) == (size_t)0)
rc = utlAT_RESULT_CODE_ERROR;
}
else if (request_type == utlAT_REQUEST_TYPE_SET)
{
unsigned int raw_framing;
unsigned int raw_parity;
raw_framing = parameter_values_p[0].value.decimal;
if (parameter_values_p[1].is_default == false)
raw_parity = parameter_values_p[1].value.decimal;
else raw_parity = parser_p->parameters.raw_parity;
/*--- check parameter values ---*/
switch (raw_framing)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
break;
default:
rc = utlAT_RESULT_CODE_ERROR;
}
switch (raw_parity)
{
case 0:
case 1:
case 2:
case 3:
break;
default:
rc = utlAT_RESULT_CODE_ERROR;
}
/*--- save parameter values if OK ---*/
if (rc == utlAT_RESULT_CODE_OK)
{
parser_p->parameters.raw_framing = raw_framing;
parser_p->parameters.raw_parity = raw_parity;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_FRAMING |
utlAT_DCE_IO_CONFIG_PENDING_PARITY;
}
}
else
rc = utlAT_RESULT_CODE_ERROR;
/*--- DTE-DCE local flow control (optional command) ---*/
}
else if (strcasecmp(command_p->name_p, "+IFC") == 0)
{
utlAssert(parameter_values_p[0].type == utlAT_DATA_TYPE_DECIMAL);
utlAssert(parameter_values_p[1].type == utlAT_DATA_TYPE_DECIMAL);
if (request_type == utlAT_REQUEST_TYPE_GET)
{
char buf[40];
size_t i;
i = utlDecimalToString(buf, parser_p->dce_io_config.flow_control.dce_by_dte, utlNumberOf(buf) - 1);
buf[i++] = parser_p->parameters.S[utlAT_SEPARATER_CHAR];
i += utlDecimalToString(buf + i, parser_p->dce_io_config.flow_control.dte_by_dce, utlNumberOf(buf) - i);
if (utlFormatExtendedResultString(parser_p, response_p, response_siz, command_p->name_p, buf) == (size_t)0)
rc = utlAT_RESULT_CODE_ERROR;
}
else if (request_type == utlAT_REQUEST_TYPE_SET)
{
unsigned int dce_by_dte;
unsigned int dte_by_dce;
dce_by_dte = parameter_values_p[0].value.decimal;
if (parameter_values_p[1].is_default == false)
dte_by_dce = parameter_values_p[1].value.decimal;
else dte_by_dce = parser_p->dce_io_config.flow_control.dte_by_dce;
/*--- check parameter values ---*/
switch (dce_by_dte)
{
case 0: /* none */
case 1: /* DC1/DC3 (and don't pass DC1/DC3 through) */
case 2: /* RTS */
case 3: /* both RTS and DC1/DC3 (and pass DC1/DC3 through) */
break;
default:
rc = utlAT_RESULT_CODE_ERROR;
}
switch (dte_by_dce)
{
case 0: /* none */
case 1: /* DC1/DC3 */
case 2: /* CTS */
break;
default:
rc = utlAT_RESULT_CODE_ERROR;
}
/*--- save parameter values if OK ---*/
if (rc == utlAT_RESULT_CODE_OK)
{
parser_p->dce_io_config.flow_control.dce_by_dte = dce_by_dte;
parser_p->dce_io_config.flow_control.dte_by_dce = dte_by_dce;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_FLOW_CONTROL;
}
}
else
rc = utlAT_RESULT_CODE_ERROR;
/*--- select DSR option (optional command) ---*/
}
else if (strcasecmp(command_p->name_p, "+IDSR") == 0)
{
utlAssert(parameter_values_p[0].type == utlAT_DATA_TYPE_DECIMAL);
if (request_type == utlAT_REQUEST_TYPE_GET)
{
char buf[20];
if (utlDecimalToString(buf, parser_p->dce_io_config.modes.dsr_mode, utlNumberOf(buf)) == (size_t)0)
rc = utlAT_RESULT_CODE_ERROR;
else if (utlFormatExtendedResultString(parser_p, response_p, response_siz, command_p->name_p, buf) == (size_t)0)
rc = utlAT_RESULT_CODE_ERROR;
}
else if (request_type == utlAT_REQUEST_TYPE_SET)
{
/*--- check parameter value and save if OK ---*/
switch (parameter_values_p[0].value.decimal)
{
case 0: /* DSR always on */
case 1: /* DSR as per V.24 */
case 2: /* DSR always on except for 5 seconds after disconnect */
parser_p->dce_io_config.modes.dsr_mode = parameter_values_p[0].value.decimal;
parser_p->states.dce_io_config_pending_mask |= utlAT_DCE_IO_CONFIG_PENDING_DSR;
break;
default:
rc = utlAT_RESULT_CODE_ERROR;
}
}
else
rc = utlAT_RESULT_CODE_ERROR;
}
/*--- if the execution of any basic build-in commands failed ---*/
if (rc != utlAT_RESULT_CODE_OK)
{
utlError(utlProcessExtendedAtCommand, "Invalid parameter for `%s' AT command at col=%d.", command_p->name_p, syntax_off);
}
return rc;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlSetDefaultParameterValue(parameter_value_p, type, access)
* INPUT
* parameter_value_p == pointer to the parameter to set
* type == the data type of `parameter_value_p'
* access == the access rights associated with `parameter_value_p'
* OUTPUT
* *parameter_value_p == a modified parameter
* RETURNS
* nothing
* DESCRIPTION
* Assigns the specified parameter a default value according to it's type.
* Modified by Rovin Yu: other file can also use this function
*---------------------------------------------------------------------------*/
void utlSetDefaultParameterValue(const utlAtParameterValue_P parameter_value_p,
const utlAtDataType_T type,
const utlAtParameterAccess_T access)
{
utlAssert(parameter_value_p != NULL);
parameter_value_p->type = type;
parameter_value_p->access = access;
parameter_value_p->is_default = true;
switch (type)
{
case utlAT_DATA_TYPE_DECIMAL: parameter_value_p->value.decimal = 0; break;
case utlAT_DATA_TYPE_HEXADECIMAL: parameter_value_p->value.hexadecimal = 0; break;
case utlAT_DATA_TYPE_BINARY: parameter_value_p->value.binary = 0; break;
case utlAT_DATA_TYPE_STRING: parameter_value_p->value.string_p = ""; break;
case utlAT_DATA_TYPE_QSTRING: parameter_value_p->value.qstring_p = ""; break;
case utlAT_DATA_TYPE_DIAL_STRING: parameter_value_p->value.dial_string_p = ""; break;
default:
(void)memset(&(parameter_value_p->value), 0, sizeof(parameter_value_p->value));
break;
}
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAtParserInitCommandParameters(xid, parameter_values_p, num_parameters)
* INPUT
* xid == an active transaction ID
* parameter_values_p == an uninitialized utlAtParameterValue_T array
* num_parameters == number of elements in array pointed to by `parameter_values_p'
* OUTPUT
* *parameter_values_p == the initialized utlAtParameterValue_T array
* RETURNS
* the number of parameters for success, utlFAILED for failure
* DESCRIPTION
* Initializes the parameter-values structure appropriately for the
* AT-command associated with `xid'.
*---------------------------------------------------------------------------*/
size_t utlAtParserInitCommandParameters(const unsigned int xid,
const utlAtParameterValue_P parameter_values_p,
const size_t num_parameters)
{
utlAtParser_P parser_p;
utlAtAsyncResponse_P async_response_p;
utlAtCommand_P2c command_p;
utlAtParameter_P parameter_p;
utlAtParameterValue_P parameter_value_p;
utlAssert(parameter_values_p != NULL);
/*--- determine the parser and async_response node associated with the given transaction ID ---*/
{
/*--- for each parser... ---*/
for (parser_p = (utlAtParser_P)parsers.head_p; parser_p != NULL; parser_p = parser_p->next_p)
{
/*--- for each pending async response for this parser... ---*/
for (async_response_p = (utlAtAsyncResponse_P)parser_p->states.async_responses.pending.head_p; async_response_p != NULL; async_response_p = async_response_p->next_p)
if (async_response_p->xid == xid)
break;
/*--- bail out? ---*/
if (async_response_p != NULL)
break;
}
/*--- is XID invalid/unknown? ---*/
if (parser_p == NULL)
{
utlError(utlAtParserInitCommandParameters, "Invalid/unknown transaction ID %d\n", xid);
return utlFAILED;
}
}
utlAssert(async_response_p->command_p != NULL);
command_p = async_response_p->command_p;
/*--- check that output array is big enough ---*/
if (num_parameters < command_p->num_parameters)
{
utlError(utlAtParserInitCommandParameters1, "Not enough room in `parameter_values_p' for all of command `%s's parameters (got %d, need %d)",
command_p->name_p, num_parameters, command_p->num_parameters);
return utlFAILED;
}
parameter_p = command_p->parameters_p;
parameter_value_p = parameter_values_p;
/*--- for each of this AT-Command's known parameters... ---*/
while (parameter_p < (command_p->parameters_p + command_p->num_parameters))
{
/*--- assign a default parameter value, according to it's data-type... ---*/
utlSetDefaultParameterValue(parameter_value_p, parameter_p->type, parameter_p->access);
parameter_p++;
parameter_value_p++;
}
return command_p->num_parameters;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlGenerateFormattedGetResponse(parser_p, command_name_p, parameters_p,
* num_parameters)
* INPUT
* parser_p == an open AT-command parser
* command_name_p == name of the AT command to execute
* parameters_p == pointer to an array of parameters
* num_parameters == the number of elements in the array pointed to by `parameters_p'
* OUTPUT
* none
* RETURNS
* utlFAILED for failure, otherwise utlSUCCESS
* DESCRIPTION
* Handles the generation (format and transmission) of standard responses
* to Extended AT command "GET" requests: converts parameter values into
* a properly formattted text string and issues the string.
*---------------------------------------------------------------------------*/
static utlReturnCode_T utlGenerateFormattedGetResponse(const utlAtParser_P parser_p,
const char *command_name_p,
const utlAtParameterValue_P2c parameter_values_p,
const size_t num_parameters)
{
char buf[utlAT_MAX_RESPONSE_LENGTH];
utlAtParameterValue_P2c parameter_value_p;
utlAtParameterValue_P2c term_parameter_value_p;
size_t i;
utlAssert(parser_p != NULL);
utlAssert(command_name_p != NULL);
utlAssert(parameter_values_p != NULL);
i = 0;
/*--- for each known parameter value... ---*/
term_parameter_value_p = parameter_values_p + num_parameters;
for (parameter_value_p = parameter_values_p; parameter_value_p < term_parameter_value_p; parameter_value_p++)
{
size_t rv;
/*--- append parameter separator (as necessary) ---*/
if (parameter_value_p > parameter_values_p)
{
if (i < utlNumberOf(buf))
buf[i++] = parser_p->parameters.S[utlAT_SEPARATER_CHAR];
}
/*--- append parameter value according to type ---*/
switch (parameter_value_p->type)
{
case utlAT_DATA_TYPE_DECIMAL:
if ((rv = utlDecimalToString(buf + i, parameter_value_p->value.decimal, sizeof(buf) - i)) == (size_t)0)
{
utlError(utlGenerateFormattedGetResponse, "Reply-buffer overflow.");
return utlFAILED;
}
i += rv;
break;
case utlAT_DATA_TYPE_HEXADECIMAL:
if ((rv = utlHexadecimalToString(buf + i, parameter_value_p->value.hexadecimal, sizeof(buf) - i)) == (size_t)0)
{
utlError(utlGenerateFormattedGetResponse1, "Reply-buffer overflow.");
return utlFAILED;
}
i += rv;
break;
case utlAT_DATA_TYPE_BINARY:
if ((rv = utlBinaryToString(buf + i, parameter_value_p->value.binary, sizeof(buf) - i)) == (size_t)0)
{
utlError(utlGenerateFormattedGetResponse2, "Reply-buffer overflow.");
return utlFAILED;
}
i += rv;
break;
case utlAT_DATA_TYPE_STRING:
case utlAT_DATA_TYPE_QSTRING:
{
const char *c_p;
if (i < utlNumberOf(buf))
buf[i++] = '"';
if (parameter_value_p->type == utlAT_DATA_TYPE_STRING) c_p = parameter_value_p->value.string_p;
else c_p = parameter_value_p->value.qstring_p;
while ((*c_p != '\0') && (i < utlNumberOf(buf)))
buf[i++] = *c_p++;
if (i < utlNumberOf(buf))
buf[i++] = '"';
/*--- no room? ---*/
if (i >= utlNumberOf(buf))
{
utlError(utlGenerateFormattedGetResponse3, "Reply-buffer overflow.");
return utlFAILED;
}
}
break;
case utlAT_DATA_TYPE_DIAL_STRING:
{
const char *c_p;
c_p = parameter_value_p->value.dial_string_p;
while ((*c_p != '\0') && (i < utlNumberOf(buf)))
buf[i++] = *c_p++;
/*--- no room? ---*/
if (i >= utlNumberOf(buf))
{
utlError(utlGenerateFormattedGetResponse4, "Reply-buffer overflow.");
return utlFAILED;
}
}
break;
default:
utlError(utlGenerateFormattedGetResponse5, "Invalid parameter_value_p->type!\n");
break;
}
}
/*--- no room? ---*/
if (i >= (utlNumberOf(buf) - 1))
{
utlError(utlGenerateFormattedGetResponse6, "Reply-buffer overflow.");
return utlFAILED;
}
buf[i] = '\0';
utlTrace(utlTRACE_AT_PARSER,
utlPrintTrace(utlGenerateFormattedGetResponse7, "AT%s (get: %s)\n", command_name_p, buf);
);
if (utlSendInfoResponse(parser_p, buf) != utlSUCCESS)
return utlFAILED;
return utlSUCCESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlExecuteExtendedAtCommand(parser_p, request_type, command_name_p,
* parameters_string_p, syntax_off)
* INPUT
* parser_p == an open AT-command parser
* request_type == type of extended AT-command request
* command_name_p == name of the AT command to execute
* parameters_string_p == was a parameter value found?
* syntax_off == offset from start of line to AT-command (only
* used for error reporting)
* OUTPUT
* none
* RETURNS
* utlAT_RESULT_CODE_ERROR for failure, other values for success
* DESCRIPTION
* Executes the extended AT command specified by `command_name_p'.
*---------------------------------------------------------------------------*/
static utlAtResultCode_T utlExecuteExtendedAtCommand(const utlAtParser_P parser_p,
const utlAtRequestType_T request_type,
const char *command_name_p,
const char *parameters_string_p,
const size_t syntax_off)
{
utlAtCommand_P2c command_p;
utlAtParameterValue_T parameter_values[utlAT_MAX_PARAMETERS];
char string_buf[utlAT_MAX_LINE_LENGTH + utlAT_MAX_PARAMETERS];
char info_text[utlAT_MAX_RESPONSE_LENGTH];
utlAtResultCode_T rc;
unsigned int proxyFlag = 0;
unsigned int paramCheckIgnore = 0;
if((parser_p == NULL) || (command_name_p == NULL))
return utlAT_RESULT_CODE_ERROR;
memset(&parameter_values, 0, sizeof(parameter_values));
utlError(utlExecuteExtendedAtCommand100, "utlExecuteExtendedAtCommand\n");
/*--- search for AT-command info ---*/
{
utlAtCommand_P2c term_command_p;
term_command_p = parser_p->commands_p + parser_p->num_commands;
for (command_p = parser_p->commands_p; command_p < term_command_p; command_p++)
if (((command_p->type == utlAT_COMMAND_TYPE_EXTENDED) ||
(command_p->type == utlAT_COMMAND_TYPE_EXACTION) ||
(command_p->type == utlAT_COMMAND_TYPE_EXTENDED_EXACTION)) &&
(tolower( command_p->name_p[1]) == tolower(command_name_p[1])) &&
(strcasecmp(command_p->name_p, command_name_p) == 0))
break;
VDBGMSG(utlExecuteExtendedAtCommand, "PRXY_DEBUG -%s,%d - parser handles command %s, with param %s, \n",
__FUNCTION__, __LINE__, command_name_p, parameters_string_p);
if (command_p >= term_command_p)
{
VDBGMSG(utlExecuteExtendedAtCommand1, "PRXY_DEBUG -%s,%d - command %s with param %s not known to parser. Look at proxy list\n",
__FUNCTION__, __LINE__, command_name_p, parameters_string_p);
//check first if the command is proxied
proxyFlag = utlCheckIfSendToProxy(command_name_p,request_type,parser_p, parameters_string_p);
if(!proxyFlag) {
//command not found in the list, and is not proxied
parser_p->peg_counts.undefined_commands++;
return utlAT_RESULT_CODE_ERROR;
}
else{
//this is a proxy cmd, use the static defention
if(parser_p->pProxyOnlyCmd != NULL)
{
paramCheckIgnore = 1;
command_p = parser_p->pProxyOnlyCmd;
memset(prxy_command_name, 0, sizeof(prxy_command_name));
strncpy(prxy_command_name, command_name_p, sizeof(prxy_command_name)-1);
}
}
}
//check if command should be proxied
if(!proxyFlag) {
//doesn't checked yet at the proxy list
proxyFlag = utlCheckIfSendToProxy(command_name_p,request_type,parser_p, parameters_string_p);
VDBGMSG(utlExecuteExtendedAtCommand2, "PRXY_DEBUG -%s,%d - proxy flag for comand %s with op %d is %d\n",
__FUNCTION__, __LINE__, command_name_p, (int)request_type,proxyFlag);
}
}
utlError(utlExecuteExtendedAtCommand3, "command=%s, request_type=%d\n", command_p->name_p, request_type);
atmcd_mult_lock(parser_p, command_p, request_type);
/*--- if we're only fetching the command syntax ---*/
if (request_type == utlAT_REQUEST_TYPE_SYNTAX)
{
rc = utlAT_RESULT_CODE_OK;
/*--- if the syntax for this command is static ---*/
if ((command_p->usage.command_syntax_p != NULL)&&(!proxyFlag))//if command should be handled by proxy - ignore static settings
{
utlTrace(utlTRACE_AT_PARSER,
utlPrintTrace(utlExecuteExtendedAtCommand4, "AT%s (query syntax: %s)\n", command_name_p, command_p->usage.command_syntax_p);
);
if (utlSendInfoResponse(parser_p, command_p->usage.command_syntax_p) != utlSUCCESS) {
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
/*--- else if the syntax for this command can be fetched ---*/
}
else if ((command_p->usage.call_backs.command_syntax_function_p != NULL)||(proxyFlag))
{
utlAtAsyncResponse_P async_response_p;
/*--- setup for pending asynchronous response ---*/
if ((async_response_p = utlSetupPendingAsyncResponse(parser_p, utlAT_ASYNC_OP_SYNTAX, command_p,proxyFlag)) == NULL) {
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
// call for proxy
if(proxyFlag) {
if(parser_p->call_backs.sendToProxy_function_p(command_name_p,
utlAT_PARAMETER_OP_SYNTAX, //action not supported for proxy
NULL,
&(async_response_p->xid),
parser_p->call_backs.arg_p)!= utlSUCCESS) {
utlError(utlExecuteExtendedAtCommand5, "PROXY_DEBUG - SET - send to proxy _function_p failed for cmd %s with param %s !\n", command_name_p,parameters_string_p );
/*--- clean ---*/
utlAbandonPendingAsyncResponse(async_response_p);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
else{
if ((command_p->usage.call_backs.command_syntax_function_p)(utlAT_PARAMETER_OP_SYNTAX,
command_name_p,
NULL,
0,
NULL,
&(async_response_p->xid),
parser_p->call_backs.arg_p) != utlSUCCESS)
{
/*--- clean ---*/
utlAbandonPendingAsyncResponse(async_response_p);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
rc = utlAT_RESULT_CODE_SUPPRESS;
}
parser_p->peg_counts.extended_commands++;
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlExecuteExtendedAtCommand6, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
if (rc == utlAT_RESULT_CODE_OK)
parser_p->commands_in_line--;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
goto out;
/*--- else if this is a set-request, parse for AT-command's parameters (if any)... ---*/
}
else if (request_type == utlAT_REQUEST_TYPE_SET)
{
utlAtParameter_P parameter_p;
utlAtParameterValue_P parameter_value_p;
char *string_buf_p;
const char *c_p;
parameter_p = command_p->parameters_p;
parameter_value_p = parameter_values; /* we'll stuff the found parameter values here */
string_buf_p = string_buf; /* we'll store decoded string-parameters here */
/*--- if any parameter values were found... ---*/
if ((parameters_string_p != NULL)&&(!paramCheckIgnore))
{
utlTrace(utlTRACE_AT_PARSER,
utlPrintTrace(utlExecuteExtendedAtCommand7, "AT%s (set: %s)\n", command_name_p, parameters_string_p);
);
/*--- for each found parameter value... ---*/
for (c_p = parameters_string_p; *c_p != '\0'; )
{
char value_string[utlAT_MAX_LINE_LENGTH];
size_t value_string_off;
bool parameter_found;
char parameter_term_char;
bool quote_state;
/*--- note offset to this parameter (for error reporting) ---*/
value_string_off = c_p - parameters_string_p;
/*--- extract parameter value (parameters separated by ',' and terminated by null) ---*/
{
char *dest_p;
quote_state = true;
parameter_found = false;
parameter_term_char = '\0';
for (dest_p = value_string; *c_p != '\0'; c_p++)
{
if(*c_p == '"')
quote_state = !quote_state;
if ((*c_p == parser_p->parameters.S[utlAT_SEPARATER_CHAR]) && quote_state)
{
parameter_term_char = *c_p++; /* skip parameter-separator character */
break;
}
*dest_p++ = *c_p;
parameter_found = true;
}
*dest_p = '\0';
}
/*--- if this command should not actually have any parameter values ---*/
if (parameter_p == NULL)
{
/*--- complain if one or more parameter values were found ---*/
if (parameter_found == true)
{
utlError(utlExecuteExtendedAtCommand8, "Unexpected AT command parameter at col=%d.", syntax_off);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
/*--- else this command should have one or more parameters... ---*/
}
else
{
/*--- were more parameter values supplied than we can parse? ---*/
if (parameter_value_p >= (parameter_values + utlAT_MAX_PARAMETERS))
{
utlError(utlExecuteExtendedAtCommand9, "Too many AT-command parameters at col=%d.", syntax_off);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
/*--- if no parameter value was found ---*/
if (parameter_found == false)
{
/*-- complain if omitted parameter value is not Read, Read-only, and is required ---*/
if (((parameter_p->access != utlAT_PARAMETER_ACCESS_READ) &&
(parameter_p->access != utlAT_PARAMETER_ACCESS_READ_ONLY)) &&
( parameter_p->presence == utlAT_PARAMETER_PRESENCE_REQUIRED))
{
utlError(utlExecuteExtendedAtCommand10, "Missing AT command parameter at col=%d.", syntax_off + value_string_off + 1);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
/*--- otherwise assign optional parameter a default value, according to it's type... ---*/
utlSetDefaultParameterValue(parameter_value_p, parameter_p->type, parameter_p->access);
/*--- else if a parameter value was found ---*/
}
else
{
unsigned int num = 0;
char *d_p;
/*--- are we attempting to set a read-only parameter? ---*/
if (parameter_p->access == utlAT_PARAMETER_ACCESS_READ_ONLY)
{
utlError(utlExecuteExtendedAtCommand11, "Attempt to modify read-only AT command parameter at col=%d.", syntax_off + value_string_off + 1);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
/*--- grab rest of parameter string if we are expecting a dial string ---*/
if ((parameter_p->type == utlAT_DATA_TYPE_DIAL_STRING) &&
(parameter_term_char == parser_p->parameters.S[utlAT_SEPARATER_CHAR]))
{
char *dest_p;
dest_p = value_string + strlen(value_string);
*dest_p++ = parser_p->parameters.S[utlAT_SEPARATER_CHAR];
while (*c_p != '\0')
*dest_p++ = *c_p++;
*dest_p = '\0';
}
parameter_value_p->type = parameter_p->type;
parameter_value_p->access = parameter_p->access;
parameter_value_p->is_default = false;
/*--- parse found parameter value according to it's known data-type... ---*/
switch (parameter_p->type)
{
case utlAT_DATA_TYPE_DECIMAL:
num = 0;
for (d_p = value_string; *d_p != '\0'; d_p++)
{
if ((*d_p >= '0') && (*d_p <= '9'))
num = (10 * num) + (*d_p - '0');
else
{
utlError(utlExecuteExtendedAtCommand12, "Extended-AT command syntax error at col=%d.", syntax_off + value_string_off + 1);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
parameter_value_p->value.decimal = num;
break;
case utlAT_DATA_TYPE_HEXADECIMAL:
num = 0;
for (d_p = value_string; *d_p != '\0'; d_p++)
{
if ((*d_p >= '0') && (*d_p <= '9')) num = (16 * num) + (*d_p - '0');
else if ((*d_p >= 'A') && (*d_p <= 'F')) num = (16 * num) + (*d_p - 'A') + 10;
else if ((*d_p >= 'a') && (*d_p <= 'f')) num = (16 * num) + (*d_p - 'a') + 10;
else
{
utlError(utlExecuteExtendedAtCommand13, "Extended-AT command syntax error at col=%d.", syntax_off + value_string_off + 1);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
parameter_value_p->value.hexadecimal = num;
break;
case utlAT_DATA_TYPE_BINARY:
num = 0;
for (d_p = value_string; *d_p != '\0'; d_p++)
{
if (*d_p == '0') num = (num * 2);
else if (*d_p == '1') num = (num * 2) + 1;
else
{
utlError(utlExecuteExtendedAtCommand14, "Extended-AT command syntax error at col=%d.", syntax_off + value_string_off + 1);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
parameter_value_p->value.binary = num;
break;
case utlAT_DATA_TYPE_STRING:
case utlAT_DATA_TYPE_QSTRING:
{
utlAtStringState_T state;
char *src_p;
bool quoted;
if (parameter_p->type == utlAT_DATA_TYPE_STRING) parameter_value_p->value.string_p = string_buf_p;
else parameter_value_p->value.qstring_p = string_buf_p;
src_p = value_string;
/*--- our regular string parsing is a little more flexible than the
V.250 standard specifies: strings do not have to be enclosed in
double-quotes, but if double-quotes are omitted, the end of
the string will be delimited by the first parameter separator
character, colon character, or end-of-line encountered. */
if (*src_p == '"')
{
src_p++;
quoted = true;
}
else if (parameter_p->type == utlAT_DATA_TYPE_QSTRING)
{
utlError(utlExecuteExtendedAtCommand15, "Extended-AT command syntax error at col=%d.", syntax_off + value_string_off + 1);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
else
quoted = false;
/*--- extract string, replacing hex escape sequences with the ASCII
characters they represent */
state = utlAT_STRING_STATE_PASS_THROUGH;
for (; *src_p != '\0'; src_p++)
{
if (quoted)
{
if (*src_p == '"')
break;
}
else
{
if ((*src_p == parser_p->parameters.S[utlAT_SEPARATER_CHAR]) || (*src_p == parser_p->parameters.S[utlAT_TERMINATER_CHAR]))
break;
}
/*--- this a possible embedded hex escape sequence? ---*/
if ((*src_p == '\\') && (parser_p->options.allow_string_escapes == true))
{
state = utlAT_STRING_STATE_EXTRACT_HEX_DIGIT1;
num = 0; /* initialize hex num */
}
else if ((state == utlAT_STRING_STATE_EXTRACT_HEX_DIGIT1) ||
(state == utlAT_STRING_STATE_EXTRACT_HEX_DIGIT2))
{
/*--- extract hexadecimal escape sequence digit ---*/
if ((*src_p >= '0') && (*src_p <= '9')) num = (16 * num) + (*src_p - '0');
else if ((*src_p >= 'A') && (*src_p <= 'F')) num = (16 * num) + (*src_p - 'A') + 10;
else if ((*src_p >= 'a') && (*src_p <= 'f')) num = (16 * num) + (*src_p - 'a') + 10;
else
{
/*--- if reverse-solidus was not followed by a hex character, just
interpret found characters as-is */
if (string_buf_p < (string_buf + utlNumberOf(string_buf) - 2))
{
*string_buf_p++ = '\\';
*string_buf_p++ = *src_p;
}
state = utlAT_STRING_STATE_PASS_THROUGH;
continue;
}
if (state == utlAT_STRING_STATE_EXTRACT_HEX_DIGIT1)
{
state = utlAT_STRING_STATE_EXTRACT_HEX_DIGIT2;
}
else if (state == utlAT_STRING_STATE_EXTRACT_HEX_DIGIT2)
{
state = utlAT_STRING_STATE_PASS_THROUGH;
/*--- save hex value as a character (if there's room) ---*/
if (string_buf_p < (string_buf + utlNumberOf(string_buf) - 1))
*string_buf_p++ = num;
}
else
state = utlAT_STRING_STATE_PASS_THROUGH;
/*--- else save character (if there's room) ---*/
}
else if (string_buf_p < (string_buf + utlNumberOf(string_buf) - 1))
*string_buf_p++ = *src_p;
}
if (string_buf_p >= (string_buf + utlNumberOf(string_buf)))
string_buf_p = string_buf + utlNumberOf(string_buf) - 1;
*string_buf_p++ = '\0';
if (quoted == true)
{
/*--- check that string ends with a double quote ---*/
if (*src_p != '"')
{
utlError(utlExecuteExtendedAtCommand16, "Extended-AT command syntax error at col=%d.", syntax_off + value_string_off + 1);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
else
{
char *first_p;
char *last_p;
int i, len;
if (parameter_p->type == utlAT_DATA_TYPE_STRING) first_p = parameter_value_p->value.string_p;
else first_p = parameter_value_p->value.qstring_p;
/*--- trim trailing white space from end of unquoted string ---*/
for (last_p = string_buf_p - 1; last_p > first_p; last_p--)
if (*(last_p - 1) != ' ')
{
*last_p = '\0';
break;
}
/*--- trim trailing white space from beginning of unquoted string ---*/
for (i=0; *(first_p+i)==' ' ; i++);
/* remove the num of space + add the null termination */
len = strlen(first_p) - i + 1;
memmove(first_p, first_p+i, len);
}
}
break;
case utlAT_DATA_TYPE_DIAL_STRING:
{
utlAtStringState_T state;
bool alphabetical_digits;
char *src_p;
parameter_value_p->value.dial_string_p = string_buf_p;
/*--- extract string, replacing hex escape sequences with the ASCII
characters they represent */
state = utlAT_STRING_STATE_PASS_THROUGH;
alphabetical_digits = false;
for (src_p = value_string; *src_p != '\0'; src_p++)
{
/*--- are we expecting alphabetical dial-string digits? ---*/
if (alphabetical_digits == true)
{
if (((*src_p >= 'a') || (*src_p <= 'z')) ||
((*src_p >= 'A') || (*src_p <= 'Z')))
{
if (string_buf_p < (string_buf + utlNumberOf(string_buf) - 1))
*string_buf_p++ = *src_p;
}
else if (*src_p != ' ')
alphabetical_digits = false;
}
if (alphabetical_digits == false)
{
/*--- this a possible embedded hex escape sequence? ---*/
if ((*src_p == '\\') && (parser_p->options.allow_string_escapes == true))
{
state = utlAT_STRING_STATE_EXTRACT_HEX_DIGIT1;
num = 0; /* initialize hex num */
}
else if ((state == utlAT_STRING_STATE_EXTRACT_HEX_DIGIT1) ||
(state == utlAT_STRING_STATE_EXTRACT_HEX_DIGIT2))
{
/*--- extract hexadecimal escape sequence digit ---*/
if ((*src_p >= '0') && (*src_p <= '9')) num = (16 * num) + (*src_p - '0');
else if ((*src_p >= 'A') && (*src_p <= 'F')) num = (16 * num) + (*src_p - 'A') + 10;
else if ((*src_p >= 'a') && (*src_p <= 'f')) num = (16 * num) + (*src_p - 'a') + 10;
else
{
/*--- if reverse-solidus was not followed by a hex character, just
interpret found characters as-is */
if (string_buf_p < (string_buf + utlNumberOf(string_buf) - 2))
{
*string_buf_p++ = '\\';
*string_buf_p++ = *src_p;
}
state = utlAT_STRING_STATE_PASS_THROUGH;
continue;
}
if (state == utlAT_STRING_STATE_EXTRACT_HEX_DIGIT1)
{
state = utlAT_STRING_STATE_EXTRACT_HEX_DIGIT2;
}
else if (state == utlAT_STRING_STATE_EXTRACT_HEX_DIGIT2)
{
state = utlAT_STRING_STATE_PASS_THROUGH;
/*--- process recognized dial-string characters ---*/
if (strchr(utlAT_DIALING_CHARACTERS, num) != NULL)
{
/*--- are zero or more alphabetical dial-string digits to follow? ---*/
if (num == '"')
alphabetical_digits = true;
/*--- save hex value as a character (if there's room) ---*/
if (string_buf_p < (string_buf + utlNumberOf(string_buf) - 1))
*string_buf_p++ = num;
}
}
else
state = utlAT_STRING_STATE_PASS_THROUGH;
/*--- else process recognized dial-string characters ---*/
}
else if (strchr(utlAT_DIALING_CHARACTERS, *src_p) != NULL)
{
/*--- are zero or more alphabetical dial-string digits to follow? ---*/
if (*src_p == '"')
alphabetical_digits = true;
/*--- save character (if there's room) ---*/
if (string_buf_p < (string_buf + utlNumberOf(string_buf) - 1))
*string_buf_p++ = *src_p;
}
}
/*--- ignore all characters following a terminator character ---*/
if (*src_p == parser_p->parameters.S[utlAT_TERMINATER_CHAR])
break;
}
if (string_buf_p >= (string_buf + utlNumberOf(string_buf)))
string_buf_p = string_buf + utlNumberOf(string_buf) - 1;
*string_buf_p++ = '\0';
}
break;
default:
utlError(utlExecuteExtendedAtCommand17, "parameter_value_p->type!\n");
break;
}
}
/*--- advance to next parameter ---*/
if (parameter_p++ >= (command_p->parameters_p + command_p->num_parameters))
{
utlError(utlExecuteExtendedAtCommand18, "Extended-AT command has too many parameters at col=%d.", syntax_off + c_p - parameters_string_p);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
parameter_value_p++;
}
}
}
else
{
if(!paramCheckIgnore) { //NULL parameters
utlTrace(utlTRACE_AT_PARSER,utlPrintTrace(utlExecuteExtendedAtCommand19, "AT%s (set)\n", command_name_p););
}
else{
utlTrace(utlTRACE_AT_PARSER,utlPrintTrace(utlExecuteExtendedAtCommand20, "AT%s (PRXY set, %s )\n", command_name_p,parameters_string_p););
}
}
if (parameter_p != NULL)
{
/*--- for each unspecified parameter value (if any)... ---*/
while (parameter_p < (command_p->parameters_p + command_p->num_parameters))
{
/*--- too many parameter values? ---*/
if (parameter_value_p >= (parameter_values + utlAT_MAX_PARAMETERS))
{
utlError(utlExecuteExtendedAtCommand21, "Too many AT-command parameters at col=%d.", syntax_off);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
/*-- complain if unspecified parameter value is not Read, Read-only, and is required ---*/
if (((parameter_p->access != utlAT_PARAMETER_ACCESS_READ) &&
(parameter_p->access != utlAT_PARAMETER_ACCESS_READ_ONLY)) &&
( parameter_p->presence == utlAT_PARAMETER_PRESENCE_REQUIRED))
{
utlError(utlExecuteExtendedAtCommand22, "Missing parameter(s) in Extended-AT command at col=%d.", syntax_off);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
/*--- assign unspecified parameter a default value, according to it's data-type... ---*/
utlSetDefaultParameterValue(parameter_value_p, parameter_p->type, parameter_p->access);
parameter_p++;
parameter_value_p++;
}
}
/*--- else if this is a get-request, prepare `parameter_values' structure... ---*/
}
else if (request_type == utlAT_REQUEST_TYPE_GET)
{
utlAtParameter_P parameter_p;
utlAtParameterValue_P parameter_value_p;
/*--- this AT-command an "action" command (which we can't fetch) ---*/
if (command_p->type == utlAT_COMMAND_TYPE_EXACTION)
{
if(strcasecmp(command_p->name_p, "*EnVsim") != 0) {
utlError(utlExecuteExtendedAtCommand23, "Attempted to perform a \"read\" for an Extended-AT \"action\" command at col=%d.", syntax_off);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
parameter_p = command_p->parameters_p;
parameter_value_p = parameter_values;
/*--- for each of this AT-Command's known parameters... ---*/
while (parameter_p < (command_p->parameters_p + command_p->num_parameters))
{
/*--- are we attempting to fetch a write-only parameter? ---*/
if (parameter_p->access == utlAT_PARAMETER_ACCESS_WRITE_ONLY)
{
utlError(utlExecuteExtendedAtCommand24, "Attempt to read write-only AT command parameter at col=%d.", syntax_off);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
/*--- too many parameter values? ---*/
if (parameter_value_p >= (parameter_values + utlAT_MAX_PARAMETERS))
{
utlError(utlExecuteExtendedAtCommand25, "Too many AT-command parameters at col=%d.", syntax_off);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
/*--- assign a default parameter value, according to it's data-type... ---*/
utlSetDefaultParameterValue(parameter_value_p, parameter_p->type, parameter_p->access);
parameter_p++;
parameter_value_p++;
}
}
/*--- process extended built-in AT-commands ---*/
{
/*--- initially assume no test response is required ---*/
info_text[0] = '\0';
if(!paramCheckIgnore) {
if ((rc = utlProcessExtendedAtCommand(parser_p,
request_type,
command_p,
parameter_values,
info_text,
utlNumberOf(info_text),
syntax_off)) == utlAT_RESULT_CODE_ERROR)
{
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
else{
//command is proxy responsebility - assume parameter check is OK
rc = utlAT_RESULT_CODE_OK;
}
}
/*--- invoke AT-command call-back (as appropriate) ---*/
switch (request_type)
{
case utlAT_REQUEST_TYPE_SET:
{
char *info_text_p;
utlAtParserOp_T at_async_op;
utlAtParameterOp_T at_parameter_op;
if (info_text[0] == '\0') info_text_p = NULL;
else info_text_p = info_text;
if ((command_p->call_backs.set_parameter_function_p != NULL)||(proxyFlag)) //allow support for commands that should be proxied
{
utlAtParameterValue_P parameter_values_p;
utlAtAsyncResponse_P async_response_p;
if (command_p->num_parameters > (size_t)0) parameter_values_p = parameter_values;
else parameter_values_p = NULL;
if(command_p->type == utlAT_COMMAND_TYPE_EXTENDED_EXACTION)
{
if(parameters_string_p)
{
at_async_op = utlAT_ASYNC_OP_SET;
at_parameter_op = utlAT_PARAMETER_OP_SET;
}
else
{
at_async_op = utlAT_ASYNC_OP_ACTION;
at_parameter_op = utlAT_PARAMETER_OP_ACTION;
}
}
else if(command_p->type == utlAT_COMMAND_TYPE_EXACTION)
{
at_async_op = utlAT_ASYNC_OP_ACTION;
at_parameter_op = utlAT_PARAMETER_OP_ACTION;
}
else
{
at_async_op = utlAT_ASYNC_OP_SET;
at_parameter_op = utlAT_PARAMETER_OP_SET;
}
/*--- setup for pending asynchronous response ---*/
if ((async_response_p = utlSetupPendingAsyncResponse(parser_p, at_async_op, command_p,proxyFlag)) == NULL)
{
utlError(utlExecuteExtendedAtCommand26, "utlSetupPendingAsyncResponse failed!\n");
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
//if proxy command - send to proxy
if (proxyFlag) {
//send to proxy
if(parser_p->call_backs.sendToProxy_function_p(command_name_p,
at_parameter_op,
parameters_string_p,
&(async_response_p->xid),
parser_p->call_backs.arg_p)!= utlSUCCESS) {
utlError(utlExecuteExtendedAtCommand27, "PROXY_DEBUG - SET - send to proxy _function_p failed for cmd %s with param %s !\n", command_name_p,parameters_string_p );
/*--- clean ---*/
utlAbandonPendingAsyncResponse(async_response_p);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
else{
//use command standard call back
if ((command_p->call_backs.set_parameter_function_p)(at_parameter_op,
command_name_p,
parameter_values_p,
command_p->num_parameters,
info_text_p,
&(async_response_p->xid),
parser_p->call_backs.arg_p) != utlSUCCESS)
{
utlError(utlExecuteExtendedAtCommand28, "set_parameter_function_p failed!\n");
/*--- clean ---*/
utlAbandonPendingAsyncResponse(async_response_p);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
} //end send to command call back
}
}
else
{
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
rc = utlAT_RESULT_CODE_SUPPRESS;
}
break;
case utlAT_REQUEST_TYPE_GET:
{
char *info_text_p;
if (info_text[0] == '\0') info_text_p = NULL;
else info_text_p = info_text;
if ((command_p->call_backs.get_parameter_function_p != NULL)||(proxyFlag))
{
utlAtParameterValue_P parameter_values_p;
utlAtAsyncResponse_P async_response_p;
if (command_p->num_parameters > (size_t)0) parameter_values_p = parameter_values;
else parameter_values_p = NULL;
/*--- setup for pending asynchronous response ---*/
if ((async_response_p = utlSetupPendingAsyncResponse(parser_p, utlAT_ASYNC_OP_GET, command_p,proxyFlag)) == NULL)
{
utlError(utlExecuteExtendedAtCommand29, "utlSetupPendingAsyncResponse failed!\n");
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
//if proxy command call proxy
if(proxyFlag) {
//send command to proxy
if(parser_p->call_backs.sendToProxy_function_p(command_name_p,
utlAT_PARAMETER_OP_GET,
parameters_string_p,
&(async_response_p->xid),
parser_p->call_backs.arg_p)!= utlSUCCESS) {
utlError(utlExecuteExtendedAtCommand30, "PROXY_DEBUG - GET - send to proxy _function_p failed for cmd %s with param %s !\n", command_name_p,parameters_string_p );
/*--- clean ---*/
utlAbandonPendingAsyncResponse(async_response_p);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
else{
//no need for proxy
if ((command_p->call_backs.get_parameter_function_p)(utlAT_PARAMETER_OP_GET,
command_name_p,
parameter_values_p,
command_p->num_parameters,
info_text_p,
&(async_response_p->xid),
parser_p->call_backs.arg_p) != utlSUCCESS)
{
/*--- clean ---*/
utlError(utlExecuteExtendedAtCommand31, "get_parameter_function_p failed!\n");
utlAbandonPendingAsyncResponse(async_response_p);
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
}
VDBGMSG(utlExecuteExtendedAtCommand32, "PROXY_DEBUG AT PARSER-%s,%d- value handle - returned xid for cmd %d \n ",
__FUNCTION__, __LINE__, async_response_p->xid);
}
else
{
parser_p->peg_counts.bad_commands++;
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
rc = utlAT_RESULT_CODE_SUPPRESS;
}
break;
default:
utlError(utlExecuteExtendedAtCommand33, "Invalid request_type: %d!\n", request_type);
break;
}
parser_p->peg_counts.extended_commands++;
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlExecuteExtendedAtCommand34, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
rc = utlAT_RESULT_CODE_ERROR;
goto out_error;
}
if (rc == utlAT_RESULT_CODE_OK)
parser_p->commands_in_line--;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
return rc;
out_error:
atmcd_mult_unlock(parser_p, command_p, request_type);
out:
return rc;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlAddDialString(parser_p, dest_p, dest_size, dial_string_p)
* INPUT
* parser_p == an open AT-command parser
* dest_p == where to start adding `dial_string_p'
* dest_size == maximum number of character to write to `dest_p'
* dial_string_p == the dial-string to add
* OUTPUT
* none
* RETURNS
* the number of characters appended for success, otherwise utlFAILED
* DESCRIPTION
* Add `dial_string_p' to the current dial string at position `dest_p',
* converting any embedded alphabetical-digits to numeric digits, and
* noting any embedded dial-string traits.
*---------------------------------------------------------------------------*/
static size_t utlAddDialString(const utlAtParser_P parser_p,
char *dest_p,
const size_t dest_size,
const char *dial_string_p)
{
bool alphabetical_digits;
char *start_dest_p;
char *term_dest_p;
utlAssert(parser_p != NULL);
utlAssert(dest_p != NULL);
utlAssert(dest_size > (size_t)0);
utlAssert(dial_string_p != NULL);
start_dest_p = dest_p;
term_dest_p = dest_p + dest_size;
alphabetical_digits = false;
for (; *dial_string_p != '\0'; dial_string_p++)
{
/*--- are we expecting alphabetical dial-string digits? ---*/
if (alphabetical_digits == true)
{
/*--- map alphabetical dial-string digit to an actual number ---*/
switch (*dial_string_p)
{
case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': *dest_p++ = '2'; break;
case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': *dest_p++ = '3'; break;
case 'g': case 'G': case 'h': case 'H': case 'i': case 'I': *dest_p++ = '4'; break;
case 'j': case 'J': case 'k': case 'K': case 'l': case 'L': *dest_p++ = '5'; break;
case 'm': case 'M': case 'n': case 'N': case 'o': case 'O': *dest_p++ = '6'; break;
case 'p': case 'P': case 'q': case 'Q': case 'r': case 'R': case 's': case 'S': *dest_p++ = '7'; break;
case 't': case 'T': case 'u': case 'U': case 'v': case 'V': *dest_p++ = '8'; break;
case 'w': case 'W': case 'x': case 'X': case 'y': case 'Y': case 'z': case 'Z': *dest_p++ = '9'; break;
case ' ':
break;
default:
alphabetical_digits = false;
break;
}
if (dest_p >= term_dest_p)
{
utlError(utlAddDialString, "Dial-string buffer overflow.");
return utlFAILED;
}
}
if (alphabetical_digits == false)
{
if (*dial_string_p == '"')
alphabetical_digits = true;
else
{
*dest_p++ = *dial_string_p;
if (dest_p >= term_dest_p)
{
utlError(utlAddDialString1, "Dial-string buffer overflow.");
return utlFAILED;
}
switch (*dial_string_p)
{
case '/': parser_p->states.dial_string.dial_string_delay++; break;
case '+': parser_p->states.dial_string.options.international = true; break;
case 'g': parser_p->states.dial_string.options.use_CCUG_SS_info = true; break;
case 'G': parser_p->states.dial_string.options.use_CCUG_SS_info = true; break;
case 'i': parser_p->states.dial_string.options.CLI_presentation = 'i'; break;
case 'I': parser_p->states.dial_string.options.CLI_presentation = 'I'; break;
default: break;
}
}
}
}
return dest_p - start_dest_p;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlParseATZ(parser_p, string_p, string_off)
* INPUT
* parser_p == an open AT-command parser
* string_p == the text characters to be parsed after the name "AT&Z"
* string_off == parmater offset (for error reporting)
* parse_len == length of string that has been parsed
* OUTPUT
* none
* RETURNS
* utlAT_RESULT_CODE_ERROR for failure, other values for success
* DESCRIPTION
* Parse the AT&Z command
*---------------------------------------------------------------------------*/
static utlAtResultCode_T utlParseATZ(const utlAtParser_P parser_p,
const char *string_p,
const size_t string_off,
int *parse_len)
{
utlAtParameterValue_T parameter_value;
unsigned int location;
size_t location_off;
const char *c_p = string_p;
utlAtResultCode_T rc = utlAT_RESULT_CODE_OK;
/*--- The following forms are accepted:
&Z<location>=<dial-string>
&Z<location>=L (save last number dialed to <location>)
&Z=<dial-string> (location defaults to 0)
&Z=L (location defaults to 0)
&Z<location>?
&ZL? (print last number dialed)
&Z? (location defaults to 0)
Note that due to the wide variety of characters that can legally
appear in dial strings (thus making it impossible, in general, to
determine where one &Z command ends and the next begins), the &Z
command must be the very last AT-command on the line. */
location = 0;
location_off = string_off;
/*--- extract (if present) location to save to (all digits following '&Z') ---*/
while ((*c_p >= '0') &&
(*c_p <= '9'))
{
location = (10 * location) + (*c_p - '0');
/*--- advance to next digit, skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
if (*c_p == '\0')
{
utlError(utlParseATZ, "Incomplete AT-command at col=%d.", string_off + c_p - string_p + 1);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
}
/*--- set default parameter value ---*/
utlSetDefaultParameterValue(&parameter_value, utlAT_DATA_TYPE_DIAL_STRING, utlAT_PARAMETER_ACCESS_READ_WRITE);
/*--- have we possibly been asked for the last-dialed dial string? ---*/
if ((*c_p == 'l') || (*c_p == 'L'))
{
/*--- advance to character following 'L', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
/*--- if we have been asked for the last-dialed dial string ---*/
if (*c_p == '?')
{
/*--- advance to character following '?', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
/*--- reply with last-dialed dial string ---*/
{
if (parser_p->states.dial_string.last[0] != '\0')
{
parameter_value.is_default = false;
utlTrace(utlTRACE_AT_PARSER,
utlPrintTrace(utlParseATZ1, "AT&ZL (get: %s)\n", parser_p->states.dial_string.last);
);
}
else
{
utlTrace(utlTRACE_AT_PARSER,
utlPrintTrace(utlParseATZ2, "AT&ZL (get)\n", location);
);
}
if (utlSendInfoResponse(parser_p, parser_p->states.dial_string.last) != utlSUCCESS)
return utlAT_RESULT_CODE_ERROR;
parameter_value.value.dial_string_p = parser_p->states.dial_string.last;
}
parser_p->peg_counts.basic_commands++;
}
else
{
utlError(utlParseATZ3, "Incomplete AT-command at col=%d.", string_off + c_p - string_p + 1);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
/*--- have we been asked for the current value? ---*/
}
else if (*c_p == '?')
{
/*--- advance to character following '?', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
/*--- reply with current value ---*/
{
const char *dial_string_p;
dial_string_p = NULL;
/*--- retrieve previously saved dial string ---*/
if (parser_p->call_backs.retrieve_dial_string_function_p != NULL)
{
char location_name[20];
const char *location_name_p;
location_name_p = location_name;
if ((utlDecimalToString(location_name, location, utlNumberOf(location_name)) == (size_t)0) ||
((parser_p->call_backs.retrieve_dial_string_function_p)(&location_name_p, &dial_string_p, parser_p->call_backs.arg_p) != utlSUCCESS))
{
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
}
if (dial_string_p != NULL)
{
parameter_value.is_default = false;
utlTrace(utlTRACE_AT_PARSER,
utlPrintTrace(utlParseATZ4, "AT&Z%d (get: %s)\n", location, dial_string_p);
);
if (utlSendInfoResponse(parser_p, dial_string_p) != utlSUCCESS)
return utlAT_RESULT_CODE_ERROR;
}
else
{
dial_string_p = "";
utlTrace(utlTRACE_AT_PARSER,
utlPrintTrace(utlParseATZ5, "AT&Z%d (get)\n", location);
);
}
parameter_value.value.dial_string_p = (char *)dial_string_p;
}
parser_p->peg_counts.basic_commands++;
/*--- have we been asked to save a new dial-string? ---*/
}
else if (*c_p == '=')
{
char dial_string[utlAT_MAX_LINE_LENGTH];
char *term_dial_string_p;
char *dial_string_p;
bool alphabetical_digits;
/*--- advance to character following '=', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
/*--- extract dial string ---*/
alphabetical_digits = false;
term_dial_string_p = dial_string + utlNumberOf(dial_string) - 1;
for (dial_string_p = dial_string; *c_p != '\0'; c_p++)
{
/*--- are we expecting alphabetical dial-string digits? ---*/
if (alphabetical_digits == true)
{
if (((*c_p >= 'a') || (*c_p <= 'z')) ||
((*c_p >= 'A') || (*c_p <= 'Z')))
{
*dial_string_p++ = *c_p;
if (dial_string_p >= term_dial_string_p)
{
utlError(utlParseATZ6, "Dial-string buffer overflow.");
return utlAT_RESULT_CODE_ERROR;
}
}
else if (*c_p != ' ')
alphabetical_digits = false;
}
if (alphabetical_digits == false)
{
/*--- insert last dialed dial-string? ---*/
if ((*c_p == 'l') || (*c_p == 'L'))
{
char *last_p;
/*--- insert last dialed dial-string ---*/
for (last_p = parser_p->states.dial_string.last; *last_p != '\0'; last_p++)
{
*dial_string_p++ = *last_p;
if (dial_string_p >= term_dial_string_p)
{
utlError(utlParseATZ7, "Dial-string buffer overflow.");
return utlAT_RESULT_CODE_ERROR;
}
}
/*--- else save recognized dial-string characters ---*/
}
else if (strchr(utlAT_DIALING_CHARACTERS, *c_p) != NULL)
{
/*--- are zero or more alphabetical dial-string digits to follow? ---*/
if (*c_p == '"')
alphabetical_digits = true;
*dial_string_p++ = *c_p;
if (dial_string_p >= term_dial_string_p)
{
utlError(utlParseATZ8, "Dial-string buffer overflow.");
return utlAT_RESULT_CODE_ERROR;
}
}
}
}
*dial_string_p = '\0';
utlTrace(utlTRACE_AT_PARSER,
utlPrintTrace(utlParseATZ9, "AT&Z%d (set: %s)\n", location, dial_string);
);
/*--- invoke save-dial-string call-back (if any) ---*/
if (parser_p->call_backs.save_dial_string_function_p != NULL)
{
char location_name[20]; /* plenty big for a 64-bit unsigned integer */
if ((utlDecimalToString(location_name, location, utlNumberOf(location_name)) == (size_t)0) ||
((parser_p->call_backs.save_dial_string_function_p)( location_name, dial_string, parser_p->call_backs.arg_p) != utlSUCCESS))
{
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
}
parameter_value.value.dial_string_p = dial_string;
parameter_value.is_default = false;
parser_p->peg_counts.basic_commands++;
}
else
{
utlError(utlParseATZ10, "Syntax error at col=%d.", string_off + c_p - string_p + 1);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
*parse_len = c_p - string_p;
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlParseATZ11, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlAT_RESULT_CODE_ERROR;
}
parser_p->commands_in_line--;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
return rc;
/*--- extract decimal parameter (if any) ---*/
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlParseATD(parser_p, string_p, string_off)
* INPUT
* parser_p == an open AT-command parser
* string_p == the text characters to be parsed after the name "ATD"
* string_off == parmater offset (for error reporting)
* parseATD == length of string that has been parsed
* OUTPUT
* none
* RETURNS
* utlAT_RESULT_CODE_ERROR for failure, other values for success
* DESCRIPTION
* Parse the ATD command
*---------------------------------------------------------------------------*/
static utlAtResultCode_T utlParseATD(const utlAtParser_P parser_p,
const char *string_p,
const size_t string_off,
int *parse_len)
{
char command_name[] = "D";
bool parameter_found = false;
char *term_dial_string_p;
char *dial_string_p;
bool alphabetical_digits;
const char * c_p = string_p;
utlAtParameterValue_T parameter_value;
utlAtResultCode_T rc;
/*--- Notes on dial string processing:
- The end of the dial-string (and thus the `D' AT-command) is
demarked by either a semicolon character or by the end of the line.
- If not immediately preceded by `>', `"' causes subsequent characters
to be interpreted as alphabetic dial-string characters (which we
translate into digits here).
- If immediately preceded by `>', `"' delimits the start of a
dial-string-store location-name. A second subsequenct `"' character
delimits the end of the dial-string-store location-name.
- 'L' is replaced by the last number dialed.
- 'S' is replaced by the stored dial-string associated with location 0.
- `S-<id>' is replaced by the stored dial-string associated with the specified location.
- Each '/' character delays initial dialing by an additional 0.125 seconds.
- A trailing ';' sets the do-call-origination flag to false.
- 'G' sets the use-CCUG-SS-info option to true.
- 'i' sets the CLI-presentation option to "allow".
- 'I' sets the CLI-presentation option to "restrict".
- '+' sets the international option to true. */
parser_p->states.dial_string.active = false;
parser_p->states.dial_string.dial_string_delay = 0; /* no delay */
parser_p->states.dial_string.c_p = parser_p->states.dial_string.buf; /* reset */
parser_p->states.dial_string.options.international = false;
parser_p->states.dial_string.options.do_call_origination = true;
parser_p->states.dial_string.options.use_CCUG_SS_info = false;
parser_p->states.dial_string.options.CLI_presentation = '\0';
alphabetical_digits = false;
term_dial_string_p = parser_p->states.dial_string.buf + utlAT_MAX_DIAL_STRING_LENGTH;
for (dial_string_p = parser_p->states.dial_string.buf; *c_p != '\0'; c_p++)
{
/*--- are we expecting alphabetical dial-string digits? ---*/
if (alphabetical_digits == true)
{
/*--- map alphabetical dial-string digit to an actual number ---*/
switch (*c_p)
{
case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': *dial_string_p++ = '2'; break;
case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': *dial_string_p++ = '3'; break;
case 'g': case 'G': case 'h': case 'H': case 'i': case 'I': *dial_string_p++ = '4'; break;
case 'j': case 'J': case 'k': case 'K': case 'l': case 'L': *dial_string_p++ = '5'; break;
case 'm': case 'M': case 'n': case 'N': case 'o': case 'O': *dial_string_p++ = '6'; break;
case 'p': case 'P': case 'q': case 'Q': case 'r': case 'R': case 's': case 'S': *dial_string_p++ = '7'; break;
case 't': case 'T': case 'u': case 'U': case 'v': case 'V': *dial_string_p++ = '8'; break;
case 'w': case 'W': case 'x': case 'X': case 'y': case 'Y': case 'z': case 'Z': *dial_string_p++ = '9'; break;
case ' ':
break;
default:
alphabetical_digits = false;
break;
}
if (dial_string_p >= term_dial_string_p)
{
utlError(utlParseATD, "Dial-string buffer overflow.");
return utlAT_RESULT_CODE_ERROR;
}
}
if (alphabetical_digits == false)
{
/*--- process recognized dial-string characters ---*/
if (strchr(utlAT_DIALING_CHARACTERS, *c_p) != NULL)
{
/*--- are zero or more alphabetical dial-string digits to follow? ---*/
if (*c_p == '"')
{
alphabetical_digits = true;
/*--- else insert last dialed dial-string? ---*/
}
else if ((*c_p == 'l') || (*c_p == 'L'))
{
size_t rv;
/*--- insert it... ---*/
if ((rv = utlAddDialString(parser_p, dial_string_p, term_dial_string_p - dial_string_p, parser_p->states.dial_string.last)) == (size_t)utlFAILED)
return utlAT_RESULT_CODE_ERROR;
dial_string_p += rv;
/*--- else this a reference to a stored dial-string? (syntax: S or S-<id>) ---*/
}
else if ((*c_p == 's') || (*c_p == 'S'))
{
unsigned int location;
alphabetical_digits = false;
/*--- advance to character following 'S', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
location = 0; /* default location */
/*--- found a location lead-in character? ---*/
if (*c_p == '-')
{
/*--- advance to character following '-', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
/*--- extract location of stored dial-string to insert ---*/
while ((*c_p >= '0') &&
(*c_p <= '9'))
{
location = (10 * location) + (*c_p - '0');
/*--- advance to next digit, skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
}
c_p--;
}
else
c_p--;
/*--- retrieve and insert dial-string... ---*/
{
const char *retrieved_p;
retrieved_p = NULL;
/*--- retrieve previously (hopefully) saved dial string ---*/
if (parser_p->call_backs.retrieve_dial_string_function_p != NULL)
{
char location_name[20];
const char *location_name_p;
location_name_p = location_name;
if ((utlDecimalToString(location_name, location, utlNumberOf(location_name)) == (size_t)0) ||
((parser_p->call_backs.retrieve_dial_string_function_p)(&location_name_p, &retrieved_p, parser_p->call_backs.arg_p) != utlSUCCESS))
{
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
}
/*--- insert dial string (if any)... ---*/
if (retrieved_p != NULL)
{
size_t rv;
if ((rv = utlAddDialString(parser_p, dial_string_p, term_dial_string_p - dial_string_p, retrieved_p)) == (size_t)utlFAILED)
return utlAT_RESULT_CODE_ERROR;
dial_string_p += rv;
}
}
/*--- else this a 3G phone-book dial-string reference? ---*/
}
else if (*c_p == '>')
{
char location_name[utlAT_MAX_STORED_LOCATION_NAME_LENGTH + 1];
char *term_location_name_p;
char *location_name_p;
enum {
utl3G_LOCATION_NAME_TYPE_NUMBER,
utl3G_LOCATION_NAME_TYPE_MEMORY_TEXT,
utl3G_LOCATION_NAME_TYPE_MEMORY_NUMBER,
utl3G_LOCATION_NAME_TYPE_STRING_START,
utl3G_LOCATION_NAME_TYPE_STRING_END
} location_name_type;
/*--- The following forms of dial-string storage references are accepted:
ATD><number> (eg: ATD>55)
ATD><mem><number> (eg: ATD>usim89)
ATD>"<string>" (eg: ATD>"fred")
Note that dial-string storage entries are indexed by
what ever text follows the "ATD>" command. This means
that dial-string storage entries that are indexed by
strings must include the double-quotes in the dial-string
storage entry. You can store phone numbers under strings
as follows (note the use of hex escapes to enter the
needed double-quotes): AT+ASTO="\22fred\22",123-4567 */
alphabetical_digits = false;
/*--- advance to character following '>', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
/*--- identify type of location_name ---*/
if (*c_p == '"') location_name_type = utl3G_LOCATION_NAME_TYPE_STRING_START;
else if (isdigit(*c_p)) location_name_type = utl3G_LOCATION_NAME_TYPE_NUMBER;
else if (isalpha(*c_p)) location_name_type = utl3G_LOCATION_NAME_TYPE_MEMORY_TEXT;
else
{
utlError(utlParseATD1, "Dial-string syntax error.");
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
/*--- extract phone-book location name ---*/
term_location_name_p = location_name + utlAT_MAX_STORED_LOCATION_NAME_LENGTH;
for (location_name_p = location_name; *c_p != '\0'; )
{
bool found_end;
found_end = false;
switch (location_name_type)
{
case utl3G_LOCATION_NAME_TYPE_NUMBER:
if (!isdigit(*c_p))
found_end = true;
break;
case utl3G_LOCATION_NAME_TYPE_MEMORY_TEXT:
if (!isalpha(*c_p))
{
if (!isdigit(*c_p))
found_end = true;
else
location_name_type = utl3G_LOCATION_NAME_TYPE_MEMORY_NUMBER;
}
break;
case utl3G_LOCATION_NAME_TYPE_MEMORY_NUMBER:
if (!isdigit(*c_p))
found_end = true;
break;
case utl3G_LOCATION_NAME_TYPE_STRING_START:
location_name_type = utl3G_LOCATION_NAME_TYPE_STRING_END;
break;
case utl3G_LOCATION_NAME_TYPE_STRING_END:
if (*c_p == '"')
{
found_end = true;
*location_name_p++ = *c_p++;
if (location_name_p >= term_location_name_p)
{
utlError(utlParseATD2, "Dial-string buffer overflow.");
return utlAT_RESULT_CODE_ERROR;
}
}
break;
default:
utlError(utlParseATD3, "Invalid location_name_type: %d\n", location_name_type);
break;
}
/*--- we reach the end of the location name? ---*/
if ((found_end == true) || (*c_p == parser_p->parameters.S[utlAT_TERMINATER_CHAR]))
break;
*location_name_p++ = *c_p++;
if (location_name_p >= term_location_name_p)
{
utlError(utlParseATD4, "Dial-string buffer overflow.");
return utlAT_RESULT_CODE_ERROR;
}
}
c_p--;
*location_name_p = '\0';
/*--- retrieve and insert dial-string... ---*/
{
const char *retrieved_p;
retrieved_p = NULL;
/*--- retrieve previously (hopefully) saved dial string ---*/
if (parser_p->call_backs.retrieve_dial_string_function_p != NULL)
{
location_name_p = location_name;
if ((parser_p->call_backs.retrieve_dial_string_function_p)((const char **)&location_name_p, &retrieved_p, parser_p->call_backs.arg_p) != utlSUCCESS)
{
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
}
/*--- insert dial string (if any)... ---*/
if (retrieved_p != NULL)
{
size_t rv;
if ((rv = utlAddDialString(parser_p, dial_string_p, term_dial_string_p - dial_string_p, retrieved_p)) == (size_t)utlFAILED)
return utlAT_RESULT_CODE_ERROR;
dial_string_p += rv;
}
}
/*--- insert regular dial-string digit ---*/
}
else
{
*dial_string_p++ = *c_p;
if (dial_string_p >= term_dial_string_p)
{
utlError(utlParseATD5, "Dial-string buffer overflow.");
return utlAT_RESULT_CODE_ERROR;
}
}
switch (*c_p)
{
case '/': parser_p->states.dial_string.dial_string_delay++; break;
case '+': parser_p->states.dial_string.options.international = true; break;
case 'g': parser_p->states.dial_string.options.use_CCUG_SS_info = true; break;
case 'G': parser_p->states.dial_string.options.use_CCUG_SS_info = true; break;
case 'i': parser_p->states.dial_string.options.CLI_presentation = 'i'; break;
case 'I': parser_p->states.dial_string.options.CLI_presentation = 'I'; break;
default: break;
}
}
else
{
/*--- ignore illegal dial-string characters ---*/
}
/*--- found end of dialing command? ---*/
if (*c_p == parser_p->parameters.S[utlAT_TERMINATER_CHAR])
{
*dial_string_p++ = *c_p++; /* save & skip semicolon character */
if (dial_string_p >= term_dial_string_p)
{
utlError(utlParseATD6, "Dial-string buffer overflow.");
return utlAT_RESULT_CODE_ERROR;
}
parser_p->states.dial_string.options.do_call_origination = false;
break;
}
}
}
*dial_string_p = '\0';
/*--- is dial-string empty? ---*/
if (strlen(parser_p->states.dial_string.buf) == (size_t)0)
{
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
parser_p->states.dial_string.active = true;
/*--- update "last dialed" dial string ---*/
strncpy(parser_p->states.dial_string.last, parser_p->states.dial_string.buf, utlAT_MAX_DIAL_STRING_LENGTH);
parser_p->states.dial_string.last[utlAT_MAX_DIAL_STRING_LENGTH] = '\0';
/*--- execute basic AT-command ---*/
{
utlSetDefaultParameterValue(&parameter_value, utlAT_DATA_TYPE_DIAL_STRING, utlAT_PARAMETER_ACCESS_READ_WRITE);
parameter_value.is_default = false;
parameter_value.value.dial_string_p = parser_p->states.dial_string.buf;
if ((rc = utlExecuteBasicAtCommand(parser_p, command_name, parameter_found, &parameter_value, string_off)) == utlAT_RESULT_CODE_ERROR)
return utlAT_RESULT_CODE_ERROR;
}
/*--- for ATD, we'll defer issuing a result code until we know the status of
the request, which will require traversal of the AT-parser state machine */
*parse_len = c_p - string_p;
return utlAT_RESULT_CODE_SUPPRESS;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlGetDefaultSParameterValue(int parameter_num)
* INPUT
* parameter_num == S parameter index
* OUTPUT
* RETURNS
* default value of given S parameter
* DESCRIPTION
* get default value of given S parameter
*---------------------------------------------------------------------------*/
static unsigned short utlGetDefaultSParameterValue(int parameter_num)
{
unsigned short val = 0;
switch(parameter_num)
{
case utlAT_AUTO_ANSWER: val = 0; break; /* disable auto-answer */
case utlAT_RING_COUNTER: val = 0; break; /* cleared */
case utlAT_ESCAPE_CHAR: val = '+'; break;
case utlAT_LINE_TERM_CHAR: val = '\015'; break; /* <carriage-val => */
case utlAT_FORMATTING_CHAR: val = '\012'; break; /* <new-line> */
case utlAT_LINE_EDIT_CHAR: val = '\010'; break; /* <back-space> */
case utlAT_BLIND_DIAL_PAUSE_TIME: val = 2; break; /* 2 seconds */
case utlAT_CONN_COMPLETE_TIMEOUT: val = 1; break; /* 1 second */
case utlAT_DIALING_PAUSE_TIME: val = 2; break; /* 2 seconds */
case utlAT_CARRIER_DETECT_TIME: val = 6; break; /* 0.6 seconds */
case utlAT_CARRIER_LOSS_TIME: val = 7; break; /* 0.7 seconds */
case utlAT_DTMF_TONE_DURATION: val = 63; break; /* 0.63 seconds */
case utlAT_ESCAPE_GUARD_TIME: val = 50; break; /* 1 second */
case utlAT_SEPARATER_CHAR: val = ','; break;
case utlAT_TERMINATER_CHAR: val = ';'; break;
case utlAT_XON_CHAR: val = 0x11; break; /* <DC1> */
case utlAT_XOFF_CHAR: val = 0x13; break; /* <DC3> */
case utlAT_SLEEP_TIME: val = 0; break; /* 0 seconds */
case utlAT_DTR_DELAY_TIME: val = 10; break; /* 0.1 seconds */
case utlAT_HOOK_FLASH_TIME: val = 5; break; /* 0.5 seconds */
case utlAT_INACTIVITY_TIME: val = 10; break; /* 100 seconds */
case utlAT_DISCONNECT_WAIT_TIME: val = 0; break; /* 0 seconds */
default: val = 0; break;
}
return val;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlParseATS(parser_p, string_p, string_off)
* INPUT
* parser_p == an open AT-command parser
* string_p == the text characters to be parsed after "ATS"
* string_off == parmater offset (for error reporting)
* parse_len == length of string that has been parsed
* OUTPUT
* none
* RETURNS
* utlAT_RESULT_CODE_ERROR for failure, other values for success
* DESCRIPTION
* Parse the ATS command
*---------------------------------------------------------------------------*/
static utlAtResultCode_T utlParseATS(const utlAtParser_P parser_p,
const char *string_p,
const size_t string_off,
int *parse_len)
{
unsigned int parameter_num;
size_t parameter_num_off;
const char *c_p = string_p;
/*--- The following forms are accepted:
S<parameter-num>=<value>
S=<value> (parameter-num defaults to 0)
S<parameter-num>?
S? (parameter-num defaults to 0)
Note that we support having multiple "S" AT-commands on
the same line. */
parameter_num = 0;
parameter_num_off = string_off; // c_p - string_p + 1;
/*--- extract (if present) S-parameter number (all digits following 'S') ---*/
while ((*c_p >= '0') &&
(*c_p <= '9'))
{
parameter_num = (10 * parameter_num) + (*c_p - '0');
/*--- advance to next digit, skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
if (*c_p == '\0')
{
utlError(utlParseATS, "Incomplete S-parameter at col=%d.", string_off + c_p - string_p + 1);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
}
/*--- check sanity of found S-parameter number ---*/
if (parameter_num >= utlNumberOf(parser_p->parameters.S))
{
utlError(utlParseATS1, "Invalid S-parameter number %d at col=%d.", parameter_num, string_off + parameter_num_off);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
/*--- have we been asked for the S-parameter's current value? ---*/
if (*c_p == '?')
{
/*--- advance to character following '?', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
if((parameter_num == utlAT_AUTO_ANSWER) && (parser_p->call_backs.getAutoAnswerDelay_function_p != NULL))
{
parser_p->call_backs.getAutoAnswerDelay_function_p(parser_p->call_backs.arg_p, &(parser_p->parameters.S[parameter_num]));
}
utlTrace(utlTRACE_AT_PARSER,
utlPrintTrace(utlParseATS2, "ATS%d (get: %d)\n", parameter_num, parser_p->parameters.S[parameter_num]);
);
/*--- reply with S-parameter's current value (a 3-digit decimal value) ---*/
{
char buf[10];
size_t i;
size_t rv;
i = 0;
/*--- pad value with leading zeros (as necessary) ---*/
if (parser_p->parameters.S[parameter_num] < 100) buf[i++] = '0';
if (parser_p->parameters.S[parameter_num] < 10) buf[i++] = '0';
if ((rv = utlDecimalToString(buf + i, parser_p->parameters.S[parameter_num], utlNumberOf(buf) - i)) == (size_t)0)
{
utlError(utlParseATS3, "Value-buffer overflow.");
return utlAT_RESULT_CODE_ERROR;
}
i += rv;
buf[i] = '\0';
if (utlSendInfoResponse(parser_p, buf) != utlSUCCESS)
return utlAT_RESULT_CODE_ERROR;
}
parser_p->peg_counts.basic_commands++;
/*--- have we been asked to set S-parameter to a new value? ---*/
}
else if (*c_p == '=')
{
unsigned int parameter_value;
size_t parameter_value_off;
/*--- advance to character following '=', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
parameter_value = 0;
parameter_value_off = c_p - string_p + 1;
/*--- was no S-parameter value specified? ---*/
if (((*c_p < '0') ||
(*c_p > '9')) && ((parser_p->options.allow_default_S_parameter_values == false) ||
((*c_p != parser_p->parameters.S[utlAT_TERMINATER_CHAR]) &&
(*c_p != parser_p->parameters.S[utlAT_LINE_TERM_CHAR]) && (*c_p != 0))))
{
utlError(utlParseATS4, "S-parameter syntax error at col=%d.", string_off + c_p - string_p + 1);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
/* if allow_default_S_parameter_values is set to true, we need to set default
* value for parameter_value */
if(((*c_p < '0') || (*c_p >'9')) && (parser_p->options.allow_default_S_parameter_values == true))
{
parameter_value = utlGetDefaultSParameterValue(parameter_num);
}
/*--- extract S-parameter's new (numeric) value ---*/
while ((*c_p >= '0') &&
(*c_p <= '9'))
{
parameter_value = (10 * parameter_value) + (*c_p - '0');
if (*++c_p == '\0')
break;
}
utlTrace(utlTRACE_AT_PARSER,
utlPrintTrace(utlParseATS5, "ATS%d (set: %d)\n", parameter_num, parameter_value);
);
/*--- process new S-parameter value ---*/
if (utlProcessSParameter(parser_p, parameter_num, parameter_value) != utlSUCCESS)
{
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
#if 0
if (parameter_num == utlAT_AUTO_ANSWER)
{
char command_name[8]="S0";
size_t command_name_off;
bool parameter_found = true;
utlAtParameterValue_T S0parameter_value;
utlSetDefaultParameterValue(&S0parameter_value, utlAT_DATA_TYPE_DECIMAL, utlAT_PARAMETER_ACCESS_READ_WRITE);
S0parameter_value.value.decimal = parameter_value;
S0parameter_value.is_default = !parameter_found;
sprintf(parser_p->basic_command_param, "=%d\r", parameter_value);
/*--- execute basic AT-command ---*/
utlError(utlParseATSS0, "%s: ATS0 will execute: value=%d!\n", __FUNCTION__, parameter_value);
if ((utlExecuteBasicAtCommand(parser_p, command_name, parameter_found, &S0parameter_value, 0)) == utlAT_RESULT_CODE_ERROR)
{
utlError(utlParseATSS1, "%s: ATS0 execute failure!\n", __FUNCTION__);
} else {
utlError(utlParseATSS2, "%s: ATS0 execute successfully!\n", __FUNCTION__);
}
}
#endif
/*--- notify application of S-parameter value change ---*/
if ( parser_p->call_backs.s_parameter_function_p != NULL)
{
if ((parser_p->call_backs.s_parameter_function_p)(parameter_num,
parameter_value,
parser_p->call_backs.arg_p) != utlSUCCESS)
{
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
}
/*--- save new S-parameter value ---*/
parser_p->parameters.S[parameter_num] = parameter_value;
parser_p->peg_counts.basic_commands++;
}
else
{
utlError(utlParseATS6, "S-parameter syntax error at col=%d.", string_off + c_p - string_p + 1);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
*parse_len = c_p - string_p;
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlParseATS7, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlAT_RESULT_CODE_ERROR;
}
parser_p->commands_in_line--;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
return utlAT_RESULT_CODE_OK;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlParseLine(parser_p, string_p, string_off)
* INPUT
* parser_p == an open AT-command parser
* string_p == the text characters to be parsed
* string_off == offset from start of line to `string_p' (for error
* reporting)
* OUTPUT
* none
* RETURNS
* utlAT_RESULT_CODE_ERROR for failure, other values for success
* DESCRIPTION
* Parses a line of text, searching for an AT command to execute, and
* executing it when found. Note that as-per the standard, only the first
* collection of AT commands on each line is parsed and executed.
*---------------------------------------------------------------------------*/
static utlAtResultCode_T utlParseLine(const utlAtParser_P parser_p,
char *input_string_p,
const size_t string_off)
{
const char *c_p;
utlAtResultCode_T rc;
char *string_p = input_string_p;
utlAssert(parser_p != NULL);
utlAssert(string_p != NULL);
ERRMSG(utlParseLine1, "[%s] %s\n", __FUNCTION__, string_p);
rc = utlAT_RESULT_CODE_OK;
parser_p->call_backs.checkSmsPara_function_p(*(int *)(parser_p->call_backs.arg_p), input_string_p);
parser_p->call_backs.convert_atcmd_str_p(*(int *)(parser_p->call_backs.arg_p), input_string_p, &string_p);
/*--- scan for AT prefix ---*/
{
char last_c;
last_c = '\0';
for (c_p = string_p; *c_p != '\0'; c_p++)
{
/*--- ignore space characters ---*/
if (*c_p == ' ')
continue;
/*--- found an AT prefix? ---*/
if (((last_c == 'A') && (*c_p == 'T')) ||
((last_c == 'a') && (*c_p == 't')))
break;
last_c = *c_p;
}
/*--- no AT prefix found? ---*/
if (*c_p == '\0')
return utlAT_RESULT_CODE_SUPPRESS;
}
/*--- advance to character following AT prefix, skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
/* Special process for the command "AT", we will delay the "OK" response until all the services have been registered */
if (*c_p == '\0')
{
utlAtParameterValue_T parameter_value;
utlSetDefaultParameterValue(&parameter_value, utlAT_DATA_TYPE_DIAL_STRING, utlAT_PARAMETER_ACCESS_READ_WRITE);
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlParseLine, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlAT_RESULT_CODE_ERROR;
}
parser_p->commands_in_line++;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
if ((rc = utlExecuteBasicAtCommand(parser_p, " ", false, &parameter_value, string_off)) == utlAT_RESULT_CODE_ERROR)
return utlAT_RESULT_CODE_ERROR;
return rc;
}
/* Count how many AT commands in one line*/
{
const char *temp = c_p;
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlParseLine1, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlAT_RESULT_CODE_ERROR;
}
#ifndef AT_CMD_ASYNC_MODE
parser_p->commands_in_line = 0; //async mode commands_in_line can't initialize as 0
#endif
while (*temp != '\0')
{
bool quoted = false;
parser_p->commands_in_line++;
/*--- skip to next AT command ---*/
for (; *temp != '\0'; temp++)
{
if(*temp == '"')
quoted = !quoted;
if (*temp == parser_p->parameters.S[utlAT_TERMINATER_CHAR] && !quoted)
{
temp++; /* skip terminator char */
break;
}
}
/*--- skip white space ---*/
while (*temp == ' ')
temp++;
}
utlPrintTrace(utlParseLine2, "%s: parser_p->commands_in_line:%d\n", __FUNCTION__, parser_p->commands_in_line);
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
}
/*--- extract and process each AT-command following the AT prefix... ---*/
while (*c_p != '\0')
{
/*--- if this is an "extended" AT command ---*/
if (strchr(utlAT_EXTENDED_COMMAND_PREFIXES, *c_p) != NULL)
{
char prefix_char;
char command_name[utlAT_MAX_COMMAND_LENGTH_WITH_PREFIX]; /* prefix character + 16 characters + null */
size_t command_name_off;
prefix_char = *c_p;
/*--- advance to character following extended command prefix, skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
command_name_off = c_p - string_p + 1;
/*--- character immediately following command prefix must be alphabetic ---*/
if (((*c_p < 'A') || (*c_p > 'Z')) &&
((*c_p < 'a') || (*c_p > 'z')))
{
utlError(utlParseLine3, "Invalid extended-AT command name syntax at col=%d.", string_off + command_name_off);
return utlAT_RESULT_CODE_ERROR;
}
/*--- extract extended command name (can be up to 16 characters long) ---*/
{
size_t i;
command_name[0] = prefix_char;
for (i = 1; *c_p != '\0'; )
{
if (i >= (utlNumberOf(command_name) - 1))
{
utlError(utlParseLine4, "Extended-AT command name too long at col=%d.", string_off + command_name_off);
return utlAT_RESULT_CODE_ERROR;
}
command_name[i++] = *c_p;
do
c_p++;
while (*c_p == ' ');
/*--- only certain characters can appear in command names ---*/
if (((*c_p < 'A') || (*c_p > 'Z')) &&
((*c_p < 'a') || (*c_p > 'z')) &&
((*c_p < '0') || (*c_p > '9')) &&
(*c_p != '!') && (*c_p != '%') &&
(*c_p != '-') && (*c_p != '.') &&
(*c_p != '/') && (*c_p != ':') && (*c_p != '_'))
break;
}
command_name[i++] = '\0';
utlAssert(i <= utlNumberOf(command_name));
}
/*--- process extended AT command... ---*/
{
/*--- have we been asked for the current parameter value(s)? ---*/
if (*c_p == '?')
{
/*--- advance to character following '?', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
/*--- get current parameter value(s) ---*/
if ((rc = utlExecuteExtendedAtCommand(parser_p, utlAT_REQUEST_TYPE_GET, command_name, NULL, string_off + c_p - string_p)) == utlAT_RESULT_CODE_ERROR)
return utlAT_RESULT_CODE_ERROR;
/*--- else have we been asked to set default parameter value(s)? ---*/
}
else if ((*c_p == '\0') || (*c_p == parser_p->parameters.S[utlAT_TERMINATER_CHAR]))
{
/*--- set parameters using default parameter value(s) ---*/
if ((rc = utlExecuteExtendedAtCommand(parser_p, utlAT_REQUEST_TYPE_SET, command_name, NULL, string_off + c_p - string_p)) == utlAT_RESULT_CODE_ERROR)
return utlAT_RESULT_CODE_ERROR;
/*--- else have we been asked to set parameter values or for the command's syntax? ---*/
}
else if (*c_p == '=')
{
/*--- advance to character following '=', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
/*--- have we been asked for the extended AT command's syntax? ---*/
if (*c_p == '?')
{
/*--- advance to character following '?', skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
/*--- reply with command syntax ---*/
if ((rc = utlExecuteExtendedAtCommand(parser_p, utlAT_REQUEST_TYPE_SYNTAX, command_name, NULL, string_off + c_p - string_p)) == utlAT_RESULT_CODE_ERROR)
return utlAT_RESULT_CODE_ERROR;
/*--- else have we been asked to set new parameter value(s)? ---*/
}
else
{
char parameters_string[utlAT_MAX_LINE_LENGTH];
size_t parameters_string_off;
bool quoted = false;
parameters_string_off = c_p - string_p;
/*--- collect AT-command's parameters into a string ---*/
{
char *dest_p;
for (dest_p = parameters_string; *c_p != '\0'; c_p++)
{
if(*c_p == '"')
quoted = !quoted;
if((*c_p == parser_p->parameters.S[utlAT_TERMINATER_CHAR]) && !quoted)
break;
*dest_p++ = *c_p;
}
*dest_p = '\0';
}
/*--- set current parameter value(s) ---*/
if ((rc = utlExecuteExtendedAtCommand(parser_p, utlAT_REQUEST_TYPE_SET, command_name, parameters_string, string_off + parameters_string_off)) == utlAT_RESULT_CODE_ERROR)
return utlAT_RESULT_CODE_ERROR;
}
}
else
{
utlError(utlParseLine5, "Invalid extended-AT command syntax at col=%d.", string_off + c_p - string_p + 1);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
}
/*--- else assume this is a "basic" AT command ---*/
}
else
{
char command_name[3];
size_t command_name_off;
bool parameter_found;
utlAtParameterValue_T parameter_value;
int parse_len = 0;
/*--- extract command name ---*/
{
size_t i;
i = 0;
command_name_off = c_p - string_p + 1;
/*--- this a two-character basic AT command? ---*/
if (strchr(utlAT_BASIC_COMMAND_PREFIXES, *c_p) != NULL)
command_name[i++] = *c_p++;
/*--- skip white space ---*/
while (*c_p == ' ')
c_p++;
/*--- command character must be alphabetic ---*/
if (((*c_p < 'A') || (*c_p > 'Z')) &&
((*c_p < 'a') || (*c_p > 'z')))
{
utlError(utlParseLine6, "AT command syntax error at col=%d.", string_off + command_name_off);
parser_p->peg_counts.bad_commands++;
return utlAT_RESULT_CODE_ERROR;
}
command_name[i++] = *c_p;
command_name[i++] = '\0';
}
/*--- advance to character following command character, skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
parameter_found = false;
/*--- this an "S-parameter" (ATS) command? ---*/
if (strcasecmp(command_name, "S") == 0)
{
rc = utlParseATS(parser_p, c_p, (string_off + c_p - string_p), &parse_len);
if (rc != utlAT_RESULT_CODE_ERROR)
c_p += parse_len + 1;
else
return rc;
/*--- else this a "dial" (ATD) command? ---*/
}
else if (strcasecmp(command_name, "D") == 0)
{
rc = utlParseATD(parser_p, c_p, (string_off + c_p - string_p), &parse_len);
if (rc != utlAT_RESULT_CODE_ERROR)
c_p += parse_len + 1;
else
return rc;
/*--- else this a "save dial string" (AT&Z) command? ---*/
}
else if (strcasecmp(command_name, "&Z") == 0)
{
rc = utlParseATZ(parser_p, c_p, (string_off + c_p - string_p), &parse_len);
if (rc != utlAT_RESULT_CODE_ERROR)
c_p += parse_len + 1;
else
return rc;
}
else
{
utlSetDefaultParameterValue(&parameter_value, utlAT_DATA_TYPE_DECIMAL, utlAT_PARAMETER_ACCESS_READ_WRITE);
/* check the parameter */
if(((*c_p < '0') || (*c_p > '9')) &&
(*c_p != parser_p->parameters.S[utlAT_TERMINATER_CHAR]) &&
(*c_p != parser_p->parameters.S[utlAT_LINE_TERM_CHAR]) &&
(*c_p != 0))
return utlAT_RESULT_CODE_ERROR;
//copy parameter string for storage if the command needs to be proxied
strncpy(parser_p->basic_command_param, c_p, sizeof(parser_p->basic_command_param)-1);
/*--- parameter (if present) must be composed of digits ---*/
while ((*c_p >= '0') &&
(*c_p <= '9'))
{
parameter_value.value.decimal = (10 * parameter_value.value.decimal) + (*c_p - '0');
parameter_found = true;
/*--- advance to next digit, skipping space characters ---*/
do
c_p++;
while (*c_p == ' ');
}
parameter_value.is_default = !parameter_found;
/*--- execute basic AT-command ---*/
if ((rc = utlExecuteBasicAtCommand(parser_p, command_name, parameter_found, &parameter_value, string_off + command_name_off)) == utlAT_RESULT_CODE_ERROR)
return utlAT_RESULT_CODE_ERROR;
}
}
/*--- skip to end of current extended-AT command ---*/
bool quoted = false;
for (; *c_p != '\0'; c_p++)
{
if(*c_p == '"')
quoted = !quoted;
if (*c_p == parser_p->parameters.S[utlAT_TERMINATER_CHAR] && !quoted)
{
c_p++; /* skip terminator char */
break;
}
}
/*--- skip white space ---*/
while (*c_p == ' ')
c_p++;
size_t commandcount = 0;
do
{
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) == utlSUCCESS)
{
commandcount = parser_p->commands_in_line;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
break;
}
} while (1);
if (commandcount > 1)
{
utlError(utlParseLine99, "goto process next command\n");
do
{
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) == utlSUCCESS)
{
if (utlIsListEmpty(parser_p->states.async_responses.pending))
{
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
break;
}
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
}
} while (1);
}
do
{
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) == utlSUCCESS)
{
commandcount = parser_p->commands_in_line;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
break;
}
} while (1);
if (commandcount == 0)
{
//if pre command abort or error,abandon subsequent commands
//or if no cmd in this cmdline
//utlError(utlParseLine100, "pre command abort or error,abandon subsequent commands\n");
break;
}
}
/*--- apply any pending configuration changes ---*/
if (parser_p->states.dce_io_config_pending_mask != (unsigned int)0)
{
if (utlAtDceIoConfigEvent(parser_p) != utlSUCCESS)
return utlAT_RESULT_CODE_ERROR;
}
if (parser_p->states.sound_config_pending_mask != (unsigned int)0)
{
if (utlAtSoundConfigEvent(parser_p) != utlSUCCESS)
return utlAT_RESULT_CODE_ERROR;
}
/*--- return result-code of last command executed ---*/
return rc;
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utAtParse(parser_p, octets_p, n)
* INPUT
* parser_p == an open AT-command parser
* octets_p == more octets to be parsed
* n == number of octets pointed to by `octets_p'
* OUTPUT
* none
* RETURNS
* utlSUCCESS for success, utlFAILED for failure
* DESCRIPTION
* Appends the characters specified by `string_p' to a line buffer,
* parsing AT-commands when possible. This routine is responsible for:
*
* - Collecting characters into lines, where lines are terminated using
* the character specified by parser_p->parameters.S[utlAT_LINE_TERM_CHAR]
* - Echoing accepted text (when echoing is enabled).
* - Processing back-space requests. In this implementation one cannot
* back-space beyond the first character of the current line.
* - Masking out the MSB of each 8-bit character.
* - Filtering out characters that are invalid AT-command characters.
* - Identifying and processing repeat-last-AT-command requests.
* - Identifying and processing embedded AT commands.
*---------------------------------------------------------------------------*/
utlReturnCode_T utlAtParse(const utlAtParser_P parser_p,
const unsigned char *octets_p,
const size_t n)
{
utlReturnCode_T parse_rc;
int echo_increase_num = 1;
utlAssert(parser_p != NULL);
utlAssert(octets_p != NULL);
parse_rc = utlSUCCESS;
/*--- if bypass mode is enabled ---*/
if (parser_p->states.bypass_mode)
{
// FIXME
// BYPASS mode is not supported yet
utlAssert(0);
utlAbsoluteTime_T curr_time;
/*--- fetch current time ---*/
if (utlCurrentTime(&curr_time) != utlSUCCESS)
curr_time = parser_p->states.escape.last_tx_time;
/*--- clear queue ---*/
parser_p->buffers.echo_p = parser_p->buffers.head_p;
parser_p->buffers.tail_p = parser_p->buffers.head_p;
/*--- pass data directly to modem implementation (if possible) ---*/
if ( parser_p->call_backs.tx_line_data_function_p != NULL)
parse_rc = (parser_p->call_backs.tx_line_data_function_p)(octets_p, n, parser_p->call_backs.arg_p);
/*--- update time of last TX ---*/
parser_p->states.escape.last_tx_time = curr_time;
/*--- else if we are on-line ---*/
}
else
{
bool overflow;
overflow = false;
/*--- transfer received characters into queue... ---*/
{
const unsigned char *o_p;
const unsigned char *term_o_p;
term_o_p = octets_p + n;
for (o_p = octets_p; o_p < term_o_p; o_p++)
{
char c;
c = ((char) *o_p); /* include non-ASCII*/
/*--- if we're dealing with a queue overflow, we're going to ignore
all characters from the point of overflow until we reach the
end of the current line */
if (parser_p->buffers.overflow)
{
/*--- have we reached the end of the current line? ---*/
if (c == parser_p->parameters.S[utlAT_LINE_TERM_CHAR])
{
parser_p->buffers.overflow = false;
/*--- arrange to report overflow just before we return ---*/
overflow = true;
}
continue;
}
/*--- append character to queue of accumulated characters ---*/
{
char *next_head_p;
/*--- compute where to place next character ---*/
next_head_p = parser_p->buffers.head_p + 1;
if (next_head_p >= parser_p->buffers.term_p)
next_head_p = parser_p->buffers.queue;
/*--- if queue is full ---*/
if (next_head_p == parser_p->buffers.tail_p)
{
parser_p->buffers.overflow = true;
utlAssert(parser_p->buffers.head_p != parser_p->buffers.tail_p);
/*--- turf from current character back to start of current line ---*/
{
char *prev_p;
if (parser_p->buffers.head_p == parser_p->buffers.queue)
prev_p = parser_p->buffers.term_p - 1;
else
prev_p = parser_p->buffers.head_p - 1;
for (;; )
{
/*--- we reach the end of the previous line? ---*/
if (*prev_p == parser_p->parameters.S[utlAT_LINE_TERM_CHAR])
{
/*--- set head pointer to point just past end of previous line ---*/
prev_p++;
if (prev_p >= parser_p->buffers.term_p)
prev_p = parser_p->buffers.queue;
parser_p->buffers.head_p = prev_p;
break;
}
/*--- no more data? ---*/
if (prev_p == parser_p->buffers.tail_p)
{
/*--- queue must be empty ---*/
parser_p->buffers.head_p = prev_p;
break;
}
/*--- backup on character ---*/
if (prev_p == parser_p->buffers.queue) prev_p = parser_p->buffers.term_p - 1;
else prev_p--;
}
}
/*--- move on, process next character ---*/
continue;
}
/*--- append character to queue ---*/
*(parser_p->buffers.head_p) = c;
parser_p->buffers.head_p = next_head_p;
}
}
}
/*--- if we're not waiting for the raw modem to respond to an async request,
process queued characters... */
/*oxoxoxo
OLD:
if (parser_p->states.async_response.timer_id == utlFAILED) {
FUTURE:
if (!utlIsListEmpty(parser_p->states.async_responses.unused)) {
TEMP HACK:
*/
if (utlAcquireExclusiveAccess(&parser_p->queue_semaphore) != utlSUCCESS)
{
utlError(utlAtParse, "Cannot exclusively acquire semaphore!\n");
return utlFAILED;
}
#ifndef AT_CMD_ASYNC_MODE
if (utlIsListEmpty(parser_p->states.async_responses.pending))
#else
if (!utlIsListEmpty(parser_p->states.async_responses.unused))
#endif
{
char *c_p;
int breakout_flag = 0;
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
for (c_p = parser_p->buffers.tail_p; c_p != parser_p->buffers.head_p; )
{
/*--- if we're currently discarding the current line ---*/
if (parser_p->states.discard_current_line)
{
/*--- discard characters until we hit a line-termination character
(or run out of characters) */
while (c_p != parser_p->buffers.head_p)
{
/*--- found line-termination character? ---*/
if (*c_p == parser_p->parameters.S[utlAT_LINE_TERM_CHAR])
{
/*--- skip line-termination character ---*/
if (++c_p >= parser_p->buffers.term_p)
c_p = parser_p->buffers.queue;
parser_p->states.discard_current_line = false;
break;
}
/*--- advance one character ---*/
if (++c_p >= parser_p->buffers.term_p)
c_p = parser_p->buffers.queue;
}
continue;
}
/*--- have we found a potential repeat-last-AT-command (A/) request? ---*/
if (*c_p == '/')
{
/*--- was there a character preceding '/'? ---*/
if (c_p != parser_p->buffers.tail_p)
{
const char *prev_c_p;
/*--- back-up to character preceding '/' ---*/
if (c_p == parser_p->buffers.queue) prev_c_p = parser_p->buffers.term_p - 1;
else prev_c_p = c_p - 1;
/*--- if we found the sequence "A/" or "a/" ---*/
if ((*prev_c_p == 'A') ||
(*prev_c_p == 'a'))
{
char *prev;
bool repeat_flag = true;
if(prev_c_p != parser_p->buffers.tail_p)
{
if(prev_c_p == parser_p->buffers.queue)
prev = parser_p->buffers.term_p - 1;
else
prev = prev_c_p - 1;
if (*prev != parser_p->parameters.S[utlAT_LINE_TERM_CHAR] && *prev != parser_p->parameters.S[utlAT_FORMATTING_CHAR])
repeat_flag = false;
}
if(repeat_flag)
{
/*--- if echoing is enabled, echo everything up to and including the "A/" or "a/" ---*/
if (parser_p->parameters.echo_text == true)
{
if (c_p >= parser_p->buffers.echo_p)
{
if (utlSendSubstring(parser_p, parser_p->buffers.echo_p, c_p - parser_p->buffers.echo_p + 1) != utlSUCCESS)
return utlFAILED;
}
else if (c_p < parser_p->buffers.echo_p && (c_p + 1) < parser_p->buffers.echo_p)
{
if (utlSendSubstring(parser_p, parser_p->buffers.echo_p, parser_p->buffers.term_p - parser_p->buffers.echo_p) != utlSUCCESS)
return utlFAILED;
if (c_p >= parser_p->buffers.queue)
if (utlSendSubstring(parser_p, parser_p->buffers.queue, c_p - parser_p->buffers.queue + 1) != utlSUCCESS)
return utlFAILED;
}
}
parser_p->buffers.echo_p = c_p + 1;
/*--- re-execute previously executed AT command ---*/
{
utlAtResultCode_T rc;
rc = utlParseLine(parser_p, parser_p->buffers.previous_line, 0);
utlPrintTrace(utlAtParse1, "[%s][%d]utlParseLine for %s is: %d\n", __FUNCTION__, __LINE__, parser_p->buffers.previous_line, rc);
if (utlSendBasicSyntaxResultCode(parser_p, rc, true) != utlSUCCESS)
return utlFAILED;
if (rc == utlAT_RESULT_CODE_ERROR)
{
/*--- discard rest of current line ---*/
parser_p->states.discard_current_line = true;
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlAtParse2, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlFAILED;
}
parser_p->commands_in_line = 0;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
utlAbandonAllPendingAsyncResponse(parser_p);
parse_rc = utlFAILED;
}
}
/*--- advance one character ---*/
if (++c_p >= parser_p->buffers.term_p)
c_p = parser_p->buffers.queue;
/*--- we've now processed up to here ---*/
parser_p->buffers.tail_p = c_p;
/*--- if we're waiting for an asynchronous request to complete ---*/
/*oxoxoxo
OLD:
if (parser_p->states.async_response.timer_id != utlFAILED)
FUTURE:
if (utlIsListEmpty(parser_p->states.async_responses.unused))
TEMP HACK:
*/
if (!utlIsListEmpty(parser_p->states.async_responses.pending))
break;
/*--- continue processing characters on current line ---*/
continue;
}
}
}
}
/*--- have we found a command-line editing character? Note that
if S3 (command-line termination) and S5 (command-line edit)
are both set to the same character and if this character is
encountered, we must treat the found character as an S3
(command-line termination) event */
if ((*c_p == parser_p->parameters.S[utlAT_LINE_EDIT_CHAR]) &&
( parser_p->parameters.S[utlAT_LINE_EDIT_CHAR] != parser_p->parameters.S[utlAT_LINE_TERM_CHAR]))
{
/*--- if echoing is enabled, echo everything up to (but excluding) line-edit character ---*/
if (parser_p->parameters.echo_text == true)
{
if (c_p > parser_p->buffers.echo_p)
{
if (utlSendSubstring(parser_p, parser_p->buffers.echo_p, c_p - parser_p->buffers.echo_p) != utlSUCCESS)
return utlFAILED;
}
else if (c_p < parser_p->buffers.echo_p && (c_p + 1) < parser_p->buffers.echo_p)
{
if (utlSendSubstring(parser_p, parser_p->buffers.echo_p, parser_p->buffers.term_p - parser_p->buffers.echo_p) != utlSUCCESS)
return utlFAILED;
if (c_p > parser_p->buffers.queue)
if (utlSendSubstring(parser_p, parser_p->buffers.queue, c_p - parser_p->buffers.queue) != utlSUCCESS)
return utlFAILED;
}
}
parser_p->buffers.echo_p = c_p;
/*--- fix up queue ---*/
{
char *prev_c_p;
char *prev_tail_p;
char *src_p;
char *dest_p;
/*--- back-up to character preceding line-edit character (if any) ---*/
if (c_p == parser_p->buffers.queue)
prev_c_p = parser_p->buffers.term_p - 1;
else
prev_c_p = c_p - 1;
/*--- identify queue-slot preceeding tail ---*/
if (parser_p->buffers.tail_p == parser_p->buffers.queue)
prev_tail_p = parser_p->buffers.term_p - 1;
else
prev_tail_p = parser_p->buffers.tail_p - 1;
/*--- if there's a character to back-space over ---*/
if (( prev_c_p != prev_tail_p) &&
(*prev_c_p != parser_p->parameters.S[utlAT_LINE_TERM_CHAR]))
{
/*--- if echoing is enabled and we have something to back-space over, echo a destructive <bs> ---*/
if ((parser_p->parameters.echo_text == true) && (isprint(*prev_c_p)))
if (utlSendString(parser_p, "\b \b") != utlSUCCESS)
return utlFAILED;
/*--- arrange to remove line-edit character and character
preceeding line-edit character from queue */
src_p = c_p;
dest_p = prev_c_p;
c_p = prev_c_p;
}
else
{
/*--- arrange to remove line-edit character from queue ---*/
src_p = c_p;
dest_p = c_p;
}
for (;; )
{
if (++src_p >= parser_p->buffers.term_p)
src_p = parser_p->buffers.queue;
if (src_p == parser_p->buffers.head_p)
break;
*dest_p = *src_p;
if (++dest_p >= parser_p->buffers.term_p)
dest_p = parser_p->buffers.queue;
}
parser_p->buffers.head_p = dest_p;
parser_p->buffers.echo_p = c_p;
continue;
}
/*--- have we found a command-line termination character? ---*/
}
else if (*c_p == parser_p->parameters.S[utlAT_LINE_TERM_CHAR])
{
char line[utlAT_MAX_LINE_LENGTH + 1];
size_t line_off;
char* next_cp;
next_cp = c_p + 1;
if (next_cp >= parser_p->buffers.term_p)
next_cp = parser_p->buffers.queue;
if (*next_cp == parser_p->parameters.S[utlAT_FORMATTING_CHAR]) {
echo_increase_num = 2;
*next_cp = 0;
}
memset(line, 0x0, sizeof(line));
line_off = parser_p->states.line_off;
parser_p->states.line_off = 0;
/*--- if echoing is enabled, echo everything up to and including the termination character ---*/
if (parser_p->parameters.echo_text == true)
{
if (c_p >= parser_p->buffers.echo_p)
{
if (utlSendSubstring(parser_p, parser_p->buffers.echo_p, c_p - parser_p->buffers.echo_p + echo_increase_num) != utlSUCCESS)
return utlFAILED;
}
else if (c_p < parser_p->buffers.echo_p && (c_p + 1) < parser_p->buffers.echo_p)
{
if (utlSendSubstring(parser_p, parser_p->buffers.echo_p, parser_p->buffers.term_p - parser_p->buffers.echo_p) != utlSUCCESS)
return utlFAILED;
if (c_p >= parser_p->buffers.queue)
if (utlSendSubstring(parser_p, parser_p->buffers.queue, c_p - parser_p->buffers.queue + echo_increase_num) != utlSUCCESS)
return utlFAILED;
}
}
parser_p->buffers.echo_p = c_p + echo_increase_num;
if (parser_p->buffers.echo_p >= parser_p->buffers.term_p)
parser_p->buffers.echo_p = parser_p->buffers.queue;
/*--- transfer one line of text from queue to line buffer... ---*/
{
char *this_p;
char *term_p;
char *src_p;
this_p = line;
term_p = line + utlNumberOf(line);
for (src_p = parser_p->buffers.tail_p; src_p != c_p; )
{
/*--- no more room in line buffer? ---*/
if (this_p >= term_p)
{
utlError(utlAtParse3, "Line too long.");
return utlFAILED;
}
/*--- only process characters that are valid in AT-commands... ---*/
if ((*src_p >= '\0') ||
(*src_p == parser_p->parameters.S[utlAT_ESCAPE_CHAR]) ||
(*src_p == parser_p->parameters.S[utlAT_LINE_TERM_CHAR]) ||
(*src_p == parser_p->parameters.S[utlAT_FORMATTING_CHAR]) ||
(*src_p == parser_p->parameters.S[utlAT_LINE_EDIT_CHAR]) ||
(*src_p == parser_p->parameters.S[utlAT_SEPARATER_CHAR]) ||
(*src_p == parser_p->parameters.S[utlAT_TERMINATER_CHAR]))
{
/*--- append character to line buffer ---*/
*this_p++ = *src_p;
*src_p = 0;
}
if (++src_p >= parser_p->buffers.term_p)
src_p = parser_p->buffers.queue;
}
*this_p = '\0';
}
parser_p->buffers.tail_p = c_p + echo_increase_num;
if (parser_p->buffers.tail_p >= parser_p->buffers.term_p)
parser_p->buffers.tail_p = parser_p->buffers.queue + (parser_p->buffers.tail_p - parser_p->buffers.term_p);
/*--- parse line (if not empty) for AT commands ---*/
if (line[0] != '\0')
{
utlAtResultCode_T rc;
rc = utlParseLine(parser_p, line, line_off);
// comment this line as for some illegal string input might result to printf parsing failure.
//utlPrintTrace("[%s][%d]utlParseLine for %s is: %d\n", __FUNCTION__, __LINE__, line, rc);
if (utlSendBasicSyntaxResultCode(parser_p, rc, true) != utlSUCCESS)
return utlFAILED;
if (rc == utlAT_RESULT_CODE_ERROR)
{
if (utlAcquireExclusiveAccess(&parser_p->cmd_cnt_semaphore) != utlSUCCESS)
{
utlError(utlAtParse4, "%s: Cannot exclusively acquire semaphore!\n", __FUNCTION__);
return utlFAILED;
}
parser_p->commands_in_line = 0;
utlReleaseExclusiveAccess(&parser_p->cmd_cnt_semaphore);
utlAbandonAllPendingAsyncResponse(parser_p);
parse_rc = utlFAILED;
}
else
{
/*--- save good AT-command to support repeat-last-AT-command feature ---*/
strncpy(parser_p->buffers.previous_line, line, sizeof(parser_p->buffers.previous_line) - 1);
}
}
}
/*--- advance to next character ---*/
/*--- in some case, c_p may equal to head_p at this point, and we need to filter out it to avoid endless-loop
mainly happened when user input "A/" in non-canonical mode */
//if (c_p != parser_p->buffers.head_p && ++c_p >= parser_p->buffers.term_p)
//c_p = parser_p->buffers.queue;
c_p = c_p + echo_increase_num;
if (c_p <= parser_p->buffers.head_p && (c_p + echo_increase_num) > parser_p->buffers.head_p) {
breakout_flag = 1;
}
if (c_p >= parser_p->buffers.term_p)
c_p = parser_p->buffers.queue + (c_p - parser_p->buffers.term_p);
/*--- if we're waiting for an asynchronous request to complete ---*/
/*oxoxoxo
OLD:
if (parser_p->states.async_response.timer_id != utlFAILED)
FUTURE:
if (utlIsListEmpty(parser_p->states.async_responses.unused))
TEMP HACK:
*/
#ifndef AT_CMD_ASYNC_MODE
if (!utlIsListEmpty(parser_p->states.async_responses.pending) || breakout_flag)
#else
if (utlIsListEmpty(parser_p->states.async_responses.unused) ||breakout_flag)
#endif
break;
else
echo_increase_num = 1;
}
/*--- if echoing is enabled, echo what has not yet been echoed ---*/
if (parser_p->parameters.echo_text == true)
{
if (c_p > parser_p->buffers.echo_p)
{
if (utlSendSubstring(parser_p, parser_p->buffers.echo_p, c_p - parser_p->buffers.echo_p) != utlSUCCESS)
return utlFAILED;
}
else if (c_p < parser_p->buffers.echo_p && (c_p + 1) < parser_p->buffers.echo_p)
{
if (utlSendSubstring(parser_p, parser_p->buffers.echo_p, parser_p->buffers.term_p - parser_p->buffers.echo_p) != utlSUCCESS)
return utlFAILED;
if (c_p > parser_p->buffers.queue)
if (utlSendSubstring(parser_p, parser_p->buffers.queue, c_p - parser_p->buffers.queue) != utlSUCCESS)
return utlFAILED;
}
}
parser_p->buffers.echo_p = c_p;
}
else
{
utlReleaseExclusiveAccess(&parser_p->queue_semaphore);
}
if (overflow)
{
utlError(utlAtParse5, "buffer overflow");
return utlFAILED;
}
}
return parse_rc;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
// DEBUG FUNCTIONS
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
#if defined(utlDEBUG) || defined(utlTEST)
/*---------------------------------------------------------------------------*
* FUNCTION
* utlVStringDumpAtParser(v_string_p, parser_p, prefix_p)
* INPUT
* v_string_p == pointer to an initialized utlVString_T structure
* parser_p == pointer to an initialized utlAtParser_T structure
* prefix_p == pointer to a prefix string
* OUTPUT
* *v_string_p == the updated utlVString_T structure
* RETURNS
* nothing
* DESCRIPTION
* Dumps AT-command parser information to `v_string_p'.
*---------------------------------------------------------------------------*/
void utlVStringDumpAtParser(const utlVString_P v_string_p,
const utlAtParser_P parser_p,
const char *prefix_p)
{
char buf[40];
size_t i, j, k;
utlAssert(v_string_p != NULL);
utlAssert(parser_p != NULL);
utlAssert(prefix_p != NULL);
(void)utlVStringPrintF(v_string_p, "%s:\n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: AT-command Parser\n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s:\n", prefix_p);
if (parser_p->info.id_p != NULL) (void)utlVStringPrintF(v_string_p, "%s: ID: %s\n", prefix_p, parser_p->info.id_p);
if (parser_p->info.manufacturer_p != NULL) (void)utlVStringPrintF(v_string_p, "%s: Manufacturer: %s\n", prefix_p, parser_p->info.manufacturer_p);
if (parser_p->info.model_p != NULL) (void)utlVStringPrintF(v_string_p, "%s: Model: %s\n", prefix_p, parser_p->info.model_p);
if (parser_p->info.revision_p != NULL) (void)utlVStringPrintF(v_string_p, "%s: Revision: %s\n", prefix_p, parser_p->info.revision_p);
if (parser_p->info.serial_number_p != NULL) (void)utlVStringPrintF(v_string_p, "%s: Serial Number: %s\n", prefix_p, parser_p->info.serial_number_p);
if (parser_p->info.object_id_p != NULL) (void)utlVStringPrintF(v_string_p, "%s: Global Object ID: %s\n", prefix_p, parser_p->info.object_id_p);
if (parser_p->info.country_code_p != NULL) (void)utlVStringPrintF(v_string_p, "%s: Country Code: %s\n", prefix_p, parser_p->info.country_code_p);
if (parser_p->info.connect_text_p != NULL) (void)utlVStringPrintF(v_string_p, "%s: Connect Text: %s\n", prefix_p, parser_p->info.connect_text_p);
(void)utlVStringPrintF(v_string_p, "%s: \n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: S-Parameters:\n", prefix_p);
k = (utlAT_NUM_S_PARAMETERS + 3) / 4;
for (i = 0; i < k; i++)
{
(void)utlVStringPrintF(v_string_p, "%s: ", prefix_p);
for (j = 0; j < 4; j++)
{
size_t l;
l = i + (j * k);
if (l < utlAT_NUM_S_PARAMETERS)
(void)utlVStringPrintF(v_string_p, " S[%02u]: %-5u", l, parser_p->parameters.S[l]);
}
(void)utlVStringPrintF(v_string_p, "\n");
}
(void)utlVStringPrintF(v_string_p, "%s: \n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: Allow default S-parameter values: %s\n", prefix_p, (parser_p->options.allow_default_S_parameter_values == true) ? "yes" : "no");
(void)utlVStringPrintF(v_string_p, "%s: Allow string escapes: %s\n", prefix_p, (parser_p->options.allow_string_escapes == true) ? "yes" : "no");
(void)utlVStringPrintF(v_string_p, "%s: Result code: %s\n", prefix_p, (parser_p->options.use_carrier_result_code == true) ? "CARRIER" : "CONNECT");
(void)utlVStringPrintF(v_string_p, "%s: \n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: Previous Line: \"%s\"\n", prefix_p, parser_p->buffers.previous_line);
(void)utlVStringPrintF(v_string_p, "%s: \n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: Recognized AT Commands:\n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: Name | Type | Parameters\n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: | | Type | Access | Presence\n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: -----------------+-----------+-------------+------------+-----------\n", prefix_p);
for (i = 0; i < parser_p->num_commands; i++)
{
size_t j;
if (i > (size_t)0)
(void)utlVStringPrintF(v_string_p, "%s: | | | | \n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: %17s | ", prefix_p, parser_p->commands_p[i].name_p);
switch (parser_p->commands_p[i].type)
{
case utlAT_COMMAND_TYPE_BASIC: (void)utlVStringPrintF(v_string_p, "Basic | "); break;
case utlAT_COMMAND_TYPE_EXTENDED: (void)utlVStringPrintF(v_string_p, "Extended | "); break;
case utlAT_COMMAND_TYPE_EXACTION: (void)utlVStringPrintF(v_string_p, "Ex-action | "); break;
case utlAT_COMMAND_TYPE_EXTENDED_EXACTION: (void) utlVStringPrintF(v_string_p, "Extended&Ex-action | "); break;
default:
(void)utlVStringPrintF(v_string_p, "%8d | ", parser_p->commands_p[i].type);
break;
}
if (parser_p->commands_p[i].num_parameters == (size_t)0)
(void)utlVStringPrintF(v_string_p, " - | - | -\n");
else for (j = 0; j < parser_p->commands_p[i].num_parameters; j++)
{
if (j > (size_t)0)
(void)utlVStringPrintF(v_string_p, "%s: | | ", prefix_p);
switch (parser_p->commands_p[i].parameters_p[j].type)
{
case utlAT_DATA_TYPE_DECIMAL: (void)utlVStringPrintF(v_string_p, " Decimal | "); break;
case utlAT_DATA_TYPE_HEXADECIMAL: (void)utlVStringPrintF(v_string_p, "Hexadecimal | "); break;
case utlAT_DATA_TYPE_BINARY: (void)utlVStringPrintF(v_string_p, " Binary | "); break;
case utlAT_DATA_TYPE_STRING: (void)utlVStringPrintF(v_string_p, " String | "); break;
case utlAT_DATA_TYPE_QSTRING: (void)utlVStringPrintF(v_string_p, " QString | "); break;
case utlAT_DATA_TYPE_DIAL_STRING: (void)utlVStringPrintF(v_string_p, "Dial-string | "); break;
default:
(void)utlVStringPrintF(v_string_p, "%10d | ", parser_p->commands_p[i].parameters_p[j].type);
break;
}
switch (parser_p->commands_p[i].parameters_p[j].access)
{
case utlAT_PARAMETER_ACCESS_READ_WRITE: (void)utlVStringPrintF(v_string_p, "Read/Write | "); break;
case utlAT_PARAMETER_ACCESS_READ: (void)utlVStringPrintF(v_string_p, " Read | "); break;
case utlAT_PARAMETER_ACCESS_READ_ONLY: (void)utlVStringPrintF(v_string_p, "Read-only | "); break;
case utlAT_PARAMETER_ACCESS_WRITE_ONLY: (void)utlVStringPrintF(v_string_p, "Write-only | "); break;
default:
(void)utlVStringPrintF(v_string_p, "%10d | ", parser_p->commands_p[i].parameters_p[j].access);
break;
}
switch (parser_p->commands_p[i].parameters_p[j].presence)
{
case utlAT_PARAMETER_PRESENCE_OPTIONAL: (void)utlVStringPrintF(v_string_p, "Optional\n"); break;
case utlAT_PARAMETER_PRESENCE_REQUIRED: (void)utlVStringPrintF(v_string_p, "Required\n"); break;
default:
(void)utlVStringPrintF(v_string_p, "%d\n", parser_p->commands_p[i].parameters_p[j].presence);
break;
}
}
}
(void)utlVStringPrintF(v_string_p, "%s: \n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: Current dial string: %s\n", prefix_p, parser_p->states.dial_string.buf);
(void)utlVStringPrintF(v_string_p, "%s: Last dial string: %s\n", prefix_p, parser_p->states.dial_string.last);
(void)utlVStringPrintF(v_string_p, "%s: Dial string delay: %d\n", prefix_p, parser_p->states.dial_string.dial_string_delay);
(void)utlVStringPrintF(v_string_p, "%s: International: %s\n", prefix_p, (parser_p->states.dial_string.options.international == true) ? "true" : "false");
(void)utlVStringPrintF(v_string_p, "%s: Do call origination: %s\n", prefix_p, (parser_p->states.dial_string.options.do_call_origination == true) ? "true" : "false");
(void)utlVStringPrintF(v_string_p, "%s: Use CCUG SS info: %s\n", prefix_p, (parser_p->states.dial_string.options.use_CCUG_SS_info == true) ? "true" : "false");
(void)utlVStringPrintF(v_string_p, "%s: CLI presentation: %s\n", prefix_p, (parser_p->states.dial_string.options.CLI_presentation == 'I') ? "restrict" :
(parser_p->states.dial_string.options.CLI_presentation == 'i') ? "allow" : "default");
(void)utlVStringPrintF(v_string_p, "%s: \n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: Go on-line: %s\n", prefix_p, (parser_p->states.go_on_line == true) ? "true" : "false");
(void)utlVStringPrintF(v_string_p, "%s: Discard Current Line: %s\n", prefix_p, (parser_p->states.discard_current_line == true) ? "true" : "false");
(void)utlVStringPrintF(v_string_p, "%s: DCE I/O config pending: %x\n", prefix_p, parser_p->states.dce_io_config_pending_mask);
(void)utlVStringPrintF(v_string_p, "%s: Sound config pending: %x\n", prefix_p, parser_p->states.sound_config_pending_mask);
(void)utlVStringPrintF(v_string_p, "%s: Line Offset: %d\n", prefix_p, parser_p->states.line_off);
(void)utlVStringPrintF(v_string_p, "%s: \n", prefix_p);
if (utlFormatDateTime(buf, utlNumberOf(buf), &(parser_p->states.escape.last_tx_time)) == utlSUCCESS)
(void)utlVStringPrintF(v_string_p, "%s: Last TX time: %s\n", prefix_p, buf);
(void)utlVStringPrintF(v_string_p, "%s: Escape state: %u\n", prefix_p, parser_p->states.escape.state);
(void)utlVStringPrintF(v_string_p, "%s: Blind dial pause time: %u.%09u\n", prefix_p, parser_p->states.blind_dial_pause_time.seconds,
parser_p->states.blind_dial_pause_time.nanoseconds);
(void)utlVStringPrintF(v_string_p, "%s: Conn. complete timeout: %u.%09u\n", prefix_p, parser_p->states.conn_complete_timeout.seconds,
parser_p->states.conn_complete_timeout.nanoseconds);
(void)utlVStringPrintF(v_string_p, "%s: Dialing pause time: %u.%09u\n", prefix_p, parser_p->states.dialing_pause_time.seconds,
parser_p->states.dialing_pause_time.nanoseconds);
(void)utlVStringPrintF(v_string_p, "%s: Carrier detect time: %u.%09u\n", prefix_p, parser_p->states.carrier_detect_time.seconds,
parser_p->states.carrier_detect_time.nanoseconds);
(void)utlVStringPrintF(v_string_p, "%s: Carrier loss time: %u.%09u\n", prefix_p, parser_p->states.carrier_loss_time.seconds,
parser_p->states.carrier_loss_time.nanoseconds);
(void)utlVStringPrintF(v_string_p, "%s: Hook flash time: %u.%09u\n", prefix_p, parser_p->states.hook_flash_time.seconds,
parser_p->states.hook_flash_time.nanoseconds);
(void)utlVStringPrintF(v_string_p, "%s: Disconnect wait time: %u.%09u\n", prefix_p, parser_p->states.inactivity_time.seconds,
parser_p->states.inactivity_time.nanoseconds);
(void)utlVStringPrintF(v_string_p, "%s: Disconnect wait time: %u.%09u\n", prefix_p, parser_p->states.disconnect_wait_time.seconds,
parser_p->states.disconnect_wait_time.nanoseconds);
(void)utlVStringPrintF(v_string_p, "%s: Delay dialing time: %u.%09u\n", prefix_p, parser_p->states.delay_dialing_time.seconds,
parser_p->states.delay_dialing_time.nanoseconds);
(void)utlVStringPrintF(v_string_p, "%s: Minimum Silence time: %u.%09u\n", prefix_p, parser_p->states.min_silence_time.seconds,
parser_p->states.min_silence_time.nanoseconds);
(void)utlVStringPrintF(v_string_p, "%s: \n", prefix_p);
(void)utlVStringPrintF(v_string_p, "%s: Basic-commands peg count: %d\n", prefix_p, parser_p->peg_counts.basic_commands);
(void)utlVStringPrintF(v_string_p, "%s: Extended-commands peg count: %d\n", prefix_p, parser_p->peg_counts.extended_commands);
(void)utlVStringPrintF(v_string_p, "%s: Undefined-commands peg count: %d\n", prefix_p, parser_p->peg_counts.undefined_commands);
(void)utlVStringPrintF(v_string_p, "%s: Bad-commands peg count: %d\n", prefix_p, parser_p->peg_counts.bad_commands);
}
/*---------------------------------------------------------------------------*
* FUNCTION
* utlDumpAtParser(file_p, parser_p)
* INPUT
* file_p == pointer to an open file
* parser_p == pointer to an initialized utlParser_T structure
* OUTPUT
* none
* RETURNS
* nothing
* DESCRIPTION
* Dumps AT-command parser information to `file_p'.
*---------------------------------------------------------------------------*/
void utlDumpAtParser( FILE *file_p,
const utlAtParser_P parser_p)
{
utlVString_T v_string;
utlAssert(file_p != NULL);
utlInitVString(&v_string);
utlVStringDumpAtParser(&v_string, parser_p, "AtParser");
utlVStringPuts(&v_string, file_p);
utlVStringFree(&v_string);
}
#endif