isdn4k-utils/vbox3/src/modem.c

551 lines
12 KiB
C

/*
** $Id: modem.c,v 1.2 1998/06/17 12:20:36 michael Exp $
**
** Copyright 1997-1998 by Michael Herold <michael@abadonna.mayn.de>
**
** $Log: modem.c,v $
** Revision 1.2 1998/06/17 12:20:36 michael
** - Changes for automake/autoconf added.
**
** Revision 1.1 1998/06/10 13:31:53 michael
** Source added.
**
*/
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termio.h>
#include <signal.h>
#include "log.h"
#include "modem.h"
#include "libvboxmodem.h"
static struct modemsetup modemsetup =
{
4, /* Echo timeout (sec) */
4, /* Command timeout (sec) */
6, /* Ring timeout (sec) */
1800, /* Alive timeout (sec) */
400 /* Toggle DTR (ms) */
};
static unsigned char lastmodemresult[VBOXMODEM_BUFFER_SIZE + 1];
static int timeoutstatus = 0;
static void modem_timeout_function(int);
static int modem_write(struct vboxmodem *, char *);
static int modem_get_echo(struct vboxmodem *, char *);
static int modem_get_rawsequence(struct vboxmodem *, char *, int);
static int modem_check_result(char *, char *);
/*************************************************************************/
/** modem_set_timeout(): Sets modem function timeout. **/
/*************************************************************************/
/** => timeout Timeout in seconds **/
/*************************************************************************/
void modem_set_timeout(int timeout)
{
if (timeout != 0)
{
timeoutstatus = 0;
signal(SIGALRM, modem_timeout_function);
siginterrupt(SIGALRM, 1);
alarm(timeout);
}
else
{
signal(SIGALRM, SIG_IGN);
alarm(0);
}
}
/*************************************************************************/
/** modem_get_timeout(): Returns the timeout status. **/
/*************************************************************************/
/** <= 1 if timeout or 0 if not **/
/*************************************************************************/
int modem_get_timeout(void)
{
return(timeoutstatus);
}
/*************************************************************************/
/** modem_get_sequence(): Reads a specified sequence from the modem. **/
/*************************************************************************/
/** => seq Sequence to read **/
/** <= 0 sequence read or -1 not read **/
/*************************************************************************/
int modem_get_sequence(struct vboxmodem *vbm, char *seq)
{
return(modem_get_rawsequence(vbm, seq, 0));
}
/*************************************************************************/
/** modem_flush(): Flushs modem input/output. **/
/*************************************************************************/
/** => Pointer to modem structure **/
/** => Flush timeout in seconds **/
/*************************************************************************/
void modem_flush(struct vboxmodem *vbm, int timeout)
{
TIO porttio;
TIO savetio;
long gotjunk = 0;
char onebyte = 0;
log_line(LOG_D, "Flushing modem%s...\n", (timeout ? " (with timeout)" : ""));
if (vboxmodem_get_termio(vbm, &porttio) == 0)
{
savetio = porttio;
porttio.c_lflag &= ~ICANON;
porttio.c_cc[VMIN] = 0;
porttio.c_cc[VTIME] = timeout;
if (vboxmodem_set_termio(vbm, &porttio) == 0)
{
while (vboxmodem_raw_read(vbm, &onebyte, 1) == 1)
{
if (gotjunk++ < 20)
{
log_line(LOG_D, "Junk: ");
log_char(LOG_D, onebyte);
log_text(LOG_D, "\n");
}
}
if (gotjunk > 20)
{
log_line(LOG_D, "Flush has junked %d byte(s)...\n", gotjunk);
}
vboxmodem_set_termio(vbm, &savetio);
}
}
tcflush(vbm->fd, TCIOFLUSH);
tcflush(vbm->fd, TCIOFLUSH);
}
/*************************************************************************/
/** modem_hangup(): Toggles the data terminal ready line to hangup the **/
/** modem. **/
/*************************************************************************/
/** => vbm Pointer to modem structure **/
/** <= 0 on success or -1 on error **/
/*************************************************************************/
int modem_hangup(struct vboxmodem *vbm)
{
TIO porttio;
TIO savetio;
log_line(LOG_D, "Hangup modem (drop dtr %d ms)...\n", modemsetup.toggle_dtr_time);
modem_flush(vbm, 1);
if (vboxmodem_get_termio(vbm, &porttio) == -1) return(-1);
savetio = porttio;
cfsetospeed(&porttio, B0);
cfsetispeed(&porttio, B0);
vboxmodem_set_termio(vbm, &porttio);
usleep(modemsetup.toggle_dtr_time * 1000);
return(vboxmodem_set_termio(vbm, &savetio));
}
/*************************************************************************/
/** modem_command(): Sends a command to the modem and waits for one or **/
/** more results. **/
/*************************************************************************/
/** => vbm Pointer to modem structure **/
/** => command Command to send **/
/** => result Needed answer(s) (seperater with '|') **/
/** <= Number of the found answer, 0 nothing found, -1 on **/
/** error **/
/*************************************************************************/
int modem_command(struct vboxmodem *vbm, char *command, char *result)
{
char line[VBOXMODEM_BUFFER_SIZE + 1];
int back;
lastmodemresult[0] = '\0';
if ((command) && (*command))
{
modem_flush(vbm, 0);
log_line(LOG_D, "Sending \"%s\"...\n", command);
if (strcmp(command, "\r") != 0)
{
if ((modem_write(vbm, command) == -1) || (modem_write(vbm, "\r") == -1))
{
log_line(LOG_E, "Can't send modem command.\n");
modem_flush(vbm, 1);
return(-1);
}
if (modem_get_echo(vbm, command) == -1)
{
log_line(LOG_E, "Can't read modem command echo.\n");
modem_flush(vbm, 1);
return(-1);
}
}
else
{
if (vboxmodem_raw_write(vbm, command, strlen(command)) == -1)
{
log_line(LOG_E, "Can't send modem command.\n");
modem_flush(vbm, 1);
return(-1);
}
}
}
if ((result) && (*result))
{
if (modem_read(vbm, line, modemsetup.commandtimeout) == -1)
{
if ((command) && (*command))
{
log_line(LOG_E, "Can't read modem command result.\n");
}
modem_flush(vbm, 1);
return(-1);
}
strcpy(lastmodemresult, line);
if (strcmp(result, "?") == 0) return(0);
if ((back = modem_check_result(line, result)) < 1)
{
log_line(LOG_E, "Modem returns unneeded command \"");
log_code(LOG_E, line);
log_text(LOG_E, "\".\n");
modem_flush(vbm, 1);
return(-1);
}
else return(back);
}
return(0);
}
/*************************************************************************/
/** modem_read(): Reads a terminated string from the modem. **/
/*************************************************************************/
/** => vbm Pointer to modem structure **/
/** => line Pointer to write buffer **/
/** => readtimeout Timeout in seconds **/
/*************************************************************************/
int modem_read(struct vboxmodem *vbm, char *line, int readtimeout)
{
char c;
int r;
int timeout;
int linelen = 0;
int havetxt = 0;
log_line(LOG_D, "Reading modem answer (%ds timeout)...\n", readtimeout);
modem_set_timeout(readtimeout);
while (((r = vboxmodem_raw_read(vbm, &c, 1)) == 1) && (linelen < (VBOXMODEM_BUFFER_SIZE - 1)))
{
if (c >= 32) havetxt = 1;
if (havetxt)
{
if (c == '\n') break;
if ((c != '\r') && (c != '\n'))
{
*line++ = c;
linelen++;
}
}
if (modem_get_timeout()) break;
}
timeout = modem_get_timeout();
modem_set_timeout(0);
*line = 0;
if ((r != 1) || (timeout) || (linelen >= (VBOXMODEM_BUFFER_SIZE - 1)))
{
log_line(LOG_W, "Can't read from modem [%d]%s.\n", r, (timeout ? " (timeout)" : ""));
return(-1);
}
return(0);
}
/*************************************************************************/
/** **/
/*************************************************************************/
int modem_wait(struct vboxmodem *vbm)
{
struct timeval timeout;
struct timeval *usetimeout;
fd_set fd;
int back;
log_line(LOG_D, "Waiting...\n");
FD_ZERO(&fd);
FD_SET(vbm->fd, &fd);
if (modemsetup.alivetimeout > 0)
{
timeout.tv_sec = modemsetup.alivetimeout;
timeout.tv_usec = modemsetup.alivetimeout * 1000;
usetimeout = &timeout;
}
else usetimeout = NULL;
back = select(FD_SETSIZE, &fd, NULL, NULL, usetimeout);
if (back <= 0)
{
if (back < 0)
{
log_line(LOG_E, "Select returns with error (%d)...\n", back);
}
else log_line(LOG_D, "Select returns with timeout...\n");
return(-1);
}
return(0);
}
void modem_set_nocarrier(struct vboxmodem *vbm, int carrier)
{
vbm->nocarrier = carrier;
}
int modem_get_nocarrier(struct vboxmodem *vbm)
{
return(vbm->nocarrier);
}
/*************************************************************************/
/** modem_timeout_function(): Function called from timeout signal hand- **/
/** ler. **/
/*************************************************************************/
/** => s Signal number **/
/*************************************************************************/
static void modem_timeout_function(int s)
{
alarm(0);
signal(SIGALRM, SIG_IGN);
log_line(LOG_D, "Modem timeout function called...\n");
timeoutstatus = 1;
}
/*************************************************************************/
/** modem_write(): Sends a null terminated string to the modem. **/
/*************************************************************************/
/** => vbm Pointer to modem structure **/
/** => s Terminated string to write **/
/*************************************************************************/
static int modem_write(struct vboxmodem *vbm, char *s)
{
if (vboxmodem_raw_write(vbm, s, strlen(s)) == strlen(s)) return(0);
return(-1);
}
/*************************************************************************/
/** modem_get_echo(): Reads modem echo. **/
/*************************************************************************/
/** => vbm Pointer to modem structure **/
/** => echo Command to get echo from **/
/*************************************************************************/
static int modem_get_echo(struct vboxmodem *vbm, char *echo)
{
return(modem_get_rawsequence(vbm, echo, 1));
}
/*************************************************************************/
/** modem_get_rawsequence(): Reads a raw sequence from modem. This is **/
/** a subroutine for modem_get_sequence() & **/
/** modem_get_echo(). **/
/*************************************************************************/
static int modem_get_rawsequence(struct vboxmodem *vbm, char *line, int echo)
{
char c;
int i;
int timeout;
timeout = (echo ? modemsetup.echotimeout : modemsetup.commandtimeout);
log_line(LOG_D, "Reading modem %s (%ds timeout)...\n", (echo ? "echo" : "sequence"), timeout);
modem_set_timeout(timeout);
for (i = 0; i < strlen(line); i++)
{
if ((vboxmodem_raw_read(vbm, &c, 1) != 1) || (modem_get_timeout()))
{
modem_set_timeout(0);
return(-1);
}
if (line[i] != c)
{
modem_set_timeout(0);
return(-1);
}
}
if (echo)
{
if ((vboxmodem_raw_read(vbm, &c, 1) != 1) || (modem_get_timeout()))
{
modem_set_timeout(0);
return(-1);
}
}
modem_set_timeout(0);
if (echo)
{
if (c != '\r') return(-1);
}
return(0);
}
/*************************************************************************/
/** modem_check_result(): Checks for a string in the modem result. **/
/*************************************************************************/
static int modem_check_result(char *have, char *need)
{
char line[VBOXMODEM_BUFFER_SIZE + 1];
char *word;
char *more;
int nr;
log_line(LOG_D, "Waiting for \"");
log_code(LOG_D, need);
log_text(LOG_D, "\"... ");
strncpy(line, need, VBOXMODEM_BUFFER_SIZE);
line[VBOXMODEM_BUFFER_SIZE] = '\0';
more = strchr(line, '|');
word = strtok(line, "|");
nr = 0;
while (word)
{
nr++;
if (strncmp(have, word, strlen(word)) == 0)
{
if (more)
{
log_text(LOG_D, "Got \"");
log_code(LOG_D, word);
log_text(LOG_D, "\" (%d).\n", nr);
}
else log_text(LOG_D, "Got it.\n");
return(nr);
}
word = strtok(NULL, "|");
}
log_text(LOG_D, "Oops!\n");
return(0);
}