diff options
Diffstat (limited to 'src/modem.c')
-rw-r--r-- | src/modem.c | 377 |
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); -} |