summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlaforge <laforge@99fdad57-331a-0410-800a-d7fa5415bdb3>2007-03-10 22:05:17 +0000
committerlaforge <laforge@99fdad57-331a-0410-800a-d7fa5415bdb3>2007-03-10 22:05:17 +0000
commit3026b93e266558671bd361b6c07546f8174974bc (patch)
treec579158bc0641847851a1369382d3dc250268ebc
parent2797211645d452446cb7b329ee9d3d3a923f8520 (diff)
* include stdio from talloc.h
* add new generic extended-response parser * add operator cache, using ext-resp-parser * add proper detection of supported %CPI modes and select highest available one * add %CTZV to vendor_ti init string git-svn-id: http://svn.openmoko.org/trunk/src/target/gsm@1316 99fdad57-331a-0410-800a-d7fa5415bdb3
-rw-r--r--include/gsmd/extrsp.h45
-rw-r--r--include/gsmd/gsmd.h2
-rw-r--r--include/gsmd/talloc.h1
-rw-r--r--src/gsmd/Makefile.am2
-rw-r--r--src/gsmd/ext_response.c213
-rw-r--r--src/gsmd/gsmd.c2
-rw-r--r--src/gsmd/operator_cache.c99
-rw-r--r--src/gsmd/unsolicited.c24
-rw-r--r--src/gsmd/vendor_ti.c44
9 files changed, 425 insertions, 7 deletions
diff --git a/include/gsmd/extrsp.h b/include/gsmd/extrsp.h
new file mode 100644
index 0000000..1f6e6a5
--- /dev/null
+++ b/include/gsmd/extrsp.h
@@ -0,0 +1,45 @@
+#ifndef _GSMD_EXTRSP_H
+#define _GSMD_EXTRSP_H
+
+/* how many tokens (CSV items) can an extended response have, max */
+#define GSM_EXTRSP_MAX_TOKENS 16
+
+/* how many individual sub-ranges can one range contain */
+#define GSM_EXTRSP_MAX_RANGES 16
+
+
+struct gsm_extrsp_range_item {
+ int min;
+ int max;
+};
+
+enum gsm_extrsp_tok_type {
+ GSMD_ECMD_RTT_NONE,
+ GSMD_ECMD_RTT_EMPTY,
+ GSMD_ECMD_RTT_NUMERIC,
+ GSMD_ECMD_RTT_STRING,
+ GSMD_ECMD_RTT_RANGE,
+};
+
+struct gsm_extrsp_tok {
+ enum gsm_extrsp_tok_type type;
+ union {
+ struct {
+ struct gsm_extrsp_range_item item[GSM_EXTRSP_MAX_RANGES];
+ int num_items;
+ } range;
+ char string[64];
+ int numeric;
+ } u;
+};
+
+struct gsm_extrsp {
+ unsigned int num_tokens;
+ struct gsm_extrsp_tok tokens[GSM_EXTRSP_MAX_TOKENS];
+};
+
+extern int extrsp_supports(const struct gsm_extrsp *er, int index, int value);
+extern struct gsm_extrsp *extrsp_parse(const void *ctx, const char *input);
+extern void extrsp_dump(const struct gsm_extrsp *er);
+
+#endif
diff --git a/include/gsmd/gsmd.h b/include/gsmd/gsmd.h
index 9f865a0..044f65d 100644
--- a/include/gsmd/gsmd.h
+++ b/include/gsmd/gsmd.h
@@ -66,6 +66,8 @@ struct gsmd {
struct llist_head busy_atcmds; /* our busy gsmd_atcmds */
struct gsmd_vendor_plugin *vendorpl;
struct gsmd_device_state dev_state;
+
+ struct llist_head operators; /* cached list of operator names */
};
struct gsmd_user {
diff --git a/include/gsmd/talloc.h b/include/gsmd/talloc.h
index 2cfd61a..7f4e925 100644
--- a/include/gsmd/talloc.h
+++ b/include/gsmd/talloc.h
@@ -26,6 +26,7 @@
/* nfsim additions */
#include <stdarg.h>
+#include <stdio.h>
/* this is only needed for compatibility with the old talloc */
typedef void TALLOC_CTX;
diff --git a/src/gsmd/Makefile.am b/src/gsmd/Makefile.am
index b431ec5..940f610 100644
--- a/src/gsmd/Makefile.am
+++ b/src/gsmd/Makefile.am
@@ -4,7 +4,7 @@ AM_CFLAGS = -std=gnu99
sbin_PROGRAMS = gsmd
gsmd_SOURCES = gsmd.c atcmd.c select.c vendor.c usock.c unsolicited.c log.c \
- vendor_ti.c talloc.c
+ vendor_ti.c talloc.c operator_cache.c ext_response.c
#gsmd_LDADD = ../libgsmd/libgsmd.la
#gsmd_LDFLAGS = -dynamic
diff --git a/src/gsmd/ext_response.c b/src/gsmd/ext_response.c
new file mode 100644
index 0000000..5b31c8d
--- /dev/null
+++ b/src/gsmd/ext_response.c
@@ -0,0 +1,213 @@
+/* gsmd extended response parser
+ *
+ * (C) 2007 by OpenMoko, Inc.
+ * Written by Harald Welte <laforge@openmoko.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+
+#include "gsmd.h"
+
+#include <gsmd/gsmd.h>
+#include <gsmd/atcmd.h>
+#include <gsmd/extrsp.h>
+#include <gsmd/talloc.h>
+
+int extrsp_supports(const struct gsm_extrsp *er, int index, int value)
+{
+ int i;
+
+ if (index >= er->num_tokens)
+ return -EINVAL;
+ if (er->tokens[index].type != GSMD_ECMD_RTT_RANGE)
+ return -EINVAL;
+
+ for (i = 0; i < er->tokens[index].u.range.num_items; i++) {
+ struct gsm_extrsp_range_item *ri =
+ &er->tokens[index].u.range.item[i];
+ if (value >= ri->min && value <= ri->max)
+ return 1;
+ }
+
+ return 0;
+}
+
+enum parser_state {
+ IDLE,
+ TOKEN_STRING,
+ TOKEN_STRING_LASTQUOTE,
+ TOKEN_NUMERIC,
+ TOKEN_RANGE,
+};
+
+/* parse a comma-separated list, possibly containing quote values */
+struct gsm_extrsp *extrsp_parse(const void *ctx, const char *input)
+{
+ const char *cur = input;
+ struct gsm_extrsp *er;
+ int cur_tok = 0;
+ enum parser_state state = IDLE;
+ char buf[512];
+ char *cur_buf = buf;
+ struct gsm_extrsp_tok *cur_token;
+
+ if (!input || strlen(input) == 0)
+ return NULL;
+
+ er = talloc(ctx, struct gsm_extrsp);
+ if (!er)
+ return NULL;
+ memset(er, 0, sizeof(*er));
+
+ while (*cur) {
+ cur_token = &er->tokens[er->num_tokens];
+
+ switch (state) {
+ case IDLE:
+ memset(buf, 0, sizeof(buf));
+ cur_buf = buf;
+
+ if (isblank(*cur))
+ break;
+ else if (*cur == '"') {
+ cur_token->type = GSMD_ECMD_RTT_STRING;
+ state = TOKEN_STRING;
+ } else if (*cur == '(') {
+ cur_token->type = GSMD_ECMD_RTT_RANGE;
+ state = TOKEN_RANGE;
+ } else if (isdigit(*cur)) {
+ cur_token->type = GSMD_ECMD_RTT_NUMERIC;
+ *cur_buf = *cur;
+ cur_buf++;
+ state = TOKEN_NUMERIC;
+ } else if (*cur == ',') {
+ cur_token->type = GSMD_ECMD_RTT_EMPTY;
+ er->num_tokens++;
+ state = IDLE;
+ }
+ break;
+ case TOKEN_NUMERIC:
+ if (*cur == ',') {
+ /* end of number */
+ cur_token->u.numeric = atoi(buf);
+ er->num_tokens++;
+ state = IDLE;
+ } else if (isdigit(*cur)) {
+ *cur_buf = *cur;
+ cur_buf++;
+ } else {
+ /* ERORR */
+ }
+ break;
+ case TOKEN_STRING:
+ if (*cur == '"') {
+ int len = strlen(buf);
+ if (len > sizeof(cur_token->u.string)-1)
+ len = sizeof(cur_token->u.string)-1;
+
+ /* end of string token */
+ strncpy(cur_token->u.string, buf, len);
+ er->num_tokens++;
+ state = TOKEN_STRING_LASTQUOTE;
+ } else {
+ *cur_buf = *cur;
+ cur_buf++;
+ }
+ break;
+ case TOKEN_STRING_LASTQUOTE:
+ if (*cur == ',')
+ state = IDLE;
+ else {
+ /* ERROR */
+ }
+ break;
+ case TOKEN_RANGE:
+ if (isdigit(*cur)) {
+ *cur_buf = *cur;
+ cur_buf++;
+ } else if (*cur == '-') {
+ /* previous number has completed */
+ cur_token->u.range.item[cur_token->u.range.num_items].min = atoi(buf);
+ memset(buf, 0, sizeof(buf));
+ cur_buf = buf;
+ } else if (*cur == ',') {
+ /* previous number has completed */
+ cur_token->u.range.item[cur_token->u.range.num_items].max = atoi(buf);
+ cur_token->u.range.num_items++;
+ } else if (*cur == ')') {
+ /* previous number has completed */
+ cur_token->u.range.item[cur_token->u.range.num_items].max = atoi(buf);
+ cur_token->u.range.num_items++;
+ state = TOKEN_STRING_LASTQUOTE;
+ er->num_tokens++;
+ } else {
+ /* ERROR */
+ }
+ break;
+ }
+ cur++;
+ }
+
+ //extrsp_dump(er);
+ return er;
+}
+
+static const char *er_tok_names[] = {
+ [GSMD_ECMD_RTT_EMPTY] = "EMPTY",
+ [GSMD_ECMD_RTT_NUMERIC] = "NUMERIC",
+ [GSMD_ECMD_RTT_STRING] = "STRING",
+ [GSMD_ECMD_RTT_RANGE] = "RANGE",
+};
+
+void extrsp_dump(const struct gsm_extrsp *er)
+{
+ int i;
+
+ DEBUGP("entering(er=%p, num_tokens=%u)\n", er, er->num_tokens);
+ for (i = 0; i < er->num_tokens; i++) {
+ const struct gsm_extrsp_tok *tok = &er->tokens[i];
+ DEBUGP("Token %u: %s: ", i, er_tok_names[tok->type]);
+ switch (tok->type) {
+ case GSMD_ECMD_RTT_EMPTY:
+ DEBUGP("\n");
+ break;
+ case GSMD_ECMD_RTT_NUMERIC:
+ DEBUGP("%d\n", tok->u.numeric);
+ break;
+ case GSMD_ECMD_RTT_STRING:
+ DEBUGP("%s\n", tok->u.string);
+ break;
+ case GSMD_ECMD_RTT_RANGE: {
+ int j;
+ for (j = 0; j < tok->u.range.num_items; j++)
+ DEBUGP("%d-%d, ", tok->u.range.item[j].min,
+ tok->u.range.item[j].max);
+ }
+
+ break;
+ }
+ }
+
+}
diff --git a/src/gsmd/gsmd.c b/src/gsmd/gsmd.c
index 6c72f97..8254ae0 100644
--- a/src/gsmd/gsmd.c
+++ b/src/gsmd/gsmd.c
@@ -282,6 +282,8 @@ int main(int argc, char **argv)
gsmd_initsettings(&g);
+ gsmd_opname_init(&g);
+
while (1) {
int ret = gsmd_select_main();
if (ret == 0)
diff --git a/src/gsmd/operator_cache.c b/src/gsmd/operator_cache.c
new file mode 100644
index 0000000..e680706
--- /dev/null
+++ b/src/gsmd/operator_cache.c
@@ -0,0 +1,99 @@
+/* gsmd cache of operator names
+ *
+ * (C) 2006-2007 by OpenMoko, Inc.
+ * Written by Harald Welte <laforge@openmoko.org>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "gsmd.h"
+
+#include <common/linux_list.h>
+#include <gsmd/gsmd.h>
+#include <gsmd/talloc.h>
+
+static void *__opc_ctx;
+
+struct opname {
+ struct llist_head list;
+ struct {
+ u_int16_t mcc; /* mobile country code */
+ u_int8_t mnc; /* mobile network code */
+ } numeric;
+ char alnum_long[16+1];
+ //char alnum_short[8+1];
+};
+
+/* add an entry to the operator name list, overwrite existing entries for
+ * same mcc/mnc */
+static int _opname_add(struct gsmd *g, struct opname *op)
+{
+ struct opname *cur, *cur2;
+
+ llist_for_each_entry_safe(cur, cur2, &g->operators, list) {
+ if (op->numeric.mcc == cur->numeric.mcc &&
+ op->numeric.mnc == cur->numeric.mnc) {
+ llist_del(&cur->list);
+ talloc_free(cur);
+ }
+ }
+ llist_add_tail(&op->list, &g->operators);
+
+ return 0;
+}
+
+int gsmd_opname_add(struct gsmd *g, const char *numeric_bcd_string,
+ const char *alnum_long)
+{
+ struct opname *op;
+ char mcc[3+1];
+ char mnc[2+1];
+
+ if (strlen(numeric_bcd_string) != 5)
+ return -EINVAL;
+
+ op = talloc(__opc_ctx, struct opname);
+ if (!op)
+ return -ENOMEM;
+
+ memset(mcc, 0, sizeof(mcc));
+ memset(mnc, 0, sizeof(mnc));
+
+ strncpy(mcc, numeric_bcd_string, 3);
+ strncpy(mnc, numeric_bcd_string+3, 2);
+
+ strncpy(op->alnum_long, alnum_long, sizeof(op->alnum_long-1));
+ op->numeric.mcc = atoi(mcc);
+ op->numeric.mnc = atoi(mnc);
+
+ return _opname_add(g, op);
+}
+
+int gsmd_opname_init(struct gsmd *g)
+{
+ INIT_LLIST_HEAD(&g->operators);
+
+ __opc_ctx = talloc_named_const(gsmd_tallocs, 1, "operator_cache");
+
+ return 0;
+}
diff --git a/src/gsmd/unsolicited.c b/src/gsmd/unsolicited.c
index 82f45f4..c355c71 100644
--- a/src/gsmd/unsolicited.c
+++ b/src/gsmd/unsolicited.c
@@ -31,6 +31,7 @@
#include <gsmd/usock.h>
#include <gsmd/event.h>
+#include <gsmd/extrsp.h>
#include <gsmd/ts0707.h>
#include <gsmd/unsolicited.h>
#include <gsmd/talloc.h>
@@ -308,6 +309,28 @@ static int ctzv_parse(char *buf, int len, const char *param,
return usock_evt_send(gsmd, ucmd, GSMD_EVT_TIMEZONE);
}
+static int copn_parse(char *buf, int len, const char *param,
+ struct gsmd *gsmd)
+{
+ struct gsm_extrsp *er = extrsp_parse(gsmd_tallocs, param);
+ int rc = 0;
+
+ if (!er)
+ return -ENOMEM;
+
+ extrsp_dump(er);
+
+ if (er->num_tokens == 2 &&
+ er->tokens[0].type == GSMD_ECMD_RTT_STRING &&
+ er->tokens[1].type == GSMD_ECMD_RTT_STRING)
+ rc = gsmd_opname_add(gsmd, er->tokens[0].u.string,
+ er->tokens[1].u.string);
+
+ talloc_free(er);
+
+ return rc;
+}
+
static const struct gsmd_unsolicit gsm0707_unsolicit[] = {
{ "RING", &ring_parse },
{ "+CRING", &cring_parse },
@@ -320,6 +343,7 @@ static const struct gsmd_unsolicit gsm0707_unsolicit[] = {
{ "+CLIP", &clip_parse },
{ "+COLP", &colp_parse },
{ "+CTZV", &ctzv_parse }, /* Timezone */
+ { "+COPN", &copn_parse }, /* operator names, treat as unsolicited */
/*
{ "+CKEV", &ckev_parse },
{ "+CDEV", &cdev_parse },
diff --git a/src/gsmd/vendor_ti.c b/src/gsmd/vendor_ti.c
index 6617edf..0cc0783 100644
--- a/src/gsmd/vendor_ti.c
+++ b/src/gsmd/vendor_ti.c
@@ -23,6 +23,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <stdio.h>
#include <errno.h>
#include "gsmd.h"
@@ -30,6 +31,9 @@
#include <gsmd/gsmd.h>
#include <gsmd/usock.h>
#include <gsmd/event.h>
+#include <gsmd/talloc.h>
+#include <gsmd/extrsp.h>
+#include <gsmd/atcmd.h>
#include <gsmd/vendorplugin.h>
#include <gsmd/unsolicited.h>
@@ -214,7 +218,7 @@ out_send:
return 0;
out_free_io:
- free(ucmd);
+ talloc_free(ucmd);
return -EIO;
}
@@ -237,7 +241,26 @@ static const struct gsmd_unsolicit ticalypso_unsolicit[] = {
static int cpi_detect_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
{
+ struct gsmd *g = ctx;
+ struct gsm_extrsp *er;
+
+ if (strncmp(resp, "%CPI: ", 6))
+ return -EINVAL;
+ resp += 6;
+ er = extrsp_parse(cmd, resp);
+ if (!er)
+ return -EINVAL;
+
+ if (extrsp_supports(er, 0, 3))
+ return gsmd_simplecmd(g, "AT%CPI=3");
+ else if (extrsp_supports(er, 0, 2))
+ return gsmd_simplecmd(g, "AT%CPI=2");
+ else
+ DEBUGP("Call Progress Indication mode 2 or 3 not supported!!\n");
+
+ talloc_free(er);
+ return 0;
}
static int ticalypso_detect(struct gsmd *g)
@@ -249,16 +272,25 @@ static int ticalypso_detect(struct gsmd *g)
static int ticalypso_initsettings(struct gsmd *g)
{
int rc;
+ struct gsmd_atcmd *cmd;
+
/* use +CTZR: to report time zone changes */
rc |= gsmd_simplecmd(g, "AT+CTZR=1");
- /* enable %CPI: call progress indication */
- rc = gsmd_simplecmd(g, "AT\%CPI=3");
+ /* use %CTZV: Report time and date */
+ rc |= gsmd_simplecmd(g, "AT%CTZV=1");
+ /* use %CGREG */
+ //rc |= gsmd_simplecmd(g, "AT%CGREG=3");
/* enable %CPRI: ciphering indications */
- rc |= gsmd_simplecmd(g, "AT\%CPRI=1");
+ rc |= gsmd_simplecmd(g, "AT%CPRI=1");
/* enable %CSQ: signal quality reports */
- rc |= gsmd_simplecmd(g, "AT\%CSQ=1");
+ rc |= gsmd_simplecmd(g, "AT%CSQ=1");
/* send unsolicited commands at any time */
- rc |= gsmd_simplecmd(g, "AT\%CUNS=0");
+ rc |= gsmd_simplecmd(g, "AT%CUNS=0");
+
+ /* enable %CPI: call progress indication */
+ cmd = atcmd_fill("AT%CPI=?", 9, &cpi_detect_cb, g, 0);
+ if (cmd)
+ atcmd_submit(g, cmd);
return rc;
}
personal git repositories of Harald Welte. Your mileage may vary