summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/gsmd/usock.h24
-rw-r--r--include/libgsmd/misc.h33
-rw-r--r--src/gsmd/usock.c128
-rw-r--r--src/libgsmd/lgsm_internals.h2
-rw-r--r--src/libgsmd/libgsmd_event.c7
-rw-r--r--src/libgsmd/libgsmd_network.c45
-rw-r--r--src/util/shell.c77
7 files changed, 263 insertions, 53 deletions
diff --git a/include/gsmd/usock.h b/include/gsmd/usock.h
index f42a766..b60e28e 100644
--- a/include/gsmd/usock.h
+++ b/include/gsmd/usock.h
@@ -66,7 +66,9 @@ enum gsmd_msg_network {
GSMD_NETWORK_VMAIL_GET = 3,
GSMD_NETWORK_VMAIL_SET = 4,
GSMD_NETWORK_OPER_GET = 5,
- GSMD_NETWORK_CIND_GET = 6,
+ GSMD_NETWORK_OPER_LIST = 6,
+ GSMD_NETWORK_CIND_GET = 7,
+ GSMD_NETWORK_DEREGISTER = 8,
};
enum gsmd_msg_sms {
@@ -358,6 +360,26 @@ struct gsmd_phonebook_support {
u_int8_t tlength;
} __attribute__ ((packed));
+/* Operator status from 3GPP TS 07.07, Clause 7.3 */
+enum gsmd_oper_status {
+ GSMD_OPER_UNKNOWN,
+ GSMD_OPER_AVAILABLE,
+ GSMD_OPER_CURRENT,
+ GSMD_OPER_FORBIDDEN,
+};
+
+/* Theoretically numeric operator code is five digits long but some
+ * operators apparently use six digit codes. */
+typedef char gsmd_oper_numeric[6];
+
+struct gsmd_msg_oper {
+ enum gsmd_oper_status stat;
+ int is_last;
+ char opname_longalpha[16];
+ char opname_shortalpha[8];
+ gsmd_oper_numeric opname_num;
+};
+
struct gsmd_msg_hdr {
u_int8_t version;
u_int8_t msg_type;
diff --git a/include/libgsmd/misc.h b/include/libgsmd/misc.h
index cbc2df0..672b980 100644
--- a/include/libgsmd/misc.h
+++ b/include/libgsmd/misc.h
@@ -10,21 +10,6 @@
extern int lgsm_phone_power(struct lgsm_handle *lh, int power);
-enum lgsm_netreg_state {
- LGSM_NETREG_ST_NOTREG = 0,
- LGSM_NETREG_ST_REG_HOME = 1,
- LGSM_NETREG_ST_NOTREG_SEARCH = 2,
- LGSM_NETREG_ST_DENIED = 3,
- LGSM_NETREG_ST_UNKNOWN = 4,
- LGSM_NETREG_ST_REG_ROAMING = 5,
-};
-
-/* Get the current network registration status */
-extern int lgsm_get_netreg_state(struct lgsm_handle *lh,
- enum lgsm_netreg_state *state);
-
-extern int lgsm_netreg_register(struct lgsm_handle *lh, int oper);
-
enum lgsm_info_type {
LGSM_INFO_TYPE_NONE = 0,
LGSM_INFO_TYPE_MANUF = 1,
@@ -58,8 +43,24 @@ extern int lgsm_voicemail_get(struct lgsm_handle *lh,
struct lgsm_addr *addr);
/* Operator Selection, Network Registration */
-/* TBD */
+extern int lgsm_oper_get(struct lgsm_handle *lh);
+extern int lgsm_opers_get(struct lgsm_handle *lh);
+extern int lgsm_netreg_register(struct lgsm_handle *lh,
+ gsmd_oper_numeric oper);
+extern int lgsm_netreg_deregister(struct lgsm_handle *lh);
+enum lgsm_netreg_state {
+ LGSM_NETREG_ST_NOTREG = 0,
+ LGSM_NETREG_ST_REG_HOME = 1,
+ LGSM_NETREG_ST_NOTREG_SEARCH = 2,
+ LGSM_NETREG_ST_DENIED = 3,
+ LGSM_NETREG_ST_UNKNOWN = 4,
+ LGSM_NETREG_ST_REG_ROAMING = 5,
+};
+
+/* Get the current network registration status */
+extern int lgsm_get_netreg_state(struct lgsm_handle *lh,
+ enum lgsm_netreg_state *state);
/* CLIP, CLIR, COLP, Call Forwarding, Call Waiting, Call Deflecting */
/* TBD */
diff --git a/src/gsmd/usock.c b/src/gsmd/usock.c
index 8496dd1..bd5bc31 100644
--- a/src/gsmd/usock.c
+++ b/src/gsmd/usock.c
@@ -349,7 +349,7 @@ static int network_sigq_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
return -ENOMEM;
gsq = (struct gsmd_signal_quality *) ucmd->buf;
- gsq->rssi = atoi(resp);
+ gsq->rssi = atoi(resp + 6);
comma = strchr(resp, ',');
if (!comma) {
talloc_free(ucmd);
@@ -362,42 +362,104 @@ static int network_sigq_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
return 0;
}
-#define GSMD_OPER_MAXLEN 16
static int network_oper_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
{
struct gsmd_user *gu = ctx;
struct gsmd_ucmd *ucmd;
- char *comma, *opname;
-
- ucmd = gsmd_ucmd_fill(GSMD_OPER_MAXLEN+1, GSMD_MSG_NETWORK,
- GSMD_NETWORK_OPER_GET, 0);
+ const char *end, *opname;
+ int format, s;
+
+ /* Format: <mode>[,<format>,<oper>] */
+ /* In case we're not registered, return an empty string. */
+ if (sscanf(resp, "+COPS: %*i,%i,\"%n", &format, &s) <= 0)
+ end = opname = resp;
+ else {
+ /* If the phone returned the opname in a short or numeric
+ * format, then it probably doesn't know the operator's full
+ * name or doesn't support it. Return any information we
+ * have in this case. */
+ if (format != 0)
+ gsmd_log(GSMD_NOTICE, "+COPS response in a format "
+ " different than long alphanumeric - "
+ " returning as is!\n");
+ opname = resp + s;
+ end = strchr(opname, '"');
+ if (!end)
+ return -EINVAL;
+ }
+
+ ucmd = gsmd_ucmd_fill(end - opname + 1, GSMD_MSG_NETWORK,
+ GSMD_NETWORK_OPER_GET, 0);
if (!ucmd)
return -ENOMEM;
- /* Format: <mode>[, <format>, <oper>] */
- comma = strchr(resp, ',');
- if (!comma)
- goto out_err;
+ memcpy(ucmd->buf, opname, end - opname);
+ ucmd->buf[end - opname] = '\0';
+
+ usock_cmd_enqueue(ucmd, gu);
- if (atoi(comma+1) != 0) {
- gsmd_log(GSMD_NOTICE, "COPS format !=0 not supported yet!\n");
- goto out_err;
+ return 0;
+}
+
+static int network_opers_parse(const char *str, struct gsmd_msg_oper out[])
+{
+ int len = 0;
+ int stat, n;
+ if (strncmp(str, "+COPS: ", 7))
+ goto final;
+ str += 7;
+
+ while (*str == '(') {
+ if (out) {
+ out->is_last = 0;
+ if (sscanf(str,
+ "(%i,\"%16[^\"]\","
+ "\"%8[^\"]\",\"%6[0-9]\")%n",
+ &stat,
+ out->opname_longalpha,
+ out->opname_shortalpha,
+ out->opname_num,
+ &n) < 4)
+ goto final;
+ out->stat = stat;
+ } else
+ if (sscanf(str,
+ "(%*i,\"%*[^\"]\","
+ "\"%*[^\"]\",\"%*[0-9]\")%n",
+ &n) < 0)
+ goto final;
+ if (n < 10 || str[n - 1] != ')')
+ goto final;
+ if (str[n] == ',')
+ n ++;
+ str += n;
+ len ++;
+ if (out)
+ out ++;
}
- comma = strchr(resp, ',');
- if (!comma || *(comma+1) != '"')
- goto out_err;
- opname = comma+2;
+final:
+ if (out)
+ out->is_last = 1;
+ return len;
+}
- memcpy(ucmd->buf, opname, strlen(opname-1));
- ucmd->buf[strlen(opname)] = '\0';
+static int network_opers_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
+{
+ struct gsmd_user *gu = ctx;
+ struct gsmd_ucmd *ucmd;
+ int len;
+
+ len = network_opers_parse(resp, 0);
+
+ ucmd = gsmd_ucmd_fill(sizeof(struct gsmd_msg_oper) * (len + 1),
+ GSMD_MSG_NETWORK, GSMD_NETWORK_OPER_LIST, 0);
+ if (!ucmd)
+ return -ENOMEM;
+ network_opers_parse(resp, (struct gsmd_msg_oper *) ucmd->buf);
usock_cmd_enqueue(ucmd, gu);
return 0;
-
-out_err:
- talloc_free(ucmd);
- return -EIO;
}
static int usock_rcv_network(struct gsmd_user *gu, struct gsmd_msg_hdr *gph,
@@ -405,11 +467,21 @@ static int usock_rcv_network(struct gsmd_user *gu, struct gsmd_msg_hdr *gph,
{
struct gsmd_atcmd *cmd;
struct gsmd_voicemail *vmail = (struct gsmd_voicemail *) gph->data;
+ gsmd_oper_numeric *oper = (gsmd_oper_numeric *) gph->data;
+ char buffer[15 + sizeof(gsmd_oper_numeric)];
+ int cmdlen;
switch (gph->msg_subtype) {
case GSMD_NETWORK_REGISTER:
- cmd = atcmd_fill("AT+COPS=0", 9+1,
- &null_cmd_cb, gu, 0);
+ if ((*oper)[0])
+ cmdlen = sprintf(buffer, "AT+COPS=1,2,\"%.*s\"",
+ sizeof(gsmd_oper_numeric), oper);
+ else
+ cmdlen = sprintf(buffer, "AT+COPS=0");
+ cmd = atcmd_fill(buffer, cmdlen + 1, &null_cmd_cb, gu, 0);
+ break;
+ case GSMD_NETWORK_DEREGISTER:
+ cmd = atcmd_fill("AT+COPS=2", 9+1, &null_cmd_cb, gu, 0);
break;
case GSMD_NETWORK_VMAIL_GET:
cmd = atcmd_fill("AT+CSVM?", 8+1, &network_vmail_cb, gu, 0);
@@ -421,8 +493,14 @@ static int usock_rcv_network(struct gsmd_user *gu, struct gsmd_msg_hdr *gph,
cmd = atcmd_fill("AT+CSQ", 6+1, &network_sigq_cb, gu, 0);
break;
case GSMD_NETWORK_OPER_GET:
+ /* Set long alphanumeric format */
+ atcmd_submit(gu->gsmd, atcmd_fill("AT+COPS=3,0", 11+1,
+ &null_cmd_cb, gu, 0));
cmd = atcmd_fill("AT+COPS?", 8+1, &network_oper_cb, gu, 0);
break;
+ case GSMD_NETWORK_OPER_LIST:
+ cmd = atcmd_fill("AT+COPS=?", 9+1, &network_opers_cb, gu, 0);
+ break;
default:
return -EINVAL;
}
diff --git a/src/libgsmd/lgsm_internals.h b/src/libgsmd/lgsm_internals.h
index bf1f4a2..94421fa 100644
--- a/src/libgsmd/lgsm_internals.h
+++ b/src/libgsmd/lgsm_internals.h
@@ -2,10 +2,12 @@
#define _LGSM_INTERNALS_H
#include <gsmd/usock.h>
+#include <libgsmd/misc.h>
struct lgsm_handle {
int fd;
lgsm_msg_handler *handler[__NUM_GSMD_MSGS];
+ enum lgsm_netreg_state netreg_state;
};
int lgsm_send(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh);
diff --git a/src/libgsmd/libgsmd_event.c b/src/libgsmd/libgsmd_event.c
index c751cad..777edd8 100644
--- a/src/libgsmd/libgsmd_event.c
+++ b/src/libgsmd/libgsmd_event.c
@@ -63,6 +63,12 @@ static int evt_demux_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh
gmh->msg_subtype >= __NUM_GSMD_EVT)
return -EINVAL;
+ switch (gmh->msg_subtype) {
+ case GSMD_EVT_NETREG:
+ lh->netreg_state = aux->u.netreg.state;
+ break;
+ }
+
if (evt_handlers[gmh->msg_subtype])
return evt_handlers[gmh->msg_subtype](lh, gmh->msg_subtype, aux);
else
@@ -71,6 +77,7 @@ static int evt_demux_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh
int lgsm_evt_init(struct lgsm_handle *lh)
{
+ lh->netreg_state = LGSM_NETREG_ST_NOTREG;
return lgsm_register_handler(lh, GSMD_MSG_EVENT, &evt_demux_msghandler);
}
diff --git a/src/libgsmd/libgsmd_network.c b/src/libgsmd/libgsmd_network.c
index 800b58c..9a21fb5 100644
--- a/src/libgsmd/libgsmd_network.c
+++ b/src/libgsmd/libgsmd_network.c
@@ -32,18 +32,49 @@
#include "lgsm_internals.h"
-int lgsm_netreg_register(struct lgsm_handle *lh, int oper)
+/* Get the current network registration status */
+int lgsm_get_netreg_state(struct lgsm_handle *lh,
+ enum lgsm_netreg_state *state)
{
- /* FIXME: implement oper selection */
- return lgsm_send_simple(lh, GSMD_MSG_NETWORK, GSMD_NETWORK_REGISTER);
+ *state = lh->netreg_state;
}
-int lgsm_signal_quality(struct lgsm_handle *lh)
+int lgsm_oper_get(struct lgsm_handle *lh)
{
- return lgsm_send_simple(lh, GSMD_MSG_NETWORK, GSMD_NETWORK_SIGQ_GET);
+ return lgsm_send_simple(lh, GSMD_MSG_NETWORK, GSMD_NETWORK_OPER_GET);
}
-int lgsmd_operator_name(struct lgsm_handle *lh)
+int lgsm_opers_get(struct lgsm_handle *lh)
{
- return lgsm_send_simple(lh, GSMD_MSG_NETWORK, GSMD_NETWORK_OPER_GET);
+ return lgsm_send_simple(lh, GSMD_MSG_NETWORK, GSMD_NETWORK_OPER_LIST);
+}
+
+int lgsm_netreg_register(struct lgsm_handle *lh, gsmd_oper_numeric oper)
+{
+ struct gsmd_msg_hdr *gmh;
+
+ gmh = lgsm_gmh_fill(GSMD_MSG_NETWORK, GSMD_NETWORK_REGISTER,
+ sizeof(gsmd_oper_numeric));
+ if (!gmh)
+ return -ENOMEM;
+
+ memcpy(gmh->data, oper, sizeof(gsmd_oper_numeric));
+
+ if (lgsm_send(lh, gmh) < gmh->len + sizeof(*gmh)) {
+ lgsm_gmh_free(gmh);
+ return -EIO;
+ }
+
+ lgsm_gmh_free(gmh);
+ return 0;
+}
+
+int lgsm_netreg_deregister(struct lgsm_handle *lh)
+{
+ return lgsm_send_simple(lh, GSMD_MSG_NETWORK, GSMD_NETWORK_DEREGISTER);
+}
+
+int lgsm_signal_quality(struct lgsm_handle *lh)
+{
+ return lgsm_send_simple(lh, GSMD_MSG_NETWORK, GSMD_NETWORK_SIGQ_GET);
}
diff --git a/src/util/shell.c b/src/util/shell.c
index 3f83d30..b2ebfd8 100644
--- a/src/util/shell.c
+++ b/src/util/shell.c
@@ -171,6 +171,54 @@ static int sms_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh)
}
}
+/* this is the handler for responses to network/operator commands */
+static int net_msghandler(struct lgsm_handle *lh, struct gsmd_msg_hdr *gmh)
+{
+ const struct gsmd_signal_quality *sq = (struct gsmd_signal_quality *)
+ ((void *) gmh + sizeof(*gmh));
+ const char *oper = (char *) gmh + sizeof(*gmh);
+ const struct gsmd_msg_oper *opers = (struct gsmd_msg_oper *)
+ ((void *) gmh + sizeof(*gmh));
+ static const char *oper_stat[] = {
+ [GSMD_OPER_UNKNOWN] = "of unknown status",
+ [GSMD_OPER_AVAILABLE] = "available",
+ [GSMD_OPER_CURRENT] = "our current operator",
+ [GSMD_OPER_FORBIDDEN] = "forbidden",
+ };
+
+ switch (gmh->msg_subtype) {
+ case GSMD_NETWORK_SIGQ_GET:
+ if (sq->rssi == 99)
+ printf("Signal undetectable\n");
+ else
+ printf("Signal quality %i dBm\n", -113 + sq->rssi * 2);
+ if (sq->ber == 99)
+ printf("Error rate undetectable\n");
+ else
+ printf("Bit error rate %i\n", sq->ber);
+ break;
+ case GSMD_NETWORK_OPER_GET:
+ if (oper[0])
+ printf("Our current operator is %s\n", oper);
+ else
+ printf("No current operator\n");
+ break;
+ case GSMD_NETWORK_OPER_LIST:
+ for (; !opers->is_last; opers ++)
+ printf("%8.*s %16.*s, %.*s for short, is %s\n",
+ sizeof(opers->opname_num),
+ opers->opname_num,
+ sizeof(opers->opname_longalpha),
+ opers->opname_longalpha,
+ sizeof(opers->opname_shortalpha),
+ opers->opname_shortalpha,
+ oper_stat[opers->stat]);
+ break;
+ default:
+ return -EINVAL;
+ }
+}
+
static int shell_help(void)
{
printf( "\tA\tAnswer incoming call\n"
@@ -178,8 +226,12 @@ static int shell_help(void)
"\tH\tHangup call\n"
"\tO\tPower On\n"
"\to\tPower Off\n"
- "\tR\tRegister Network\n"
+ "\tr\tRegister to network\n"
+ "\tR\tRegister to given operator (R=number)\n"
"\tU\tUnregister from netowrk\n"
+ "\tP\tPrint current operator\n"
+ "\tL\tDetect available operators\n"
+ "\tQ\tRead signal quality\n"
"\tT\tSend DTMF Tone\n"
"\tpd\tPB Delete (pb=index)\n"
"\tpr\tPB Read (pr=index)\n"
@@ -208,6 +260,7 @@ int shell_main(struct lgsm_handle *lgsmh)
lgsm_register_handler(lgsmh, GSMD_MSG_PASSTHROUGH, &pt_msghandler);
lgsm_register_handler(lgsmh, GSMD_MSG_PHONEBOOK, &pb_msghandler);
lgsm_register_handler(lgsmh, GSMD_MSG_SMS, &sms_msghandler);
+ lgsm_register_handler(lgsmh, GSMD_MSG_NETWORK, &net_msghandler);
fcntl(0, F_SETFD, O_NONBLOCK);
fcntl(lgsm_fd(lgsmh), F_SETFD, O_NONBLOCK);
@@ -272,12 +325,28 @@ int shell_main(struct lgsm_handle *lgsmh)
} else if (!strcmp(buf, "o")) {
printf("Power-Off\n");
lgsm_phone_power(lgsmh, 0);
- } else if (!strcmp(buf, "R")) {
+ } else if (!strcmp(buf, "r")) {
printf("Register\n");
- lgsm_netreg_register(lgsmh, 0);
+ lgsm_netreg_register(lgsmh, "\0 ");
+ } else if (buf[0] == 'R') {
+ printf("Register to operator\n");
+ ptr = strchr(buf, '=');
+ if (!ptr || strlen(ptr) < 6)
+ printf("No.\n");
+ else
+ lgsm_netreg_register(lgsmh, ptr + 1);
} else if (!strcmp(buf, "U")) {
printf("Unregister\n");
- lgsm_netreg_register(lgsmh, 2);
+ lgsm_netreg_deregister(lgsmh);
+ } else if (!strcmp(buf, "P")) {
+ printf("Read current opername\n");
+ lgsm_oper_get(lgsmh);
+ } else if (!strcmp(buf, "L")) {
+ printf("List operators\n");
+ lgsm_opers_get(lgsmh);
+ } else if (!strcmp(buf, "Q")) {
+ printf("Signal strength\n");
+ lgsm_signal_quality(lgsmh);
} else if (!strcmp(buf, "q")) {
exit(0);
} else if (buf[0] == 'T') {
personal git repositories of Harald Welte. Your mileage may vary