From a04e6effed0b81ec7495df2a1448484645b20cc5 Mon Sep 17 00:00:00 2001 From: laforge Date: Fri, 12 Jan 2007 23:57:33 +0000 Subject: - use talloc (of samba project) to debug memory allocations and simplify code - introduce new ucmd_alloc() function - add DTMF support to gsmd, libgsmd and gsmd-util - fix crash of libgsmd when events don't have handlers registered - implement call progress for TI modem - split modem init string in separate commands to fit our parser git-svn-id: http://svn.openmoko.org/trunk/src/target/gsm@544 99fdad57-331a-0410-800a-d7fa5415bdb3 --- include/gsmd/event.h | 21 ++++++++++ include/gsmd/gsmd.h | 4 ++ include/gsmd/usock.h | 14 +++++++ include/libgsmd/voicecall.h | 3 ++ src/gsmd/atcmd.c | 58 +++++++++++++++---------- src/gsmd/gsmd.c | 45 +++++++++++++++++++- src/gsmd/sms_cb.c | 3 +- src/gsmd/unsolicited.c | 35 ++++++++++------ src/gsmd/usock.c | 59 ++++++++++++++++++++++---- src/gsmd/vendor_ti.c | 93 ++++++++++++++++++++++++++++++++++++++--- src/libgsmd/libgsmd_voicecall.c | 25 +++++++++++ src/util/atcmd.c | 5 +++ src/util/event.c | 49 ++++++++++++++++++++++ src/util/shell.c | 19 ++++++++- 14 files changed, 378 insertions(+), 55 deletions(-) diff --git a/include/gsmd/event.h b/include/gsmd/event.h index 6ae4e02..acffba5 100644 --- a/include/gsmd/event.h +++ b/include/gsmd/event.h @@ -55,4 +55,25 @@ enum gsmd_netreg_state { __NUM_GSMD_NETREG }; +enum gsmd_call_progress { + GSMD_CALLPROG_SETUP = 0, + GSMD_CALLPROG_DISCONNECT = 1, + GSMD_CALLPROG_ALERT = 2, + GSMD_CALLPROG_CALL_PROCEED = 3, + GSMD_CALLPROG_SYNC = 4, + GSMD_CALLPROG_PROGRESS = 5, + GSMD_CALLPROG_CONNECTED = 6, + GSMD_CALLPROG_RELEASE = 7, + GSMD_CALLPROG_REJECT = 8, + GSMD_CALLPROG_UNKNOWN = 9, + __NUM_GSMD_CALLPROG +}; + +enum gsmd_call_direction { + GSMD_CALL_DIR_MO = 0, /* Mobile Originated (Outgoing) */ + GSMD_CALL_DIR_MT = 1, /* Mobile Terminated (Incoming) */ + GSMD_CALL_DIR_CCBS = 2, /* network initiated MO */ + GSMD_CALL_DIR_MO_REDIAL = 3, /* Mobile Originated Redial */ +}; + #endif diff --git a/include/gsmd/gsmd.h b/include/gsmd/gsmd.h index 9b99a15..22058b2 100644 --- a/include/gsmd/gsmd.h +++ b/include/gsmd/gsmd.h @@ -10,6 +10,8 @@ #include #include +void *gsmd_tallocs; + /* Refer to 3GPP TS 07.07 v 7.8.0, Chapter 4.1 */ #define LGSM_ATCMD_F_EXTENDED 0x01 /* as opposed to basic */ #define LGSM_ATCMD_F_PARAM 0x02 /* as opposed to action */ @@ -87,6 +89,8 @@ void __gsmd_log(int level, const char *file, int line, const char *function, con #define DEBUGP(x, args ...) gsmd_log(GSMD_DEBUG, x, ## args) +extern int gsmd_simplecmd(struct gsmd *gsmd, char *cmdtxt); + #endif /* __GSMD__ */ #endif /* _GSMD_H */ diff --git a/include/gsmd/usock.h b/include/gsmd/usock.h index 94e8c37..457cc01 100644 --- a/include/gsmd/usock.h +++ b/include/gsmd/usock.h @@ -34,6 +34,7 @@ enum gsmd_msg_voicecall_type { GSMD_VOICECALL_DIAL = 1, GSMD_VOICECALL_HANGUP = 2, GSMD_VOICECALL_ANSWER = 3, + GSMD_VOICECALL_DTMF = 4, }; /* Handset / MT related commands */ @@ -66,6 +67,11 @@ struct gsmd_addr { char number[GSMD_ADDR_MAXLEN+1]; } __attribute__ ((packed)); +struct gsmd_dtmf { + u_int8_t len; + char dtmf[0]; +} __attribute__ ((packed)); + struct gsmd_signal_quality { u_int8_t rssi; u_int8_t ber; @@ -105,6 +111,13 @@ struct gsmd_evt_auxdata { struct { struct gsmd_signal_quality sigq; } signal; + struct { + enum gsmd_call_progress prog; + struct gsmd_addr addr; + u_int8_t ibt:1, + tch:1, + dir:2; + } call_status; } u; } __attribute__((packed)); @@ -134,6 +147,7 @@ struct gsmd_ucmd { char buf[]; } __attribute__ ((packed)); +extern struct gsmd_ucmd *ucmd_alloc(int extra_size); extern int usock_init(struct gsmd *g); extern void usock_cmd_enqueue(struct gsmd_ucmd *ucmd, struct gsmd_user *gu); extern struct gsmd_ucmd *usock_build_event(u_int8_t type, u_int8_t subtype, u_int8_t len); diff --git a/include/libgsmd/voicecall.h b/include/libgsmd/voicecall.h index 4fffe4e..4141283 100644 --- a/include/libgsmd/voicecall.h +++ b/include/libgsmd/voicecall.h @@ -15,4 +15,7 @@ extern int lgsm_voice_in_accept(struct lgsm_handle *lh); /* Terminate outgoing (or incoming) voice call */ extern int lgsm_voice_hangup(struct lgsm_handle *lh); +/* Send DTMF character during voice call */ +extern int lgsm_voice_dtmf(struct lgsm_handle *lh, char dtmf_char); + #endif diff --git a/src/gsmd/atcmd.c b/src/gsmd/atcmd.c index 7f95465..c5c0a04 100644 --- a/src/gsmd/atcmd.c +++ b/src/gsmd/atcmd.c @@ -14,6 +14,7 @@ #include #include #include +#include #include /* libgsmd / gsmd AT command interpreter / parser / constructor @@ -22,6 +23,8 @@ * Written for First International Computer, Inc., Taiwan */ +static void *__atcmd_ctx; + enum final_result_codes { GSMD_RESULT_OK = 0, GSMD_RESULT_ERR = 1, @@ -153,7 +156,7 @@ static int parse_final_result(const char *res) static int ml_parse(const char *buf, int len, void *ctx) { struct gsmd *g = ctx; - struct gsmd_atcmd *cmd; + struct gsmd_atcmd *cmd = NULL; int rc = 0, final = 0; DEBUGP("buf=`%s'(%d)\n", buf, len); @@ -165,7 +168,9 @@ static int ml_parse(const char *buf, int len, void *ctx) /* responses come in order, so first response has to be for first * command we sent, i.e. first entry in list */ - cmd = llist_entry(g->busy_atcmds.next, struct gsmd_atcmd, list); + if (!llist_empty(&g->busy_atcmds)) + cmd = llist_entry(g->busy_atcmds.next, + struct gsmd_atcmd, list); /* we have to differentiate between the following cases: * @@ -194,15 +199,16 @@ static int ml_parse(const char *buf, int len, void *ctx) unsigned long err_nr; err_nr = strtoul(colon+1, NULL, 10); DEBUGP("error number %lu\n", err_nr); - cmd->ret = err_nr; + if (cmd) + cmd->ret = err_nr; final = 1; goto final_cb; } - if (strncmp(buf, &cmd->buf[2], colon-buf)) { + if (!cmd || strncmp(buf, &cmd->buf[2], colon-buf)) { /* Assuming Case 'B' */ DEBUGP("extd reply `%s' to cmd `%s', must be " - "unsolicited\n", buf, &cmd->buf[2]); + "unsolicited\n", buf, cmd ? &cmd->buf[2] : "NONE"); colon++; if (colon > buf+len) colon = NULL; @@ -216,19 +222,21 @@ static int ml_parse(const char *buf, int len, void *ctx) /* contine, not 'B' */ } - if (cmd->buf[2] != '+') { - gsmd_log(GSMD_ERROR, "extd reply to non-extd command?\n"); - return -EINVAL; - } + if (cmd) { + if (cmd->buf[2] != '+') { + gsmd_log(GSMD_ERROR, "extd reply to non-extd command?\n"); + return -EINVAL; + } - /* if we survive till here, it's a valid extd response - * to an extended command and thus Case 'A' */ + /* if we survive till here, it's a valid extd response + * to an extended command and thus Case 'A' */ - /* FIXME: solve multi-line responses ! */ - if (cmd->buflen < len) - len = cmd->buflen; + /* FIXME: solve multi-line responses ! */ + if (cmd->buflen < len) + len = cmd->buflen; - memcpy(cmd->buf, buf, len); + memcpy(cmd->buf, buf, len); + } } else { if (!strcmp(buf, "RING")) { /* this is the only non-extended unsolicited return @@ -237,18 +245,20 @@ static int ml_parse(const char *buf, int len, void *ctx) } if (!strcmp(buf, "ERROR") || - ((g->flags & GSMD_FLAG_V0) && cmd->buf[0] == '4')) { + ((g->flags & GSMD_FLAG_V0) && buf[0] == '4')) { /* Part of Case 'C' */ DEBUGP("unspecified error\n"); - cmd->ret = 4; + if (cmd) + cmd->ret = 4; final = 1; goto final_cb; } if (!strncmp(buf, "OK", 2) - || ((g->flags & GSMD_FLAG_V0) && cmd->buf[0] == '0')) { + || ((g->flags & GSMD_FLAG_V0) && buf[0] == '0')) { /* Part of Case 'C' */ - cmd->ret = 0; + if (cmd) + cmd->ret = 0; final = 1; goto final_cb; } @@ -274,8 +284,10 @@ static int ml_parse(const char *buf, int len, void *ctx) final_cb: /* if we reach here, the final result code of a command has been reached */ + if (!cmd) + return rc; - if (cmd->ret != 0) + if (cmd && cmd->ret != 0) generate_event_from_cme(g, cmd->ret); if (!cmd->cb) { @@ -292,7 +304,7 @@ final_cb: if (final) { /* remove from list of currently executing cmds */ llist_del(&cmd->list); - free(cmd); + talloc_free(cmd); /* if we're finished with current commands, but still have pending * commands: we want to WRITE again */ @@ -377,7 +389,7 @@ struct gsmd_atcmd *atcmd_fill(const char *cmd, int rlen, if (rlen > buflen) buflen = rlen; - atcmd = malloc(sizeof(*atcmd)+ buflen); + atcmd = talloc_size(__atcmd_ctx, sizeof(*atcmd)+ buflen); if (!atcmd) return NULL; @@ -421,6 +433,8 @@ void atcmd_drain(int fd) /* init atcmd parser */ int atcmd_init(struct gsmd *g, int sockfd) { + __atcmd_ctx = talloc_named_const(gsmd_tallocs, 1, "atcmds"); + g->gfd_uart.fd = sockfd; g->gfd_uart.when = GSMD_FD_READ; g->gfd_uart.data = g; diff --git a/src/gsmd/gsmd.c b/src/gsmd/gsmd.c index c56e74c..a16e6b5 100644 --- a/src/gsmd/gsmd.c +++ b/src/gsmd/gsmd.c @@ -5,6 +5,7 @@ #include #include #include +#include #define _GNU_SOURCE #include @@ -19,6 +20,7 @@ #include #include #include +#include static int gsmd_test_atcb(struct gsmd_atcmd *cmd, void *ctx, char *resp) { @@ -40,8 +42,22 @@ int gsmd_initsettings(struct gsmd *gsmd) { int rc; + /* echo on, verbose */ rc |= gsmd_simplecmd(gsmd, "ATE0V1"); - rc |= gsmd_simplecmd(gsmd, "AT+CRC=1;+CREG=2;+CMEE=1;+CLIP=1;+COLP=1;+CTZR=1;+CFUN=1"); + /* use +CRING instead of RING */ + rc |= gsmd_simplecmd(gsmd, "AT+CRC=1"); + /* enable +CREG: unsolicited response if registration status changes */ + rc |= gsmd_simplecmd(gsmd, "AT+CREG=2"); + /* use +CME ERROR: instead of ERROR */ + rc |= gsmd_simplecmd(gsmd, "AT+CMEE=1"); + /* use +CLIP: to indicate CLIP */ + rc |= gsmd_simplecmd(gsmd, "AT+CLIP=1"); + /* use +COLP: to indicate COLP */ + rc |= gsmd_simplecmd(gsmd, "AT+COLP=1"); + /* use +CTZR: to report time zone changes */ + rc |= gsmd_simplecmd(gsmd, "AT+CTZR=1"); + /* power on the phone */ + rc |= gsmd_simplecmd(gsmd, "AT+CFUN=1"); if (gsmd->vendorpl && gsmd->vendorpl->initsettings) return gsmd->vendorpl->initsettings(gsmd); @@ -108,6 +124,7 @@ static struct option opts[] = { { "device", 1, NULL, 'p' }, { "speed", 1, NULL, 's' }, { "logfile", 1, NULL, 'l' }, + { "leak-report", 0, NULL, 'L' }, { 0, 0, 0, 0 } }; @@ -125,6 +142,20 @@ static void print_help(void) ); } +static void sig_handler(int signr) +{ + switch (signr) { + case SIGTERM: + case SIGINT: + talloc_report_full(gsmd_tallocs, stderr); + exit(0); + break; + case SIGUSR1: + talloc_report_full(gsmd_tallocs, stderr); + break; + } +} + int main(int argc, char **argv) { int fd, argch; @@ -134,12 +165,22 @@ int main(int argc, char **argv) char *device = "/dev/ttyUSB0"; char *logfile = "syslog"; + signal(SIGTERM, sig_handler); + signal(SIGINT, sig_handler); + signal(SIGSEGV, sig_handler); + signal(SIGUSR1, sig_handler); + + gsmd_tallocs = talloc_named_const(NULL, 1, "GSMD"); + /*FIXME: parse commandline, set daemonize, device, ... */ - while ((argch = getopt_long(argc, argv, "Vdhp:s:l:", opts, NULL)) != -1) { + while ((argch = getopt_long(argc, argv, "VLdhp:s:l:", opts, NULL)) != -1) { switch (argch) { case 'V': /* FIXME */ break; + case 'L': + talloc_enable_leak_report_full(); + break; case 'd': daemonize = 1; break; diff --git a/src/gsmd/sms_cb.c b/src/gsmd/sms_cb.c index 5ca8956..5c62333 100644 --- a/src/gsmd/sms_cb.c +++ b/src/gsmd/sms_cb.c @@ -62,8 +62,7 @@ struct gsmd_sms_storage { static int usock_cpms_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) { struct gsmd_user *gu = ctx; - struct gsmd_ucmd *ucmd = malloc(sizeof(*ucmd) + - sizeof(struct gsmd_sms_storage)); + struct gsmd_ucmd *ucmd = ucmd_alloc(sizeof(struct gsmd_sms_storage)); DEBUGP("entering(cmd=%p, gu=%p)\n", cmd, gu); diff --git a/src/gsmd/unsolicited.c b/src/gsmd/unsolicited.c index 57a4e59..0c12f32 100644 --- a/src/gsmd/unsolicited.c +++ b/src/gsmd/unsolicited.c @@ -11,10 +11,11 @@ #include #include #include +#include struct gsmd_ucmd *usock_build_event(u_int8_t type, u_int8_t subtype, u_int8_t len) { - struct gsmd_ucmd *ucmd = malloc(sizeof(*ucmd)+len); + struct gsmd_ucmd *ucmd = ucmd_alloc(len); if (!ucmd) return NULL; @@ -29,11 +30,10 @@ struct gsmd_ucmd *usock_build_event(u_int8_t type, u_int8_t subtype, u_int8_t le static struct gsmd_ucmd *ucmd_copy(const struct gsmd_ucmd *orig) { - int size = sizeof(*orig) + orig->hdr.len; - struct gsmd_ucmd *copy = malloc(size); + struct gsmd_ucmd *copy = ucmd_alloc(orig->hdr.len); if (copy) - memcpy(copy, orig, size); + memcpy(copy, orig, orig->hdr.len); return copy; } @@ -47,17 +47,25 @@ int usock_evt_send(struct gsmd *gsmd, struct gsmd_ucmd *ucmd, u_int32_t evt) llist_for_each_entry(gu, &gsmd->users, list) { if (gu->subscriptions & (1 << evt)) { - struct gsmd_ucmd *cpy = ucmd_copy(ucmd); - usock_cmd_enqueue(ucmd, gu); - num_sent++; - ucmd = cpy; - if (!ucmd) { - fprintf(stderr, "can't allocate memory for copy of ucmd\n"); - return num_sent; + if (num_sent == 0) + usock_cmd_enqueue(ucmd, gu); + else { + struct gsmd_ucmd *cpy = ucmd_copy(ucmd); + if (!cpy) { + fprintf(stderr, + "can't allocate memory for " + "copy of ucmd\n"); + return num_sent; + } + usock_cmd_enqueue(cpy, gu); } + num_sent++; } } + if (num_sent == 0) + talloc_free(ucmd); + return num_sent; } @@ -101,7 +109,7 @@ static int cring_parse(char *buf, int len, const char *param, struct gsmd *gsmd) aux->u.call.type = GSMD_CALL_FAX; } else if (!strncmp(param, "GPRS ", 5)) { /* FIXME: change event type to GPRS */ - free(ucmd); + talloc_free(ucmd); return 0; } /* FIXME: parse all the ALT* profiles, Chapter 6.11 */ @@ -322,7 +330,8 @@ int unsolicited_parse(struct gsmd *g, char *buf, int len, const char *param) rc = vpl->unsolicit[i].parse(buf, len, colon, g); if (rc < 0) gsmd_log(GSMD_ERROR, "error %d during parse of " - "unsolicied response `%s'\n", rc, buf); + "vendor unsolicied response `%s'\n", + rc, buf); return rc; } } diff --git a/src/gsmd/usock.c b/src/gsmd/usock.c index 42f8099..4f2ba4f 100644 --- a/src/gsmd/usock.c +++ b/src/gsmd/usock.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,15 @@ #include #include #include +#include + +static void *__ucmd_ctx, *__gu_ctx; + +struct gsmd_ucmd *ucmd_alloc(int extra_size) +{ + return talloc_size(__ucmd_ctx, + sizeof(struct gsmd_ucmd) + extra_size); +} void usock_cmd_enqueue(struct gsmd_ucmd *ucmd, struct gsmd_user *gu) { @@ -31,7 +41,7 @@ static int usock_cmd_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) { struct gsmd_user *gu = ctx; int rlen = strlen(resp)+1; - struct gsmd_ucmd *ucmd = malloc(sizeof(*ucmd)+rlen); + struct gsmd_ucmd *ucmd = ucmd_alloc(rlen); DEBUGP("entering(cmd=%p, gu=%p)\n", cmd, gu); @@ -83,6 +93,8 @@ static int usock_rcv_voicecall(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, { struct gsmd_atcmd *cmd = NULL; struct gsmd_addr *ga; + struct gsmd_dtmf *gd; + int atcmd_len; switch (gph->msg_subtype) { case GSMD_VOICECALL_DIAL: @@ -92,6 +104,8 @@ static int usock_rcv_voicecall(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, ga->number[GSMD_ADDR_MAXLEN] = '\0'; cmd = atcmd_fill("ATD", 5 + strlen(ga->number), &usock_cmd_cb, gu, gph->id); + if (!cmd) + return -ENOMEM; sprintf(cmd->buf, "ATD%s;", ga->number); /* FIXME: number type! */ break; @@ -101,6 +115,26 @@ static int usock_rcv_voicecall(struct gsmd_user *gu, struct gsmd_msg_hdr *gph, case GSMD_VOICECALL_ANSWER: cmd = atcmd_fill("ATA", 4, &usock_cmd_cb, gu, gph->id); break; + case GSMD_VOICECALL_DTMF: + if (len < sizeof(*gph) + sizeof(*gd)) + return -EINVAL; + + gd = (struct gsmd_dtmf *) ((void *)gph + sizeof(*gph)); + if (len < sizeof(*gph) + sizeof(*gd) + gd->len) + return -EINVAL; + + /* FIXME: we don't yet support DTMF of multiple digits */ + if (gd->len != 1) + return -EINVAL; + + atcmd_len = 1 + strlen("AT+VTS=") + (gd->len * 2); + cmd = atcmd_fill("AT+VTS=", atcmd_len, &usock_cmd_cb, + gu, gph->id); + if (!cmd) + return -ENOMEM; + + sprintf(cmd->buf, "AT+VTS=%c;", gd->dtmf[0]); + break; default: return -EINVAL; } @@ -173,11 +207,12 @@ static int network_vmail_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) { struct gsmd_user *gu = ctx; struct gsmd_voicemail *vmail; - struct gsmd_ucmd *ucmd = malloc(sizeof(*ucmd)+sizeof(*vmail)); + struct gsmd_ucmd *ucmd; char *comma; DEBUGP("entering(cmd=%p, gu=%p)\n", cmd, gu); + ucmd = ucmd_alloc(sizeof(*vmail)); if (!ucmd) return -ENOMEM; @@ -218,7 +253,7 @@ static int network_vmail_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) out_free_einval: gsmd_log(GSMD_ERROR, "can't understand voicemail response\n"); - free(ucmd); + talloc_free(ucmd); return -EINVAL; } @@ -227,7 +262,7 @@ static struct gsmd_ucmd *gsmd_ucmd_fill(int len, u_int8_t msg_type, u_int8_t msg { struct gsmd_ucmd *ucmd; - ucmd = malloc(sizeof(*ucmd)+len); + ucmd = ucmd_alloc(len); if (!ucmd) return NULL; @@ -256,7 +291,7 @@ static int network_sigq_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp) gsq->rssi = atoi(resp); comma = strchr(resp, ','); if (!comma) { - free(ucmd); + talloc_free(ucmd); return -EIO; } gsq->ber = atoi(comma+1); @@ -336,12 +371,14 @@ static int gsmd_usock_user_cb(int fd, unsigned int what, void *data) /* read data from socket, determine what he wants */ rcvlen = read(fd, buf, sizeof(buf)); if (rcvlen == 0) { + DEBUGP("EOF, this client has just vanished\n"); /* EOF, this client has just vanished */ gsmd_unregister_fd(&gu->gfd); close(fd); /* destroy whole user structure */ llist_del(&gu->list); - /* FIXME: delete budy ucmds from finished_ucmds */ + /* FIXME: delete busy ucmds from finished_ucmds */ + talloc_free(gu); return 0; } else if (rcvlen < 0) return rcvlen; @@ -370,8 +407,9 @@ static int gsmd_usock_user_cb(int fd, unsigned int what, void *data) break; } + DEBUGP("successfully sent cmd %p to user %p, freeing\n", ucmd, gu); llist_del(&ucmd->list); - free(ucmd); + talloc_free(ucmd); } if (llist_empty(&gu->finished_ucmds)) gu->gfd.when &= ~GSMD_FD_WRITE; @@ -389,7 +427,7 @@ static int gsmd_usock_cb(int fd, unsigned int what, void *data) /* FIXME: implement this */ if (what & GSMD_FD_READ) { /* new incoming connection */ - newuser = malloc(sizeof(*newuser)); + newuser = talloc(__gu_ctx, struct gsmd_user); if (!newuser) return -ENOMEM; @@ -397,7 +435,7 @@ static int gsmd_usock_cb(int fd, unsigned int what, void *data) if (newuser->gfd.fd < 0) { DEBUGP("error accepting incoming conn: `%s'\n", strerror(errno)); - free(newuser); + talloc_free(newuser); } newuser->gfd.when = GSMD_FD_READ; newuser->gfd.data = newuser; @@ -419,6 +457,9 @@ int usock_init(struct gsmd *g) struct sockaddr_un sun; int fd, rc; + __ucmd_ctx = talloc_named_const(gsmd_tallocs, 1, "ucmd"); + __gu_ctx = talloc_named_const(gsmd_tallocs, 1, "gsmd_user"); + fd = socket(PF_UNIX, GSMD_UNIX_SOCKET_TYPE, 0); if (fd < 0) return fd; diff --git a/src/gsmd/vendor_ti.c b/src/gsmd/vendor_ti.c index 53ca7ed..6ec12c5 100644 --- a/src/gsmd/vendor_ti.c +++ b/src/gsmd/vendor_ti.c @@ -43,7 +43,7 @@ ti_setopt(struct gsmd *gh, int optname, const void *optval, int optlen) #endif -static int csq_parse(char *buf, int len, char *param, +static int csq_parse(char *buf, int len, const char *param, struct gsmd *gsmd) { char *tok; @@ -57,7 +57,7 @@ static int csq_parse(char *buf, int len, char *param, aux = (struct gsmd_evt_auxdata *) ucmd->buf; - tok = strtok(param, ","); + tok = strtok(buf, ","); if (!tok) goto out_free_io; @@ -81,12 +81,85 @@ out_free_io: static int cpri_parse(char *buf, int len, const char *param, struct gsmd *gsmd) { - + /* FIXME: parse ciphering indication */ } +/* Call Progress Information */ static int cpi_parse(char *buf, int len, const char *param, struct gsmd *gsmd) { + char *tok; + struct gsmd_evt_auxdata *aux; + struct gsmd_ucmd *ucmd = usock_build_event(GSMD_MSG_EVENT, + GSMD_EVT_OUT_STATUS, + sizeof(*aux)); + + DEBUGP("entering cpi_parse param=`%s'\n", param); + if (!ucmd) + return -EINVAL; + + aux = (struct gsmd_evt_auxdata *) ucmd->buf; + + /* Format: cId, msgType, ibt, tch, dir,[mode],[number],[type],[alpha],[cause],line */ + + /* call ID */ + tok = strtok(buf, ","); + if (!tok) + goto out_free_io; + /* message type (layer 3) */ + tok = strtok(NULL, ","); + if (!tok) + goto out_free_io; + aux->u.call_status.prog = atoi(tok); + + /* in-band tones */ + tok = strtok(NULL, ","); + if (!tok) + goto out_free_io; + + if (*tok == '1') + aux->u.call_status.ibt = 1; + else + aux->u.call_status.ibt = 0; + + /* TCH allocated */ + tok = strtok(NULL, ","); + if (!tok) + goto out_free_io; + + if (*tok == '1') + aux->u.call_status.tch = 1; + else + aux->u.call_status.tch = 0; + + /* direction */ + tok = strtok(NULL, ","); + if (!tok) + goto out_free_io; + + switch (*tok) { + case '0': + case '1': + case '2': + case '3': + aux->u.call_status.dir = (*tok - '0'); + break; + default: + break; + } + + /* mode */ + tok = strtok(NULL, ","); + if (!tok) + goto out_free_io; + + usock_evt_send(gsmd, ucmd, GSMD_EVT_OUT_STATUS); + + return 0; + +out_free_io: + free(ucmd); + return -EIO; } static const struct gsmd_unsolicit ticalypso_unsolicit[] = { @@ -114,13 +187,23 @@ static int ticalypso_detect(struct gsmd *g) static int ticalypso_initsettings(struct gsmd *g) { - return gsmd_simplecmd(g, "AT\%CPI=3;\%CPRI=1;\%CSQ=1"); + int rc; + /* enable %CPI: call progress indication */ + rc = gsmd_simplecmd(g, "AT\%CPI=3"); + /* enable %CPRI: ciphering indications */ + rc |= gsmd_simplecmd(g, "AT\%CPRI=1"); + /* enable %CSQ: signal quality reports */ + rc |= gsmd_simplecmd(g, "AT\%CSQ=1"); + /* send unsolicited commands at any time */ + rc |= gsmd_simplecmd(g, "AT\%CUNS=0"); + + return rc; } static struct gsmd_vendor_plugin plugin_ticalypso = { .name = "TI Calypso", .num_unsolicit = ARRAY_SIZE(ticalypso_unsolicit), - .unsolicit = &ticalypso_unsolicit, + .unsolicit = ticalypso_unsolicit, .detect = &ticalypso_detect, .initsettings = &ticalypso_initsettings, }; diff --git a/src/libgsmd/libgsmd_voicecall.c b/src/libgsmd/libgsmd_voicecall.c index 2a1d6e6..b21ca37 100644 --- a/src/libgsmd/libgsmd_voicecall.c +++ b/src/libgsmd/libgsmd_voicecall.c @@ -51,6 +51,31 @@ int lgsm_voice_out_init(struct lgsm_handle *lh, return 0; } +int lgsm_voice_dtmf(struct lgsm_handle *lh, char dtmf_char) +{ + struct gsmd_msg_hdr *gmh; + struct gsmd_dtmf *gd; + int rc; + + gmh = lgsm_gmh_fill(GSMD_MSG_VOICECALL, + GSMD_VOICECALL_DTMF, sizeof(*gd)+1); + if (!gmh) + return -ENOMEM; + gd = (struct gsmd_dtmf *) gmh->data; + gd->len = 1; + gd->dtmf[0] = dtmf_char; + + rc = lgsm_send(lh, gmh); + if (rc < gmh->len + sizeof(*gd)+1) { + lgsm_gmh_free(gmh);; + return -EIO; + } + + lgsm_gmh_free(gmh); + + return 0; +} + int lgsm_voice_in_accept(struct lgsm_handle *lh) { return lgsm_send_simple(lh, GSMD_MSG_VOICECALL, GSMD_VOICECALL_ANSWER); diff --git a/src/util/atcmd.c b/src/util/atcmd.c index 2e2c016..5e95ca0 100644 --- a/src/util/atcmd.c +++ b/src/util/atcmd.c @@ -25,14 +25,19 @@ int atcmd_main(struct lgsm_handle *lgsmh) lgsm_register_handler(lgsmh, GSMD_MSG_PASSTHROUGH, &pt_msghandler); +#if 1 fcntl(0, F_SETFD, O_NONBLOCK); + fcntl(1, F_SETFD, O_NONBLOCK); + fcntl(2, F_SETFD, O_NONBLOCK); fcntl(lgsm_fd(lgsmh), F_SETFD, O_NONBLOCK); +#endif FD_ZERO(&readset); while (1) { fd_set readset; int gsm_fd = lgsm_fd(lgsmh); + FD_SET(0, &readset); FD_SET(gsm_fd, &readset); diff --git a/src/util/event.c b/src/util/event.c index bff895d..014abda 100644 --- a/src/util/event.c +++ b/src/util/event.c @@ -1,6 +1,7 @@ #include #include +#include #include #include @@ -18,6 +19,13 @@ static int clip_handler(struct lgsm_handle *lh, int evt, struct gsmd_evt_auxdata return 0; } +static int colp_handler(struct lgsm_handle *lh, int evt, struct gsmd_evt_auxdata *aux) +{ + printf("EVENT: Outgoing call colp = %s\n", aux->u.colp.addr.number); + + return 0; +} + static int netreg_handler(struct lgsm_handle *lh, int evt, struct gsmd_evt_auxdata *aux) { printf("EVENT: Netreg "); @@ -56,14 +64,55 @@ static int sigq_handler(struct lgsm_handle *lh, int evt, struct gsmd_evt_auxdata return 0; } +static const char *cprog_names[] = { + [GSMD_CALLPROG_SETUP] = "SETUP", + [GSMD_CALLPROG_DISCONNECT] = "DISCONNECT", + [GSMD_CALLPROG_ALERT] = "ALERT", + [GSMD_CALLPROG_CALL_PROCEED] = "PROCEED", + [GSMD_CALLPROG_SYNC] = "SYNC", + [GSMD_CALLPROG_PROGRESS] = "PROGRESS", + [GSMD_CALLPROG_CONNECTED] = "CONNECTED", + [GSMD_CALLPROG_RELEASE] = "RELEASE", + [GSMD_CALLPROG_REJECT] = "REJECT", + [GSMD_CALLPROG_UNKNOWN] = "UNKNOWN", +}; + +static const char *cdir_names[] = { + [GSMD_CALL_DIR_MO] = "Outgoing", + [GSMD_CALL_DIR_MT] = "Incoming", + [GSMD_CALL_DIR_CCBS] = "CCBS", + [GSMD_CALL_DIR_MO_REDIAL] = "Outgoing Redial", +}; + +static int cprog_handler(struct lgsm_handle *lh, int evt, struct gsmd_evt_auxdata *aux) +{ + const char *name, *dir; + + if (aux->u.call_status.prog >= ARRAY_SIZE(cprog_names)) + name = "UNDEFINED"; + else + name = cprog_names[aux->u.call_status.prog]; + + if (aux->u.call_status.dir >= ARRAY_SIZE(cdir_names)) + dir = ""; + else + dir = cdir_names[aux->u.call_status.dir]; + + printf("EVENT: %s Call Progress: %s\n", dir, name); + + return 0; +} + int event_init(struct lgsm_handle *lh) { int rc; rc = lgsm_evt_handler_register(lh, GSMD_EVT_IN_CALL, &incall_handler); rc |= lgsm_evt_handler_register(lh, GSMD_EVT_IN_CLIP, &clip_handler); + rc |= lgsm_evt_handler_register(lh, GSMD_EVT_OUT_COLP, &colp_handler); rc |= lgsm_evt_handler_register(lh, GSMD_EVT_NETREG, &netreg_handler); rc |= lgsm_evt_handler_register(lh, GSMD_EVT_SIGNAL, &sigq_handler); + rc |= lgsm_evt_handler_register(lh, GSMD_EVT_OUT_STATUS, &cprog_handler); return rc; } diff --git a/src/util/shell.c b/src/util/shell.c index b90cd19..b8f0877 100644 --- a/src/util/shell.c +++ b/src/util/shell.c @@ -25,6 +25,7 @@ static int shell_help(void) "\tO\tPower On\n" "\to\tPower Off\n" "\tR\tRegister Netowrk\n" + "\tq\tQuit\n" ); } @@ -43,6 +44,8 @@ int shell_main(struct lgsm_handle *lgsmh) FD_ZERO(&readset); + printf("# "); + while (1) { fd_set readset; int gsm_fd = lgsm_fd(lgsmh); @@ -63,8 +66,7 @@ int shell_main(struct lgsm_handle *lgsmh) rc = lgsm_handle_packet(lgsmh, buf, rc); } if (FD_ISSET(0, &readset)) { - /* we've received something on stdin. send it as passthrough - * to gsmd */ + /* we've received something on stdin. */ printf("# "); rc = fscanf(stdin, "%s", buf); if (rc == EOF) { @@ -84,6 +86,8 @@ int shell_main(struct lgsm_handle *lgsmh) lgsm_voice_hangup(lgsmh); } else if (buf[0] == 'D') { struct lgsm_addr addr; + if (strlen(buf) < 2) + continue; printf("Dial %s\n", buf+1); addr.type = 129; strncpy(addr.addr, buf+1, sizeof(addr.addr)-1); @@ -93,11 +97,22 @@ int shell_main(struct lgsm_handle *lgsmh) printf("Answer\n"); lgsm_voice_in_accept(lgsmh); } else if (!strcmp(buf, "O")) { + printf("Power-On\n"); lgsm_phone_power(lgsmh, 1); } else if (!strcmp(buf, "o")) { + printf("Power-Off\n"); lgsm_phone_power(lgsmh, 0); } else if (!strcmp(buf, "R")) { + printf("Register\n"); + lgsm_phone_power(lgsmh, 0); lgsm_netreg_register(lgsmh, 0); + } else if (!strcmp(buf, "q")) { + exit(0); + } else if (buf[0] == 'T') { + if (strlen(buf) < 2) + continue; + printf("DTMF: %c\n", buf[1]); + lgsm_voice_dtmf(lgsmh, buf[1]); } else { printf("Unknown command `%s'\n", buf); } -- cgit v1.2.3