diff options
Diffstat (limited to 'src/gsmd')
-rw-r--r-- | src/gsmd/atcmd.c | 58 | ||||
-rw-r--r-- | src/gsmd/gsmd.c | 45 | ||||
-rw-r--r-- | src/gsmd/sms_cb.c | 3 | ||||
-rw-r--r-- | src/gsmd/unsolicited.c | 35 | ||||
-rw-r--r-- | src/gsmd/usock.c | 59 | ||||
-rw-r--r-- | src/gsmd/vendor_ti.c | 93 |
6 files changed, 240 insertions, 53 deletions
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 <gsmd/ts0707.h> #include <gsmd/gsmd.h> #include <gsmd/atcmd.h> +#include <gsmd/talloc.h> #include <gsmd/unsolicited.h> /* 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 <errno.h> #include <fcntl.h> #include <termios.h> +#include <signal.h> #define _GNU_SOURCE #include <getopt.h> @@ -19,6 +20,7 @@ #include <gsmd/select.h> #include <gsmd/usock.h> #include <gsmd/vendorplugin.h> +#include <gsmd/talloc.h> 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 <gsmd/event.h> #include <gsmd/ts0707.h> #include <gsmd/unsolicited.h> +#include <gsmd/talloc.h> 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 <unistd.h> #include <string.h> #include <errno.h> +#include <ctype.h> #include <sys/socket.h> #include <sys/un.h> @@ -14,6 +15,15 @@ #include <gsmd/select.h> #include <gsmd/atcmd.h> #include <gsmd/usock.h> +#include <gsmd/talloc.h> + +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, }; |