summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2004-01-02 22:09:35 +0000
committerLennart Poettering <lennart@poettering.net>2004-01-02 22:09:35 +0000
commit263b632288c0662ae4ef08925291f8d1a55c1311 (patch)
tree52445a08f025ace8f9ee4db5700a73690252d13a
parent33f01df1c95b43b66e2f0bab2b972dd9e03faeb5 (diff)
this stuff works great
git-svn-id: file:///home/lennart/svn/public/ivam2/trunk@10 dbf6933d-3bce-0310-9bcc-ed052ba35b35
-rw-r--r--src/Makefile2
-rw-r--r--src/buffio.c436
-rw-r--r--src/buffio.h52
-rw-r--r--src/dle.c16
-rw-r--r--src/dle.h4
-rw-r--r--src/exec.c24
-rw-r--r--src/main.c1
-rw-r--r--src/modem.c575
-rw-r--r--src/modem.h13
-rw-r--r--src/modemman.c2
-rw-r--r--src/util.c12
11 files changed, 926 insertions, 211 deletions
diff --git a/src/Makefile b/src/Makefile
index 5aff207..65ee382 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2,7 +2,7 @@ CC=gcc
CFLAGS=`pkg-config --cflags libdaemon shbuf liboop` -Wall -O0 -pipe -g
LIBS=`pkg-config --libs libdaemon shbuf liboop` -Wall -O0 -pipe -g
-ivamd: modem.o main.o modemman.o buffio.o exec.o dle.o lock.o util.o msntab.o
+ivamd: modem.o main.o modemman.o buffio.o exec.o dle.o lock.o util.o msntab.o timevalarith.o
$(CC) $(LIBS) -o $@ $^
clean:
diff --git a/src/buffio.c b/src/buffio.c
index 25ba5a4..727821d 100644
--- a/src/buffio.c
+++ b/src/buffio.c
@@ -3,16 +3,21 @@
#include <string.h>
#include <errno.h>
#include <stdio.h>
+#include <sys/socket.h>
#include <oop.h>
#include <libdaemon/dlog.h>
+#include <libdaemon/dnonblock.h>
#include "buffio.h"
#include "main.h"
+#include "timevalarith.h"
-#define BUFSIZE 10240
+//#define IODEBUG 1
+#define BUFSIZE PIPE_BUF
+static void* buffio_timeout_cb(oop_source *source, struct timeval tv, void *user);
static void* buffio_read_cb(oop_source *source, int fd, oop_event event, void *user);
static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void *user);
@@ -29,6 +34,9 @@ struct buffio* buffio_new(int ifd, int ofd) {
b->ifd = ifd;
b->ofd = ofd;
+
+ daemon_nonblock(b->ifd, 1);
+ daemon_nonblock(b->ofd, 1);
b->input_buf = malloc(b->input_max_length = BUFSIZE);
assert(b->input_buf);
@@ -46,36 +54,99 @@ struct buffio* buffio_new(int ifd, int ofd) {
return b;
}
-static void buffio_close_fd(struct buffio *b) {
+void buffio_close_input_fd(struct buffio *b) {
assert(b);
- if (b->b_read_cb)
+ if (b->b_read_cb) {
event_source->cancel_fd(event_source, b->ifd, OOP_READ);
+ b->b_read_cb = 0;
+ }
- if (b->b_write_cb)
+ if (b->ifd >= 0) {
+
+ if (b->ofd != b->ifd)
+ close(b->ifd);
+
+ b->ifd = -1;
+ }
+}
+
+void buffio_close_output_fd(struct buffio *b) {
+ assert(b);
+
+ if (b->b_write_cb) {
event_source->cancel_fd(event_source, b->ofd, OOP_WRITE);
+ b->b_write_cb = 0;
+ }
- if (b->ifd >= 0)
- close(b->ifd);
- if (b->ofd >= 0 && b->ofd != b->ifd)
- close(b->ofd);
+ if (b->ofd >= 0) {
- b->b_read_cb = b->b_write_cb = 0;
- b->ifd = b->ofd = -1;
+ if (b->ofd != b->ifd)
+ close(b->ofd);
+
+ b->ofd = -1;
+ }
}
-
void buffio_free(struct buffio *b) {
- buffio_close_fd(b);
+ buffio_close_input_fd(b);
+ buffio_close_output_fd(b);
+
+ if (b->delaying)
+ event_source->cancel_time(event_source, b->timeout_tv, buffio_timeout_cb, b);
+
free(b->output_buf);
free(b->input_buf);
free(b);
}
+static void* buffio_user_cb(oop_source *source, struct timeval tv, void *user) {
+ struct buffio *b = user;
+ enum buffio_sched_cb s;
+
+ int r = 0;
+ assert(b && source && source == event_source);
+
+ s = b->sched_cb;
+ b->sched_cb = BUFFIO_SCHED_CB_IDLE;
+
+ if (s & BUFFIO_SCHED_CB_INPUT_READY && b->input_ready_cb && b->input_length)
+ r |= b->input_ready_cb(b, b->user);
+ if (s & BUFFIO_SCHED_CB_OUTPUT_EMPTY && b->output_empty_cb && !b->output_length)
+ r |= b->output_empty_cb(b, b->user);
+ if (s & BUFFIO_SCHED_CB_EOF && b->eof_cb)
+ r |= b->eof_cb(b, b->user);
+ if (s & BUFFIO_SCHED_CB_EPIPE && b->epipe_cb)
+ r |= b->epipe_cb(b, b->user);
+ if (s & BUFFIO_SCHED_CB_ERROR && b->error_cb)
+ r |= b->error_cb(b, b->user);
+
+ return r == 0 ? OOP_CONTINUE : OOP_HALT;
+}
+
+
+static void buffio_sched_cb(struct buffio *b, enum buffio_sched_cb s) {
+ if (s & BUFFIO_SCHED_CB_INPUT_READY && !b->input_ready_cb) s &= ~BUFFIO_SCHED_CB_INPUT_READY;
+ if (s & BUFFIO_SCHED_CB_OUTPUT_EMPTY && !b->output_empty_cb) s &= ~BUFFIO_SCHED_CB_OUTPUT_EMPTY;
+ if (s & BUFFIO_SCHED_CB_EOF && !b->eof_cb) s &= ~BUFFIO_SCHED_CB_EOF;
+ if (s & BUFFIO_SCHED_CB_EPIPE && !b->epipe_cb) s = ~BUFFIO_SCHED_CB_EPIPE;
+ if (s & BUFFIO_SCHED_CB_ERROR && !b->error_cb) s = ~BUFFIO_SCHED_CB_ERROR;
+
+ if (s == BUFFIO_SCHED_CB_IDLE)
+ return;
+
+ if (!b->sched_cb) {
+ assert(event_source);
+ event_source->on_time(event_source, OOP_TIME_NOW, buffio_user_cb, b);
+ }
+
+ b->sched_cb |= s;
+}
+
#ifdef IODEBUG
-static void esc_print(const char*c, const uint8_t *b, size_t l) {
+static void esc_print(struct buffio *bio, const char*c, const uint8_t *b, size_t l) {
size_t i;
- fprintf(stderr, "%s", c);
+ fprintf(stderr, "[%p] %s", bio, c);
for (i = 0; i < l; i++, b++)
fputc(*b < 32 ? '.' : *b, stderr);
@@ -83,7 +154,7 @@ static void esc_print(const char*c, const uint8_t *b, size_t l) {
};
#endif
-static void buffio_normalize(struct buffio *b) {
+inline static void buffio_normalize(struct buffio *b) {
assert(b);
assert(b->input_index < b->input_max_length);
@@ -92,14 +163,20 @@ static void buffio_normalize(struct buffio *b) {
if (!b->input_length) /* This will optimize throughput a bit */
b->input_index = 0;
+ assert(b->output_index < b->output_max_length);
+ assert(b->output_length <= b->output_max_length);
+
if (!b->output_length) /* This will optimize throughput a bit */
b->output_index = 0;
}
static void buffio_set_input_callbacks(struct buffio *b) {
- assert(b && b->ifd >= 0 && event_source);
+ assert(b && event_source);
- if (b->input_length <= b->input_watermark) {
+ if (b->ifd == -1)
+ return;
+
+ if (!b->readable && b->input_length < b->input_watermark) {
if (!b->b_read_cb) { /* Enable the callback */
event_source->on_fd(event_source, b->ifd, OOP_READ, buffio_read_cb, b);
b->b_read_cb = 1;
@@ -113,9 +190,12 @@ static void buffio_set_input_callbacks(struct buffio *b) {
}
static void buffio_set_output_callbacks(struct buffio *b) {
- assert(b && b->ofd >= 0 && event_source);
+ assert(b && event_source);
- if (b->output_length >= b->output_watermark) {
+ if (b->ofd == -1)
+ return;
+
+ if (!b->writable && b->output_length >= b->output_watermark) {
if (!b->b_write_cb) { /* Enable the callback */
event_source->on_fd(event_source, b->ofd, OOP_WRITE, buffio_write_cb, b);
b->b_write_cb = 1;
@@ -128,11 +208,44 @@ static void buffio_set_output_callbacks(struct buffio *b) {
}
}
-static void* buffio_read_cb(oop_source *source, int fd, oop_event event, void *user) {
- struct buffio *b = user;
- assert(source && b && b->ifd == fd && event == OOP_READ);
+static void buffio_delay(struct buffio *b, unsigned bytes) {
+ assert(event_source && event_source->on_time);
+
+ if (b->delaying) {
+ event_source->cancel_time(event_source, b->timeout_tv, buffio_timeout_cb, b);
+ b->delaying = 0;
+ }
+
+ if (bytes && b->output_delay_usec) {
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ /* Time when the last write operation is complete */
+ b->finish_tv = timeval_add(timeval_max(b->finish_tv, now), (uint64_t) bytes * b->output_delay_usec);
+
+ /* Time when we want to write the next data to the device */
+ b->timeout_tv = timeval_sub(b->finish_tv, b->output_latency_usec);
+
+ assert(event_source && event_source->on_time);
+ event_source->on_time(event_source, b->timeout_tv, buffio_timeout_cb, b);
+ b->delaying = 1;
+
+// daemon_log(LOG_INFO, "%u bytes, now = %lu|%lu; finish = %lu|%lu; timeout = %lu|%lu", bytes, now.tv_sec, now.tv_usec, b->finish_tv.tv_sec, b->finish_tv.tv_usec, b->timeout_tv.tv_sec, b->timeout_tv.tv_usec);
+ }
+}
- if (b->input_length < b->input_max_length) {
+void buffio_set_delay_usec(struct buffio *b, unsigned long delay, unsigned long latency) {
+ assert(b);
+
+ b->output_delay_usec = delay;
+ b->output_latency_usec = latency;
+}
+
+static void do_read(struct buffio *b) {
+ assert(b);
+
+ if (b->readable && b->input_length < b->input_watermark) {
ssize_t s;
size_t m, i;
@@ -141,41 +254,47 @@ static void* buffio_read_cb(oop_source *source, int fd, oop_event event, void *u
m = b->input_max_length-b->input_length;
if (m > b->input_max_length-i)
m = b->input_max_length-i;
-
- if ((s = read(fd, b->input_buf+i, m)) < 0) {
+
+ s = read(b->ifd, b->input_buf+i, m);
+ b->readable = 0;
+
+ //daemon_log(LOG_INFO, "%p: Read %u (%u) bytes.", b, s, m);
+
+ if (s < 0) {
daemon_log(LOG_ERR, "Failed to read from file descriptor: %s", strerror(errno));
- return OOP_HALT;
- }
+ buffio_close_input_fd(b);
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_ERROR);
+ return;
+
+ } else if (!s) {
+ buffio_close_input_fd(b);
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_EOF);
+ return;
+
+ } else {
#ifdef IODEBUG
- esc_print("INPUT: ", b->input_buf+i, s);
+ esc_print(b, "INPUT: ", b->input_buf+i, s);
#endif
-
- if (!s) {
- buffio_close_fd(b);
- if (b->eof_cb)
- b->eof_cb(b, b->user);
- return OOP_CONTINUE;
+
+ assert(s <= m);
+ b->input_length += s;
+ buffio_normalize(b);
}
-
- assert(s <= m);
- b->input_length += s;
}
- buffio_normalize(b);
buffio_set_input_callbacks(b);
- if (b->input_length && b->input_ready_cb)
- b->input_ready_cb(b, b->user);
+ if (b->input_length)
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_INPUT_READY);
- return OOP_CONTINUE;
+ return;
}
-static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void *user) {
- struct buffio *b = user;
- assert(source && b && b->ofd == fd && event == OOP_WRITE);
+static void do_write(struct buffio *b) {
+ assert(b);
- if (b->output_length > 0) {
+ if (!b->delaying && b->writable && b->output_length >= b->output_watermark) {
ssize_t s;
size_t m;
@@ -183,37 +302,73 @@ static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void *
if (m > b->output_max_length-b->output_index)
m = b->output_max_length-b->output_index;
- if ((s = write(fd, b->output_buf+b->output_index, m)) < 0) {
- daemon_log(LOG_ERR, "Failed to write to file descriptor: %s", strerror(errno));
- return OOP_HALT;
- }
+ s = write(b->ofd, b->output_buf+b->output_index, m);
+ b->writable = 0;
-#ifdef IODEBUG
- esc_print("OUTPUT: ", b->output_buf+b->output_index, s);
-#endif
+ //daemon_log(LOG_INFO, "%p: Wrote %u (%u) bytes.", b, s, m);
- if (!s) {
- buffio_close_fd(b);
- if (b->eof_cb)
- b->eof_cb(b, b->user);
- return OOP_CONTINUE;
+ if (s < 0) {
+ buffio_close_output_fd(b);
+
+ if (errno == EPIPE) {
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_EPIPE);
+ return;
+
+ } else {
+ daemon_log(LOG_ERR, "Failed to write to file descriptor: %s", strerror(errno));
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_ERROR);
+ return;
+ }
}
+
+#ifdef IODEBUG
+ esc_print(b, "OUTPUT: ", b->output_buf+b->output_index, s);
+#endif
- assert(s <= m);
+ assert(s > 0 && s <= m);
b->output_index = (b->output_index + s) % b->output_max_length;
b->output_length -= s;
- }
- buffio_normalize(b);
- buffio_set_output_callbacks(b);
+ buffio_normalize(b);
+ buffio_delay(b, s);
- if (!b->output_length && b->output_empty_cb)
- b->output_empty_cb(b, b->user);
+ if (!b->output_length)
+ buffio_sched_cb(b, BUFFIO_SCHED_CB_OUTPUT_EMPTY);
+ } else
+ buffio_set_output_callbacks(b);
+
+ return;
+}
+
+static void* buffio_read_cb(oop_source *source, int fd, oop_event event, void *user) {
+ struct buffio *b = user;
+ assert(source && b && b->ifd == fd && event == OOP_READ);
+
+ b->readable = 1;
+ do_read(b);
return OOP_CONTINUE;
+}
+static void* buffio_write_cb(oop_source *source, int fd, oop_event event, void *user) {
+ struct buffio *b = user;
+ assert(source && b && b->ofd == fd && event == OOP_WRITE);
+
+ b->writable = 1;
+ do_write(b);
+
+ return OOP_CONTINUE;
}
+static void* buffio_timeout_cb(oop_source *source, struct timeval tv, void *user) {
+ struct buffio *b = user;
+ assert(source && b && b->delaying);
+
+ b->delaying = 0;
+ do_write(b);
+
+ return OOP_CONTINUE;
+}
int buffio_write(struct buffio *b, const uint8_t *d, size_t l) {
assert(b && d && l);
@@ -229,9 +384,6 @@ int buffio_write(struct buffio *b, const uint8_t *d, size_t l) {
i = (b->output_index + b->output_length) % b->output_max_length;
m = l;
- if (m > b->output_max_length-b->output_length)
- m = b->output_max_length-b->output_length;
-
if (m > b->output_max_length-i)
m = b->output_max_length-i;
@@ -241,9 +393,9 @@ int buffio_write(struct buffio *b, const uint8_t *d, size_t l) {
b->output_length += m;
}
- buffio_set_output_callbacks(b);
buffio_normalize(b);
-
+
+ do_write(b);
return 0;
}
@@ -266,6 +418,13 @@ void buffio_flush_input(struct buffio *b) {
buffio_set_input_callbacks(b);
}
+void buffio_flush_output(struct buffio *b) {
+ assert(b);
+
+ b->output_length = b->output_index = 0;
+ buffio_set_output_callbacks(b);
+}
+
int buffio_find_input(struct buffio *b, const char *c) {
size_t l, cl, i;
@@ -284,7 +443,8 @@ int buffio_find_input(struct buffio *b, const char *c) {
b->input_length = l-cl;
buffio_normalize(b);
- buffio_set_input_callbacks(b);
+
+ do_read(b);
return 1;
}
}
@@ -293,7 +453,7 @@ int buffio_find_input(struct buffio *b, const char *c) {
}
-char *buffio_read_line(struct buffio *b, char *c, size_t l) {
+char* buffio_read_line(struct buffio *b, char *c, size_t l) {
size_t i;
assert(b && c && l);
@@ -312,8 +472,10 @@ char *buffio_read_line(struct buffio *b, char *c, size_t l) {
b->input_index = (b->input_index+j+1) % b->input_max_length;
b->input_length -= j+1;
- buffio_set_input_callbacks(b);
+
buffio_normalize(b);
+
+ do_read(b);
return c;
}
}
@@ -341,3 +503,135 @@ void buffio_dump(struct buffio *b) {
}
fprintf(stderr, "]\n");
}
+
+void buffio_set_input_watermark(struct buffio *b, ssize_t w) {
+ assert(b);
+
+ if (w < 0)
+ b->input_watermark = b->input_max_length;
+ else
+ b->input_watermark = w;
+
+ assert(b->input_watermark > 0 && b->input_watermark <= b->input_max_length);
+ buffio_set_input_callbacks(b);
+}
+
+void buffio_set_output_watermark(struct buffio *b, ssize_t w) {
+ assert(b);
+
+ if (w < 0)
+ b->output_watermark = b->output_max_length;
+ else
+ b->output_watermark = w;
+
+ assert(b->output_watermark > 0 && b->output_watermark <= b->output_max_length);
+ buffio_set_output_callbacks(b);
+}
+
+
+const uint8_t* buffio_read_ptr(struct buffio *b, size_t *l) {
+ assert(b && l);
+
+ *l = b->input_length;
+
+ if (*l > b->input_max_length - b->input_index)
+ *l = b->input_max_length - b->input_index;
+
+ return b->input_buf + b->input_index;
+}
+
+void buffio_read_ptr_inc(struct buffio *b, size_t l) {
+ assert(b);
+
+ if (!l)
+ return;
+
+ assert(l <= b->input_length && l <= b->input_max_length - b->input_index);
+ b->input_index += l;
+
+ while (b->input_index >= b->input_max_length)
+ b->input_index -= b->input_max_length;
+
+ b->input_length -= l;
+
+ buffio_normalize(b);
+
+ do_read(b);
+}
+
+uint8_t* buffio_write_ptr(struct buffio *b, size_t *l) {
+ size_t j;
+ assert(b && l);
+
+ j = (b->output_index + b->output_length) % b->output_max_length;
+
+ *l = b->output_max_length - b->output_length;
+ if (*l > b->output_max_length - j)
+ *l = b->output_max_length - j;
+
+ return b->output_buf + j;
+}
+
+void buffio_write_ptr_inc(struct buffio *b, size_t l) {
+ int j;
+ assert(b);
+
+ if (!l)
+ return;
+
+ j = (b->output_index + b->output_length) % b->output_max_length;
+ assert(l <= b->output_max_length - b->output_length && l <= b->output_max_length - j);
+
+ b->output_length += l;
+
+ buffio_normalize(b);
+
+ do_write(b);
+}
+
+int buffio_output_is_empty(struct buffio *b) {
+ assert(b);
+
+ return b->output_length == 0;
+}
+
+void buffio_dump_lines(struct buffio *b) {
+ int r, i;
+ assert(b);
+
+ r = 0;
+
+ for (i = 0; i < b->input_length; i++)
+ if (b->input_buf[(b->input_index+i) % b->input_max_length] == '\n')
+ r = i+1;
+
+ if (r > 0) {
+ b->input_index = (b->input_index+r) % b->input_max_length;
+ b->input_length -= r;
+
+ buffio_normalize(b);
+
+ do_read(b);
+ }
+}
+
+void buffio_set_fds(struct buffio *b, int ifd, int ofd) {
+ assert(b && b->ifd == -1 && b->ofd == -1);
+
+ b->ifd = ifd;
+ b->ofd = ofd;
+
+ daemon_nonblock(b->ifd, 1);
+ daemon_nonblock(b->ofd, 1);
+
+ b->readable = b->writable = 0;
+
+ buffio_set_input_callbacks(b);
+ buffio_set_output_callbacks(b);
+}
+
+int buffio_can_write(struct buffio *b, size_t l) {
+ assert(b);
+
+ return l <= b->output_max_length - b->output_length;
+}
diff --git a/src/buffio.h b/src/buffio.h
index e0a2d0a..a41ec76 100644
--- a/src/buffio.h
+++ b/src/buffio.h
@@ -4,6 +4,15 @@
#include <inttypes.h>
#include <sys/types.h>
+enum buffio_sched_cb {
+ BUFFIO_SCHED_CB_IDLE = 0,
+ BUFFIO_SCHED_CB_INPUT_READY = 1,
+ BUFFIO_SCHED_CB_OUTPUT_EMPTY = 2,
+ BUFFIO_SCHED_CB_EOF = 4,
+ BUFFIO_SCHED_CB_EPIPE = 8,
+ BUFFIO_SCHED_CB_ERROR = 16
+};
+
struct buffio {
int ifd;
int ofd;
@@ -18,9 +27,21 @@ struct buffio {
void *user;
- void (*eof_cb) (struct buffio *b, void *user);
- void (*output_empty_cb) (struct buffio *b, void *user);
- void (*input_ready_cb) (struct buffio *b, void *user);
+ int readable;
+ int writable;
+
+ int (*input_ready_cb) (struct buffio *b, void *user);
+ int (*output_empty_cb) (struct buffio *b, void *user);
+ int (*eof_cb) (struct buffio *b, void *user);
+ int (*epipe_cb) (struct buffio *b, void *user);
+ int (*error_cb) (struct buffio *b, void *user);
+
+ enum buffio_sched_cb sched_cb;
+
+ uint32_t output_delay_usec;
+ uint32_t output_latency_usec;
+ int delaying;
+ struct timeval finish_tv, timeout_tv;
};
struct buffio* buffio_new(int ifd, int ofd);
@@ -29,11 +50,34 @@ void buffio_free(struct buffio *b);
int buffio_write(struct buffio *b, const uint8_t *d, size_t l);
int buffio_print(struct buffio *b, const char *s);
-int buffio_command(struct buffio *b, const char *c);
void buffio_flush_input(struct buffio *b);
+void buffio_flush_output(struct buffio *b);
+
+int buffio_command(struct buffio *b, const char *c);
int buffio_find_input(struct buffio *b, const char *c);
char *buffio_read_line(struct buffio *b, char *c, size_t l);
void buffio_dump(struct buffio *b);
+void buffio_set_input_watermark(struct buffio *b, ssize_t w);
+void buffio_set_output_watermark(struct buffio *b, ssize_t w);
+
+const uint8_t* buffio_read_ptr(struct buffio *b, size_t *l);
+void buffio_read_ptr_inc(struct buffio *b, size_t l);
+
+uint8_t* buffio_write_ptr(struct buffio *b, size_t *l);
+void buffio_write_ptr_inc(struct buffio *b, size_t l);
+
+void buffio_close_input_fd(struct buffio *b);
+void buffio_close_output_fd(struct buffio *b);
+
+int buffio_output_is_empty(struct buffio *b);
+void buffio_dump_lines(struct buffio *b);
+
+void buffio_set_fds(struct buffio *b, int ifd, int ofd);
+
+void buffio_set_delay_usec(struct buffio *b, unsigned long usec, unsigned long latency);
+
+int buffio_can_write(struct buffio *b, size_t l);
+
#endif
diff --git a/src/dle.c b/src/dle.c
index 680c82f..1cf9fa7 100644
--- a/src/dle.c
+++ b/src/dle.c
@@ -2,7 +2,7 @@
#include "dle.h"
-size_t dle_decode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld, void (*dle_func) (uint8_t c, void *user), void *user, int *dle_flag) {
+size_t dle_decode(const uint8_t* s, size_t ls, uint8_t* d, size_t *ld, int (*dle_func) (uint8_t c, void *user), void *user, int *dle_flag) {
size_t ns, nd;
assert(s && ls && d && ld && *ld && dle_flag);
@@ -12,8 +12,11 @@ size_t dle_decode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld, void (*dle_func
if (s[ns] == DLE)
d[nd++] = DLE;
else {
- if (dle_func)
- dle_func(s[ns], user);
+ if (dle_func)
+ if (dle_func(s[ns], user) < 0) {
+ ns++;
+ break;
+ }
}
*dle_flag = 0;
@@ -29,12 +32,15 @@ size_t dle_decode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld, void (*dle_func
return ns;
}
-size_t dle_encode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld) {
+size_t dle_encode(const uint8_t* s, size_t ls, uint8_t* d, size_t *ld) {
size_t ns, nd;
for (ns = nd = 0; ns < ls && nd < *ld; ns++) {
- if (s[ns] == DLE)
+ if (s[ns] == DLE) {
+ if (nd+1 >= *ld)
+ break;
d[nd++] = DLE;
+ }
d[nd++] = s[ns];
}
diff --git a/src/dle.h b/src/dle.h
index 72bfd55..31d511b 100644
--- a/src/dle.h
+++ b/src/dle.h
@@ -9,7 +9,7 @@
#define ETX 0x03
#define DC4 0x14
-size_t dle_decode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld, void (*dle_func) (uint8_t c, void *user), void *user, int *dle_flag);
-size_t dle_encode(uint8_t* s, size_t ls, uint8_t* d, size_t *ld);
+size_t dle_decode(const uint8_t* s, size_t ls, uint8_t* d, size_t *ld, int (*dle_func) (uint8_t c, void *user), void *user, int *dle_flag);
+size_t dle_encode(const uint8_t* s, size_t ls, uint8_t* d, size_t *ld);
#endif
diff --git a/src/exec.c b/src/exec.c
index 728c574..d9195c6 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -15,7 +15,7 @@
struct process_info {
pid_t pid;
- int killed;
+ int dead;
int stderr_pipe;
process_exit_cb_t cb;
void *user;
@@ -60,7 +60,6 @@ static void close_child_pipe(struct process_info *p) {
}
-
static void free_process_info(struct process_info *p) {
assert (p);
close_child_pipe(p);
@@ -103,22 +102,29 @@ static void *oop_sigchld_cb(oop_source *source, int sig, void *user) {
struct process_info *p;
assert(source && sig == SIGCHLD);
- if ((pid = wait(&status)) <= 0) {
+ if ((pid = waitpid(-1, &status, WUNTRACED)) <= 0) {
daemon_log(LOG_ERR, "wait() failed: %s", strerror(errno));
return OOP_HALT;
}
+ if (WIFSTOPPED(status)) {
+ daemon_log(LOG_ERR, "Child process stopped!");
+ return OOP_CONTINUE;
+ }
+
if (!(p = find_process(pid))) {
daemon_log(LOG_WARNING, "Got SIGCHLD for unknown process, reaping");
return OOP_CONTINUE;
}
- assert(p && p->cb);
+ assert(p && !p->dead);
- if (!p->killed)
+ p->dead = 1;
+ if (p->cb)
p->cb(pid, status, p->user);
- remove_process(pid);
+ if (p->stderr_pipe < 0)
+ remove_process(pid);
return OOP_CONTINUE;
}
@@ -149,6 +155,10 @@ static void *oop_read_cb(oop_source *source, int fd, oop_event event, void *user
if (s == 0) { /* EOF */
close_child_pipe(p);
+
+ if (p->dead)
+ remove_process(p->pid);
+
return OOP_CONTINUE;
}
@@ -224,7 +234,6 @@ pid_t child_process_create(const char *file, char *const argv[], int *ifd, int *
p->cb = cb;
p->user = user;
p->next = procs;
- p->killed = 0;
p->stderr_pipe = stderr_fds[0];
close(stderr_fds[1]);
@@ -324,6 +333,5 @@ int child_process_kill(pid_t pid) {
return -1;
}
- p->killed = 1;
return 0;
}
diff --git a/src/main.c b/src/main.c
index f80b605..9710be9 100644
--- a/src/main.c
+++ b/src/main.c
@@ -35,6 +35,7 @@ int main_loop(void) {
event_source->on_signal(event_source, SIGINT, oop_exit_cb, NULL);
event_source->on_signal(event_source, SIGTERM, oop_exit_cb, NULL);
+ signal(SIGPIPE, SIG_IGN);
if (oop_sys_run(sys) == OOP_ERROR) {
daemon_log(LOG_ERR, "oop_sys_new() returned OOP_ERROR");
diff --git a/src/modem.c b/src/modem.c
index 4cf9a49..71a8eb4 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -9,6 +9,7 @@
#include <sys/time.h>
#include <time.h>
#include <stdio.h>
+#include <sys/wait.h>
#include <oop.h>
@@ -24,26 +25,34 @@
/* Baudrate for the TTY. Should be greater the 64000. */
#define BAUD_RATE B115200
-#define RESET_IDLE_TIME 2
+#define RESET_IDLE_TIME 10
#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 INIT_AT_COMMANDS 12
+#define INPUT_WATERMARK_DEFAULT -1
+#define INPUT_WATERMARK_CONNECTION 2
+#define INPUT_WATERMARK_CHILD 1
+
+#define INIT_AT_COMMANDS 16
static const char* const init_at_commands[INIT_AT_COMMANDS*2] = {
- "\nAT&F\n",
+ "\nAT&F\n", /* Reset to fabric defaults */
"OK\r\n",
- "ATI\n",
+ "ATI\n", /* Make sure this is an isdn4linux device */
"Linux ISDN\r\nOK\r\n",
- "AT&B16\n",
+ "AT+FCLASS=8\n", /* switch to audio mode */
"OK\r\n",
- "AT+FCLASS=8\n",
+ "AT+VIP\n", /* Reset audio parameters */
"OK\r\n",
-
- "AT+VSM=6\n",
+
+ "AT+VSM=6\n", /* Set uLaw encoding */
"OK\r\n",
"ATS18=1\n",
@@ -55,97 +64,75 @@ static const char* const init_at_commands[INIT_AT_COMMANDS*2] = {
"ATS13.4=0\n",
"OK\r\n",
- "ATS13.0=1\n",
+ "ATS13.0=1\n", /* direct tty send */
+ "OK\r\n",
+
+ "AT\n", //S13.2=0\n", /* Don't hangup on DTR low */
"OK\r\n",
"ATS23=1\n",
"OK\r\n",
+
+ "ATS16=1\n", /* send packet size */
+ "OK\r\n",
+
+ "AT\n", //S12.3=0\n", /* DCD always on */
+ "OK\r\n",
+
+ "AT\n", //S12.6=0\n", /* DSR always on */
+ "OK\r\n",
- "ATS0=0\n",
+ "ATS0=0\n", /* No automatic call answering */
"OK\r\n",
- "AT&L%s\n",
+ "AT&L%s\n", /* Listen on this MSN */
"OK\r\n",
};
-static void modem_output_empty_cb(struct buffio *b, void *user);
-static void modem_input_ready_cb(struct buffio *b, void *user);
+static const char hup_sequence[] = { DLE, DC4, DLE, ETX, 0 };
+static const char ath_sequence[] = "\nATH\n";
+
+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);
static void modem_init_cycle(struct modem *m);
static void modem_timeout(struct modem *m, int t);
+static void copy_child_to_modem(struct modem *m);
+static void copy_modem_to_child(struct modem *m);
+static int modem_reopen(struct modem *m);
+void modem_close(struct modem *m);
+
struct modem *modem_open(const char *dev) {
struct modem *m;
- int fd = -1, n;
- struct termios ts, ts2;
assert(dev);
if (device_lock(dev) != 0)
return NULL;
- if ((fd = open(dev, O_RDWR|O_NDELAY)) < 0) {
- daemon_log(LOG_ERR, "Failed to open device <%s>: %s", dev, strerror(errno));
- goto fail;
- }
-
- if ((n = fcntl(fd, F_GETFL, 0)) < 0 || fcntl(fd, F_SETFL, n & ~O_NDELAY) < 0) {
- daemon_log(LOG_ERR, "Failed to remove O_NDELAY flag from device: %s", strerror(errno));
- goto fail;
- }
-
- memset(&ts, 0, sizeof ts);
- ts.c_cflag = CRTSCTS | IGNPAR | HUPCL | CS8;
- ts.c_iflag = IGNPAR;
- ts.c_oflag = 0;
- ts.c_lflag = 0;
-
- ts.c_cc[VMIN] = 1;
- ts.c_cc[VTIME] = 0;
-
- cfsetospeed(&ts, BAUD_RATE);
- cfsetispeed(&ts, BAUD_RATE);
-
- tcflush(fd, TCIFLUSH);
-
- if (tcsetattr(fd, TCSANOW, &ts) < 0) {
- daemon_log(LOG_ERR, "Failed to set TTY attributes: %s", strerror(errno));
- goto fail;
- }
-
- if (tcgetattr(fd, &ts2) < 0 || memcmp(&ts, &ts2, sizeof(ts)) != 0) {
- daemon_log(LOG_ERR, "Failed to set TTY attributes");
- /*goto fail;*/ /* DON'T FORGET TO REMOVE THE COMMENT MARKS */
- }
-
m = malloc(sizeof(struct modem));
assert(m);
memset(m, 0, sizeof(struct modem));
+
m->dev = strdup(dev);
assert(m->dev);
- m->buffio = buffio_new(fd, fd);
- assert(m->buffio);
-
- m->buffio->output_empty_cb = modem_output_empty_cb;
- m->buffio->input_ready_cb = modem_input_ready_cb;
- m->buffio->user = m;
m->child_pid = -1;
-
+ m->child_buffio = NULL;
m->local_msn = "46";
m->tabentry = NULL;
-
- modem_init_cycle(m);
+ if (modem_reopen(m) < 0)
+ goto fail;
+
+ modem_init_cycle(m);
+
return m;
fail:
- if (fd >= 0)
- close(fd);
-
- device_unlock(dev);
-
+ modem_close(m);
return NULL;
}
@@ -161,14 +148,28 @@ void modem_close(struct modem *m) {
if (m->child_pid != -1)
child_process_kill(m->child_pid);
- buffio_free(m->buffio);
+ if (m->child_buffio)
+ buffio_free(m->child_buffio);
+
+ if (m->buffio)
+ buffio_free(m->buffio);
+
device_unlock(m->dev);
+
free(m->dev);
free(m->caller_number);
free(m->ring_number);
free(m);
}
+static int error_cb(struct buffio *b, void *user) {
+ struct modem *m = user;
+ assert(m && (m->buffio == b || m->child_buffio == b));
+
+ daemon_log(LOG_ERR, "Buffio (%s) failure, exiting...", m->buffio == b ? "modem" : "child");
+ return -1;
+}
+
static void modem_timeout(struct modem *m, int t) {
assert(m);
@@ -192,7 +193,9 @@ static void modem_next_command(struct modem *m) {
p = init_at_commands[m->command_index*2];
- if (m->command_index == 11) {
+ 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);
p = tmp;
}
@@ -210,7 +213,13 @@ static void modem_init_cycle(struct modem *m) {
modem_next_command(m);
}
-void modem_force_hangup(struct modem *m) {
+static void modem_sched_reset(struct modem *m) {
+ daemon_log(LOG_INFO, "Reinitializing in %i seconds.", RESET_IDLE_TIME);
+ m->state = MODEM_STATE_IDLE;
+ modem_timeout(m, RESET_IDLE_TIME);
+}
+
+static void modem_force_hangup(struct modem *m) {
struct termios pts;
assert(m && m->buffio);
@@ -225,6 +234,193 @@ void modem_force_hangup(struct modem *m) {
tcsetattr(m->buffio->ifd, TCSANOW, &pts);
}
+static void modem_hangup_seq(struct modem *m) {
+ assert(m);
+
+ if (m->child_buffio) {
+
+ if (buffio_output_is_empty(m->child_buffio))
+ buffio_close_output_fd(m->child_buffio);
+
+ if (m->child_buffio->ifd == -1 && m->child_buffio->ofd == -1) {
+ buffio_free(m->child_buffio);
+ m->child_buffio = NULL;
+ }
+ }
+
+ if (m->child_buffio && m->child_buffio->ofd != -1) {
+ /* Draining output is required */
+ daemon_log(LOG_INFO, "Waiting for child drain.");
+ m->state = MODEM_STATE_CHILD_DRAIN;
+ modem_timeout(m, CHILD_TIMEOUT);
+
+ } else if (m->child_buffio) {
+ /* Draining input is required */
+ daemon_log(LOG_INFO, "Waiting for child EOF.");
+ m->state = MODEM_STATE_CHILD_WAIT_EOF;
+ buffio_close_output_fd(m->child_buffio);
+ buffio_flush_input(m->child_buffio);
+ modem_timeout(m, CHILD_TIMEOUT);
+
+ } else if (m->child_pid != (pid_t) -1) {
+ daemon_log(LOG_INFO, "Killing child.");
+ m->state = MODEM_STATE_CHILD_KILL;
+ child_process_kill(m->child_pid);
+ modem_timeout(m, CHILD_TIMEOUT);
+
+ } else
+ modem_sched_reset(m);
+}
+
+static void modem_hangup(struct modem *m, int b) {
+ size_t l;
+ assert(m);
+ daemon_log(LOG_INFO, "Hanging up.");
+
+ buffio_set_input_watermark(m->buffio, INPUT_WATERMARK_DEFAULT);
+ buffio_set_delay_usec(m->buffio, 0, 0);
+
+ l = strlen(ath_sequence) + (b ? strlen(hup_sequence) : 0);
+
+ if (buffio_can_write(m->buffio, l)) {
+
+ if (b)
+ buffio_command(m->buffio, hup_sequence);
+
+ buffio_command(m->buffio, ath_sequence);
+ m->state = MODEM_STATE_HANGUP;
+ } else
+ m->state = b ? MODEM_STATE_PRE_HANGUP_HUPSEQ : MODEM_STATE_PRE_HANGUP_ATHSEQ;
+
+ modem_timeout(m, HANGUP_TIMEOUT);
+}
+
+static void child_exit_cb(pid_t t, int status, void *user) {
+ struct modem *m = user;
+ assert(m && m->child_pid == t);
+
+ if (WIFEXITED(status))
+ daemon_log(WEXITSTATUS(status) == 0 ? LOG_INFO : LOG_WARNING, "Child exited with return code %i.", WEXITSTATUS(status));
+ else if (WIFSIGNALED(status))
+ daemon_log(LOG_WARNING, "Child exited by signal %i.", WTERMSIG(status));
+ else
+ daemon_log(LOG_ERR, "Child exited due to unknown cause.");
+
+ m->child_pid = (pid_t) -1;
+
+ switch (m->state) {
+
+ case MODEM_STATE_CHILD_KILL:
+ modem_hangup_seq(m);
+ break;
+
+ default:
+ break;
+ }
+}
+
+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)
+ copy_child_to_modem(m);
+ else
+ buffio_flush_input(m->child_buffio);
+
+ return 0;
+}
+
+static int child_eof_cb(struct buffio *b, void *user) {
+ struct modem *m = user;
+ assert(m && m->child_buffio == b);
+
+ daemon_log(LOG_INFO, "Got child EOF.");
+
+ switch (m->state) {
+
+ case MODEM_STATE_CONNECTION:
+ modem_hangup(m, 1);
+ break;
+
+ case MODEM_STATE_CHILD_WAIT_EOF:
+ modem_hangup_seq(m);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int child_epipe_cb(struct buffio *b, void *user) {
+ struct modem *m = user;
+ assert(m && m->child_buffio == b);
+
+ daemon_log(LOG_INFO, "Got child EPIPE.");
+
+ assert(m->child_buffio);
+ buffio_flush_output(m->child_buffio);
+
+ switch (m->state) {
+
+ case MODEM_STATE_CONNECTION:
+ modem_hangup(m, 1);
+ break;
+
+ case MODEM_STATE_CHILD_DRAIN:
+ modem_hangup_seq(m);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int child_output_empty_cb(struct buffio *b, void *user) {
+ struct modem *m = user;
+ assert(m && m->child_buffio == b);
+
+ switch (m->state) {
+
+ case MODEM_STATE_CONNECTION:
+ copy_modem_to_child(m);
+ break;
+
+ case MODEM_STATE_CHILD_DRAIN:
+ modem_hangup_seq(m);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+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->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)
+ 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);
+ 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->error_cb = error_cb;
+
+ return 0;
+}
static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user) {
struct modem *m = (struct modem*) user;
@@ -236,14 +432,17 @@ static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user) {
case MODEM_STATE_CALLER_NUMBER:
case MODEM_STATE_RING_EXPECT:
case MODEM_STATE_RING:
+ case MODEM_STATE_PRE_HANGUP_HUPSEQ:
+ case MODEM_STATE_PRE_HANGUP_ATHSEQ:
+ case MODEM_STATE_HANGUP:
daemon_log(LOG_ERR, "Timeout reached for device <%s>", m->dev);
return OOP_HALT;
- case MODEM_STATE_ANSWER:
+ case MODEM_STATE_ATA:
case MODEM_STATE_VTXVRX:
- daemon_log(LOG_ERR, "Connection failed for device <%s>", m->dev);
+ daemon_log(LOG_ERR, "Connection not established for device <%s>", m->dev);
m->state = MODEM_STATE_IDLE;
modem_timeout(m, RESET_IDLE_TIME);
return OOP_CONTINUE;
@@ -252,13 +451,36 @@ static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user) {
modem_init_cycle(m);
return OOP_CONTINUE;
+
+ case MODEM_STATE_CHILD_DRAIN:
+ daemon_log(LOG_INFO, "Child output buffer did not drain, flushing.");
+ assert(m->child_buffio);
+ buffio_flush_output(m->child_buffio);
+
+ modem_hangup_seq(m);
+ return OOP_CONTINUE;
+ case MODEM_STATE_CHILD_WAIT_EOF:
+
+ daemon_log(LOG_INFO, "Child did not EOF, closing child pipes.");
+ assert(m->child_buffio);
+ buffio_free(m->child_buffio);
+ m->child_buffio = NULL;
+
+ modem_hangup_seq(m);
+
+ return OOP_CONTINUE;
+
+ case MODEM_STATE_CHILD_KILL:
+ daemon_log(LOG_INFO, "Child did not react on killing, exiting.");
+ return OOP_HALT;
+
default:
assert(0);
}
}
-static void modem_input_ready_cb(struct buffio *b, void *user) {
+static int modem_input_ready_cb(struct buffio *b, void *user) {
struct modem *m = user;
assert(b && m && m->buffio == b);
@@ -280,7 +502,8 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
modem_input_ready_cb(b, user);
} else
modem_next_command(m);
- }
+ } else
+ buffio_dump_lines(m->buffio);
break;
}
@@ -291,7 +514,8 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
m->state = MODEM_STATE_CALLER_NUMBER;
modem_timeout(m, BASIC_TIMEOUT);
modem_input_ready_cb(b, user);
- }
+ } else
+ buffio_dump_lines(m->buffio);
break;
@@ -318,7 +542,9 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
if (buffio_find_input(m->buffio, "RING/")) {
m->state = MODEM_STATE_RING;
modem_input_ready_cb(b, user);
- }
+ } else
+ buffio_dump_lines(m->buffio);
+
break;
case MODEM_STATE_RING: {
@@ -342,7 +568,7 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
m->tabentry = t;
daemon_log(LOG_INFO, "Accepting call.");
- m->state = MODEM_STATE_ANSWER;
+ m->state = MODEM_STATE_ATA;
modem_timeout(m, ACCEPT_TIMEOUT);
buffio_command(m->buffio, "ATA\n");
@@ -358,28 +584,27 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
break;
}
- case MODEM_STATE_ANSWER:
+ case MODEM_STATE_ATA:
if (buffio_find_input(m->buffio, "VCON\r\n")) {
assert(m->tabentry);
if (m->tabentry->action == CALL_ACTION_ACCEPT) {
daemon_log(LOG_INFO, "Call accepted, changing to voice mode.");
- buffio_command(m->buffio, "AT+VTX+VRX");
+ buffio_command(m->buffio, "AT+VTX+VRX\n");
m->state = MODEM_STATE_VTXVRX;
} else if (m->tabentry->action == CALL_ACTION_HANGUP) {
modem_timeout(m, 0);
-
daemon_log(LOG_INFO, "Call accepted for hang up.");
- m->state = MODEM_STATE_ATH;
- modem_input_ready_cb(b, user);
+ modem_hangup(m, 0);
} else
assert(0);
- } else if (buffio_find_input(m->buffio, "NO CARRIER\n\n")) {
+
+ } else if (buffio_find_input(m->buffio, "NO CARRIER\r\n")) {
daemon_log(LOG_INFO, "Failed to accept call, carrier lost");
- m->state = MODEM_STATE_IDLE;
- modem_timeout(m, RESET_IDLE_TIME);
- }
+ modem_sched_reset(m);
+ } else
+ buffio_dump_lines(m->buffio);
break;
@@ -390,58 +615,190 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
m->state = MODEM_STATE_CONNECTION;
daemon_log(LOG_INFO, "Voice connection established.");
- modem_input_ready_cb(b, user);
- } else if (buffio_find_input(m->buffio, "NO CARRIER\n\n")) {
+
+ if (modem_start_child(m) < 0) {
+ daemon_log(LOG_ERR, "Failed to start child process");
+ modem_hangup(m, 1);
+ } else {
+ m->dle_flag = 0;
+ buffio_set_input_watermark(m->buffio, INPUT_WATERMARK_CONNECTION);
+ buffio_set_delay_usec(m->buffio, ISDN_DELAY_USEC, ISDN_LATENCY_USEC);
+ modem_input_ready_cb(b, user);
+ }
+
+ } else if (buffio_find_input(m->buffio, "NO CARRIER\r\n")) {
daemon_log(LOG_INFO, "Failed to accept call, carrier lost");
- m->state = MODEM_STATE_IDLE;
- modem_timeout(m, RESET_IDLE_TIME);
+ modem_sched_reset(m);
}
break;
case MODEM_STATE_CONNECTION:
- m->state = MODEM_STATE_HANGUP;
- modem_input_ready_cb(b, user);
+ copy_modem_to_child(m);
break;
-
- case MODEM_STATE_HANGUP: {
- static const char hup[] = { DLE, DC4, DLE, ETX, 0 };
- buffio_command(m->buffio, hup);
-
- /* pass over ... */
- }
-
- case MODEM_STATE_ATH: {
- daemon_log(LOG_INFO, "Hanging up.");
- buffio_command(m->buffio, "ATH\n");
- m->state = MODEM_STATE_FORCE_HANGUP;
- break;
- }
default:
break;
}
-}
-
-static void modem_output_empty_cb(struct buffio *b, void *user) {
+ return 0;
+}
+
+static int modem_output_empty_cb(struct buffio *b, void *user) {
struct modem *m = user;
assert(b && m && m->buffio == b);
switch (m->state) {
- case MODEM_STATE_FORCE_HANGUP:
+ case MODEM_STATE_CONNECTION:
+ copy_child_to_modem(m);
+ break;
+
+ case MODEM_STATE_PRE_HANGUP_HUPSEQ:
+ if (b)
+ buffio_command(m->buffio, hup_sequence);
+ /* pass over */
+
+ case MODEM_STATE_PRE_HANGUP_ATHSEQ:
+ buffio_command(m->buffio, ath_sequence);
+ m->state = MODEM_STATE_HANGUP;
+
+ daemon_log(LOG_INFO, "Delayed HANGUP sequence");
+
+ /* Don't restart timeout here */
+ break;
+
+ case MODEM_STATE_HANGUP:
modem_force_hangup(m);
- daemon_log(LOG_INFO, "Hang up successful, reinitializing in %i seconds.", RESET_IDLE_TIME);
- m->state = MODEM_STATE_IDLE;
- modem_timeout(m, RESET_IDLE_TIME);
+ modem_hangup_seq(m);
break;
default:
break;
}
+
+ return 0;
}
+static int modem_dle_cb(uint8_t c, void *user) {
+ struct modem *m = user;
+ assert(m);
+
+ switch (c) {
+ case ETX :
+ case DC4 :
+ daemon_log(LOG_INFO, "Recieved hangup sequence from peer.");
+
+ assert(m->state == MODEM_STATE_CONNECTION);
+
+ buffio_flush_output(m->buffio);
+ modem_hangup(m, 1);
+ return -1;
+
+ default:
+ daemon_log(LOG_INFO, "Recieved DTMF character '%c'", c);
+ }
+ return 0;
+}
+
+static void copy_modem_to_child(struct modem *m) {
+ const uint8_t *sp;
+ uint8_t *dp;
+ 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);
+
+ if (sp && sl && dp && dl) {
+ sl = dle_decode(sp, sl, dp, &dl, modem_dle_cb, m, &m->dle_flag);
+
+ /* It may be the case that dle_decode recieved a
+ hangup sequence! */
+
+ if (m->state == MODEM_STATE_CONNECTION)
+ buffio_read_ptr_inc(m->buffio, sl);
+ buffio_write_ptr_inc(m->child_buffio, dl);
+ }
+}
+
+static void copy_child_to_modem(struct modem *m) {
+ size_t sl, dl;
+ const uint8_t *sp;
+ uint8_t *dp;
+
+ assert(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) {
+ sl = dle_encode(sp, sl, dp, &dl);
+ buffio_read_ptr_inc(m->child_buffio, sl);
+ buffio_write_ptr_inc(m->buffio, dl);
+ }
+}
+
+static int modem_reopen(struct modem *m) {
+ int fd;
+ struct termios ts;
+ assert(m && m->dev);
+
+ if (m->buffio) {
+ daemon_log(LOG_INFO, "Closing TTY device '%s'.");
+ buffio_close_output_fd(m->buffio);
+ buffio_close_input_fd(m->buffio);
+ }
+ daemon_log(LOG_INFO, "Trying to open 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));
+ goto fail;
+ }
+
+ daemon_log(LOG_INFO, "TTY device '%s' successfully opened.", m->dev);
+
+ if (tcgetattr(fd, &ts) < 0) {
+ daemon_log(LOG_ERR, "Failed to get TTY attributes: %s", strerror(errno));
+ goto fail;
+ }
+
+ cfmakeraw(&ts);
+
+ ts.c_cflag |= HUPCL | CS8 | CLOCAL;
+ ts.c_iflag |= IGNPAR | IGNBRK;
+
+ ts.c_cc[VMIN] = 1;
+ ts.c_cc[VTIME] = 0;
+
+ cfsetospeed(&ts, BAUD_RATE);
+ cfsetispeed(&ts, BAUD_RATE);
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (tcsetattr(fd, TCSANOW, &ts) < 0) {
+ daemon_log(LOG_ERR, "Failed to set TTY attributes: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (!m->buffio) {
+ m->buffio = buffio_new(fd, fd);
+ assert(m->buffio);
+
+ 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;
+ } else
+ buffio_set_fds(m->buffio, fd, fd);
+
+ return 0;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
diff --git a/src/modem.h b/src/modem.h
index a19ff38..320467a 100644
--- a/src/modem.h
+++ b/src/modem.h
@@ -10,13 +10,16 @@ enum modem_state {
MODEM_STATE_CALLER_NUMBER,
MODEM_STATE_RING_EXPECT,
MODEM_STATE_RING,
- MODEM_STATE_ANSWER,
+ MODEM_STATE_ATA,
MODEM_STATE_VTXVRX,
MODEM_STATE_CONNECTION,
MODEM_STATE_CONNECTION_SHBUF,
+ MODEM_STATE_PRE_HANGUP_HUPSEQ,
+ MODEM_STATE_PRE_HANGUP_ATHSEQ,
MODEM_STATE_HANGUP,
- MODEM_STATE_ATH,
- MODEM_STATE_FORCE_HANGUP,
+ MODEM_STATE_CHILD_DRAIN, /* wait until no further data may/has to be written to the child */
+ MODEM_STATE_CHILD_WAIT_EOF, /* wait until no further data may be read from child */
+ MODEM_STATE_CHILD_KILL,
MODEM_STATE_IDLE
};
@@ -30,17 +33,19 @@ struct modem {
enum modem_state state;
int command_index;
+ struct buffio *child_buffio;
pid_t child_pid;
struct timeval timeout;
- int dle_flag;
char *local_msn;
char *ring_number;
char *caller_number;
struct tabentry *tabentry;
+
+ int dle_flag;
};
struct modem *modem_open(const char *dev);
diff --git a/src/modemman.c b/src/modemman.c
index f7521e3..cccaef9 100644
--- a/src/modemman.c
+++ b/src/modemman.c
@@ -10,7 +10,7 @@
#include "modem.h"
#define MAX_CHANNELS 10
-#define TTY_START 11
+#define TTY_START 12
struct llist {
struct modem *modem;
diff --git a/src/util.c b/src/util.c
index d236253..3ba6506 100644
--- a/src/util.c
+++ b/src/util.c
@@ -13,34 +13,34 @@ char *basename(char *path) {
}
ssize_t loop_read(int FILEDES, void *BUFFER, size_t SIZE) {
- int c = 0;
+ ssize_t c = 0;
while (SIZE > 0) {
- int r = read(FILEDES, BUFFER, SIZE);
+ ssize_t r = read(FILEDES, BUFFER, SIZE);
if (r <= 0)
break;
SIZE -= r;
c += r;
- BUFFER = ((void*) (((char*) BUFFER) + r));
+ BUFFER += r;
}
return c;
}
ssize_t loop_write(int FILEDES, const void *BUFFER, size_t SIZE) {
- int c = 0;
+ ssize_t c = 0;
while (SIZE > 0) {
- int r = write(FILEDES, BUFFER, SIZE);
+ ssize_t r = write(FILEDES, BUFFER, SIZE);
if (r <= 0)
break;
SIZE -= r;
c += r;
- BUFFER = ((void*) (((char*) BUFFER) + r));
+ BUFFER += r;
}
return c;