diff options
Diffstat (limited to 'src/ivcall.c')
-rw-r--r-- | src/ivcall.c | 597 |
1 files changed, 427 insertions, 170 deletions
diff --git a/src/ivcall.c b/src/ivcall.c index 28cf890..549d56f 100644 --- a/src/ivcall.c +++ b/src/ivcall.c @@ -22,6 +22,7 @@ #include "config.h" #endif +#include <assert.h> #include <termios.h> #include <fcntl.h> #include <stdlib.h> @@ -38,85 +39,108 @@ #include <sys/types.h> #include <sys/time.h> #include <sys/select.h> +#include <inttypes.h> + +#ifdef HAVE_SPANDSP +#include <tiffio.h> +#include <spandsp.h> +#endif #include "util.h" #include "lock.h" +#include "g711.h" -// Those nifty DLE-sequences +/* Those nifty DLE-sequences*/ #define DLE 0x10 #define ETX 0x03 #define DC4 0x14 -// Baudrate for the TTY. Should be greater the 64000. +/* Baudrate for the TTY. Should be greater the 64000.*/ #define BAUD_RATE B115200 -// Every single commands gets a timeout of 2 seconds. +/* Every single commands gets a timeout of 2 seconds.*/ #define COMMAND_TIMEOUT 2 -// The default timeout for waiting that the call gets accepted. +/* The default timeout for waiting that the call gets accepted.*/ #define DEFAULT_TIMEOUT 30 -// Initialisation of the modem +#define BUFSIZE 10240 + +/* Initialisation of the modem*/ const char *at_commands[] = { "AT&F\n", "OK\r\n", "ATI\n", "Linux ISDN\r\nOK", "AT&B128\n", "OK\r\n", "AT+FCLASS=8\n", "OK\r\n", + "AT+VIP\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=1\n", "OK\r\n", + "ATS13.4=0\n", "OK\r\n", + "ATS13.0=1\n", "OK\r\n", + "ATS16=1\n", "OK\r\n", "ATS23=1\n", "OK\r\n", 0 }; -// Local MSN +#ifdef HAVE_SPANDSP +/* Tiff file to send */ +char *fax_tiff_file = NULL; + +/* Do fax */ +int fax_enabled = 0; + +/* Local identifier for fax */ +char *fax_ident = NULL; +#endif + +/* Local MSN*/ char *source_msn = NULL; -// Remote MSN +/* Remote MSN*/ char *destination_msn = NULL; -// Modem-device +/* Modem-device*/ char *device = NULL; -// Our name +/* Our name*/ char *appname = NULL; -// The default timeout +/* The default timeout*/ int timeout = DEFAULT_TIMEOUT; -// Enable debug-mode or not? +/* Enable debug-mode or not?*/ int debug = 0; -// We got a ctrl-c +/* We got a ctrl-c*/ volatile int interrupted = 0; -// Got an SIGARLM? +/* Got an SIGARLM?*/ volatile int alarmed = 0; -// Shall we wait for a call? +/* Shall we wait for a call?*/ int answer = 0; -// After how many RINGs we shall answer? +/* After how many RINGs we shall answer?*/ int rings = 1; -// Saved TTY configuration +/* Saved TTY configuration*/ struct termios saved_termios; -// Our ctrl-c handler +/* Our ctrl-c handler */ void sigint(int sig) { - const char *s = "Got SIGINT\n"; - write(2, s, strlen(s)); + const char s[] = "Got SIGINT\n"; + loop_write(2, s, strlen(s)); interrupted = 1; } void sigalrm(int sig) { - const char *s = "Timeout\n"; - write(2, s, strlen(s)); + const char s[] = "Timeout\n"; + loop_write(2, s, strlen(s)); alarmed = 1; } -// Waits for a certain string from the modem up to a certain time. +/* Waits for a certain string from the modem up to a certain time.*/ int tty_waitfor(int fd, const char *what, int t) { static char buf[256]; char *p = buf; @@ -160,7 +184,7 @@ int tty_waitfor(int fd, const char *what, int t) { return r; } -// Write a string to the modem +/* Write a string to the modem*/ int tty_puts(int fd, const char *s) { int l = strlen(s); @@ -176,14 +200,14 @@ int tty_puts(int fd, const char *s) { return 0; } -// Issue a command to the modem an wait for an answer +/* Issue a command to the modem an wait for an answer */ int tty_command(int fd, const char *cmd, const char* resp, int t) { tcflush(fd, TCIFLUSH); tty_puts(fd, cmd); return tty_waitfor(fd, resp, t); } -// Hang up the modem +/* Hang up the modem */ int hard_hangup(int fd) { struct termios pts; @@ -211,7 +235,7 @@ int hard_hangup(int fd) { return 0; } -// Opens the modem and sets the baud rate +/* Opens the modem and sets the baud rate*/ int tty_open(const char *device) { struct termios pts, pts2; int fd = -1, n; @@ -256,14 +280,14 @@ fail: return -1; } -// Closes the modem device and resets the baudrate +/* Closes the modem device and resets the baudrate*/ void tty_close(int fd) { tcflush(fd, TCIOFLUSH); tcsetattr(fd, TCSANOW, &saved_termios); close(fd); } -// Detects the DCD line +/* Detects the DCD line*/ int detect_carrier(int fd) { unsigned int arg; @@ -273,15 +297,21 @@ int detect_carrier(int fd) { return arg & TIOCM_CAR ? 1 : 0; } -// Filters out DLE-sequences from modem -char *filter_dle(unsigned char *data, int *size, int *flag, int *quit) { - unsigned char *ret, *m, *s, *d; - int l; +/* Filters out DLE-sequences from modem */ +uint8_t *filter_dle(uint8_t *data, size_t size, size_t *ret_size, int *flag, int *quit) { + uint8_t *ret, *s, *d; + size_t l; + + assert(data); + assert(size > 0); + assert(ret_size); + assert(flag); + assert(quit); - if (!(ret = malloc(*size))) + if (!(ret = malloc(size))) return NULL; - for (l = *size, s = data, d = ret; l > 0; l--, s++) { + for (l = size, s = data, d = ret; l > 0; l--, s++) { if (*flag) { switch (*s) { case DLE : @@ -294,54 +324,47 @@ char *filter_dle(unsigned char *data, int *size, int *flag, int *quit) { break; default : - // We recieved some DTMF signals, but we ignore them. + /* We received some DTMF signals, but we ignore them. */ break; } *flag = 0; - continue; - } - - if (*s == DLE) + } else if (*s == DLE) *flag = 1; else *(d++) = *s; - } - *size = d-ret; + *ret_size = d-ret; - if (!(m = realloc(ret, *size))) { - free(ret); - return NULL; - } - - return m; + return ret; } -// Escape DLE-bytes for sending data to the modem -char* escape_dle(unsigned char *data, int *size) { - unsigned char *ret, *m, *s, *d; - int l, ms; +/* Escape DLE-bytes for sending data to the modem */ +uint8_t* escape_dle(uint8_t *data, size_t size, size_t *ret_size) { + uint8_t *ret, *s, *d; + size_t l, ms; - if (!(ret = malloc(ms = (int) (*size*1.05)))) + if (!(ret = malloc(ms = (size_t) (size*1.05)))) return NULL; + + *ret_size = size; - for (l = *size, s = data, d = ret; l > 0; l--, s++) { + for (l = size, s = data, d = ret; l > 0; l--, s++) { if (*s == DLE) { *(d++) = DLE; - (*size)++; + (*ret_size)++; - if (ms < *size) { + if (ms < *ret_size) { int n = d-ret; + uint8_t *m; - fprintf(stderr, "REALLOC!\n"); - - if (!(m = realloc(ret, ms = (int) (*size*1.05)))) { + if (!(m = realloc(ret, ms = (size_t) (*ret_size*1.05)))) { free(ret); return NULL; } + ret = m; d = ret+n; } @@ -350,79 +373,17 @@ char* escape_dle(unsigned char *data, int *size) { *(d++) = *s; } - if (!(m = realloc(ret, *size))) { - free(ret); - return NULL; - } - - return m; + return ret; } - -// The function that does the modem work -int go(void) { - int i, flag, warned = 0, r = -1, fd = -1, quit; - char *incoming = NULL, *outgoing = NULL; - int incoming_idx = 0, incoming_len = 0, +static int cat_loop(int fd) { + uint8_t *incoming = NULL, *outgoing = NULL; + ssize_t incoming_idx = 0, incoming_len = 0, outgoing_idx = 0, outgoing_len = 0; - int fm; - char fn[PATH_MAX]; - static char buf[1024]; + int warned = 0; + int fm, r = -1, flag = 0, quit = 0; + uint8_t buf[BUFSIZE]; - if ((device ? device_lock(device, appname) : device_lock_first(appname, fn, sizeof(fn))) < 0) { - fprintf(stderr, "Could not get lock on device: %s\n", strerror(errno)); - goto fail; - } - - if (!device) - if (!(device = strdup(fn))) { - fprintf(stderr, "Out of memory!\n"); - goto fail; - } - - if ((fd = tty_open(device)) < 0) { - fprintf(stderr, "Could not open device: %s\n", strerror(errno)); - goto fail; - } - - for (i = 0; at_commands[i] != 0; i+=2) - if (tty_command(fd, at_commands[i], at_commands[i+1], COMMAND_TIMEOUT) < 0) { - fprintf(stderr, "Initialisation failure.\n"); - goto fail; - } - - if (answer) { - snprintf(buf, sizeof(buf), "AT&L%s\n", source_msn); - if (tty_command(fd, buf, "OK\r\n", COMMAND_TIMEOUT) < 0) { - fprintf(stderr, "Initialisation failure.\n"); - goto fail; - } - } else { - snprintf(buf, sizeof(buf), "AT&E%s\n", source_msn); - if (tty_command(fd, buf, "OK\r\n", COMMAND_TIMEOUT) < 0) { - fprintf(stderr, "Initialisation failure.\n"); - goto fail; - } - } - - if (answer) - snprintf(buf, sizeof(buf), "ATS0=%i\n", rings); - else - snprintf(buf, sizeof(buf), "ATD%s\n", destination_msn); - - if (tty_command(fd, buf, "VCON\r\n", timeout) < 0) { - fprintf(stderr, "Failed to accept connection.\n"); - r = -2; - goto fail; - } - - if (tty_command(fd, "AT+VTX+VRX\n", "CONNECT\r\n\r\nCONNECT\r\n", COMMAND_TIMEOUT) < 0) { - fprintf(stderr, "Could not switch to full-duplex audio: %s\n", strerror(errno)); - goto fail; - } - - flag = quit= 0; - if (fd > 1) fm = fd+1; else @@ -435,12 +396,10 @@ int go(void) { if ((c = detect_carrier(fd)) < 0) { if (errno != EIO) fprintf(stderr, "Failure reading DCD line: %s\n", strerror(errno)); - goto fail; + goto finish; } else if (!c) { - if (outgoing) { - free(outgoing); - outgoing = NULL; - } + free(outgoing); + outgoing = NULL; quit = 1; break; } @@ -476,20 +435,20 @@ int go(void) { continue; fprintf(stderr, "select(): %s\n", strerror(errno)); - goto fail; + goto finish; } if (!outgoing && FD_ISSET(0, &rfds)) { if ((outgoing_len = read(0, buf, sizeof(buf))) < 0) { fprintf(stderr, "read(): %s\n", strerror(errno)); - goto fail; + goto finish; } if (!outgoing_len) quit = 1; else { - outgoing = escape_dle(buf, &outgoing_len); + outgoing = escape_dle(buf, outgoing_len, (size_t*) &outgoing_len); outgoing_idx = 0; } } @@ -498,7 +457,7 @@ int go(void) { ssize_t l; if ((l = write(fd, outgoing+outgoing_idx, outgoing_len-outgoing_idx)) < 0) { fprintf(stderr, "write(): %s\n", strerror(errno)); - goto fail; + goto finish; } if (!l) @@ -518,13 +477,13 @@ int go(void) { if (!incoming && FD_ISSET(fd, &rfds)) { if ((incoming_len = read(fd, buf, sizeof(buf))) < 0) { fprintf(stderr, "read(): %s\n", strerror(errno)); - goto fail; + goto finish; } if (!incoming_len) quit = 1; else { - incoming = filter_dle(buf, &incoming_len, &flag, &quit); + incoming = filter_dle(buf, incoming_len, (size_t*) &incoming_len, &flag, &quit); incoming_idx = 0; } } @@ -544,7 +503,7 @@ int go(void) { } else { if ((l = write(1, incoming+incoming_idx, incoming_len-incoming_idx)) < 0) { fprintf(stderr, "write(1): %s\n", strerror(errno)); - goto fail; + goto finish; } if (!l) @@ -561,12 +520,286 @@ int go(void) { } } } + + r = 0; + +finish: + + free(incoming); + free(outgoing); + + return r; +} + +#ifdef HAVE_SPANDSP + +#define NSAMPLES 240 + +static void phase_e_handler(t30_state_t *s, void *user_data, int result) { + t30_stats_t t; + char local_ident[21]; + char far_ident[21]; + + if (!result) { + fprintf(stderr, "Phase E not successful.\n"); + return; + } + + fax_get_transfer_statistics(s, &t); + fax_get_far_ident(s, far_ident); + fax_get_local_ident(s, local_ident); + + fprintf(stderr, + "* Remote station ID: %s\n" + "* Local station ID: %s\n" + "* Pages transferred: %i\n" + "* Image resolution: %ix%i\n" + "* Transfer rate: %i\n", + far_ident, + local_ident, + t.pages_transferred, + t.column_resolution, t.row_resolution, + t.bit_rate); +} + +static void phase_d_handler(t30_state_t *s, void *user_data, int result) { + t30_stats_t t; + + if (!result) { + fprintf(stderr, "Phase D not sucessful.\n"); + return; + } + + fax_get_transfer_statistics(s, &t); + fprintf(stderr, + "* Pages transferred: %i\n" + "* Image size: %ix%i\n" + "* Image resolution: %ix%i\n" + "* Transfer rate: %i\n" + "* Bad rows: %i\n" + "* Longest bad row run: %i\n" + "* Compression type: %i\n" + "* Image size: %i\n", + t.pages_transferred, + t.columns, t.rows, + t.column_resolution, t.row_resolution, + t.bit_rate, + t.bad_rows, + t.longest_bad_row_run, + t.encoding, + t.image_size); +} + +static int fax_loop(int fd) { + t30_state_t fax; + uint8_t *incoming_ulaw = NULL, *outgoing_raw = NULL; + size_t outgoing_raw_index = 0, outgoing_raw_length = 0; + int r = -1; + int flag = 0, quit = 0; + + assert(fd >= 0); + + fax_init(&fax, !answer, NULL); + if (fax_ident) + fax_set_local_ident(&fax, fax_ident); + + if (answer) + fax_set_rx_file(&fax, fax_tiff_file); + else + fax_set_tx_file(&fax, fax_tiff_file); + + fax_set_phase_e_handler(&fax, phase_e_handler, NULL); + fax_set_phase_d_handler(&fax, phase_d_handler, NULL); + + while (!quit && !interrupted) { + int q; + struct timeval tv; + fd_set rfds; + + tv.tv_sec = 0; + tv.tv_usec = 10000; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + + if (select(fd+1, &rfds, NULL, NULL, &tv) < 0) { + fprintf(stderr, "select(): %s\n", strerror(errno)); + goto finish; + } + +/* putchar('.'); */ +/* fflush(stdout); */ + + if (FD_ISSET(fd, &rfds)) { + uint8_t incoming_raw[NSAMPLES]; + int16_t samples[NSAMPLES]; + size_t nsamples, i; + ssize_t rx; + if ((rx = read(fd, incoming_raw, NSAMPLES)) <= 0) { + fprintf(stderr, "read(): %s\n", rx < 0 ? strerror(errno) : "EOF"); + goto finish; + } + + if (!(incoming_ulaw = filter_dle(incoming_raw, (size_t) rx, &nsamples, &flag, &quit))) + goto finish; + + assert(nsamples <= NSAMPLES); + + for (i = 0; i < nsamples; i++) + samples[i] = st_ulaw2linear16(incoming_ulaw[i]); + + free(incoming_ulaw); + incoming_ulaw = NULL; + + if (fax_rx_process(&fax, samples, nsamples) != 0) { + fprintf(stderr, "Got FAX EOF\n"); + break; + } + +/* putchar('x'); */ +/* fflush(stdout); */ + + +/* fprintf(stderr, "Processing %i RX samples\n", nsamples); */ + } + + if (ioctl(fd, TIOCOUTQ, &q) < 0) { + fprintf(stderr, "TIOCOUTQ failed: %s\n", strerror(errno)); + goto finish; + } + + if (q < NSAMPLES) { + + if (!outgoing_raw) { + int16_t samples[NSAMPLES]; + size_t nsamples; + + if ((nsamples = fax_tx_process(&fax, samples, NSAMPLES)) > 0) { + uint8_t outgoing_ulaw[NSAMPLES]; + size_t i; + +/* putchar('o'); */ +/* fflush(stdout); */ + +/* fprintf(stderr, "Produced %i TX samples\n", nsamples); */ + + for (i = 0; i < nsamples; i++) + outgoing_ulaw[i] = st_14linear2ulaw(samples[i] >> 2); + + if (!(outgoing_raw = escape_dle(outgoing_ulaw, nsamples, &outgoing_raw_length))) + goto finish; + + outgoing_raw_index = 0; + } + } + + if (outgoing_raw) { + ssize_t tx; + size_t t = outgoing_raw_length - outgoing_raw_index; + + assert(outgoing_raw_index < outgoing_raw_length); + assert(t > 0); + + if ((tx = write(fd, outgoing_raw + outgoing_raw_index, t)) < 0) { + fprintf(stderr, "write(): %s\n", strerror(errno)); + goto finish; + } + + outgoing_raw_index += tx; + + if (outgoing_raw_index >= outgoing_raw_length) { + free(outgoing_raw); + outgoing_raw = NULL; + + outgoing_raw_index = outgoing_raw_length = 0; + } + } + } + + + } + + r = 0; + +finish: + + free(incoming_ulaw); + free(outgoing_raw); + + return r; +} + +#endif + +/* The function that does the modem work*/ +int go(void) { + int i, r = -1, fd = -1; + char fn[PATH_MAX]; + char buf[64]; + + if ((device ? device_lock(device, appname) : device_lock_first(appname, fn, sizeof(fn))) < 0) { + fprintf(stderr, "Could not get lock on device: %s\n", strerror(errno)); + goto fail; + } + + if (!device) + if (!(device = strdup(fn))) { + fprintf(stderr, "Out of memory!\n"); + goto fail; + } + + if ((fd = tty_open(device)) < 0) { + fprintf(stderr, "Could not open device: %s\n", strerror(errno)); + goto fail; + } + + for (i = 0; at_commands[i] != 0; i+=2) + if (tty_command(fd, at_commands[i], at_commands[i+1], COMMAND_TIMEOUT) < 0) { + fprintf(stderr, "Initialisation failure.\n"); + goto fail; + } + + if (answer) { + snprintf(buf, sizeof(buf), "AT&L%s\n", source_msn); + if (tty_command(fd, buf, "OK\r\n", COMMAND_TIMEOUT) < 0) { + fprintf(stderr, "Initialisation failure.\n"); + goto fail; + } + } else { + snprintf(buf, sizeof(buf), "AT&E%s\n", source_msn); + if (tty_command(fd, buf, "OK\r\n", COMMAND_TIMEOUT) < 0) { + fprintf(stderr, "Initialisation failure.\n"); + goto fail; + } + } + + if (answer) + snprintf(buf, sizeof(buf), "ATS0=%i\n", rings); + else + snprintf(buf, sizeof(buf), "ATD%s\n", destination_msn); + + if (tty_command(fd, buf, "VCON\r\n", timeout) < 0) { + fprintf(stderr, "Failed to accept connection.\n"); + r = -2; + goto fail; + } + + if (tty_command(fd, "AT+VTX+VRX\n", "CONNECT\r\n\r\nCONNECT\r\n", COMMAND_TIMEOUT) < 0) { + fprintf(stderr, "Could not switch to full-duplex audio: %s\n", strerror(errno)); + goto fail; + } + + +#ifdef HAVE_SPANDSP + if ((fax_enabled ? fax_loop(fd) : cat_loop(fd)) < 0) + goto fail; +#else + if (cat_loop(fd) < 0) + goto fail; +#endif - buf[0] = DLE; - buf[1] = DC4; - buf[2] = DLE; - buf[3] = ETX; + buf[0] = DLE; buf[1] = DC4; buf[2] = DLE; buf[3] = ETX; loop_write(fd, buf, 4); tcdrain(fd); tty_puts(fd, "ATH\n"); @@ -575,12 +808,6 @@ int go(void) { fail: - if (incoming) - free(incoming); - - if (outgoing) - free(outgoing); - if (fd >= 0) { hard_hangup(fd); tty_puts(fd, "AT&F\n"); @@ -599,37 +826,50 @@ struct option long_options[] = { {"answer", 0, 0, 'a'}, {"ring", 1, 0, 'r'}, {"version", 0, 0, 'V'}, +#ifdef HAVE_SPANDSP + {"fax", 1, 0, 'F'}, + {"local-ident", 1, 0, 'n'}, +#endif {0, 0, 0, 0} }; -// Most important part of the program +/* Most important part of the program*/ void usage() { printf("Usage: %s [options] <source-msn> <destination-msn>\n" " %s -a [options] <msn>\n" "\n" "Options:\n" - " -h, --help Shows this usage information\n" - " -t, --timeout=TIMEOUT Sets a timeout (%i)\n" - " -d, --device=DEVICE Selects a device (if not specified, the first available /dev/ttyInn is used)\n" - " -v, --verbose Enables debug mode (%s)\n" - " -a, --answer Wait for incoming call instead of calling out (%s)\n" - " -r, --ring=RINGS On incoming call answer after given RINGs (%i)\n" - " -V, --version Show %s version number\n", - appname, appname, + " -h, --help Shows this usage information\n" + " -t, --timeout=TIMEOUT Sets a timeout (%i)\n" + " -d, --device=DEVICE Selects a device (if not specified, the first available /dev/ttyInn is used)\n" + " -v, --verbose Enables debug mode (%s)\n" + " -a, --answer Wait for incoming call instead of calling out (%s)\n" + " -r, --ring=RINGS On incoming call answer after given RINGs (%i)\n" + " -V, --version Show %s version number\n" +#ifdef HAVE_SPANDSP + " -F, --fax=FILE Send/Receive facsimile in the specified TIFF file (%s)\n" + " -n, --local-ident=IDENT Identification string for local peer during facsimile transmission (%s)\n" +#endif + , appname, appname, timeout, debug ? "on" : "off", answer ? "on" : "off", rings, - appname); + appname +#ifdef HAVE_SPANDSP + , fax_tiff_file, + fax_ident +#endif + ); } -// Parses command line and executes go() +/* Parses command line and executes go()*/ int main (int argc, char *argv[]) { int c, r = 1; - appname = basename(argv[0]); + appname = get_filename(argv[0]); - while((c = getopt_long(argc, argv, "ht:d:var:V", long_options, NULL)) >= 0) { + while((c = getopt_long(argc, argv, "ht:d:var:VF:n:", long_options, NULL)) >= 0) { switch (c) { case 'V' : @@ -656,10 +896,8 @@ int main (int argc, char *argv[]) { case 'd' : if (optarg) { - if (device) - free(device); - else - device = strdup(optarg); + free(device); + device = strdup(optarg); } else { fprintf(stderr, "Expected argument on device option.\n"); goto finish; @@ -687,6 +925,20 @@ int main (int argc, char *argv[]) { } break; +#ifdef HAVE_SPANDSP + case 'F': + free(fax_tiff_file); + fax_tiff_file = strdup(optarg); + + fax_enabled = 1; + break; + + case 'n': + free(fax_ident); + fax_ident = strdup(optarg); + break; +#endif + case '?' : goto finish; } @@ -724,9 +976,14 @@ int main (int argc, char *argv[]) { finish: - if (source_msn) free (source_msn); - if (destination_msn) free(destination_msn); - if (device) free(device); + free(source_msn); + free(destination_msn); + free(device); + +#ifdef HAVE_SPANDSP + free(fax_ident); + free(fax_tiff_file); +#endif return r; } |