summaryrefslogtreecommitdiffstats
path: root/src/modem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modem.c')
-rw-r--r--src/modem.c377
1 files changed, 224 insertions, 153 deletions
diff --git a/src/modem.c b/src/modem.c
index 1269fbb..4cf9a49 100644
--- a/src/modem.c
+++ b/src/modem.c
@@ -1,9 +1,80 @@
+#include <stddef.h>
+#include <termios.h>
+#include <assert.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+
+#include <oop.h>
+
+#include <libdaemon/dlog.h>
+
#include "modem.h"
#include "lock.h"
+#include "exec.h"
+#include "main.h"
+#include "msntab.h"
+#include "dle.h"
/* Baudrate for the TTY. Should be greater the 64000. */
#define BAUD_RATE B115200
+#define RESET_IDLE_TIME 2
+#define INIT_TIMEOUT 5
+#define ACCEPT_TIMEOUT 5
+#define BASIC_TIMEOUT 5
+
+#define INIT_AT_COMMANDS 12
+static const char* const init_at_commands[INIT_AT_COMMANDS*2] = {
+ "\nAT&F\n",
+ "OK\r\n",
+
+ "ATI\n",
+ "Linux ISDN\r\nOK\r\n",
+
+ "AT&B16\n",
+ "OK\r\n",
+
+ "AT+FCLASS=8\n",
+ "OK\r\n",
+
+ "AT+VSM=6\n",
+ "OK\r\n",
+
+ "ATS18=1\n",
+ "OK\r\n",
+
+ "ATS14=4\n",
+ "OK\r\n",
+
+ "ATS13.4=0\n",
+ "OK\r\n",
+
+ "ATS13.0=1\n",
+ "OK\r\n",
+
+ "ATS23=1\n",
+ "OK\r\n",
+
+ "ATS0=0\n",
+ "OK\r\n",
+
+ "AT&L%s\n",
+ "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 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);
+
struct modem *modem_open(const char *dev) {
struct modem *m;
int fd = -1, n;
@@ -12,7 +83,7 @@ struct modem *modem_open(const char *dev) {
assert(dev);
if (device_lock(dev) != 0)
- return -1;
+ return NULL;
if ((fd = open(dev, O_RDWR|O_NDELAY)) < 0) {
daemon_log(LOG_ERR, "Failed to open device <%s>: %s", dev, strerror(errno));
@@ -43,9 +114,9 @@ struct modem *modem_open(const char *dev) {
goto fail;
}
- if (tcgetattr(fd, &ts2) < 0 || memcmp(&ts, &ts2) != 0) {
+ if (tcgetattr(fd, &ts2) < 0 || memcmp(&ts, &ts2, sizeof(ts)) != 0) {
daemon_log(LOG_ERR, "Failed to set TTY attributes");
- goto fail;
+ /*goto fail;*/ /* DON'T FORGET TO REMOVE THE COMMENT MARKS */
}
m = malloc(sizeof(struct modem));
@@ -53,14 +124,19 @@ struct modem *modem_open(const char *dev) {
memset(m, 0, sizeof(struct modem));
m->dev = strdup(dev);
assert(m->dev);
- m->buffio = buffio_new(fd);
+ m->buffio = buffio_new(fd, fd);
assert(m->buffio);
+
m->buffio->output_empty_cb = modem_output_empty_cb;
- m->buffio->input_ready_cb = modem_output_ready_cb;
+ m->buffio->input_ready_cb = modem_input_ready_cb;
+ m->buffio->user = m;
m->child_pid = -1;
- modem_init(m);
+ m->local_msn = "46";
+ m->tabentry = NULL;
+
+ modem_init_cycle(m);
return m;
@@ -77,124 +153,110 @@ fail:
void modem_close(struct modem *m) {
assert(m);
+ modem_timeout(m, 0);
+
+ if (m->tabentry)
+ msntab_unref(m->tabentry);
+
if (m->child_pid != -1)
child_process_kill(m->child_pid);
buffio_free(m->buffio);
device_unlock(m->dev);
free(m->dev);
+ free(m->caller_number);
+ free(m->ring_number);
free(m);
}
-static void* oop_timeout_cb(oop_source *source,struct timeval tv,void *user) {
- struct modem *m = (struct modem*) user;
- assert(source && user);
-
- daemon_log(LOG_ERR, "Timeout reached for device <%s>", m->device);
- return OOP_HALT;
-}
-
static void modem_timeout(struct modem *m, int t) {
- struct timeval tv = { t, 0 };
assert(m);
assert(event_source && event_source->on_time);
event_source->cancel_time(event_source, m->timeout, oop_timeout_cb, m);
if (t > 0) {
- gettimeofday(&m->timeout);
- m->tv_sec += t;
+ gettimeofday(&m->timeout, NULL);
+ m->timeout.tv_sec += t;
assert(event_source && event_source->on_time);
event_source->on_time(event_source, m->timeout, oop_timeout_cb, m);
}
}
-static void* oop_init_cb(oop_source *source, int fd, oop_event event, void *user) {
- struct modem *m = (struct modem*) user;
- assert(source && user);
- assert(m && m->fd == fd && m->mode == MODEM_STATE_INIT);
-
- if (event == OOP_READ) {
- if (modem_read(m) < 0)
- return OOP_HALT;
-
- } else if (event == OOP_WRITE) {
- modem_write(m);
- }
+static void modem_next_command(struct modem *m) {
+ const char *p;
+ char tmp[64];
+ assert(m && m->buffio && m->state == MODEM_STATE_INIT);
+ assert(m->command_index < INIT_AT_COMMANDS);
- return OOP_CONTINUE;
+ p = init_at_commands[m->command_index*2];
+
+ if (m->command_index == 11) {
+ snprintf(tmp, sizeof(tmp), p, m->local_msn);
+ p = tmp;
+ }
+
+ buffio_command(m->buffio, p);
}
-static void* oop_audio_simple_cb(oop_source *source, int fd, oop_event event, void *user) {
- struct modem *m = (struct modem*) user;
- assert(source && user);
- assert(m && m->mode == MODEM_STATE_AUDIO_SIMPLE);
+static void modem_init_cycle(struct modem *m) {
+ assert(m);
- if (event == OOP_READ) {
- modem_read(m);
- } else if (event == OOP_WRITE) {
- modem_write(m);
- }
+ daemon_log(LOG_INFO, "Initializing modem.");
+ m->state = MODEM_STATE_INIT;
+ m->command_index = 0;
+ modem_timeout(m, INIT_TIMEOUT);
+ modem_next_command(m);
+}
+
+void modem_force_hangup(struct modem *m) {
+ struct termios pts;
+ assert(m && m->buffio);
+
+ tcgetattr(m->buffio->ifd, &pts);
+ cfsetospeed(&pts, B0);
+ cfsetispeed(&pts, B0);
+ tcsetattr(m->buffio->ifd, TCSANOW, &pts);
- return OOP_CONTINUE;
+ tcgetattr(m->buffio->ifd, &pts);
+ cfsetospeed(&pts, BAUD_RATE);
+ cfsetispeed(&pts, BAUD_RATE);
+ tcsetattr(m->buffio->ifd, TCSANOW, &pts);
}
-static void* oop_audio_shbuf_cb(oop_source *source, int fd, oop_event event, void *user) {
+
+static void* oop_timeout_cb(oop_source *source, struct timeval tv, void *user) {
struct modem *m = (struct modem*) user;
assert(source && user);
- assert(m && m->mode == MODEM_STATE_AUDIO_SHBUF);
-
- if (event == OOP_READ) {
- modem_read(m);
- } else if (event == OOP_WRITE) {
- modem_write(m);
- }
- return OOP_CONTINUE;
-}
+ switch (m->state) {
+ case MODEM_STATE_INIT:
-#define INIT_AT_COMMANDS 12
+ case MODEM_STATE_CALLER_NUMBER:
+ case MODEM_STATE_RING_EXPECT:
+ case MODEM_STATE_RING:
+
+ daemon_log(LOG_ERR, "Timeout reached for device <%s>", m->dev);
+ return OOP_HALT;
-static const char* const init_at_commands[INIT_AT_COMMANDS*2] = {
- "\nAT&F\n",
- "OK\r\n",
-
- "ATI\n",
- "Linux ISDN\r\nOK\r\n",
-
- "AT&B16\n",
- "OK\r\n",
-
- "AT+FCLASS=8\n",
- "OK\r\n",
-
- "AT+VSM=6\n",
- "OK\r\n",
-
- "ATS18=1\n",
- "OK\r\n",
-
- "ATS14=4\n",
- "OK\r\n",
-
- "ATS13.4=0\n",
- "OK\r\n",
-
- "ATS13.0=1\n",
- "OK\r\n",
-
- "ATS23=1\n",
- "OK\r\n",
-
- "AT&E%s\n",
- "OK\r\n",
-
- "ATS0=0\n",
- "OK\r\n"
-};
+ case MODEM_STATE_ANSWER:
+ case MODEM_STATE_VTXVRX:
+ daemon_log(LOG_ERR, "Connection failed for device <%s>", m->dev);
+ m->state = MODEM_STATE_IDLE;
+ modem_timeout(m, RESET_IDLE_TIME);
+ return OOP_CONTINUE;
+ case MODEM_STATE_IDLE:
+
+ modem_init_cycle(m);
+ return OOP_CONTINUE;
+
+ default:
+ assert(0);
+ }
+}
static void modem_input_ready_cb(struct buffio *b, void *user) {
struct modem *m = user;
@@ -203,18 +265,19 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
switch (m->state) {
case MODEM_STATE_INIT: {
- char *p;
+ const char *p;
assert(m->command_index < INIT_AT_COMMANDS);
p = init_at_commands[m->command_index*2+1];
-
+
if (buffio_find_input(m->buffio, p)) {
m->command_index++;
if (m->command_index >= INIT_AT_COMMANDS) {
+ daemon_log(LOG_INFO, "Modem successfully initialised, waiting for calls.");
m->state = MODEM_STATE_CALLER_NUMBER_EXPECT;
modem_timeout(m, 0);
- modem_input_init_cb(b, user);
+ modem_input_ready_cb(b, user);
} else
modem_next_command(m);
}
@@ -226,7 +289,8 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
if (buffio_find_input(m->buffio, "CALLER NUMBER: ")) {
m->state = MODEM_STATE_CALLER_NUMBER;
- modem_input_init_cb(b, user);
+ modem_timeout(m, BASIC_TIMEOUT);
+ modem_input_ready_cb(b, user);
}
break;
@@ -236,12 +300,14 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
char c[64];
if (buffio_read_line(m->buffio, c, sizeof(c))) {
+
+ c[strcspn(c, "\n\r")] = 0;
free(m->caller_number);
m->caller_number = strdup(c);
assert(m->caller_number);
m->state = MODEM_STATE_RING_EXPECT;
- modem_input_init_cb(b, user);
+ modem_input_ready_cb(b, user);
}
break;
@@ -251,7 +317,7 @@ 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_init_cb(b, user);
+ modem_input_ready_cb(b, user);
}
break;
@@ -260,16 +326,32 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
char c[64];
if (buffio_read_line(m->buffio, c, sizeof(c))) {
+ struct tabentry *t;
+
+ c[strcspn(c, "\n\r")] = 0;
free(m->ring_number);
m->ring_number = strdup(c);
assert(m->ring_number);
- if (msntab_check_call(m->ring_number, m->caller_number)) {
- modem_send_command(m, "ATA\n");
+ modem_timeout(m, 0);
+ 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_ANSWER;
+ 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_init_cb(b, user);
+ modem_input_ready_cb(b, user);
}
}
@@ -279,32 +361,65 @@ static void modem_input_ready_cb(struct buffio *b, void *user) {
case MODEM_STATE_ANSWER:
if (buffio_find_input(m->buffio, "VCON\r\n")) {
+ assert(m->tabentry);
- buffio_command(m->buffio, "AT+VTX+VRX");
- m->state = MODEM_STATE_VTXVRX;
+ if (m->tabentry->action == CALL_ACTION_ACCEPT) {
+ daemon_log(LOG_INFO, "Call accepted, changing to voice mode.");
+ buffio_command(m->buffio, "AT+VTX+VRX");
+ 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);
+ } else
+ assert(0);
+ } else if (buffio_find_input(m->buffio, "NO CARRIER\n\n")) {
+ daemon_log(LOG_INFO, "Failed to accept call, carrier lost");
+ m->state = MODEM_STATE_IDLE;
+ modem_timeout(m, RESET_IDLE_TIME);
}
+
break;
case MODEM_STATE_VTXVRX:
if (buffio_find_input(m->buffio, "CONNECT\r\n\r\nCONNECT\r\n")) {
+ modem_timeout(m, 0);
+
m->state = MODEM_STATE_CONNECTION;
- modem_input_init_cb(b, user);
+ daemon_log(LOG_INFO, "Voice connection established.");
+ modem_input_ready_cb(b, user);
+ } else if (buffio_find_input(m->buffio, "NO CARRIER\n\n")) {
+ daemon_log(LOG_INFO, "Failed to accept call, carrier lost");
+ m->state = MODEM_STATE_IDLE;
+ modem_timeout(m, RESET_IDLE_TIME);
}
-
+
break;
case MODEM_STATE_CONNECTION:
-
m->state = MODEM_STATE_HANGUP;
+ modem_input_ready_cb(b, user);
break;
case MODEM_STATE_HANGUP: {
+ static const char hup[] = { DLE, DC4, DLE, ETX, 0 };
+ buffio_command(m->buffio, hup);
+
+ /* pass over ... */
+ }
- modem_send_command(m, { DLE, DC4, DLE, ETX, 0 });
+ 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;
}
}
@@ -317,60 +432,16 @@ static void modem_output_empty_cb(struct buffio *b, void *user) {
case MODEM_STATE_FORCE_HANGUP:
modem_force_hangup(m);
- modem_init(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);
break;
- }
-}
-
-void modem_force_hangup(struct modem *m) {
- struct termios pts;
- assert(m && m->buffio);
- tcgetattr(m->buffio->fd, &pts);
- cfsetospeed(&pts, B0);
- cfsetispeed(&pts, B0);
- tcsetattr(m->buffio->fd, TCSANOW, &pts);
-
- tcgetattr(m->buffio->fd, &pts);
- cfsetospeed(&pts, BAUD_RATE);
- cfsetispeed(&pts, BAUD_RATE);
- tcsetattr(m->buffio->fd, TCSANOW, &pts);
-}
-
-
-static void modem_send_command(struct modem *m, const char *p) {
- assert(m && m->buffio);
-
- buffio_input_flush(m->buffio);
- m->buffio->input_cb = modem_input_cb;
- buffio_print(m->buffio, p);
+ default:
+ break;
+ }
}
-static void modem_next_command(struct modem *m) {
- char *p;
- char tmp[64];
- assert(m && m->buffio && (m->state == MODEM_STATE_INIT || m->state == MODEM_STATE_DONE));
- assert(m->command_index < INIT_AT_COMMANDS)
-
- p = init_at_commands[m->command_index*2];
- if (m->command_index == 10) {
- snprintf(tmp, sizeof(tmp), p, m->local_msn);
- p = tmp;
- }
-
- buffio_input_flush(m->buffio);
-
- m->buffio->input_cb = modem_input_cb;
- buffio_command(m->buffio, p);
-}
-static void modem_init(struct modem *m) {
- assert(m);
- m->state = MODEM_STATE_INIT;
- m->command_index = 0;
-
- modem_timeout(m, 10);
- modem_next_command(m);
-}