summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/gsmd/atcmd.h1
-rw-r--r--src/gsmd/atcmd.c108
-rw-r--r--src/gsmd/usock.c10
3 files changed, 84 insertions, 35 deletions
diff --git a/include/gsmd/atcmd.h b/include/gsmd/atcmd.h
index 0d6c62a..719b943 100644
--- a/include/gsmd/atcmd.h
+++ b/include/gsmd/atcmd.h
@@ -9,6 +9,7 @@ typedef int atcmd_cb_t(struct gsmd_atcmd *cmd, void *ctx, char *resp);
extern struct gsmd_atcmd *atcmd_fill(const char *cmd, int rlen, atcmd_cb_t *cb, void *ctx, u_int16_t id);
extern int atcmd_submit(struct gsmd *g, struct gsmd_atcmd *cmd);
+extern int cancel_atcmd(struct gsmd *g, struct gsmd_atcmd *cmd);
extern int atcmd_init(struct gsmd *g, int sockfd);
extern void atcmd_drain(int fd);
diff --git a/src/gsmd/atcmd.c b/src/gsmd/atcmd.c
index 69e1d12..4442bd7 100644
--- a/src/gsmd/atcmd.c
+++ b/src/gsmd/atcmd.c
@@ -200,6 +200,52 @@ static int parse_final_result(const char *res)
return -1;
}
+static inline void atcmd_wake_pending_queue (struct gsmd *g)
+{
+ g->gfd_uart.when |= GSMD_FD_WRITE;
+}
+
+static inline void atcmd_wait_pending_queue (struct gsmd *g)
+{
+ g->gfd_uart.when &= ~GSMD_FD_WRITE;
+}
+
+
+static int atcmd_done(struct gsmd *g, struct gsmd_atcmd *cmd, const char *buf)
+{
+ int rc = 0;
+ if (!cmd->cb) {
+ gsmd_log(GSMD_NOTICE, "command without cb!!!\n");
+ } else {
+ DEBUGP("Calling final cmd->cb()\n");
+ /* send final result code if there is no information
+ * response in mlbuf */
+ if (g->mlbuf_len) {
+ cmd->resp = g->mlbuf;
+ g->mlbuf[g->mlbuf_len] = 0;
+ } else {
+ cmd->resp = buf;
+ }
+ rc = cmd->cb(cmd, cmd->ctx, cmd->resp);
+ DEBUGP("Clearing mlbuf\n");
+ g->mlbuf_len = 0;
+ }
+
+ /* remove from list of currently executing cmds */
+ llist_del(&cmd->list);
+ talloc_free(cmd);
+
+ /* if we're finished with current commands, but still have pending
+ * commands: we want to WRITE again */
+ if (llist_empty(&g->busy_atcmds)) {
+ //g->clear_to_send = 1;
+ if (!llist_empty(&g->pending_atcmds)) {
+ atcmd_wake_pending_queue(g);
+ }
+ }
+ return rc;
+}
+
static int ml_parse(const char *buf, int len, void *ctx)
{
struct gsmd *g = ctx;
@@ -389,9 +435,8 @@ static int ml_parse(const char *buf, int len, void *ctx)
}
g->mlunsolicited = 0;
g->mlbuf_len = 0;
- return rc;
}
- return 0;
+ return rc;
final_cb:
/* if reach here, the final result code of a command has been reached */
@@ -405,32 +450,7 @@ final_cb:
if (cmd && cms_error)
generate_event_from_cms(g, cmd->ret);
- if (!cmd->cb) {
- gsmd_log(GSMD_NOTICE, "command without cb!!!\n");
- } else {
- DEBUGP("Calling final cmd->cb()\n");
- /* send final result code if there is no information
- * response in mlbuf */
- if (g->mlbuf_len) {
- cmd->resp = g->mlbuf;
- g->mlbuf[g->mlbuf_len] = 0;
- } else
- cmd->resp = buf;
- rc = cmd->cb(cmd, cmd->ctx, cmd->resp);
- DEBUGP("Clearing mlbuf\n");
- g->mlbuf_len = 0;
- }
-
- /* remove from list of currently executing cmds */
- llist_del(&cmd->list);
- talloc_free(cmd);
-
- /* if we're finished with current commands, but still have pending
- * commands: we want to WRITE again */
- if (llist_empty(&g->busy_atcmds) && !llist_empty(&g->pending_atcmds))
- g->gfd_uart.when |= GSMD_FD_WRITE;
-
- return rc;
+ return atcmd_done(g, cmd, buf);
}
/* called when the modem asked for a new line of a multiline atcmd */
@@ -438,7 +458,7 @@ static int atcmd_prompt(void *data)
{
struct gsmd *g = data;
- g->gfd_uart.when |= GSMD_FD_WRITE;
+ atcmd_wake_pending_queue(g);
}
/* callback to be called if [virtual] UART has some data for us */
@@ -510,7 +530,7 @@ static int atcmd_select_cb(int fd, unsigned int what, void *data)
}
/* Either pending_atcmds is empty or a command has to wait */
- g->gfd_uart.when &= ~GSMD_FD_WRITE;
+ atcmd_wait_pending_queue(g);
}
return 0;
@@ -547,15 +567,37 @@ struct gsmd_atcmd *atcmd_fill(const char *cmd, int rlen,
/* submit an atcmd in the global queue of pending atcmds */
int atcmd_submit(struct gsmd *g, struct gsmd_atcmd *cmd)
{
+ int empty;
DEBUGP("submitting command `%s'\n", cmd->buf);
- if (llist_empty(&g->pending_atcmds))
- g->gfd_uart.when |= GSMD_FD_WRITE;
+ empty = llist_empty(&g->pending_atcmds);
llist_add_tail(&cmd->list, &g->pending_atcmds);
-
+ if (empty) {
+ atcmd_wake_pending_queue(g);
+ }
return 0;
}
+/* cancel a currently executing atcmd by issuing the command given as
+ * parameter, usually AT ot ATH. */
+int cancel_atcmd(struct gsmd *g, struct gsmd_atcmd *cmd)
+{
+ struct gsmd_atcmd *cur;
+ if (llist_empty(&g->busy_atcmds)) {
+ return atcmd_submit(g, cmd);
+ }
+ cur = llist_entry(g->busy_atcmds.next, struct gsmd_atcmd, list);
+ DEBUGP("cancelling command `%s' with an `%s'\n", cur->buf, cmd->buf);
+
+ if (g->mlbuf_len) {
+ DEBUGP("Discarding mlbuf: %.*s\n", g->mlbuf_len, g->mlbuf);
+ g->mlbuf_len = 0;
+ }
+
+ llist_add(&cmd->list, &g->pending_atcmds);
+ return atcmd_done(g, cur, "OK");
+}
+
void atcmd_drain(int fd)
{
int rc;
diff --git a/src/gsmd/usock.c b/src/gsmd/usock.c
index a53c8a1..a07fd6f 100644
--- a/src/gsmd/usock.c
+++ b/src/gsmd/usock.c
@@ -135,7 +135,13 @@ static int usock_rcv_voicecall(struct gsmd_user *gu, struct gsmd_msg_hdr *gph,
break;
case GSMD_VOICECALL_HANGUP:
/* ATH0 is not supported by QC, we hope ATH is supported by everone */
- cmd = atcmd_fill("ATH", 5, &usock_cmd_cb, gu, gph->id);
+ cmd = atcmd_fill("ATH", 4, &usock_cmd_cb, gu, gph->id);
+
+ /* This command is special because it needs to be sent to
+ * the MS even if a command is currently executing. */
+ if (cmd) {
+ return cancel_atcmd(gu->gsmd, cmd);
+ }
break;
case GSMD_VOICECALL_ANSWER:
cmd = atcmd_fill("ATA", 4, &usock_cmd_cb, gu, gph->id);
@@ -167,7 +173,7 @@ static int usock_rcv_voicecall(struct gsmd_user *gu, struct gsmd_msg_hdr *gph,
if (cmd)
return atcmd_submit(gu->gsmd, cmd);
else
- return 0;
+ return -ENOMEM;
}
static int null_cmd_cb(struct gsmd_atcmd *cmd, void *ctx, char *resp)
personal git repositories of Harald Welte. Your mileage may vary