diff options
-rw-r--r-- | include/gsmd/usock.h | 24 | ||||
-rw-r--r-- | include/libgsmd/misc.h | 33 | ||||
-rw-r--r-- | src/gsmd/usock.c | 128 | ||||
-rw-r--r-- | src/libgsmd/lgsm_internals.h | 2 | ||||
-rw-r--r-- | src/libgsmd/libgsmd_event.c | 7 | ||||
-rw-r--r-- | src/libgsmd/libgsmd_network.c | 45 | ||||
-rw-r--r-- | src/util/shell.c | 77 |
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') { |