summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/gsmd/event.h21
-rw-r--r--include/gsmd/gsmd.h4
-rw-r--r--include/gsmd/usock.h14
-rw-r--r--include/libgsmd/voicecall.h3
-rw-r--r--src/gsmd/atcmd.c58
-rw-r--r--src/gsmd/gsmd.c45
-rw-r--r--src/gsmd/sms_cb.c3
-rw-r--r--src/gsmd/unsolicited.c35
-rw-r--r--src/gsmd/usock.c59
-rw-r--r--src/gsmd/vendor_ti.c93
-rw-r--r--src/libgsmd/libgsmd_voicecall.c25
-rw-r--r--src/util/atcmd.c5
-rw-r--r--src/util/event.c49
-rw-r--r--src/util/shell.c19
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 <gsmd/vendorplugin.h>
#include <gsmd/select.h>
+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 <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,
};
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 <stdio.h>
#include <string.h>
+#include <common/linux_list.h>
#include <libgsmd/libgsmd.h>
#include <libgsmd/event.h>
@@ -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);
}
personal git repositories of Harald Welte. Your mileage may vary