summaryrefslogtreecommitdiffstats
path: root/src/modem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modem.c')
-rw-r--r--src/modem.c213
1 files changed, 167 insertions, 46 deletions
diff --git a/src/modem.c b/src/modem.c
index 71a8eb4..ef79b20 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -10,6 +10,7 @@
#include <time.h>
#include <stdio.h>
#include <sys/wait.h>
+#include <stdlib.h>
#include <oop.h>
@@ -21,22 +22,31 @@
#include "main.h"
#include "msntab.h"
#include "dle.h"
+#include "dtmffifo.h"
/* Baudrate for the TTY. Should be greater the 64000. */
#define BAUD_RATE B115200
-#define RESET_IDLE_TIME 10
+#define RESET_IDLE_TIME 2
+#define RING_TIME 5
+
#define INIT_TIMEOUT 5
#define ACCEPT_TIMEOUT 5
#define BASIC_TIMEOUT 5
#define CHILD_TIMEOUT 10
#define HANGUP_TIMEOUT 10
-#define ISDN_DELAY_USEC 125L
-#define ISDN_LATENCY_USEC 25000L
-#define INPUT_WATERMARK_DEFAULT -1
-#define INPUT_WATERMARK_CONNECTION 2
-#define INPUT_WATERMARK_CHILD 1
+#define ISDN_DELAY_USEC 125L /* The time a single byte written to the ISDN takes to be played */
+#define ISDN_LATENCY_USEC 50000L /* Request the next byte to be written to the ISDN this many usecs before the last one is finished playing */
+
+#define INPUT_RANGE_DEFAULT -1
+#define OUTPUT_RANGE_DEFAULT -1
+
+#define INPUT_RANGE_CONNECTION 96
+#define OUTPUT_RANGE_CONNECTION 1024
+
+#define INPUT_RANGE_CHILD 64
+#define OUTPUT_RANGE_CHILD 1024
#define INIT_AT_COMMANDS 16
static const char* const init_at_commands[INIT_AT_COMMANDS*2] = {
@@ -67,7 +77,7 @@ static const char* const init_at_commands[INIT_AT_COMMANDS*2] = {
"ATS13.0=1\n", /* direct tty send */
"OK\r\n",
- "AT\n", //S13.2=0\n", /* Don't hangup on DTR low */
+ "ATS13.6=1\n", /* Get special RUNG messages */
"OK\r\n",
"ATS23=1\n",
@@ -92,6 +102,7 @@ static const char* const init_at_commands[INIT_AT_COMMANDS*2] = {
static const char hup_sequence[] = { DLE, DC4, DLE, ETX, 0 };
static const char ath_sequence[] = "\nATH\n";
+static int modem_output_request_cb(struct buffio *b, void *user);
static int modem_output_empty_cb(struct buffio *b, void *user);
static int modem_input_ready_cb(struct buffio *b, void *user);
static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user);
@@ -105,12 +116,12 @@ static int modem_reopen(struct modem *m);
void modem_close(struct modem *m);
struct modem *modem_open(const char *dev) {
- struct modem *m;
+ struct modem *m = NULL;
assert(dev);
if (device_lock(dev) != 0)
- return NULL;
+ goto fail;
m = malloc(sizeof(struct modem));
assert(m);
@@ -121,7 +132,7 @@ struct modem *modem_open(const char *dev) {
m->child_pid = -1;
m->child_buffio = NULL;
- m->local_msn = "46";
+ m->listen_msn = "46";
m->tabentry = NULL;
if (modem_reopen(m) < 0)
@@ -132,7 +143,9 @@ struct modem *modem_open(const char *dev) {
return m;
fail:
- modem_close(m);
+ if (m)
+ modem_close(m);
+
return NULL;
}
@@ -140,8 +153,13 @@ fail:
void modem_close(struct modem *m) {
assert(m);
+ daemon_log(LOG_INFO, "Closing modem on TTY device '%s'", m->dev);
+
modem_timeout(m, 0);
+ if (m->dtmf_fifo)
+ dtmf_fifo_free(m->dtmf_fifo);
+
if (m->tabentry)
msntab_unref(m->tabentry);
@@ -196,7 +214,7 @@ static void modem_next_command(struct modem *m) {
if (m->command_index == 0)
buffio_command(m->buffio, hup_sequence);
else if (m->command_index == 15) {
- snprintf(tmp, sizeof(tmp), p, m->local_msn);
+ snprintf(tmp, sizeof(tmp), p, m->listen_msn);
p = tmp;
}
@@ -277,8 +295,10 @@ static void modem_hangup(struct modem *m, int b) {
assert(m);
daemon_log(LOG_INFO, "Hanging up.");
- buffio_set_input_watermark(m->buffio, INPUT_WATERMARK_DEFAULT);
+ buffio_set_input_range(m->buffio, INPUT_RANGE_DEFAULT);
+ buffio_set_output_range(m->buffio, OUTPUT_RANGE_DEFAULT);
buffio_set_delay_usec(m->buffio, 0, 0);
+ buffio_set_prebuf(m->buffio, 0);
l = strlen(ath_sequence) + (b ? strlen(hup_sequence) : 0);
@@ -295,6 +315,17 @@ static void modem_hangup(struct modem *m, int b) {
modem_timeout(m, HANGUP_TIMEOUT);
}
+static void modem_accept(struct modem *m) {
+ assert(m);
+
+ daemon_log(LOG_INFO, "Accepting call.");
+
+ m->state = MODEM_STATE_ATA;
+ modem_timeout(m, ACCEPT_TIMEOUT);
+
+ buffio_command(m->buffio, "ATA\n");
+}
+
static void child_exit_cb(pid_t t, int status, void *user) {
struct modem *m = user;
assert(m && m->child_pid == t);
@@ -307,6 +338,10 @@ static void child_exit_cb(pid_t t, int status, void *user) {
daemon_log(LOG_ERR, "Child exited due to unknown cause.");
m->child_pid = (pid_t) -1;
+ if (m->dtmf_fifo) {
+ dtmf_fifo_free(m->dtmf_fifo);
+ m->dtmf_fifo = NULL;
+ }
switch (m->state) {
@@ -323,9 +358,10 @@ static int child_input_ready_cb(struct buffio *b, void *user) {
struct modem *m = (struct modem*) user;
assert(m && m->child_buffio == b);
- if (m->state == MODEM_STATE_CONNECTION)
+ if (m->state == MODEM_STATE_CONNECTION) {
+ //daemon_log(LOG_INFO, "child_input_ready");
copy_child_to_modem(m);
- else
+ } else
buffio_flush_input(m->child_buffio);
return 0;
@@ -380,7 +416,7 @@ static int child_epipe_cb(struct buffio *b, void *user) {
return 0;
}
-static int child_output_empty_cb(struct buffio *b, void *user) {
+static int child_output_request_cb(struct buffio *b, void *user) {
struct modem *m = user;
assert(m && m->child_buffio == b);
@@ -405,18 +441,34 @@ static int modem_start_child(struct modem *m) {
int ifd, ofd;
assert(m && m->child_pid == (pid_t) -1 && m->tabentry && m->state == MODEM_STATE_CONNECTION);
+ assert(!m->dtmf_fifo);
+ if (!(m->dtmf_fifo = dtmf_fifo_new()))
+ return -1;
+
+ setenv("RINGMSN", m->ring_number ? m->ring_number : "", 1);
+ setenv("CALLERMSN", m->caller_number ? m->caller_number : "", 1);
+ assert(m->dtmf_fifo->fname);
+ setenv("DTMFFIFO", m->dtmf_fifo->fname, 1);
+ if (m->listen_msn)
+ setenv("LISTENMSN", m->listen_msn, 1);
+ else
+ unsetenv("LISTENMSN");
+
assert(m->tabentry->args && m->tabentry->args[0]);
- if ((m->child_pid = child_process_create(m->tabentry->args[0], m->tabentry->args, &ifd, &ofd, child_exit_cb, m)) < 0)
+ if ((m->child_pid = child_process_create(m->tabentry->args[0], m->tabentry->args, &ifd, &ofd, child_exit_cb, m)) < 0) {
+ dtmf_fifo_free(m->dtmf_fifo);
return -1;
+ }
assert(ifd >= 0 && ofd >= 0 && !m->child_buffio);
m->child_buffio = buffio_new(ifd, ofd);
- buffio_set_input_watermark(m->child_buffio, INPUT_WATERMARK_CHILD);
+ buffio_set_input_range(m->child_buffio, INPUT_RANGE_CHILD);
+ buffio_set_output_range(m->child_buffio, OUTPUT_RANGE_CHILD);
m->child_buffio->user = m;
m->child_buffio->eof_cb = child_eof_cb;
m->child_buffio->epipe_cb = child_epipe_cb;
m->child_buffio->input_ready_cb = child_input_ready_cb;
- m->child_buffio->output_empty_cb = child_output_empty_cb;
+ m->child_buffio->output_request_cb = child_output_request_cb;
m->child_buffio->error_cb = error_cb;
return 0;
@@ -439,6 +491,10 @@ static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user) {
daemon_log(LOG_ERR, "Timeout reached for device <%s>", m->dev);
return OOP_HALT;
+ case MODEM_STATE_RINGING:
+ modem_accept(m);
+ return OOP_CONTINUE;
+
case MODEM_STATE_ATA:
case MODEM_STATE_VTXVRX:
@@ -555,6 +611,7 @@ static int modem_input_ready_cb(struct buffio *b, void *user) {
struct tabentry *t;
c[strcspn(c, "\n\r")] = 0;
+
free(m->ring_number);
m->ring_number = strdup(c);
assert(m->ring_number);
@@ -563,27 +620,46 @@ static int modem_input_ready_cb(struct buffio *b, void *user) {
daemon_log(LOG_INFO, "Incoming call from [%s] to [%s]", m->caller_number, m->ring_number);
if ((t = msntab_check_call(m->ring_number, m->caller_number)) && (t->action == CALL_ACTION_ACCEPT || t->action == CALL_ACTION_HANGUP)) {
+
if (m->tabentry)
msntab_unref(m->tabentry);
m->tabentry = t;
- daemon_log(LOG_INFO, "Accepting call.");
-
- m->state = MODEM_STATE_ATA;
- modem_timeout(m, ACCEPT_TIMEOUT);
- buffio_command(m->buffio, "ATA\n");
- } else {
- if (t)
- msntab_unref(t);
- daemon_log(LOG_INFO, "Ignoring call.");
- m->state = MODEM_STATE_CALLER_NUMBER_EXPECT;
- modem_input_ready_cb(b, user);
+ if (t->rings <= 0)
+ modem_accept(m);
+ else {
+ daemon_log(LOG_INFO, "Will accept call after %u rings (%u seconds).", t->rings, t->rings*RING_TIME);
+ m->state = MODEM_STATE_RINGING;
+ modem_timeout(m, RING_TIME*t->rings);
+ }
+
+ break;
}
+
+ daemon_log(LOG_INFO, "Ignoring call.");
+
+ if (t)
+ msntab_unref(t);
+
+ m->state = MODEM_STATE_CALLER_NUMBER_EXPECT;
+ modem_input_ready_cb(b, user);
}
break;
}
+ case MODEM_STATE_RINGING:
+
+ if (buffio_find_input(m->buffio, "RUNG\r\n")) {
+ daemon_log(LOG_INFO, "Peer hung up prematurely.");
+ m->state = MODEM_STATE_CALLER_NUMBER_EXPECT;
+ modem_timeout(m, 0);
+ modem_input_ready_cb(b, user);
+ } else
+ buffio_dump_lines(m->buffio);
+
+ break;
+
case MODEM_STATE_ATA:
if (buffio_find_input(m->buffio, "VCON\r\n")) {
@@ -621,8 +697,13 @@ static int modem_input_ready_cb(struct buffio *b, void *user) {
modem_hangup(m, 1);
} else {
m->dle_flag = 0;
- buffio_set_input_watermark(m->buffio, INPUT_WATERMARK_CONNECTION);
+ buffio_set_input_range(m->buffio, INPUT_RANGE_CONNECTION);
+ buffio_set_output_range(m->buffio, OUTPUT_RANGE_CONNECTION);
buffio_set_delay_usec(m->buffio, ISDN_DELAY_USEC, ISDN_LATENCY_USEC);
+ buffio_set_prebuf(m->buffio, 1);
+
+ m->flush_msg = 0;
+
modem_input_ready_cb(b, user);
}
@@ -650,10 +731,6 @@ static int modem_output_empty_cb(struct buffio *b, void *user) {
switch (m->state) {
- case MODEM_STATE_CONNECTION:
- copy_child_to_modem(m);
- break;
-
case MODEM_STATE_PRE_HANGUP_HUPSEQ:
if (b)
buffio_command(m->buffio, hup_sequence);
@@ -663,8 +740,6 @@ static int modem_output_empty_cb(struct buffio *b, void *user) {
buffio_command(m->buffio, ath_sequence);
m->state = MODEM_STATE_HANGUP;
- daemon_log(LOG_INFO, "Delayed HANGUP sequence");
-
/* Don't restart timeout here */
break;
@@ -680,6 +755,25 @@ static int modem_output_empty_cb(struct buffio *b, void *user) {
return 0;
}
+static int modem_output_request_cb(struct buffio *b, void *user) {
+ struct modem *m = user;
+ assert(b && m && m->buffio == b);
+
+ switch (m->state) {
+
+ case MODEM_STATE_CONNECTION:
+ //daemon_log(LOG_INFO, "modem_output_request");
+ copy_child_to_modem(m);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
static int modem_dle_cb(uint8_t c, void *user) {
struct modem *m = user;
assert(m);
@@ -696,10 +790,10 @@ static int modem_dle_cb(uint8_t c, void *user) {
return -1;
default:
- daemon_log(LOG_INFO, "Recieved DTMF character '%c'", c);
+ assert(m->dtmf_fifo);
+ dtmf_fifo_pass(m->dtmf_fifo, c);
+ return 0;
}
-
- return 0;
}
static void copy_modem_to_child(struct modem *m) {
@@ -708,10 +802,27 @@ static void copy_modem_to_child(struct modem *m) {
size_t sl, dl;
assert(m->buffio && m->child_buffio);
- sp = buffio_read_ptr(m->buffio, &sl);
- dp = buffio_write_ptr(m->child_buffio, &dl);
+
+ for (;;) {
+ sp = buffio_read_ptr(m->buffio, &sl);
+ dp = buffio_write_ptr(m->child_buffio, &dl);
+
+ if (sp && sl && (!dp || !dl)) {
+ if (!m->flush_msg) {
+ daemon_log(LOG_INFO, "Child too slow, output buffer overflow, flushing.");
+ m->flush_msg = 1;
+ }
+
+ buffio_flush_output(m->child_buffio);
+ continue;
+ }
+
+ break;
+ }
if (sp && sl && dp && dl) {
+ //daemon_log(LOG_INFO, "copy modem->child (%lu->%lu)", (unsigned long) m->buffio->input_length, (unsigned long) (m->child_buffio->output_range - m->child_buffio->output_length));
+
sl = dle_decode(sp, sl, dp, &dl, modem_dle_cb, m, &m->dle_flag);
/* It may be the case that dle_decode recieved a
@@ -728,11 +839,15 @@ static void copy_child_to_modem(struct modem *m) {
const uint8_t *sp;
uint8_t *dp;
- assert(m->child_buffio && m->buffio);
+ assert(m && m->child_buffio && m->buffio);
+
sp = buffio_read_ptr(m->child_buffio, &sl);
dp = buffio_write_ptr(m->buffio, &dl);
-
+
if (sp && dp && sl && dl) {
+
+ //daemon_log(LOG_INFO, "copy child->modem (%lu->%lu)", (unsigned long) m->child_buffio->input_length, (unsigned long) (m->buffio->output_range - m->buffio->output_length));
+
sl = dle_encode(sp, sl, dp, &dl);
buffio_read_ptr_inc(m->child_buffio, sl);
buffio_write_ptr_inc(m->buffio, dl);
@@ -750,7 +865,7 @@ static int modem_reopen(struct modem *m) {
buffio_close_input_fd(m->buffio);
}
- daemon_log(LOG_INFO, "Trying to open TTY device '%s' ...", m->dev);
+ daemon_log(LOG_INFO, "Trying to open modem on TTY device '%s' ...", m->dev);
if ((fd = open(m->dev, O_RDWR|O_NDELAY)) < 0) {
daemon_log(LOG_ERR, "Failed to open device '%s': %s", m->dev, strerror(errno));
@@ -786,11 +901,17 @@ static int modem_reopen(struct modem *m) {
m->buffio = buffio_new(fd, fd);
assert(m->buffio);
+ m->buffio->output_request_cb = modem_output_request_cb;
m->buffio->output_empty_cb = modem_output_empty_cb;
m->buffio->input_ready_cb = modem_input_ready_cb;
m->buffio->error_cb = error_cb;
-
+
m->buffio->user = m;
+
+ buffio_set_input_range(m->buffio, INPUT_RANGE_DEFAULT);
+ buffio_set_output_range(m->buffio, OUTPUT_RANGE_DEFAULT);
+ buffio_set_delay_usec(m->buffio, 0, 0);
+
} else
buffio_set_fds(m->buffio, fd, fd);