From d24a3f265ec4344b5502ec57df3cf8358f6f1499 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 5 Jan 2004 22:24:10 +0000 Subject: many changes git-svn-id: file:///home/lennart/svn/public/ivam2/trunk@14 dbf6933d-3bce-0310-9bcc-ed052ba35b35 --- src/modem.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 167 insertions(+), 46 deletions(-) (limited to 'src/modem.c') 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 #include #include +#include #include @@ -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); -- cgit