/*
** Ericsson AB, AT Library
** ST-Ericsson U300 RIL
**
** Copyright (C) Ericsson AB 2010
** Copyright (C) ST-Ericsson AB 2008-2009
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
** Based on reference-ril by The Android Open Source Project.
**
** Modified for ST-Ericsson U300 modems.
** Author: Christian Bejram <christian.bejram@stericsson.com>
**
** Modified for Ericsson AB modems
** Author: Indrek Peri <Indrek.Peri@Ericsson.com>
*/
#define AT_ERROR_SIMULATION 1
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include "at_channel.h"
#include "at_misc.h"
#ifdef AT_ERROR_SIMULATION
#include <time.h>
struct es_tbl_elem {
char cmd[32];
int error;
};
#define AT_ERR_SIM_RANDOM 1
#define AT_ERR_SIM_TABLE 2
#define AT_ERR_SIM_NONE 3
static int g_err_sim_type = AT_ERR_SIM_NONE;
#define AT_ES_TBL_SZ 32
struct es_tbl_elem g_es_table[AT_ES_TBL_SZ];
static int g_es_seeded = 0;
static int g_es_call_count = 0;
static int g_es_modulus = 3;
static int g_es_table_read = 0; // read the table file
#endif // AT_ERROR_SIMULATION
/* Just check this file */
#ifdef __GNUC__
#pragma GCC diagnostic warning "-pedantic"
#endif
/** Returns 1 if line starts with prefix, 0 if it does not. */
int strStartsWith(const char *line, const char *prefix)
{
for (; *line != '\0' && *prefix != '\0'; line++, prefix++) {
if (*line != *prefix) {
return 0;
}
}
return *prefix == '\0';
}
struct ATResponse *at_response_new()
{
return (struct ATResponse *) calloc(1, sizeof(struct ATResponse));
}
void at_add_intermediate(const char *line, struct ATResponse *resp)
{
struct linked_list *tmp;
tmp = malloc(sizeof(struct linked_list));
if (!tmp) {
LOGE("Memory allocation failed");
return;
}
tmp->data = (void *) strdup(line);
tmp->next = resp->p_intermediates;
resp->p_intermediates = tmp;
}
/**
* Very simple function that extract and returns whats between ElementBeginTag
* and ElementEndTag.
*
* Optional ppszRemainingDocument that can return a pointer to the remaining
* of the document to be "scanned". This can be used if subsequent
* scanning/searching is desired.
*
* This function is used to extract the parameters from the XML result
* returned by U3xx during a PDP Context setup, and used to parse the
* tuples of operators returned from AT+COPS.
*
* const char* document - Document to be scanned
* const char* elementBeginTag - Begin tag to scan for, return whats
* between begin/end tags
* const char* elementEndTag - End tag to scan for, return whats
* between begin/end tags
* char** remainingDocumen t - Can return the a pointer to the remaining
* of pszDocument, this parameter is optional
*
* return char* containing whats between begin/end tags, allocated on the
* heap, need to free this.
* return NULL if nothing is found.
*/
char *getFirstElementValue(const char *document,
const char *elementBeginTag,
const char *elementEndTag,
char **remainingDocument)
{
char *value = NULL;
char *start = NULL;
char *end = NULL;
if (document != NULL && elementBeginTag != NULL
&& elementEndTag != NULL) {
start = strstr(document, elementBeginTag);
if (start != NULL) {
end = strstr(start, elementEndTag);
if (end != NULL) {
int n = strlen(elementBeginTag);
int m = end - (start + n);
value = (char *) malloc((m + 1) * sizeof(char));
strncpy(value, (start + n), m);
value[m] = (char) 0;
/* Optional, return a pointer to the remaining document,
to be used when document contains many tags with same name. */
if (remainingDocument != NULL) {
*remainingDocument = end + strlen(elementEndTag);
}
}
}
}
return value;
}
char char2nib(char c)
{
if (c >= 0x30 && c <= 0x39)
return c - 0x30;
if (c >= 0x41 && c <= 0x46)
return c - 0x41 + 0xA;
if (c >= 0x61 && c <= 0x66)
return c - 0x61 + 0xA;
return 0;
}
/* If multiline is needed add type here but we think now that
AT_SINGLE_LINE is default. */
struct at_prefix_finder_t {
char *cmd;
char *prefix;
};
/* Match is done now by looping, set more frequent commands to
beginning of table (like creg) */
#define AT_PREFIX_TABLE_MAX 6
struct at_prefix_finder_t at_prefixes[AT_PREFIX_TABLE_MAX] = {
{"AT+CREG?", "+CREG"},
{"AT+CFUN?", "+CFUN"},
{"AT+CGREG?", "+CGREG"},
{"AT+COPS?", "+COPS"},
{"AT+CIMI", "+CIMI"}, /* cimi does not have prefix but we use it internally */
{"AT*EIAC=", "*EIAC"}
};
void at_find_prefix(struct ATCommand *cmd)
{
int i, len;
if (cmd->type != AT_CHECK_PREFIX)
return;
for (i = 0; i < AT_PREFIX_TABLE_MAX; i++) {
if (i > AT_PREFIX_TABLE_MAX) {
LOGE("%s:at prefix out of range", __FUNCTION__);
return;
}
len = strlen(at_prefixes[i].cmd);
if (strncmp(at_prefixes[i].cmd, cmd->command, len) == 0) {
cmd->type = AT_SINGLE_LINE;
strncpy(cmd->responsePrefix, at_prefixes[i].prefix,
AT_MIN_LEN);
return;
}
}
LOGI("No prefix found");
cmd->type = AT_OK_ONLY;
}
/* Error simulation */
/* ------------------------------------ */
#ifdef AT_ERROR_SIMULATION
// Define here your random number range, error happening will calculated
// by using modulus. How many times rundom number is dividable by modulus.
#define AT_ES_MIN 1
#define AT_ES_MAX 10 //10000
#define AT_ES_TYPE_POS 0
#define AT_ES_MOD_POS 1
/*
e2gpssuplnirepy
at*eiad
at+creg?
at+creg=
at+cfun
at*eiac
at+cgdcont
at*e2gpssupl
at+cimi
at*eiauvw
at*e2gpssuplni
at*e2gpsstat
at*e2certun
at*e2gpsctl
at*e2certunreply
at*e2gpsnpd
ate
at*e2gpseph
at*e2gpsloc
at*e2gpstime
at+cops?
at+cops=
at+cgreg?
at+cgreg=
*/
static int es_generate_random()
{
int min, max, rn;
if (!g_es_seeded) {
srand(time(NULL)); // random seed on most compilers
g_es_seeded = 1;
}
min = AT_ES_MIN;
max = AT_ES_MAX;
rn = min + (int) ((float) max * rand() / (RAND_MAX + (float) min));
//j=1+(int)(10.0*rand()/(RAND_MAX+1.0));
return rn;
}
static void es_read_table()
{
#define O_MAX 50
FILE *file;
char *filename = "/etc/at_err_sim.txt";
char str[O_MAX];
char *help;
int i, ret, err;
for (i = 0; i < AT_ES_TBL_SZ; i++) {
g_es_table[i].cmd[0] = 0;
g_es_table[i].error = 0;
}
file = fopen(filename, "r");
if (file == NULL) {
LOGE("Failed to open file %s", filename);
return;
} else
LOGI("Opened err_sim file %s", filename);
for (i = 0; i < AT_ES_TBL_SZ; i++) {
str[0] = 0;
help = fgets(str, O_MAX, file);
err = ferror(file);
if (err) {
LOGE("Failed to read from err_sim, error=%d %s %d", err, str,
i);
goto exit_out;
} else if (feof(file)) {
LOGI("End of file reached %d", i);
goto exit_out;
}
if (str[0] == '#')
continue;
ret =
sscanf(str, "%s %d\n", g_es_table[i].cmd,
&g_es_table[i].error);
if (ret != 2) {
LOGE("sscanf read error %d %s", ret, str);
}
} // loop
exit_out:
LOGI("Mode %d, modulus %d, idx %d", g_es_table[AT_ES_TYPE_POS].error,
g_es_table[AT_ES_MOD_POS].error, i);
fclose(file);
}
static int es_is_error(const char *str)
{
int i = 0;
if (!g_es_table_read) {
es_read_table();
g_es_table_read = 1;
if (!strncmp
(g_es_table[AT_ES_TYPE_POS].cmd, "err_sim_type", AT_MIN_LEN)) {
g_err_sim_type = g_es_table[AT_ES_TYPE_POS].error;
}
if (!strncmp(g_es_table[AT_ES_MOD_POS].cmd, "modulus", AT_MIN_LEN)) {
g_es_modulus = g_es_table[AT_ES_MOD_POS].error;
}
}
if (g_err_sim_type == AT_ERR_SIM_RANDOM) {
int ret;
int rn = es_generate_random();
// ERROR = RANDOM_NUM % MODULUS
// First we get random number [MIN...MAX]
// Second, we make modulus for random number
// If you take modulus as 2,3,5,7,9 etc then you get
// certain error happening deviation
// The bigger the modulus = less error happens
ret = !(rn % g_es_modulus);
LOGI("rn %d, m %d, r %d", rn, g_es_modulus, ret);
return ret;
}
if (g_err_sim_type == AT_ERR_SIM_TABLE) {
for (i = 0; i < AT_ES_TBL_SZ; i++) {
if (strstr(str, g_es_table[i].cmd)) {
LOGI("cmd %s e %d", g_es_table[i].cmd,
g_es_table[i].error);
return g_es_table[i].error;
}
}
return 0; // no error
}
return 0; // no error
}
void es_reset_call_count()
{
g_es_call_count = 0;
g_es_table_read = 0; // next time read table again
}
/* This function is called when command gets OK. Here we can inject
our simulated error response */
void es_error_simulation(struct ATCommand *cmd)
{
if (es_is_error(cmd->command))
cmd->response->success = AT_FINAL_ERROR;
g_es_call_count++;
}
#endif // AT_ERROR_SIMULATION