Added parity generation.
This commit is contained in:
commit
9c807bde6a
43
CHANGES
Normal file
43
CHANGES
Normal file
@ -0,0 +1,43 @@
|
||||
1.0
|
||||
Initial release
|
||||
1.0rc2
|
||||
Cleaned up code
|
||||
10.rc3
|
||||
changed char fields to unsigned char
|
||||
Changed many functions to return status
|
||||
Added macros for __FUNCTION__ keyword, for compiler sanity
|
||||
Added include headers needed for other UNIX platforms
|
||||
1.0rc4
|
||||
Changed cfg->connection to cfg->conn_type
|
||||
Added support for send file text to connections on actions
|
||||
If data received while off-hook and no conn, go on-hook.
|
||||
Clean up parseRegister action in getcmd.c
|
||||
Add support for ':' and '-' extended commands
|
||||
Cleaned up functions to only exit at one point
|
||||
1.0rc5
|
||||
Moved includes to be in correct order
|
||||
Fixed Q0 and Q1 (they were reversed)
|
||||
1.0rc6
|
||||
Add support for direct connection
|
||||
1.0rc7
|
||||
Fixed mislocated tcsetattr in serial.com:ser_init_conn
|
||||
1.0rc8
|
||||
Fixed bug in ip_thread (calling a null number would turn on ip_thread, but no connection, generating a dump
|
||||
Fixed bugs in dce_set_flow_control
|
||||
Fixed behavior of parms parsing for &Z (complex) and S (simple)
|
||||
Defaulted code to not assume /dev/ttyS0 as serport
|
||||
1.0rc9
|
||||
Fixed bug that would stop first session if second call came in
|
||||
Fixed outbound call not bringing DCD high.
|
||||
1.0rc10
|
||||
Added some more debugging information
|
||||
Changed logging to go to stdout from stderr
|
||||
Fixed Phone book matching function to use logger
|
||||
1.0rc11
|
||||
Added ip232 support with custom protocol for use with matching modified version of WinVICE 1.19
|
||||
Added full support for Telnet Binary Transmission (RFC 856)
|
||||
1.0rc12
|
||||
Added code to hook SIG_IO to SIG_IGN. Newer versions of Linux are terminating when IO arrives.
|
||||
|
||||
1.0fz1 - Chris Osborn <fozztexx@fozztexx.com> 2016-May-24
|
||||
Strip parity when looking for commands, generate matching parity on modem responses
|
29
Makefile
Normal file
29
Makefile
Normal file
@ -0,0 +1,29 @@
|
||||
SRC=src
|
||||
SRCS = $(SRC)/bridge.c $(SRC)/debug.c $(SRC)/getcmd.c $(SRC)/ip.c $(SRC)/init.c $(SRC)/modem_core.c $(SRC)/nvt.c $(SRC)/serial.c $(SRC)/ip232.c $(SRC)/util.c $(SRC)/phone_book.c $(SRC)/shared.c $(SRC)/tcpser.c $(SRC)/line.c $(SRC)/dce.c
|
||||
OBJS = $(SRC)/bridge.o $(SRC)/debug.o $(SRC)/getcmd.o $(SRC)/ip.o $(SRC)/init.o $(SRC)/modem_core.o $(SRC)/nvt.o $(SRC)/serial.o $(SRC)/ip232.o $(SRC)/util.o $(SRC)/phone_book.o $(SRC)/shared.o $(SRC)/tcpser.o $(SRC)/dce.o $(SRC)/line.o
|
||||
CC = gcc
|
||||
DEF =
|
||||
CFLAGS = -g $(DEF) -Wall
|
||||
LDFLAGS = -lpthread
|
||||
DEPEND = makedepend $(DEF) $(CFLAGS)
|
||||
|
||||
all: tcpser
|
||||
|
||||
#.o.c:
|
||||
# $(CC) $(CFLAGS) -c $*.c
|
||||
|
||||
$(SRCS):
|
||||
$(CC) $(CFLAGS) -c $*.c
|
||||
|
||||
tcpser: $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJS)
|
||||
|
||||
depend: $(SRCS)
|
||||
$(DEPEND) $(SRCS)
|
||||
|
||||
clean:
|
||||
-rm tcpser *.bak $(SRC)/*~ $(SRC)/*.o $(SRC)/*.bak core
|
||||
|
||||
|
||||
# DO NOT DELETE THIS LINE -- make depend depends on it.
|
||||
|
28
Makefile.solaris
Normal file
28
Makefile.solaris
Normal file
@ -0,0 +1,28 @@
|
||||
SRC=src
|
||||
SRCS = $(SRC)/bridge.c $(SRC)/debug.c $(SRC)/getcmd.c $(SRC)/ip.c $(SRC)/init.c $(SRC)/modem_core.c $(SRC)/nvt.c $(SRC)/serial.c $(SRC)/ip232.c $(SRC)/util.c $(SRC)/phone_book.c $(SRC)/shared.c $(SRC)/tcpser.c $(SRC)/line.c $(SRC)/dce.c
|
||||
OBJS = $(SRC)/bridge.o $(SRC)/debug.o $(SRC)/getcmd.o $(SRC)/ip.o $(SRC)/init.o $(SRC)/modem_core.o $(SRC)/nvt.o $(SRC)/serial.o $(SRC)/ip232.o $(SRC)/util.o $(SRC)/phone_book.o $(SRC)/shared.o $(SRC)/tcpser.o $(SRC)/dce.o $(SRC)/line.o
|
||||
CC = gcc
|
||||
DEF =
|
||||
CFLAGS = -O $(DEF) -Wall
|
||||
LDFLAGS = -lpthread -lsocket -lnsl -lresolv
|
||||
DEPEND = makedepend $(DEF) $(CFLAGS)
|
||||
|
||||
all: tcpser
|
||||
|
||||
#.o.c:
|
||||
# $(CC) $(CFLAGS) -c $*.c
|
||||
|
||||
$(SRCS):
|
||||
$(CC) $(CFLAGS) -c $*.c
|
||||
|
||||
tcpser: $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJS)
|
||||
|
||||
depend: $(SRCS)
|
||||
$(DEPEND) $(SRCS)
|
||||
|
||||
clean:
|
||||
-rm tcpser *.bak $(SRC)/*~ $(SRC)/*.o $(SRC)/*.bak core
|
||||
|
||||
|
||||
# DO NOT DELETE THIS LINE -- make depend depends on it.
|
29
Makefile.win32
Normal file
29
Makefile.win32
Normal file
@ -0,0 +1,29 @@
|
||||
SRC=src
|
||||
SRCS = $(SRC)/bridge.c $(SRC)/debug.c $(SRC)/getcmd.c $(SRC)/ip.c $(SRC)/init.c $(SRC)/modem_core.c $(SRC)/nvt.c $(SRC)/serial.c $(SRC)/ip232.c $(SRC)/util.c $(SRC)/phone_book.c $(SRC)/shared.c $(SRC)/tcpser.c $(SRC)/line.c $(SRC)/dce.c
|
||||
OBJS = $(SRC)/bridge.o $(SRC)/debug.o $(SRC)/getcmd.o $(SRC)/ip.o $(SRC)/init.o $(SRC)/modem_core.o $(SRC)/nvt.o $(SRC)/serial.o $(SRC)/ip232.o $(SRC)/util.o $(SRC)/phone_book.o $(SRC)/shared.o $(SRC)/tcpser.o $(SRC)/dce.o $(SRC)/line.o
|
||||
CC = gcc
|
||||
DEF =
|
||||
CFLAGS = -O $(DEF) -Wall -DWIN32
|
||||
LDFLAGS =
|
||||
DEPEND = makedepend $(DEF) $(CFLAGS)
|
||||
|
||||
all: tcpser
|
||||
|
||||
#.o.c:
|
||||
# $(CC) $(CFLAGS) -c $*.c
|
||||
|
||||
$(SRCS):
|
||||
$(CC) $(CFLAGS) -c $*.c
|
||||
|
||||
tcpser: $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJS)
|
||||
|
||||
depend: $(SRCS)
|
||||
$(DEPEND) $(SRCS)
|
||||
|
||||
clean:
|
||||
-rm tcpser.exe *.bak $(SRC)/*~ $(SRC)/*.o $(SRC)/*.bak core
|
||||
|
||||
|
||||
# DO NOT DELETE THIS LINE -- make depend depends on it.
|
||||
|
234
README
Normal file
234
README
Normal file
@ -0,0 +1,234 @@
|
||||
|
||||
README
|
||||
|
||||
Application:
|
||||
|
||||
TCPSER turns a PC serial port into an emulated Hayes compatible modem that
|
||||
uses TCP/IP for incoming and outgoing connections. It can be used to allow
|
||||
older applications and systems designed for modem use to operate on the
|
||||
Internet. TCPSER supports all standard Hayes commands, and understands
|
||||
extended and vendor proprietary commands (though it does not implement
|
||||
many of them). TCPSER can be used for both inbound and outbound connections.
|
||||
|
||||
License:
|
||||
|
||||
TCPSER is distributed under the GPL 2.0 or later
|
||||
|
||||
Executable/Building:
|
||||
|
||||
UNIX/Linux/BSD:
|
||||
|
||||
Simply untar the archive into a directory, and use the appropriate make command
|
||||
generate the exectutable. If unsure, try the default make command first.
|
||||
|
||||
Default/Linux: make
|
||||
Solaris: make -f Makefile.solaris
|
||||
*BSD: gmake
|
||||
|
||||
Windows 95/OSR2/98/SE/ME/NT/2000/XP/2003:
|
||||
|
||||
The application archive contains a pregenerated Windows 32 bit executable
|
||||
(tcpser.exe). No compilation should be required, though the GNU toolchain and
|
||||
the CYGWIN POSIX libraries can be used to regenerate the executable if desired:
|
||||
|
||||
Win32: make -f Makefile.win32
|
||||
|
||||
Note that at least the cygwin1.dll library is required to operate tcpser under
|
||||
Windows. I recommend downloading a recent version from www.cygwin.com
|
||||
|
||||
This version of tcpser supports setting up an ip232 port instead of using a
|
||||
real serial port. This is for use with the version of WinVICE 1.19 that has
|
||||
the ACIA fix and ip232 support, allowing WinVICE to use tcpser as if it was
|
||||
connected via a serial cable.
|
||||
|
||||
Operation:
|
||||
|
||||
tcpser -d <dev> -s <speed> -l <log_level> -t <tracing options> ...
|
||||
|
||||
-or-
|
||||
|
||||
tcpser -v <port> -s <speed> -l <log_level> -t <tracing options> ...
|
||||
|
||||
samples:
|
||||
|
||||
tcpser -d /dev/ttyS0 -s 38400 -l 7 -tsSiI -i "s0=1" -p 6400
|
||||
|
||||
Will start tcpser on ttyS0 at 38400 bps, level 7 logging, tracing of
|
||||
inbound serial, outbound serial, inbound IP, outbound IP, init
|
||||
modem to answer after 1 ring, and listen for incoming connections on port
|
||||
6400
|
||||
|
||||
tcpser -v 25232 -s 38400 -l 4 -p 23
|
||||
|
||||
Will set up an ip232 port at 25232, report 38400 bps connections,
|
||||
level 4 logging, and listen for incoming connections on port 23.
|
||||
|
||||
tcpser -h will provide additional information
|
||||
|
||||
tcpser can be configured to send the contents of a file upon:
|
||||
|
||||
connect ; -c -C
|
||||
answer ; -a -A
|
||||
no-answer ; -I
|
||||
busy ; -B
|
||||
inactivity-timeout ; -T
|
||||
|
||||
For connect and answer, there are separate options for sending a file to the
|
||||
local serial connection (-c, -a) and the remote IP connection (-C, -A).
|
||||
|
||||
If tcpser connects to a telnet service, tcpser will negotiate the connection
|
||||
using the telnet protocol. If telnet is detected, then tcpser will support
|
||||
RFC 856 (Telnet Binary Transmission), so that 8-bit file transfers will
|
||||
work correctly.
|
||||
|
||||
tcpser can be configured to support multiple serial/ip232 ports on one TCP/IP
|
||||
port. Simply repeat the -s and -d/-v parameters on the command line for each
|
||||
serial/ip232 port to be configured. Options s,S,a,A,c,C,I, and T will
|
||||
"propagate" to subsequent connections, unless they are redefined. Defaults
|
||||
for s and S are 38400. This configuration enables the operation of a
|
||||
multi-line BBS on one TCP/IP port.
|
||||
|
||||
Frequently used addresses can be configured in the "phonebook", like so:
|
||||
|
||||
tcpser .... -nhome=jbrain.com:6400
|
||||
|
||||
This is also useful for systems that do not accept non-numeric phone numbers:
|
||||
|
||||
tcpser .... -n5551212=bestbbs.net
|
||||
|
||||
One can even "hide" a regular IP address or DNS entry by aliasing it to
|
||||
something else:
|
||||
|
||||
tcpser .... -njbrain.com=bestbbs.com
|
||||
|
||||
At this point, phonebook support is very alpha, so use with care.
|
||||
|
||||
Emulation:
|
||||
|
||||
All of the standard Hayes commands should behave as expected. Some of
|
||||
of the proprietary commands are not implemented, but should not cause
|
||||
errors.
|
||||
|
||||
Examples:
|
||||
|
||||
ats0=1 ; set number of rings to answer
|
||||
ata ; answer the line
|
||||
ath0 ; hang up
|
||||
ats12? ; query S register 12
|
||||
ate0 ; turn off echo
|
||||
at&k3 ; set flow control to RTS/CTS
|
||||
atdtjbrain.com:6400 ; "dial" jbrain.com, port 6400 (defaults to port 23)
|
||||
atdl ; "dial" last number
|
||||
a/ ; repeat last command
|
||||
|
||||
Commands can be chained, as on a regular modem:
|
||||
|
||||
ats0=1z&c1&k3%f0s3=13dtjbrain.com
|
||||
|
||||
tcpser supports the Hayes break sequence semantics, so +++ should operate
|
||||
correctly, even if the sequence of characters is used in normal data
|
||||
transmissions.i
|
||||
|
||||
Cable:
|
||||
|
||||
tcpser can be used with a regular null modem cable, but it utilizes the DTR
|
||||
line on the PC serial port to reflect the state of the DCD line as seen by the
|
||||
target system. On a normal null-modem cable, DTR is mapped to DCD/DSR, which
|
||||
implies DSR will also reflect the state of DCD on the target machine. However,
|
||||
some systems (notably those utilizing the 6551 ACIA communication IC) will not
|
||||
transmit unless DSR is held high. In this case, a quick qorkaround is to force
|
||||
DCD to be held high by adding -i"&c0" to the tcpser parameter list. However,
|
||||
this also prevents normal operation of the DCD line, which is needed by some
|
||||
BBS systems. A more permanent solution is to construct a modified null-modem
|
||||
cable or modify an existing cable to the following specifications:
|
||||
|
||||
PC Target
|
||||
|
||||
CTS-----RTS
|
||||
RTS-----CTS
|
||||
SND-----RCV
|
||||
RCV-----SND
|
||||
|
||||
DTR-----DCD
|
||||
|
||||
DCD-+-+-DTR
|
||||
| |
|
||||
DSR-+ +-DSR
|
||||
|
||||
GND-----GND
|
||||
|
||||
This differs from a regular null-modem cable in that the target machine has DSR
|
||||
looped to DTR, not to DCD. Note that this cable is directional.
|
||||
|
||||
Normally, the target machine will configure DSR to float to a high state if
|
||||
unconnected. As well, PCs do not require a valid DSR line for operation. Thus,
|
||||
a simpler cable can be constructed that is bi-directional:
|
||||
|
||||
CTS-----RTS
|
||||
RTS-----CTS
|
||||
SND-----RCV
|
||||
RCV-----SND
|
||||
DTR-----DCD
|
||||
DCD-----DTR
|
||||
GND-----GND
|
||||
|
||||
Unless there are issues, we recommend this simplified version, as it can be
|
||||
installed in either direction.
|
||||
|
||||
As an even simpler solution, many have simply taken a normal rs232 DE-9F to
|
||||
DE-9M cable and removed pin 6 from the male end (DSR). This is fine, but the
|
||||
cable must be installed between the null modem adapter and the target machine:
|
||||
|
||||
PC ----- null-modem adapter ----- cable with pin 6 removed ------ target machine
|
||||
|
||||
Any other configuration will not work correctly.
|
||||
|
||||
Platform notes:
|
||||
|
||||
Win32 users should use /dev/ttyS0-3 for COM1-4. At present, using "com1:"
|
||||
does not operate correctly.
|
||||
|
||||
Raymond Day sends the following Ubuntu 7.10 autorun scripts:
|
||||
|
||||
In:
|
||||
|
||||
/etc/init.d/
|
||||
|
||||
Make a file named something like tcpser with this code in it:
|
||||
|
||||
#!/bin/sh
|
||||
# Start tcpser at 1200 boud on both RS232 ports for Q-Link Reloaded.
|
||||
case "$1" in
|
||||
'start')
|
||||
tcpser -d /dev/ttyS0 -s 1200 -i"e0&k0&c0" -n"5551212"=66.135.39.36:5190&
|
||||
tcpser -d /dev/ttyS1 -p 6401 -s 1200 -i"e0&k0&c0" -n"5551212"=66.135.39.36:5190&
|
||||
;;
|
||||
'stop')
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 { start | stop }"
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
|
||||
This has been tested on the following platforms:
|
||||
|
||||
Linux 2.4.20-8
|
||||
Windows XP
|
||||
Windows XP SP1
|
||||
Slackware 10.0
|
||||
|
||||
Help:
|
||||
|
||||
tcpser has a small but active user community. Help can be found by asking a
|
||||
question in comp.sys.cbm, on the NEWNet #c64friends IRC channel, or by emailing
|
||||
the author.
|
||||
-----------------------------------
|
||||
Jim Brain
|
||||
brain@jbrain.com
|
||||
www.jbrain.com
|
||||
|
||||
|
||||
The ip232 support was added by Anthony Tolle. For questions regarding that,
|
||||
e-mail atolle@gcns.com
|
||||
|
2
bridge
Normal file
2
bridge
Normal file
@ -0,0 +1,2 @@
|
||||
tcpser-1.0rc10/tcpser -d /dev/ttyS1 -s 1200 -i"e0" -tsS -n"5551212=ltshosting.net:5190"
|
||||
|
452
src/bridge.c
Normal file
452
src/bridge.c
Normal file
@ -0,0 +1,452 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/socket.h> // for recv...
|
||||
#include <unistd.h> // for read...
|
||||
#include <stdlib.h> // for exit...
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "nvt.h"
|
||||
#include "modem_core.h"
|
||||
#include "ip.h"
|
||||
//#include "serial.h"
|
||||
#include "getcmd.h"
|
||||
#include "bridge.h"
|
||||
|
||||
const char MDM_NO_ANSWER[] = "NO ANSWER\n";
|
||||
|
||||
//const char CONNECT_NOTICE[] = "\r\nCONNECTING...\r\n";
|
||||
|
||||
//const char TELNET_NOTICE[] = "TELNET MODE ENABLED\r\n";
|
||||
|
||||
int accept_connection(modem_config *cfg)
|
||||
{
|
||||
LOG_ENTER();
|
||||
|
||||
cfg->line_data.fd = ip_accept(cfg->line_data.sfd);
|
||||
if (cfg->line_data.fd > -1) {
|
||||
cfg->line_data.valid_conn = TRUE;
|
||||
if (cfg->data.direct_conn == TRUE) {
|
||||
cfg->conn_type = MDM_CONN_INCOMING;
|
||||
mdm_off_hook(cfg);
|
||||
cfg->cmd_mode = TRUE;
|
||||
}
|
||||
else {
|
||||
//line_write(cfg,(char*)CONNECT_NOTICE,strlen(CONNECT_NOTICE));
|
||||
cfg->rings = 0;
|
||||
mdm_send_ring(cfg);
|
||||
}
|
||||
// tell parent I got it.
|
||||
LOG(LOG_DEBUG, "Informing parent task that I am busy");
|
||||
writePipe(cfg->data.mp[0][1], MSG_ACCEPTED);
|
||||
}
|
||||
LOG_EXIT();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int parse_ip_data(modem_config *cfg, char *data, int len)
|
||||
{
|
||||
// I'm going to cheat and assume it comes in chunks.
|
||||
int i = 0;
|
||||
char ch;
|
||||
char text[1025];
|
||||
int text_len = 0;
|
||||
|
||||
if (cfg->line_data.first_char == TRUE) {
|
||||
cfg->line_data.first_char = FALSE;
|
||||
if ((data[0] == 0xff) || (data[0] == 0x1a)) {
|
||||
//line_write(cfg,(char*)TELNET_NOTICE,strlen(TELNET_NOTICE));
|
||||
LOG(LOG_INFO, "Detected telnet");
|
||||
cfg->line_data.is_telnet = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg->line_data.is_telnet == TRUE) {
|
||||
while (i < len) {
|
||||
ch = data[i];
|
||||
if (NVT_IAC == ch) {
|
||||
// what if we roll off the end?
|
||||
ch = data[i + 1];
|
||||
switch (ch) {
|
||||
case NVT_WILL:
|
||||
case NVT_DO:
|
||||
case NVT_WONT:
|
||||
case NVT_DONT:
|
||||
/// again, overflow issues...
|
||||
LOG(LOG_INFO, "Parsing nvt command");
|
||||
parse_nvt_command(cfg->line_data.fd, &cfg->line_data.nvt_data, ch, data[i + 2]);
|
||||
i += 3;
|
||||
break;
|
||||
case NVT_SB: // sub negotiation
|
||||
// again, overflow...
|
||||
i +=
|
||||
parse_nvt_subcommand(cfg->line_data.fd, &cfg->line_data.nvt_data, data + i,
|
||||
len - i);
|
||||
break;
|
||||
case NVT_IAC:
|
||||
if (cfg->line_data.nvt_data.binary_recv)
|
||||
text[text_len++] = NVT_IAC;
|
||||
// fall through to skip this sequence
|
||||
default:
|
||||
// ignore...
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
text[text_len++] = data[i++];
|
||||
}
|
||||
if (text_len == 1024) {
|
||||
text[text_len] = 0;
|
||||
// write to serial...
|
||||
mdm_write(cfg, text, text_len);
|
||||
text_len = 0;
|
||||
}
|
||||
}
|
||||
if (text_len) {
|
||||
text[text_len] = 0;
|
||||
// write to serial...
|
||||
mdm_write(cfg, text, text_len);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mdm_write(cfg, data, len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ip_thread(void *arg)
|
||||
{
|
||||
modem_config *cfg = (modem_config *) arg;
|
||||
|
||||
int action_pending = FALSE;
|
||||
fd_set readfs;
|
||||
int max_fd;
|
||||
int res = 0;
|
||||
char buf[256];
|
||||
int rc;
|
||||
|
||||
|
||||
LOG_ENTER();
|
||||
while (TRUE) {
|
||||
FD_ZERO(&readfs);
|
||||
FD_SET(cfg->data.cp[1][0], &readfs);
|
||||
max_fd = cfg->data.cp[1][0];
|
||||
if (action_pending == FALSE
|
||||
&& cfg->conn_type != MDM_CONN_NONE
|
||||
&& cfg->cmd_mode == FALSE
|
||||
&& cfg->line_data.fd > -1 && cfg->line_data.valid_conn == TRUE) {
|
||||
FD_SET(cfg->line_data.fd, &readfs);
|
||||
max_fd = MAX(max_fd, cfg->line_data.fd);
|
||||
}
|
||||
max_fd++;
|
||||
rc = select(max_fd, &readfs, NULL, NULL, NULL);
|
||||
if (rc == -1) {
|
||||
ELOG(LOG_WARN, "Select returned error");
|
||||
// handle error
|
||||
}
|
||||
else {
|
||||
// we got data
|
||||
if (cfg->line_data.valid_conn == TRUE && FD_ISSET(cfg->line_data.fd, &readfs)) { // socket
|
||||
LOG(LOG_DEBUG, "Data available on socket");
|
||||
res = recv(cfg->line_data.fd, buf, sizeof(buf), 0);
|
||||
if (0 >= res) {
|
||||
LOG(LOG_INFO, "No socket data read, assume closed peer");
|
||||
writePipe(cfg->data.cp[0][1], MSG_DISCONNECT);
|
||||
action_pending = TRUE;
|
||||
}
|
||||
else {
|
||||
LOG(LOG_DEBUG, "Read %d bytes from socket", res);
|
||||
buf[res] = 0;
|
||||
log_trace(TRACE_IP_IN, buf, res);
|
||||
parse_ip_data(cfg, buf, res);
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(cfg->data.cp[1][0], &readfs)) { // pipe
|
||||
|
||||
res = read(cfg->data.cp[1][0], buf, sizeof(buf) - 1);
|
||||
LOG(LOG_DEBUG, "IP thread notified");
|
||||
action_pending = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_EXIT();
|
||||
}
|
||||
|
||||
void *ctrl_thread(void *arg)
|
||||
{
|
||||
modem_config *cfg = (modem_config *) arg;
|
||||
int status;
|
||||
int new_status;
|
||||
|
||||
LOG_ENTER();
|
||||
|
||||
status = dce_get_control_lines(cfg);
|
||||
while (status > -1) {
|
||||
new_status = dce_check_control_lines(cfg);
|
||||
if (new_status > -1 && status != new_status) {
|
||||
// something changed
|
||||
if ((status & MDM_CL_DTR_HIGH) != (new_status & MDM_CL_DTR_HIGH)) {
|
||||
if ((new_status & MDM_CL_DTR_HIGH) == 0) {
|
||||
LOG(LOG_INFO, "DTR has gone low");
|
||||
writePipe(cfg->data.wp[0][1], MSG_DTR_DOWN);
|
||||
}
|
||||
else {
|
||||
LOG(LOG_INFO, "DTR has gone high");
|
||||
writePipe(cfg->data.wp[0][1], MSG_DTR_UP);
|
||||
}
|
||||
}
|
||||
}
|
||||
status = new_status;
|
||||
}
|
||||
LOG_EXIT();
|
||||
// need to quit application, as status cannot be obtained.
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
||||
int spawn_ctrl_thread(modem_config *cfg)
|
||||
{
|
||||
int rc;
|
||||
pthread_t thread_id;
|
||||
|
||||
rc = pthread_create(&thread_id, NULL, ctrl_thread, (void *) cfg);
|
||||
LOG(LOG_ALL, "CTRL thread ID=%d", (int) thread_id);
|
||||
|
||||
if (rc < 0) {
|
||||
ELOG(LOG_FATAL, "CTRL thread could not be started");
|
||||
exit(-1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spawn_ip_thread(modem_config *cfg)
|
||||
{
|
||||
int rc;
|
||||
pthread_t thread_id;
|
||||
|
||||
rc = pthread_create(&thread_id, NULL, ip_thread, (void *) cfg);
|
||||
LOG(LOG_ALL, "IP thread ID=%d", (int) thread_id);
|
||||
|
||||
if (rc < 0) {
|
||||
ELOG(LOG_FATAL, "IP thread could not be started");
|
||||
exit(-1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *run_bridge(void *arg)
|
||||
{
|
||||
modem_config *cfg = (modem_config *) arg;
|
||||
struct timeval timer;
|
||||
struct timeval *ptimer;
|
||||
int max_fd = 0;
|
||||
fd_set readfs;
|
||||
int res = 0;
|
||||
char buf[256];
|
||||
int rc = 0;
|
||||
|
||||
int last_conn_type;
|
||||
int last_cmd_mode = cfg->cmd_mode;
|
||||
|
||||
|
||||
LOG_ENTER();
|
||||
|
||||
if (-1 == pipe(cfg->data.wp[0])) {
|
||||
ELOG(LOG_FATAL, "Control line watch task incoming IPC pipe could not be created");
|
||||
exit(-1);
|
||||
}
|
||||
if (-1 == pipe(cfg->data.cp[0])) {
|
||||
ELOG(LOG_FATAL, "IP thread incoming IPC pipe could not be created");
|
||||
exit(-1);
|
||||
}
|
||||
if (-1 == pipe(cfg->data.cp[1])) {
|
||||
ELOG(LOG_FATAL, "IP thread outgoing IPC pipe could not be created");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
spawn_ctrl_thread(cfg);
|
||||
spawn_ip_thread(cfg);
|
||||
|
||||
mdm_set_control_lines(cfg);
|
||||
strncpy(cfg->cur_line, cfg->config0, sizeof(cfg->cur_line));
|
||||
last_conn_type = cfg->conn_type;
|
||||
last_cmd_mode = cfg->cmd_mode;
|
||||
cfg->allow_transmit = FALSE;
|
||||
// call some functions behind the scenes
|
||||
mdm_disconnect(cfg);
|
||||
mdm_parse_cmd(cfg);
|
||||
// if direct connection, and num length > 0, dial number.
|
||||
if (cfg->data.direct_conn == TRUE) {
|
||||
if (strlen(cfg->data.direct_conn_num) > 0 && cfg->data.direct_conn_num[0] != ':') {
|
||||
// we have a direct number to connect to.
|
||||
strncpy(cfg->dialno, cfg->data.direct_conn_num, sizeof(cfg->dialno));
|
||||
if (0 != line_connect(cfg)) {
|
||||
LOG(LOG_FATAL, "Cannot connect to Direct line address!");
|
||||
// probably should exit...
|
||||
exit(-1);
|
||||
}
|
||||
else {
|
||||
cfg->conn_type = MDM_CONN_OUTGOING;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
cfg->allow_transmit = TRUE;
|
||||
for (;;) {
|
||||
if (last_conn_type != cfg->conn_type) {
|
||||
LOG(LOG_ALL, "Connection status change, handling");
|
||||
//writePipe(cfg->data.mp[0][1],MSG_NOTIFY);
|
||||
writePipe(cfg->data.cp[1][1], MSG_NOTIFY);
|
||||
if (cfg->conn_type == MDM_CONN_OUTGOING) {
|
||||
if (strlen(cfg->data.local_connect) > 0) {
|
||||
writeFile(cfg->data.local_connect, cfg->line_data.fd);
|
||||
}
|
||||
if (strlen(cfg->data.remote_connect) > 0) {
|
||||
writeFile(cfg->data.remote_connect, cfg->line_data.fd);
|
||||
}
|
||||
}
|
||||
else if (cfg->conn_type == MDM_CONN_INCOMING) {
|
||||
if (strlen(cfg->data.local_answer) > 0) {
|
||||
writeFile(cfg->data.local_answer, cfg->line_data.fd);
|
||||
}
|
||||
if (strlen(cfg->data.remote_answer) > 0) {
|
||||
writeFile(cfg->data.remote_answer, cfg->line_data.fd);
|
||||
}
|
||||
}
|
||||
last_conn_type = cfg->conn_type;
|
||||
}
|
||||
if (last_cmd_mode != cfg->cmd_mode) {
|
||||
writePipe(cfg->data.cp[1][1], MSG_NOTIFY);
|
||||
last_cmd_mode = cfg->cmd_mode;
|
||||
}
|
||||
LOG(LOG_ALL, "Waiting for modem/control line/timer/socket activity");
|
||||
LOG(LOG_ALL, "Command Mode=%d, Connection status=%d", cfg->cmd_mode, cfg->conn_type);
|
||||
max_fd = MAX(cfg->data.mp[1][0], cfg->dce_data.fd);
|
||||
max_fd = MAX(max_fd, cfg->data.wp[0][0]);
|
||||
max_fd = MAX(max_fd, cfg->data.cp[0][0]);
|
||||
FD_ZERO(&readfs);
|
||||
FD_SET(cfg->data.mp[1][0], &readfs);
|
||||
FD_SET(cfg->dce_data.fd, &readfs);
|
||||
FD_SET(cfg->data.wp[0][0], &readfs);
|
||||
FD_SET(cfg->data.cp[0][0], &readfs);
|
||||
ptimer = NULL;
|
||||
if (cfg->cmd_mode == FALSE) {
|
||||
if (cfg->pre_break_delay == FALSE || cfg->break_len == 3) {
|
||||
LOG(LOG_ALL, "Setting timer for break delay");
|
||||
timer.tv_sec = 0;
|
||||
timer.tv_usec = cfg->s[12] * 20000;
|
||||
ptimer = &timer;
|
||||
}
|
||||
else if (cfg->pre_break_delay == TRUE && cfg->break_len > 0) {
|
||||
LOG(LOG_ALL, "Setting timer for inter-break character delay");
|
||||
timer.tv_sec = 1; // 1 second
|
||||
timer.tv_usec = 0;
|
||||
ptimer = &timer;
|
||||
}
|
||||
else if (cfg->s[30] != 0) {
|
||||
LOG(LOG_ALL, "Setting timer for inactivity delay");
|
||||
timer.tv_sec = cfg->s[30] * 10;
|
||||
timer.tv_usec = 0;
|
||||
ptimer = &timer;
|
||||
}
|
||||
}
|
||||
else if (cfg->cmd_mode == TRUE && cfg->conn_type == MDM_CONN_NONE
|
||||
&& cfg->line_data.valid_conn == TRUE) {
|
||||
LOG(LOG_ALL, "Setting timer for rings");
|
||||
timer.tv_sec = 4;
|
||||
timer.tv_usec = 0;
|
||||
ptimer = &timer;
|
||||
}
|
||||
max_fd++;
|
||||
rc = select(max_fd, &readfs, NULL, NULL, ptimer);
|
||||
if (rc == -1) {
|
||||
ELOG(LOG_WARN, "Select returned error");
|
||||
// handle error
|
||||
}
|
||||
else if (rc == 0) {
|
||||
// timer popped.
|
||||
if (cfg->cmd_mode == TRUE && cfg->conn_type == MDM_CONN_NONE
|
||||
&& cfg->line_data.valid_conn == TRUE) {
|
||||
if (cfg->s[0] == 0 && cfg->rings == 10) {
|
||||
// not going to answer, send some data back to IP and disconnect.
|
||||
if (strlen(cfg->data.no_answer) == 0) {
|
||||
line_write(cfg, (char *) MDM_NO_ANSWER, strlen(MDM_NO_ANSWER));
|
||||
}
|
||||
else {
|
||||
writeFile(cfg->data.no_answer, cfg->line_data.fd);
|
||||
}
|
||||
mdm_disconnect(cfg);
|
||||
}
|
||||
else
|
||||
mdm_send_ring(cfg);
|
||||
|
||||
}
|
||||
else
|
||||
mdm_handle_timeout(cfg);
|
||||
}
|
||||
if (FD_ISSET(cfg->dce_data.fd, &readfs)) { // serial port
|
||||
LOG(LOG_DEBUG, "Data available on serial port");
|
||||
res = dce_read(cfg, buf, sizeof(buf));
|
||||
LOG(LOG_DEBUG, "Read %d bytes from serial port", res);
|
||||
if (res > 0) {
|
||||
if (cfg->conn_type == MDM_CONN_NONE && cfg->off_hook == TRUE) {
|
||||
// this handles the case where atdt goes off hook, but no
|
||||
// connection
|
||||
mdm_disconnect(cfg);
|
||||
mdm_send_response(MDM_RESP_OK, cfg);
|
||||
}
|
||||
else {
|
||||
mdm_parse_data(cfg, buf, res);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (FD_ISSET(cfg->data.wp[0][0], &readfs)) { // control pipe
|
||||
res = read(cfg->data.wp[0][0], buf, sizeof(buf) - 1);
|
||||
buf[res] = 0;
|
||||
LOG(LOG_DEBUG, "Received %c from Control line watch task", buf[0]);
|
||||
switch (buf[0]) {
|
||||
case MSG_DTR_DOWN:
|
||||
// DTR drop, close any active connection and put
|
||||
// in cmd_mode
|
||||
mdm_disconnect(cfg);
|
||||
break;
|
||||
case MSG_DTR_UP:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(cfg->data.cp[0][0], &readfs)) { // ip thread pipe
|
||||
res = read(cfg->data.cp[0][0], buf, sizeof(buf));
|
||||
LOG(LOG_DEBUG, "Received %c from ip thread", buf[0]);
|
||||
switch (buf[0]) {
|
||||
case MSG_DISCONNECT:
|
||||
if (cfg->data.direct_conn == TRUE) {
|
||||
// what should we do here...
|
||||
LOG(LOG_ERROR,
|
||||
"Direct Connection Link broken, disconnecting and awaiting new direct connection");
|
||||
cfg->data.direct_conn = FALSE;
|
||||
mdm_disconnect(cfg);
|
||||
cfg->data.direct_conn = TRUE;
|
||||
}
|
||||
else {
|
||||
mdm_disconnect(cfg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(cfg->data.mp[1][0], &readfs)) { // parent pipe
|
||||
LOG(LOG_DEBUG, "Data available on incoming IPC pipe");
|
||||
res = read(cfg->data.mp[1][0], buf, sizeof(buf));
|
||||
switch (buf[0]) {
|
||||
case MSG_ACCEPT: // accept connection.
|
||||
accept_connection(cfg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_EXIT();
|
||||
}
|
20
src/bridge.h
Normal file
20
src/bridge.h
Normal file
@ -0,0 +1,20 @@
|
||||
#define MSG_NOT_ACTIVE 'a'
|
||||
#define MSG_ACTIVE 'A'
|
||||
#define MSG_NOT_LISTENING 'l'
|
||||
#define MSG_LISTENING 'L'
|
||||
#define MSG_ACCEPT '+'
|
||||
#define MSG_ACCEPTED '+'
|
||||
#define MSG_DTR_DOWN 'd'
|
||||
#define MSG_DTR_UP 'D'
|
||||
#define MSG_RTS_DOWN 'r'
|
||||
#define MSG_RTS_UP 'R'
|
||||
#define MSG_CD_DOWN 'c'
|
||||
#define MSG_CD_UP 'c'
|
||||
#define MSG_RNG_DOWN 'n'
|
||||
#define MSG_RNG_UP 'N'
|
||||
#define MSG_DISCONNECT 'D'
|
||||
#define MSG_NOTIFY 'N'
|
||||
|
||||
int accept_connection(modem_config *);
|
||||
int parse_ip_data(modem_config *cfg, char *data, int len);
|
||||
void *run_bridge(void *arg);
|
156
src/dce.c
Normal file
156
src/dce.c
Normal file
@ -0,0 +1,156 @@
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "serial.h"
|
||||
#include "modem_core.h"
|
||||
#include "ip232.h" // needs modem_core.h
|
||||
#include "dce.h"
|
||||
|
||||
|
||||
|
||||
int dce_init_config(modem_config *cfg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int dce_init_conn(modem_config *cfg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
LOG_ENTER();
|
||||
if (cfg->dce_data.is_ip232) {
|
||||
rc = ip232_init_conn(cfg);
|
||||
}
|
||||
else {
|
||||
rc = ser_init_conn(cfg->dce_data.tty, cfg->dte_speed);
|
||||
cfg->dce_data.fd = rc;
|
||||
}
|
||||
|
||||
LOG_EXIT();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int dce_set_flow_control(modem_config *cfg, int opts)
|
||||
{
|
||||
int status = 0;
|
||||
int rc = 0;
|
||||
|
||||
LOG_ENTER();
|
||||
if (opts == 0) {
|
||||
LOG(LOG_ALL, "Setting NONE flow control");
|
||||
}
|
||||
else {
|
||||
if ((opts & MDM_FC_RTS) != 0) {
|
||||
LOG(LOG_ALL, "Setting RTSCTS flow control");
|
||||
status |= CRTSCTS;
|
||||
}
|
||||
if ((opts && MDM_FC_XON) != 0) {
|
||||
status |= (IXON | IXOFF);
|
||||
LOG(LOG_ALL, "Setting XON/XOFF flow control");
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg->dce_data.is_ip232) {
|
||||
rc = ip232_set_flow_control(cfg, status);
|
||||
}
|
||||
else {
|
||||
rc = ser_set_flow_control(cfg->dce_data.fd, status);
|
||||
}
|
||||
|
||||
LOG_EXIT()
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dce_set_control_lines(modem_config *cfg, int state)
|
||||
{
|
||||
int status = 0;
|
||||
int rc;
|
||||
|
||||
LOG_ENTER();
|
||||
if ((state & MDM_CL_CTS_HIGH) != 0) {
|
||||
LOG(LOG_ALL, "Setting CTS pin high");
|
||||
status |= TIOCM_RTS;
|
||||
}
|
||||
else {
|
||||
LOG(LOG_ALL, "Setting CTS pin low");
|
||||
//status &= ~TIOCM_RTS;
|
||||
}
|
||||
if ((state & MDM_CL_DCD_HIGH) != 0) {
|
||||
LOG(LOG_ALL, "Setting DCD pin high");
|
||||
status |= TIOCM_DTR;
|
||||
}
|
||||
else {
|
||||
LOG(LOG_ALL, "Setting DCD pin low");
|
||||
//status &= ~TIOCM_DTR;
|
||||
}
|
||||
|
||||
if (cfg->dce_data.is_ip232) {
|
||||
rc = ip232_set_control_lines(cfg, status);
|
||||
}
|
||||
else {
|
||||
rc = ser_set_control_lines(cfg->dce_data.fd, status);
|
||||
}
|
||||
|
||||
LOG_EXIT();
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dce_get_control_lines(modem_config *cfg)
|
||||
{
|
||||
int status;
|
||||
int rc_status;
|
||||
|
||||
if (cfg->dce_data.is_ip232) {
|
||||
status = ip232_get_control_lines(cfg);
|
||||
}
|
||||
else {
|
||||
status = ser_get_control_lines(cfg->dce_data.fd);
|
||||
}
|
||||
|
||||
if (status > -1) {
|
||||
rc_status = ((status & TIOCM_DSR) != 0 ? MDM_CL_DTR_HIGH : 0);
|
||||
}
|
||||
else {
|
||||
rc_status = status;
|
||||
}
|
||||
|
||||
return rc_status;
|
||||
}
|
||||
|
||||
int dce_check_control_lines(modem_config *cfg)
|
||||
{
|
||||
int status = 0;
|
||||
int new_status = 0;
|
||||
|
||||
LOG_ENTER();
|
||||
status = dce_get_control_lines(cfg);
|
||||
new_status = status;
|
||||
while (new_status > -1 && status == new_status) {
|
||||
usleep(100000);
|
||||
new_status = dce_get_control_lines(cfg);
|
||||
}
|
||||
|
||||
LOG_EXIT();
|
||||
return new_status;
|
||||
}
|
||||
|
||||
|
||||
int dce_write(modem_config *cfg, char data[], int len)
|
||||
{
|
||||
if (cfg->dce_data.is_ip232) {
|
||||
return ip232_write(cfg, data, len);
|
||||
}
|
||||
return ser_write(cfg->dce_data.fd, data, len);
|
||||
}
|
||||
|
||||
int dce_read(modem_config *cfg, char data[], int len)
|
||||
{
|
||||
if (cfg->dce_data.is_ip232) {
|
||||
return ip232_read(cfg, data, len);
|
||||
}
|
||||
return ser_read(cfg->dce_data.fd, data, len);
|
||||
}
|
16
src/dce.h
Normal file
16
src/dce.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef DCE_H
|
||||
#define DCE_H 1
|
||||
|
||||
int dce_init(void);
|
||||
int dce_init_config(modem_config *cfg);
|
||||
int dce_init_conn(modem_config *cfg);
|
||||
int dce_set_flow_control(modem_config *cfg, int opts);
|
||||
int dce_set_control_lines(modem_config *cfg, int state);
|
||||
int dce_get_control_lines(modem_config *cfg);
|
||||
int dce_check_control_lines(modem_config *cfg);
|
||||
int dce_read(modem_config *cfg, char *data, int len);
|
||||
int dce_write(modem_config *cfg, char *data, int len);
|
||||
|
||||
//int dce_check_for_break(modem_config *cfg, char ch, int chars_left);
|
||||
|
||||
#endif
|
141
src/debug.c
Normal file
141
src/debug.c
Normal file
@ -0,0 +1,141 @@
|
||||
#include <stdlib.h> // for exit...
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#define DEBUG_VARS 1 // need this so we don't get extern defs
|
||||
#include "debug.h"
|
||||
|
||||
int log_level = 0;
|
||||
FILE *log_file;
|
||||
int trace_flags = 0;
|
||||
char *trace_type[9]; // cheesy, but I can't think of another o(1) way
|
||||
char *log_desc[LOG_TRACE + 1];
|
||||
pthread_mutex_t log_mutex;
|
||||
|
||||
|
||||
int log_init()
|
||||
{
|
||||
log_file = stdout;
|
||||
log_level = 0;
|
||||
trace_flags = 0;
|
||||
trace_type[TRACE_MODEM_IN] = "RS<-";
|
||||
trace_type[TRACE_MODEM_OUT] = "RS->";
|
||||
trace_type[TRACE_IP_IN] = "IP<-";
|
||||
trace_type[TRACE_IP_OUT] = "IP->";
|
||||
log_desc[LOG_FATAL] = "FATAL";
|
||||
log_desc[LOG_ERROR] = "ERROR";
|
||||
log_desc[LOG_WARN] = "WARN";
|
||||
log_desc[LOG_INFO] = "INFO";
|
||||
log_desc[LOG_DEBUG] = "DEBUG";
|
||||
log_desc[LOG_ENTER_EXIT] = "ENTER_EXIT";
|
||||
log_desc[LOG_ALL] = "DEBUG_X";
|
||||
log_desc[LOG_TRACE] = "";
|
||||
if (-1 == pthread_mutex_init(&log_mutex, NULL)) {
|
||||
perror("Could not create Log Mutex");
|
||||
exit(-1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void log_set_file(FILE * a)
|
||||
{
|
||||
log_file = a;
|
||||
}
|
||||
|
||||
|
||||
void log_set_level(int a)
|
||||
{
|
||||
log_level = a;
|
||||
}
|
||||
|
||||
|
||||
void log_set_trace_flags(int a)
|
||||
{
|
||||
trace_flags = a;
|
||||
}
|
||||
|
||||
|
||||
int log_get_trace_flags()
|
||||
{
|
||||
return trace_flags;
|
||||
}
|
||||
|
||||
|
||||
void log_trace(int type, char *line, int len)
|
||||
{
|
||||
int i = 0;
|
||||
int ch;
|
||||
char data[64] = "\0";
|
||||
char *dptr = NULL;
|
||||
char text[17];
|
||||
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
if ((type & trace_flags) != 0) {
|
||||
text[16] = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
if ((i % 16) == 0) {
|
||||
// beginning of line
|
||||
dptr = data;
|
||||
sprintf(dptr, "%4.4x|", i);
|
||||
}
|
||||
ch = line[i];
|
||||
sprintf(dptr + 5 + ((i % 16) * 3), "%2.2x", ch);
|
||||
if (ch > 31 && ch < 127) {
|
||||
text[i % 16] = ch;
|
||||
}
|
||||
else {
|
||||
text[i % 16] = '.';
|
||||
}
|
||||
if ((i % 16) == 15) {
|
||||
log_start(LOG_TRACE);
|
||||
fprintf(log_file, "%s|%s|%s|", trace_type[type], data, text);
|
||||
log_end();
|
||||
}
|
||||
else {
|
||||
sprintf(dptr + 7 + ((i % 16) * 3), " ");
|
||||
}
|
||||
}
|
||||
i = i % 16;
|
||||
if (i > 0) {
|
||||
for (; i < 16; i++) {
|
||||
sprintf(dptr + 5 + ((i % 16) * 3), " ");
|
||||
if ((i % 16) != 15) {
|
||||
sprintf(dptr + 7 + ((i % 16) * 3), " ");
|
||||
}
|
||||
text[i % 16] = ' ';
|
||||
}
|
||||
log_start(LOG_TRACE);
|
||||
fprintf(log_file, "%s|%s|%s|", trace_type[type], data, text);
|
||||
}
|
||||
log_end();
|
||||
}
|
||||
}
|
||||
|
||||
void log_start(int level)
|
||||
{
|
||||
char t[23];
|
||||
time_t now;
|
||||
|
||||
if (-1 == pthread_mutex_lock(&log_mutex)) {
|
||||
perror("Could not lock the log mutex");
|
||||
}
|
||||
else {
|
||||
// we have the lock.
|
||||
now = time(NULL);
|
||||
strftime(t, 22, "%Y-%m-%d %H:%M:%S", localtime(&now));
|
||||
fprintf(log_file, "%s:%5.5d:%s:", t, (int) pthread_self(), log_desc[level]);
|
||||
//free(t);
|
||||
}
|
||||
}
|
||||
|
||||
void log_end()
|
||||
{
|
||||
fprintf(log_file, "\n");
|
||||
fflush(log_file);
|
||||
if (-1 == pthread_mutex_unlock(&log_mutex)) {
|
||||
perror("Could not lock the log mutex");
|
||||
}
|
||||
}
|
69
src/debug.h
Normal file
69
src/debug.h
Normal file
@ -0,0 +1,69 @@
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H 1
|
||||
|
||||
#define LOG_TRACE 10
|
||||
#define LOG_ALL 7
|
||||
#define LOG_ENTER_EXIT 6
|
||||
#define LOG_DEBUG 5
|
||||
#define LOG_INFO 4
|
||||
#define LOG_WARN 3
|
||||
#define LOG_ERROR 2
|
||||
#define LOG_FATAL 1
|
||||
#define LOG_NONE 0
|
||||
|
||||
#define TRACE_MODEM_IN 1
|
||||
#define TRACE_MODEM_OUT 2
|
||||
#define TRACE_IP_IN 4
|
||||
#define TRACE_IP_OUT 8
|
||||
|
||||
#include <stdio.h> // needed for strerror
|
||||
#include <string.h> // needed for strerror
|
||||
#include <errno.h> // needed for errno
|
||||
|
||||
#if __STDC_VERSION__ < 199901L
|
||||
#if __GNUC__ >= 2
|
||||
#define __func__ __FUNCTION__
|
||||
#else
|
||||
#define __func__ "<unknown>"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#define LOG(a,args...) do { \
|
||||
if(a <= log_level) { \
|
||||
log_start(a); \
|
||||
fprintf(log_file,args); \
|
||||
log_end(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define ELOG(a,args...) do { \
|
||||
if(a <= log_level) { \
|
||||
log_start(a); \
|
||||
fprintf(log_file,args); \
|
||||
fprintf(log_file," (%s)\n",strerror(errno)); \
|
||||
log_end(); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define LOG_ENTER() LOG(LOG_ENTER_EXIT,"Entering %s function",__func__);
|
||||
#define LOG_EXIT() LOG(LOG_ENTER_EXIT,"Exitting %s function",__func__);
|
||||
int log_init(void);
|
||||
void log_set_file(FILE * a);
|
||||
void log_set_level(int a);
|
||||
int log_get_trace_flags();
|
||||
void log_set_trace_flags(int a);
|
||||
void log_trace(int type, char *line, int len);
|
||||
void log_start(int level);
|
||||
void log_end();
|
||||
|
||||
#endif
|
||||
#ifndef DEBUG_VARS
|
||||
#define DEBUG_VARS 1
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
extern int log_level;
|
||||
extern FILE *log_file;
|
||||
|
||||
#endif
|
275
src/getcmd.c
Normal file
275
src/getcmd.c
Normal file
@ -0,0 +1,275 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "getcmd.h"
|
||||
|
||||
int getData(char line[], int *index, int len, int *data_start, int *data_end, int complex_parse)
|
||||
{
|
||||
|
||||
int alpha = FALSE;
|
||||
int done = FALSE;
|
||||
|
||||
*data_start = *index;
|
||||
|
||||
while (*index < len && done != TRUE) {
|
||||
// I'm going to assume either
|
||||
// a number
|
||||
// a string with a space
|
||||
switch (line[*index]) {
|
||||
case ' ':
|
||||
if (!complex_parse && *index != *data_start) {
|
||||
// leave space, next call will skip it.
|
||||
done = TRUE;
|
||||
}
|
||||
else if (*index != *data_start) {
|
||||
// we are complex, add the space and continue.
|
||||
(*index)++;
|
||||
}
|
||||
else {
|
||||
// we have not started, eat space and continue.
|
||||
(*index)++;
|
||||
*data_start = *index;
|
||||
}
|
||||
break;
|
||||
case 0:
|
||||
done = TRUE;
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9': // isnum
|
||||
(*index)++;
|
||||
break;
|
||||
default:
|
||||
if (!complex_parse && *index != *data_start && 0 == alpha) {
|
||||
// we were a number, but we've hit an alpha 'S0=137S...'
|
||||
done = TRUE;
|
||||
}
|
||||
else {
|
||||
(*index)++;
|
||||
alpha = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
*data_end = (*index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getNumber(char line[], int *index, int len)
|
||||
{
|
||||
int num = 0;
|
||||
int found = FALSE;
|
||||
|
||||
while (*index < len && 0 != isdigit(line[*index])) {
|
||||
num = num * 10 + line[(*index)++] - '0';
|
||||
found = 1;
|
||||
}
|
||||
if (FALSE == found)
|
||||
return -1;
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
int skip(char line[], int *index, int len, char ch)
|
||||
{
|
||||
while (*index < len && ch == line[*index])
|
||||
(*index)++;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int getCommand(char line[], int flags, int *index, int *num, int len)
|
||||
{
|
||||
int cmd = line[(*index)++];
|
||||
|
||||
*num = getNumber(line, index, len);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
|
||||
int parseCommand(char line[], int flags, int *index, int *num, int len)
|
||||
{
|
||||
int cmd = getCommand(line, flags, index, num, len);
|
||||
|
||||
if (0 < cmd && 0 > *num)
|
||||
*num = 0;
|
||||
return toupper(cmd) | flags;
|
||||
}
|
||||
|
||||
|
||||
int parseRegister(char line[],
|
||||
int flags,
|
||||
int *index,
|
||||
int *num, int len, int *data_start, int *data_end, int complex_parse)
|
||||
{
|
||||
// need to handle S<num>?, which queries that S register.
|
||||
|
||||
int cmd = 0;
|
||||
|
||||
cmd = getCommand(line, flags, index, num, len);
|
||||
if (0 > num)
|
||||
return AT_CMD_ERR;
|
||||
skip(line, index, len, ' ');
|
||||
if (len == *index)
|
||||
return AT_CMD_ERR;
|
||||
switch (line[(*index)++]) {
|
||||
case '=':
|
||||
// set a register
|
||||
skip(line, index, len, ' ');
|
||||
if (0 > getData(line, index, len, data_start, data_end, complex_parse))
|
||||
return AT_CMD_ERR;
|
||||
break;
|
||||
case '?':
|
||||
// query a register
|
||||
flags |= AT_CMD_FLAG_QUERY;
|
||||
if (*num < 0)
|
||||
*num = 0;
|
||||
break;
|
||||
default:
|
||||
return AT_CMD_ERR;
|
||||
}
|
||||
return toupper(cmd) | flags;
|
||||
}
|
||||
|
||||
int getcmd(char line[], int *index, int *num, int *data_start, int *data_end)
|
||||
{
|
||||
int len = 0;
|
||||
int cmd = AT_CMD_END;
|
||||
|
||||
*num = 0;
|
||||
*data_start = 0;
|
||||
*data_end = 0;
|
||||
|
||||
if (line == NULL)
|
||||
return AT_CMD_NONE;
|
||||
len = strlen(line);
|
||||
while (*index < len) {
|
||||
cmd = toupper(line[*index]);
|
||||
switch (cmd) {
|
||||
case ' ':
|
||||
break;
|
||||
case 0:
|
||||
return AT_CMD_END;
|
||||
case '%':
|
||||
(*index)++;
|
||||
while (*index < len) {
|
||||
switch (toupper(line[*index])) {
|
||||
case ' ':
|
||||
break;
|
||||
case 0:
|
||||
return AT_CMD_ERR;
|
||||
default:
|
||||
return parseCommand(line, AT_CMD_FLAG_PRO_PCT, index, num, len);
|
||||
}
|
||||
(*index)++;
|
||||
}
|
||||
break;
|
||||
case '\\':
|
||||
(*index)++;
|
||||
while (*index < len) {
|
||||
switch (toupper(line[*index])) {
|
||||
case ' ':
|
||||
break;
|
||||
case 0:
|
||||
return AT_CMD_ERR;
|
||||
default:
|
||||
return parseCommand(line, AT_CMD_FLAG_PRO_BACK, index, num, len);
|
||||
}
|
||||
(*index)++;
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
(*index)++;
|
||||
while (*index < len) {
|
||||
switch (toupper(line[*index])) {
|
||||
case ' ':
|
||||
break;
|
||||
case 0:
|
||||
return AT_CMD_ERR;
|
||||
default:
|
||||
return parseCommand(line, AT_CMD_FLAG_PRO_COLON, index, num, len);
|
||||
}
|
||||
(*index)++;
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
(*index)++;
|
||||
while (*index < len) {
|
||||
switch (toupper(line[*index])) {
|
||||
case ' ':
|
||||
break;
|
||||
case 0:
|
||||
return AT_CMD_ERR;
|
||||
default:
|
||||
return parseCommand(line, AT_CMD_FLAG_PRO_MINUS, index, num, len);
|
||||
}
|
||||
(*index)++;
|
||||
}
|
||||
break;
|
||||
case '&':
|
||||
(*index)++;
|
||||
while (*index < len) {
|
||||
switch (toupper(line[*index])) {
|
||||
case ' ':
|
||||
break;
|
||||
case 0:
|
||||
return AT_CMD_ERR;
|
||||
case 'Z':
|
||||
return parseRegister(line, AT_CMD_FLAG_EXT, index, num, len, data_start, data_end,
|
||||
TRUE);
|
||||
default:
|
||||
return parseCommand(line, AT_CMD_FLAG_EXT, index, num, len);
|
||||
}
|
||||
(*index)++;
|
||||
}
|
||||
break;
|
||||
case 'D': // handle Dialing.
|
||||
(*index)++;
|
||||
*num = 0;
|
||||
while (*index < len) {
|
||||
switch (toupper(line[*index])) {
|
||||
case 0:
|
||||
return cmd;
|
||||
case 'T':
|
||||
case 'P':
|
||||
case 'L':
|
||||
*num = toupper(line[*index]);
|
||||
(*index)++;
|
||||
default:
|
||||
getData(line, index, len, data_start, data_end, TRUE);
|
||||
return cmd;
|
||||
}
|
||||
(*index)++;
|
||||
}
|
||||
return cmd;
|
||||
case 'S':
|
||||
return parseRegister(line, AT_CMD_FLAG_BAS, index, num, len, data_start, data_end, FALSE);
|
||||
default:
|
||||
return parseCommand(line, AT_CMD_FLAG_BAS, index, num, len);
|
||||
}
|
||||
(*index)++;
|
||||
}
|
||||
return cmd;
|
||||
|
||||
}
|
||||
|
||||
int main_getcmd(int argc, char **argv)
|
||||
{
|
||||
char data[] = "DT 555-1212";
|
||||
int index = 0, num = 0, start = 0, end = 0;
|
||||
int cmd = 0;
|
||||
|
||||
while (cmd != AT_CMD_END) {
|
||||
cmd = getcmd(data, &index, &num, &start, &end);
|
||||
printf("Cmd: %c Index: %d Num: %d Start: %d End: %d\n", cmd, index, num, start, end);
|
||||
}
|
||||
return 0;
|
||||
}
|
28
src/getcmd.h
Normal file
28
src/getcmd.h
Normal file
@ -0,0 +1,28 @@
|
||||
#define AT_CMD_END -1
|
||||
#define AT_CMD_ERR -2
|
||||
#define AT_CMD_NONE -4
|
||||
#define AT_CMD_IERR -3
|
||||
#define AT_CMD_FLAG_BAS 0
|
||||
#define AT_CMD_FLAG_EXT 256
|
||||
#define AT_CMD_FLAG_PRO_PCT 512
|
||||
#define AT_CMD_FLAG_PRO_BACK 1024
|
||||
#define AT_CMD_FLAG_PRO_MINUS 2048
|
||||
#define AT_CMD_FLAG_PRO_DOLLAR 4096
|
||||
#define AT_CMD_FLAG_PRO_COLON 8192
|
||||
#define AT_CMD_FLAG_QUERY 32768
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
int getData(char line[], int *index, int len, int *data_start, int *data_end, int simple_parse);
|
||||
int getNumber(char line[], int *index, int len);
|
||||
int skip(char line[], int *index, int len, char ch);
|
||||
int getCommand(char line[], int flags, int *index, int *num, int len);
|
||||
int parseCommand(char line[], int flags, int *index, int *num, int len);
|
||||
int parseRegister(char line[],
|
||||
int flags,
|
||||
int *index,
|
||||
int *num, int len, int *data_start, int *data_end, int simple_parse);
|
||||
int getcmd(char line[], int *index, int *num, int *data_start, int *data_end);
|
191
src/init.c
Normal file
191
src/init.c
Normal file
@ -0,0 +1,191 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // for exit,atoi
|
||||
#include <unistd.h>
|
||||
#include "debug.h"
|
||||
#include "phone_book.h"
|
||||
#include "init.h"
|
||||
|
||||
void print_help(char *name)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <parameters>\n", name);
|
||||
fprintf(stderr, " -p port to listen on (defaults to 6400)\n");
|
||||
fprintf(stderr, " -t trace flags: (can be combined)\n");
|
||||
fprintf(stderr, " 's' = modem input\n");
|
||||
fprintf(stderr, " 'S' = modem output\n");
|
||||
fprintf(stderr, " 'i' = IP input\n");
|
||||
fprintf(stderr, " 'I' = IP input\n");
|
||||
fprintf(stderr, " -l 0 (NONE), 1 (FATAL) - 7 (DEBUG_X) (defaults to 0)\n");
|
||||
fprintf(stderr, " -L log file (defaults to stderr)\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " The following can be repeated for each modem desired\n");
|
||||
fprintf(stderr, " (-s, -S, and -i will apply to any subsequent device if not set again)\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " -d serial device (e.g. /dev/ttyS0). Cannot be used with -v\n");
|
||||
fprintf(stderr, " -v tcp port for VICE RS232 (e.g. 25232). Cannot be used with -d\n");
|
||||
fprintf(stderr, " -s serial port speed (defaults to 38400)\n");
|
||||
fprintf(stderr, " -S speed modem will report (defaults to -s value)\n");
|
||||
fprintf(stderr, " -I invert DCD pin\n");
|
||||
fprintf(stderr, " -n add phone entry (number=replacement)\n");
|
||||
fprintf(stderr, " -a filename to send to local side upon answer\n");
|
||||
fprintf(stderr, " -A filename to send to remote side upon answer\n");
|
||||
fprintf(stderr, " -c filename to send to local side upon connect\n");
|
||||
fprintf(stderr, " -C filename to send to remote side upon connect\n");
|
||||
fprintf(stderr, " -N filename to send when no answer\n");
|
||||
fprintf(stderr, " -B filename to send when modem(s) busy\n");
|
||||
fprintf(stderr, " -T filename to send upon inactivity timeout\n");
|
||||
fprintf(stderr,
|
||||
" -i modem init string (defaults to '', leave off 'at' prefix when specifying)\n");
|
||||
fprintf(stderr,
|
||||
" -D direct connection (follow with hostname:port for caller, : for receiver)\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int init(int argc,
|
||||
char **argv,
|
||||
modem_config cfg[], int max_modem, int *port, char *all_busy, int all_busy_len)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int opt = 0;
|
||||
int trace_flags = 0;
|
||||
char *tok;
|
||||
int dce_set = FALSE;
|
||||
int tty_set = FALSE;
|
||||
|
||||
LOG_ENTER();
|
||||
*port = 6400;
|
||||
mdm_init_config(&cfg[0]);
|
||||
cfg[0].dte_speed = 38400;
|
||||
cfg[0].dce_speed = 38400;
|
||||
|
||||
while (opt > -1 && i < max_modem) {
|
||||
opt = getopt(argc, argv, "p:s:S:d:v:hw:i:Il:L:t:n:a:A:c:C:N:B:T:D:");
|
||||
switch (opt) {
|
||||
case 't':
|
||||
trace_flags = log_get_trace_flags();
|
||||
for (j = 0; j < strlen(optarg); j++) {
|
||||
switch (optarg[j]) {
|
||||
case 's':
|
||||
trace_flags |= TRACE_MODEM_IN;
|
||||
break;
|
||||
case 'S':
|
||||
trace_flags |= TRACE_MODEM_OUT;
|
||||
break;
|
||||
case 'i':
|
||||
trace_flags |= TRACE_IP_IN;
|
||||
break;
|
||||
case 'I':
|
||||
trace_flags |= TRACE_IP_OUT;
|
||||
break;
|
||||
}
|
||||
log_set_trace_flags(trace_flags);
|
||||
}
|
||||
break;
|
||||
case 'a':
|
||||
strncpy(cfg[i].data.local_answer, optarg, sizeof(cfg[i].data.local_answer));
|
||||
break;
|
||||
case 'A':
|
||||
strncpy(cfg[i].data.remote_answer, optarg, sizeof(cfg[i].data.remote_answer));
|
||||
break;
|
||||
case 'c':
|
||||
strncpy(cfg[i].data.local_connect, optarg, sizeof(cfg[i].data.local_connect));
|
||||
break;
|
||||
case 'C':
|
||||
strncpy(cfg[i].data.remote_connect, optarg, sizeof(cfg[i].data.remote_connect));
|
||||
break;
|
||||
case 'B':
|
||||
strncpy(all_busy, optarg, all_busy_len);
|
||||
break;
|
||||
case 'N':
|
||||
strncpy(cfg[i].data.no_answer, optarg, sizeof(cfg[i].data.no_answer));
|
||||
break;
|
||||
case 'T':
|
||||
strncpy(cfg[i].data.inactive, optarg, sizeof(cfg[i].data.inactive));
|
||||
break;
|
||||
case 'i':
|
||||
strncpy(cfg[i].config0, optarg, 255);
|
||||
break;
|
||||
case 'I':
|
||||
cfg[i].invert_dcd = TRUE;
|
||||
break;
|
||||
case 'p':
|
||||
*port = (atoi(optarg));
|
||||
break;
|
||||
case 'n':
|
||||
tok = strtok(optarg, "=");
|
||||
pb_add(tok, strtok(NULL, "="));
|
||||
break;
|
||||
case 'l':
|
||||
log_set_level(atoi(optarg));
|
||||
break;
|
||||
case 'L':
|
||||
log_set_file(fopen(optarg, "w+"));
|
||||
// should check to see if an error occurred...
|
||||
break;
|
||||
case 's':
|
||||
cfg[i].dte_speed = atoi(optarg);
|
||||
LOG(LOG_ALL, "Setting DTE speed to %d", cfg[i].dte_speed);
|
||||
if (dce_set == FALSE)
|
||||
cfg[i].dce_speed = cfg[i].dte_speed;
|
||||
break;
|
||||
case '?':
|
||||
case 'h':
|
||||
print_help(argv[0]);
|
||||
break;
|
||||
case 'd':
|
||||
case 'v':
|
||||
if (tty_set) {
|
||||
if (++i < max_modem) {
|
||||
dce_set = FALSE;
|
||||
mdm_init_config(&cfg[i]);
|
||||
cfg[i].dte_speed = cfg[i - 1].dte_speed;
|
||||
cfg[i].dce_speed = cfg[i - 1].dce_speed;
|
||||
cfg[i].dce_data.is_ip232 = FALSE;
|
||||
strncpy(cfg[i].config0, cfg[i - 1].config0, sizeof(cfg[i].config0));
|
||||
strncpy(cfg[i].data.local_connect, cfg[i - 1].data.local_connect,
|
||||
sizeof(cfg[i].data.local_connect));
|
||||
strncpy(cfg[i].data.remote_connect, cfg[i - 1].data.remote_connect,
|
||||
sizeof(cfg[i].data.remote_connect));
|
||||
strncpy(cfg[i].data.local_answer, cfg[i - 1].data.local_answer,
|
||||
sizeof(cfg[i].data.local_answer));
|
||||
strncpy(cfg[i].data.remote_answer, cfg[i - 1].data.remote_answer,
|
||||
sizeof(cfg[i].data.remote_answer));
|
||||
strncpy(cfg[i].data.no_answer, cfg[i - 1].data.no_answer,
|
||||
sizeof(cfg[i].data.no_answer));
|
||||
strncpy(cfg[i].data.inactive, cfg[i - 1].data.inactive, sizeof(cfg[i].data.inactive));
|
||||
}
|
||||
else {
|
||||
LOG(LOG_WARN, "Maximum modems defined - ignoring extra");
|
||||
break;
|
||||
}
|
||||
}
|
||||
strncpy(cfg[i].dce_data.tty, optarg, sizeof(cfg[i].dce_data.tty));
|
||||
cfg[i].dce_data.is_ip232 = ('v' == opt);
|
||||
tty_set = TRUE;
|
||||
break;
|
||||
case 'S':
|
||||
cfg[i].dce_speed = atoi(optarg);
|
||||
dce_set = TRUE;
|
||||
break;
|
||||
case 'D':
|
||||
cfg[i].data.direct_conn = TRUE;
|
||||
strncpy(cfg[i].data.direct_conn_num, optarg, sizeof(cfg[i].data.direct_conn_num));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tty_set) {
|
||||
if (i < max_modem)
|
||||
++i;
|
||||
}
|
||||
else {
|
||||
// no modems defined
|
||||
LOG(LOG_FATAL, "No modems defined");
|
||||
print_help(argv[0]);
|
||||
}
|
||||
|
||||
LOG(LOG_DEBUG, "Read configuration for %i serial port(s)", i);
|
||||
|
||||
LOG_EXIT();
|
||||
return i;
|
||||
}
|
11
src/init.h
Normal file
11
src/init.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#include "modem_core.h"
|
||||
|
||||
void print_help(char *name);
|
||||
int init(int argc,
|
||||
char **argv,
|
||||
modem_config cfg[], int max_modem, int *port, char *all_busy, int all_busy_len);
|
175
src/ip.c
Normal file
175
src/ip.c
Normal file
@ -0,0 +1,175 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h> // for read...
|
||||
#include <stdlib.h> // for atoi...
|
||||
|
||||
#include "debug.h"
|
||||
#include "ip.h"
|
||||
|
||||
|
||||
const int BACK_LOG = 5;
|
||||
|
||||
int ip_init_server_conn(int port)
|
||||
{
|
||||
int sSocket = 0, on = 0, rc = 0;
|
||||
struct sockaddr_in serverName = { 0 };
|
||||
|
||||
LOG_ENTER();
|
||||
|
||||
LOG(LOG_DEBUG, "Creating server socket");
|
||||
|
||||
sSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (-1 == sSocket) {
|
||||
ELOG(LOG_FATAL, "Server socket could not be created");
|
||||
}
|
||||
else {
|
||||
|
||||
/*
|
||||
* turn off bind address checking, and allow
|
||||
* port numbers to be reused - otherwise
|
||||
* the TIME_WAIT phenomenon will prevent
|
||||
* binding to these addreG.
|
||||
*/
|
||||
|
||||
on = 1;
|
||||
|
||||
rc = setsockopt(sSocket, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)
|
||||
);
|
||||
if (-1 == rc) {
|
||||
ELOG(LOG_ERROR, "bind address checking could not be turned off");
|
||||
}
|
||||
|
||||
serverName.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serverName.sin_family = AF_INET;
|
||||
|
||||
/* network-order */
|
||||
serverName.sin_port = htons(port);
|
||||
|
||||
LOG(LOG_DEBUG, "Binding server socket to port %d", port);
|
||||
rc = bind(sSocket, (struct sockaddr *) &serverName, sizeof(serverName)
|
||||
);
|
||||
if (-1 == rc) {
|
||||
ELOG(LOG_FATAL, "Server socket could not be bound to port");
|
||||
sSocket = -1;
|
||||
}
|
||||
else {
|
||||
LOG(LOG_INFO, "Server socket bound to port");
|
||||
|
||||
rc = listen(sSocket, BACK_LOG);
|
||||
LOG(LOG_INFO, "Server socket listening for connections");
|
||||
if (-1 == rc) {
|
||||
ELOG(LOG_FATAL, "Server socket could not listen on port");
|
||||
sSocket = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_EXIT();
|
||||
return sSocket;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int ip_connect(char addy[])
|
||||
{
|
||||
struct sockaddr_in pin;
|
||||
struct in_addr cin_addr;
|
||||
struct hostent *hp;
|
||||
int sd = 0;
|
||||
int port = 23;
|
||||
char *address;
|
||||
char *tmp;
|
||||
|
||||
LOG_ENTER();
|
||||
|
||||
address = (char *) strtok(addy, ":");
|
||||
tmp = (char *) strtok((char *) 0, ":");
|
||||
if (tmp != NULL && strlen(tmp) > 0) {
|
||||
port = atoi(tmp);
|
||||
}
|
||||
|
||||
LOG(LOG_DEBUG, "Calling %s", addy);
|
||||
memset(&pin, 0, sizeof(pin));
|
||||
|
||||
/* go find out about the desired host machine */
|
||||
if ((hp = gethostbyname(address)) == 0) {
|
||||
// well, not a DNS entry... Treat as IP...
|
||||
if (1 != inet_aton(address, &cin_addr)) {
|
||||
ELOG(LOG_ERROR, "Host %s was invalid", addy);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
cin_addr = *((struct in_addr *) (hp->h_addr));
|
||||
}
|
||||
|
||||
pin.sin_family = AF_INET;
|
||||
pin.sin_addr.s_addr = cin_addr.s_addr;
|
||||
pin.sin_port = htons(port);
|
||||
|
||||
/* grab an Internet domain socket */
|
||||
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
ELOG(LOG_ERROR, "could not create client socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* connect to PORT on HOST */
|
||||
if (connect(sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) {
|
||||
ELOG(LOG_ERROR, "could not connect to address");
|
||||
return -1;
|
||||
}
|
||||
LOG(LOG_INFO, "Connection to %s established", addy);
|
||||
LOG_EXIT();
|
||||
return sd;
|
||||
}
|
||||
|
||||
int ip_accept(int sSocket)
|
||||
{
|
||||
struct sockaddr_in clientName = { 0 };
|
||||
socklen_t clientLength = sizeof(clientName);
|
||||
int cSocket = -1;
|
||||
|
||||
LOG_ENTER();
|
||||
|
||||
(void) memset(&clientName, 0, sizeof(clientName));
|
||||
|
||||
cSocket = accept(sSocket, (struct sockaddr *) &clientName, &clientLength);
|
||||
if (-1 == cSocket) {
|
||||
ELOG(LOG_ERROR, "Could not accept incoming connection");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (-1 == getpeername(cSocket, (struct sockaddr *) &clientName, &clientLength)) {
|
||||
ELOG(LOG_WARN, "Could not obtain peer name");
|
||||
}
|
||||
else {
|
||||
LOG(LOG_INFO, "Connection accepted from %s", inet_ntoa(clientName.sin_addr)
|
||||
);
|
||||
}
|
||||
LOG_EXIT();
|
||||
return cSocket;
|
||||
}
|
||||
|
||||
int ip_disconnect(int fd)
|
||||
{
|
||||
if (fd > -1)
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ip_write(int fd, char *data, int len)
|
||||
{
|
||||
log_trace(TRACE_IP_OUT, data, len);
|
||||
return write(fd, data, len);
|
||||
}
|
||||
|
||||
int ip_read(int fd, char *data, int len)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = recv(fd, data, len, 0);
|
||||
log_trace(TRACE_IP_IN, data, res);
|
||||
return res;
|
||||
}
|
17
src/ip.h
Normal file
17
src/ip.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef IP_H
|
||||
#define IP_H
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
int ip_init(void);
|
||||
int ip_init_server_conn(int port);
|
||||
int ip_connect(char addy[]);
|
||||
int ip_accept(int sSocket);
|
||||
int ip_disconnect(int fd);
|
||||
int ip_write(int fd, char *data, int len);
|
||||
int ip_read(int fd, char *data, int len);
|
||||
|
||||
#endif
|
259
src/ip232.c
Normal file
259
src/ip232.c
Normal file
@ -0,0 +1,259 @@
|
||||
#include <sys/socket.h> // for recv...
|
||||
#include <stdlib.h> // for exit...
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "modem_core.h"
|
||||
#include "ip.h"
|
||||
#include "ip232.h"
|
||||
|
||||
void *ip232_thread(void *arg)
|
||||
{
|
||||
modem_config *cfg = (modem_config *) arg;
|
||||
int accept_pending = FALSE;
|
||||
int rc;
|
||||
int res = 0;
|
||||
char buf[256];
|
||||
|
||||
fd_set readfs;
|
||||
int max_fd = 0;
|
||||
int cSocket;
|
||||
|
||||
LOG_ENTER();
|
||||
for (;;) {
|
||||
FD_ZERO(&readfs);
|
||||
FD_SET(cfg->dce_data.dp[1][0], &readfs);
|
||||
max_fd = cfg->dce_data.dp[1][0];
|
||||
if (accept_pending == FALSE) {
|
||||
FD_SET(cfg->dce_data.sSocket, &readfs);
|
||||
max_fd = MAX(max_fd, cfg->dce_data.sSocket);
|
||||
}
|
||||
LOG(LOG_ALL, "Waiting for incoming ip232 connections");
|
||||
rc = select(max_fd + 1, &readfs, NULL, NULL, NULL);
|
||||
if (rc < 0) {
|
||||
|
||||
// handle error
|
||||
}
|
||||
else {
|
||||
if (FD_ISSET(cfg->dce_data.dp[1][0], &readfs)) { // pipe
|
||||
res = read(cfg->dce_data.dp[1][0], buf, sizeof(buf) - 1);
|
||||
LOG(LOG_DEBUG, "ip232 thread notified");
|
||||
accept_pending = FALSE;
|
||||
}
|
||||
if (FD_ISSET(cfg->dce_data.sSocket, &readfs)) { // ip connection
|
||||
if (cfg->dce_data.ip232_is_connected) {
|
||||
LOG(LOG_DEBUG, "Already have ip232 connection, rejecting new");
|
||||
|
||||
// already have a connection... accept and close
|
||||
cSocket = ip_accept(cfg->dce_data.sSocket);
|
||||
if (cSocket > -1) {
|
||||
close(cSocket);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG(LOG_DEBUG, "Incoming ip232 connection");
|
||||
writePipe(cfg->dce_data.dp[0][1], MSG_ACCEPT);
|
||||
accept_pending = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_EXIT();
|
||||
}
|
||||
|
||||
int spawn_ip232_thread(modem_config *cfg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
pthread_t thread_id;
|
||||
|
||||
rc = pthread_create(&thread_id, NULL, ip232_thread, (void *) cfg);
|
||||
LOG(LOG_ALL, "ip232 thread ID=%d", (int) thread_id);
|
||||
if (rc < 0) {
|
||||
ELOG(LOG_FATAL, "ip232 thread could not be started");
|
||||
exit(-1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ip232_init_conn(modem_config *cfg)
|
||||
{
|
||||
int rc = -1;
|
||||
int port;
|
||||
|
||||
LOG_ENTER();
|
||||
LOG(LOG_INFO, "Opening ip232 device");
|
||||
port = atoi(cfg->dce_data.tty);
|
||||
rc = ip_init_server_conn(port);
|
||||
if (rc < 0) {
|
||||
ELOG(LOG_FATAL, "Could not initialize ip232 server socket");
|
||||
exit(-1);
|
||||
}
|
||||
if (-1 == pipe(cfg->dce_data.dp[0])) {
|
||||
ELOG(LOG_FATAL, "ip232 thread incoming IPC pipe could not be created");
|
||||
exit(-1);
|
||||
}
|
||||
if (-1 == pipe(cfg->dce_data.dp[1])) {
|
||||
ELOG(LOG_FATAL, "ip232 thread outgoing IPC pipe could not be created");
|
||||
exit(-1);
|
||||
}
|
||||
cfg->dce_data.sSocket = rc;
|
||||
cfg->dce_data.ip232_is_connected = FALSE;
|
||||
cfg->dce_data.fd = cfg->dce_data.dp[0][0];
|
||||
spawn_ip232_thread(cfg);
|
||||
LOG(LOG_INFO, "ip232 device configured");
|
||||
LOG_EXIT();
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ip232_set_flow_control(modem_config *cfg, int status)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ip232_get_control_lines(modem_config *cfg)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (cfg->dce_data.ip232_is_connected && cfg->dce_data.ip232_dtr) {
|
||||
status |= TIOCM_DSR;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int ip232_set_control_lines(modem_config *cfg, int state)
|
||||
{
|
||||
int dcd;
|
||||
char cmd[2];
|
||||
|
||||
if (cfg->dce_data.ip232_is_connected) {
|
||||
dcd = (state & TIOCM_DTR) ? TRUE : FALSE;
|
||||
if (dcd != cfg->dce_data.ip232_dcd) {
|
||||
cfg->dce_data.ip232_dcd = dcd;
|
||||
cmd[0] = 255;
|
||||
cmd[1] = dcd ? 1 : 0;
|
||||
ip_write(cfg->dce_data.fd, cmd, sizeof(cmd));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ip232_write(modem_config *cfg, char *data, int len)
|
||||
{
|
||||
int retval;
|
||||
int i = 0;
|
||||
int double_iac = FALSE;
|
||||
char text[1024];
|
||||
int text_len = 0;
|
||||
|
||||
log_trace(TRACE_MODEM_OUT, data, len);
|
||||
retval = len;
|
||||
if (cfg->dce_data.ip232_is_connected) {
|
||||
while (i < len) {
|
||||
if (double_iac) {
|
||||
text[text_len++] = 255;
|
||||
double_iac = FALSE;
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
if (255 == data[i]) {
|
||||
text[text_len++] = 255;
|
||||
double_iac = TRUE;
|
||||
}
|
||||
else {
|
||||
text[text_len++] = data[i++];
|
||||
}
|
||||
}
|
||||
if (text_len == sizeof(text)) {
|
||||
retval = ip_write(cfg->dce_data.fd, text, text_len);
|
||||
text_len = 0;
|
||||
}
|
||||
}
|
||||
if (text_len) {
|
||||
retval = ip_write(cfg->dce_data.fd, text, text_len);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
int ip232_read(modem_config *cfg, char *data, int len)
|
||||
{
|
||||
int res;
|
||||
int rc;
|
||||
char buf[256];
|
||||
int i = 0;
|
||||
char ch;
|
||||
int text_len = 0;
|
||||
|
||||
LOG_ENTER();
|
||||
if (len > sizeof(buf)) {
|
||||
LOG(LOG_FATAL, "ip232_read: len > sizeof(buf)");
|
||||
exit(-1);
|
||||
}
|
||||
if (cfg->dce_data.ip232_is_connected) {
|
||||
res = recv(cfg->dce_data.fd, buf, len, 0);
|
||||
if (0 >= res) {
|
||||
LOG(LOG_INFO, "No ip232 socket data read, assume closed peer");
|
||||
ip_disconnect(cfg->dce_data.fd);
|
||||
cfg->dce_data.fd = cfg->dce_data.dp[0][0];
|
||||
cfg->dce_data.ip232_is_connected = FALSE;
|
||||
}
|
||||
else {
|
||||
LOG(LOG_DEBUG, "Read %d bytes from ip232 socket", res);
|
||||
log_trace(TRACE_MODEM_IN, buf, res);
|
||||
while (i < res) {
|
||||
ch = buf[i];
|
||||
if (cfg->dce_data.ip232_iac) {
|
||||
cfg->dce_data.ip232_iac = FALSE;
|
||||
switch (ch) {
|
||||
case 0:
|
||||
cfg->dce_data.ip232_dtr = FALSE;
|
||||
break;
|
||||
case 1:
|
||||
cfg->dce_data.ip232_dtr = TRUE;
|
||||
break;
|
||||
case 255:
|
||||
data[text_len++] = 255;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (255 == ch) {
|
||||
cfg->dce_data.ip232_iac = TRUE;
|
||||
}
|
||||
else {
|
||||
data[text_len++] = ch;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
// not connected
|
||||
res = read(cfg->dce_data.dp[0][0], buf, sizeof(buf));
|
||||
switch (buf[0]) {
|
||||
case MSG_ACCEPT: // accept connection.
|
||||
LOG(LOG_INFO, "Accepting ip232 connection...");
|
||||
rc = ip_accept(cfg->dce_data.sSocket);
|
||||
if (res > -1) {
|
||||
cfg->dce_data.fd = rc;
|
||||
cfg->dce_data.ip232_is_connected = TRUE;
|
||||
cfg->dce_data.ip232_dtr = FALSE;
|
||||
cfg->dce_data.ip232_dcd = FALSE;
|
||||
writePipe(cfg->dce_data.dp[1][1], MSG_ACCEPTED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG_EXIT();
|
||||
return text_len;
|
||||
}
|
19
src/ip232.h
Normal file
19
src/ip232.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef IP232_H
|
||||
#define IP232_H 1
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif /* */
|
||||
|
||||
#define MSG_ACCEPT '+'
|
||||
#define MSG_ACCEPTED '+'
|
||||
int ip232_init_conn(modem_config *);
|
||||
int ip232_set_flow_control(modem_config *, int status);
|
||||
int ip232_get_control_lines(modem_config *);
|
||||
int ip232_set_control_lines(modem_config *, int state);
|
||||
int ip232_write(modem_config *, char *data, int len);
|
||||
int ip232_read(modem_config *, char *data, int len);
|
||||
|
||||
|
||||
#endif /* */
|
106
src/line.c
Normal file
106
src/line.c
Normal file
@ -0,0 +1,106 @@
|
||||
#include "debug.h"
|
||||
#include "modem_core.h"
|
||||
#include "phone_book.h"
|
||||
#include "ip.h"
|
||||
#include "bridge.h"
|
||||
#include "line.h"
|
||||
|
||||
int line_init_config(modem_config *cfg)
|
||||
{
|
||||
cfg->line_data.fd = -1;
|
||||
cfg->line_data.is_telnet = FALSE;
|
||||
cfg->line_data.first_char = TRUE;
|
||||
cfg->line_data.valid_conn = FALSE;
|
||||
nvt_init_config(&cfg->line_data.nvt_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int line_write(modem_config *cfg, char *data, int len)
|
||||
{
|
||||
int retval;
|
||||
int i = 0;
|
||||
int double_iac = FALSE;
|
||||
char text[1024];
|
||||
int text_len = 0;
|
||||
|
||||
if (cfg->line_data.is_telnet && cfg->line_data.nvt_data.binary_xmit) {
|
||||
retval = 0;
|
||||
while (i < len) {
|
||||
if (double_iac) {
|
||||
text[text_len++] = NVT_IAC;
|
||||
double_iac = FALSE;
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
if (NVT_IAC == data[i]) {
|
||||
text[text_len++] = NVT_IAC;
|
||||
double_iac = TRUE;
|
||||
}
|
||||
else {
|
||||
text[text_len++] = data[i++];
|
||||
}
|
||||
}
|
||||
if (text_len == 1024) {
|
||||
retval = ip_write(cfg->line_data.fd, text, text_len);
|
||||
text_len = 0;
|
||||
}
|
||||
}
|
||||
if (text_len) {
|
||||
retval = ip_write(cfg->line_data.fd, text, text_len);
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
return ip_write(cfg->line_data.fd, data, len);
|
||||
}
|
||||
|
||||
|
||||
int line_listen(modem_config *cfg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int line_off_hook(modem_config *cfg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int line_connect(modem_config *cfg)
|
||||
{
|
||||
char *addy = cfg->dialno;
|
||||
|
||||
LOG(LOG_INFO, "Connecting");
|
||||
addy = pb_search(addy);
|
||||
cfg->line_data.fd = ip_connect(addy);
|
||||
if (cfg->line_data.fd > -1) {
|
||||
LOG(LOG_ALL, "Connected to %s", addy);
|
||||
cfg->line_data.valid_conn = TRUE;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
LOG(LOG_ALL, "Could not connect to %s", addy);
|
||||
cfg->line_data.valid_conn = FALSE;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int line_disconnect(modem_config *cfg)
|
||||
{
|
||||
LOG(LOG_INFO, "Disconnecting");
|
||||
if (cfg->data.direct_conn == TRUE) {
|
||||
LOG(LOG_INFO, "Direct connection active, maintaining link");
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
cfg->line_data.is_telnet = FALSE;
|
||||
cfg->line_data.first_char = TRUE;
|
||||
if (cfg->line_data.valid_conn == TRUE) {
|
||||
ip_disconnect(cfg->line_data.fd);
|
||||
cfg->line_data.valid_conn = FALSE;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
14
src/line.h
Normal file
14
src/line.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef LINE_H
|
||||
#define LINE_H 1
|
||||
|
||||
int line_init(void);
|
||||
int line_init_conn(modem_config *cfg);
|
||||
int line_init_config(modem_config *cfg);
|
||||
int line_read(modem_config *cfg, char *data, int len);
|
||||
int line_write(modem_config *cfg, char *data, int len);
|
||||
int line_listen(modem_config *cfg);
|
||||
int line_off_hook(modem_config *cfg);
|
||||
int line_connect(modem_config *cfg);
|
||||
int line_disconnect(modem_config *cfg);
|
||||
|
||||
#endif
|
697
src/modem_core.c
Normal file
697
src/modem_core.c
Normal file
@ -0,0 +1,697 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h> // for atoi
|
||||
|
||||
#include "getcmd.h"
|
||||
#include "debug.h"
|
||||
#include "modem_core.h"
|
||||
|
||||
char *mdm_responses[37];
|
||||
|
||||
int mdm_init()
|
||||
{
|
||||
mdm_responses[MDM_RESP_OK] = "OK";
|
||||
mdm_responses[MDM_RESP_RING] = "RING";
|
||||
mdm_responses[MDM_RESP_ERROR] = "ERROR";
|
||||
mdm_responses[MDM_RESP_CONNECT] = "CONNECT";
|
||||
mdm_responses[MDM_RESP_NO_CARRIER] = "NO CARRIER";
|
||||
mdm_responses[MDM_RESP_CONNECT_1200] = "CONNECT 1200";
|
||||
mdm_responses[MDM_RESP_NO_DIALTONE] = "NO DIALTONE";
|
||||
mdm_responses[MDM_RESP_BUSY] = "BUSY";
|
||||
mdm_responses[MDM_RESP_NO_ANSWER] = "NO ANSWER";
|
||||
mdm_responses[MDM_RESP_CONNECT_0600] = "CONNECT 0600";
|
||||
mdm_responses[MDM_RESP_CONNECT_2400] = "CONNECT 2400";
|
||||
mdm_responses[MDM_RESP_CONNECT_4800] = "CONNECT 4800";
|
||||
mdm_responses[MDM_RESP_CONNECT_9600] = "CONNECT 9600";
|
||||
mdm_responses[MDM_RESP_CONNECT_7200] = "CONNECT 7200";
|
||||
mdm_responses[MDM_RESP_CONNECT_12000] = "CONNECT 12000";
|
||||
mdm_responses[MDM_RESP_CONNECT_14400] = "CONNECT 14400";
|
||||
mdm_responses[MDM_RESP_CONNECT_19200] = "CONNECT 19200";
|
||||
mdm_responses[MDM_RESP_CONNECT_38400] = "CONNECT 38400";
|
||||
mdm_responses[MDM_RESP_CONNECT_57600] = "CONNECT 57600";
|
||||
mdm_responses[MDM_RESP_CONNECT_115200] = "CONNECT 115200";
|
||||
mdm_responses[MDM_RESP_CONNECT_234000] = "CONNECT 230400";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int get_connect_response(int speed, int level)
|
||||
{
|
||||
if (level == 0) {
|
||||
return MDM_RESP_CONNECT;
|
||||
}
|
||||
switch (speed) {
|
||||
case 115200:
|
||||
return MDM_RESP_CONNECT_115200;
|
||||
case 57600:
|
||||
return MDM_RESP_CONNECT_57600;
|
||||
case 38400:
|
||||
return MDM_RESP_CONNECT_38400;
|
||||
case 19200:
|
||||
return MDM_RESP_CONNECT_19200;
|
||||
case 9600:
|
||||
return MDM_RESP_CONNECT_9600;
|
||||
case 4800:
|
||||
return MDM_RESP_CONNECT_4800;
|
||||
case 2400:
|
||||
return MDM_RESP_CONNECT_2400;
|
||||
case 1200:
|
||||
return MDM_RESP_CONNECT_1200;
|
||||
case 600:
|
||||
return MDM_RESP_CONNECT_0600;
|
||||
}
|
||||
return MDM_RESP_CONNECT;
|
||||
}
|
||||
|
||||
void mdm_init_config(modem_config *cfg)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
cfg->send_responses = TRUE;
|
||||
cfg->connect_response = 0;
|
||||
cfg->response_code_level = 4;
|
||||
cfg->text_responses = TRUE;
|
||||
cfg->echo = TRUE;
|
||||
cfg->cmd_mode = TRUE;
|
||||
cfg->conn_type = MDM_CONN_NONE;
|
||||
cfg->off_hook = FALSE;
|
||||
cfg->line_ringing = FALSE;
|
||||
cfg->cur_line_idx = 0;
|
||||
|
||||
for (i = 0; i < 100; i++) {
|
||||
cfg->s[i] = 0;
|
||||
}
|
||||
cfg->s[2] = 43;
|
||||
cfg->s[3] = 13;
|
||||
cfg->s[4] = 10;
|
||||
cfg->s[5] = 8;
|
||||
cfg->s[6] = 2;
|
||||
cfg->s[7] = 50;
|
||||
cfg->s[8] = 2;
|
||||
cfg->s[9] = 6;
|
||||
cfg->s[10] = 14;
|
||||
cfg->s[11] = 95;
|
||||
cfg->s[12] = 50;
|
||||
|
||||
cfg->crlf[0] = cfg->s[3];
|
||||
cfg->crlf[1] = cfg->s[4];
|
||||
cfg->crlf[2] = 0;
|
||||
|
||||
cfg->dial_type = 0;
|
||||
cfg->last_dial_type = 0;
|
||||
cfg->disconnect_delay = 0;
|
||||
|
||||
cfg->pre_break_delay = FALSE;
|
||||
cfg->break_len = 0;
|
||||
|
||||
cfg->memory_dial = FALSE;
|
||||
cfg->dsr_active = FALSE;
|
||||
cfg->dsr_on = TRUE;
|
||||
cfg->dcd_on = FALSE;
|
||||
cfg->found_a = FALSE;
|
||||
cfg->cmd_started = FALSE;
|
||||
cfg->allow_transmit = TRUE;
|
||||
cfg->invert_dsr = FALSE;
|
||||
cfg->invert_dcd = FALSE;
|
||||
|
||||
cfg->config0[0] = '\0';
|
||||
cfg->config1[0] = '\0';
|
||||
|
||||
dce_init_config(cfg);
|
||||
sh_init_config(cfg);
|
||||
line_init_config(cfg);
|
||||
}
|
||||
|
||||
int get_new_cts_state(modem_config *cfg, int up)
|
||||
{
|
||||
return MDM_CL_CTS_HIGH;
|
||||
}
|
||||
|
||||
int get_new_dsr_state(modem_config *cfg, int up)
|
||||
{
|
||||
if (cfg->dsr_on == TRUE)
|
||||
return (cfg->invert_dsr == TRUE ? MDM_CL_DSR_LOW : MDM_CL_DSR_HIGH);
|
||||
if ((up == TRUE && cfg->invert_dsr == FALSE)
|
||||
|| (up == FALSE && cfg->invert_dsr == TRUE)
|
||||
)
|
||||
return MDM_CL_DSR_HIGH;
|
||||
else
|
||||
return MDM_CL_DSR_LOW;
|
||||
}
|
||||
|
||||
int get_new_dcd_state(modem_config *cfg, int up)
|
||||
{
|
||||
if (cfg->dcd_on == TRUE)
|
||||
return (cfg->invert_dcd == TRUE ? MDM_CL_DCD_LOW : MDM_CL_DCD_HIGH);
|
||||
if ((up == TRUE && cfg->invert_dcd == FALSE)
|
||||
|| (up == FALSE && cfg->invert_dcd == TRUE)
|
||||
)
|
||||
return MDM_CL_DCD_HIGH;
|
||||
else
|
||||
return MDM_CL_DCD_LOW;
|
||||
}
|
||||
|
||||
int mdm_set_control_lines(modem_config *cfg)
|
||||
{
|
||||
int state = 0;
|
||||
int up = (cfg->conn_type == MDM_CONN_NONE ? FALSE : TRUE);
|
||||
|
||||
state |= get_new_cts_state(cfg, up);
|
||||
state |= get_new_dsr_state(cfg, up);
|
||||
state |= get_new_dcd_state(cfg, up);
|
||||
|
||||
LOG(LOG_INFO, "Control Lines: DSR:%d DCD:%d CTS:%d",
|
||||
((state & MDM_CL_DSR_HIGH) != 0 ? 1 : 0),
|
||||
((state & MDM_CL_DCD_HIGH) != 0 ? 1 : 0), ((state & MDM_CL_CTS_HIGH) != 0 ? 1 : 0)
|
||||
);
|
||||
|
||||
dce_set_control_lines(cfg, state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mdm_write_char(modem_config *cfg, char data)
|
||||
{
|
||||
char str[2];
|
||||
|
||||
str[0] = data;
|
||||
mdm_write(cfg, str, 1);
|
||||
}
|
||||
|
||||
void mdm_write(modem_config *cfg, char data[], int len)
|
||||
{
|
||||
if (cfg->allow_transmit == TRUE) {
|
||||
dce_write(cfg, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void mdm_write_parity(modem_config *cfg, char *data, int len)
|
||||
{
|
||||
unsigned char *buf;
|
||||
int i;
|
||||
unsigned int v;
|
||||
|
||||
|
||||
if (cfg->allow_transmit == TRUE) {
|
||||
buf = malloc(len);
|
||||
memcpy(buf, data, len);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
v = buf[i];
|
||||
v = (((v * 0x0101010101010101ULL) & 0x8040201008040201ULL) % 0x1FF) & 1;
|
||||
buf[i] |= (((cfg->parity >> !v)) & 1) << 7;
|
||||
}
|
||||
|
||||
mdm_write(cfg, (char *) buf, len);
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void mdm_send_response(int msg, modem_config *cfg)
|
||||
{
|
||||
char msgID[17];
|
||||
|
||||
LOG(LOG_DEBUG, "Sending %s response to modem", mdm_responses[msg]);
|
||||
if (cfg->send_responses == TRUE) {
|
||||
mdm_write_parity(cfg, cfg->crlf, 2);
|
||||
if (cfg->text_responses == TRUE) {
|
||||
LOG(LOG_ALL, "Sending text response");
|
||||
mdm_write_parity(cfg, mdm_responses[msg], strlen(mdm_responses[msg]));
|
||||
}
|
||||
else {
|
||||
LOG(LOG_ALL, "Sending numeric response");
|
||||
sprintf(msgID, "%d", msg);
|
||||
mdm_write_parity(cfg, msgID, strlen(msgID));
|
||||
}
|
||||
mdm_write_parity(cfg, cfg->crlf, 2);
|
||||
}
|
||||
}
|
||||
|
||||
int mdm_off_hook(modem_config *cfg)
|
||||
{
|
||||
|
||||
LOG(LOG_INFO, "taking modem off hook");
|
||||
cfg->off_hook = TRUE;
|
||||
cfg->cmd_mode = FALSE;
|
||||
line_off_hook(cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdm_answer(modem_config *cfg)
|
||||
{
|
||||
if (cfg->line_ringing == TRUE) {
|
||||
cfg->conn_type = MDM_CONN_INCOMING;
|
||||
mdm_off_hook(cfg);
|
||||
mdm_set_control_lines(cfg);
|
||||
mdm_print_speed(cfg);
|
||||
}
|
||||
else if (cfg->conn_type == MDM_CONN_INCOMING) {
|
||||
// we are connected, just go off hook.
|
||||
mdm_off_hook(cfg);
|
||||
mdm_set_control_lines(cfg);
|
||||
}
|
||||
else {
|
||||
mdm_disconnect(cfg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdm_print_speed(modem_config *cfg)
|
||||
{
|
||||
int speed;
|
||||
|
||||
switch (cfg->connect_response) {
|
||||
case 2:
|
||||
speed = cfg->dte_speed;
|
||||
break;
|
||||
default:
|
||||
speed = cfg->dce_speed;
|
||||
break;
|
||||
|
||||
}
|
||||
mdm_send_response(get_connect_response(speed, cfg->response_code_level), cfg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdm_connect(modem_config *cfg)
|
||||
{
|
||||
mdm_off_hook(cfg);
|
||||
if (cfg->conn_type == MDM_CONN_NONE) {
|
||||
if (line_connect(cfg) == 0) {
|
||||
cfg->conn_type = MDM_CONN_OUTGOING;
|
||||
mdm_set_control_lines(cfg);
|
||||
mdm_print_speed(cfg);
|
||||
}
|
||||
else {
|
||||
cfg->conn_type = MDM_CONN_OUTGOING; // so disconnect will print NO CARRIER
|
||||
mdm_disconnect(cfg);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int mdm_listen(modem_config *cfg)
|
||||
{
|
||||
return line_listen(cfg);
|
||||
}
|
||||
|
||||
|
||||
int mdm_disconnect(modem_config *cfg)
|
||||
{
|
||||
int type;
|
||||
|
||||
LOG_ENTER();
|
||||
LOG(LOG_INFO, "Disconnecting modem");
|
||||
cfg->cmd_mode = TRUE;
|
||||
cfg->off_hook = FALSE;
|
||||
cfg->break_len = 0;
|
||||
cfg->line_ringing = FALSE;
|
||||
cfg->pre_break_delay = FALSE;
|
||||
if (0 == line_disconnect(cfg)) {
|
||||
type = cfg->conn_type;
|
||||
cfg->conn_type = MDM_CONN_NONE;
|
||||
mdm_set_control_lines(cfg);
|
||||
if (type != MDM_CONN_NONE) {
|
||||
mdm_send_response(MDM_RESP_NO_CARRIER, cfg);
|
||||
usleep(cfg->disconnect_delay * 1000);
|
||||
}
|
||||
cfg->rings = 0;
|
||||
mdm_listen(cfg);
|
||||
}
|
||||
else {
|
||||
// line still connected.
|
||||
}
|
||||
LOG_EXIT();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdm_parse_cmd(modem_config *cfg)
|
||||
{
|
||||
int done = FALSE;
|
||||
int index = 0;
|
||||
int num = 0;
|
||||
int start = 0;
|
||||
int end = 0;
|
||||
int cmd = AT_CMD_NONE;
|
||||
char *command = cfg->cur_line;
|
||||
char tmp[256];
|
||||
|
||||
LOG_ENTER();
|
||||
|
||||
LOG(LOG_DEBUG, "Evaluating AT%s", command);
|
||||
|
||||
while (TRUE != done) {
|
||||
if (cmd != AT_CMD_ERR) {
|
||||
cmd = getcmd(command, &index, &num, &start, &end);
|
||||
LOG(LOG_DEBUG, "Command: %c (%d), Flags: %d, index=%d, num=%d, data=%d-%d",
|
||||
(cmd > -1 ? cmd & 0xff : ' '), cmd, cmd >> 8, index, num, start, end);
|
||||
}
|
||||
switch (cmd) {
|
||||
case AT_CMD_ERR:
|
||||
mdm_send_response(MDM_RESP_ERROR, cfg);
|
||||
done = TRUE;
|
||||
break;
|
||||
case AT_CMD_END:
|
||||
if (cfg->cmd_mode == TRUE)
|
||||
mdm_send_response(MDM_RESP_OK, cfg);
|
||||
done = TRUE;
|
||||
break;
|
||||
case AT_CMD_NONE:
|
||||
done = TRUE;
|
||||
break;
|
||||
case 'O':
|
||||
case 'A':
|
||||
mdm_answer(cfg);
|
||||
cmd = AT_CMD_END;
|
||||
done = TRUE;
|
||||
break;
|
||||
case 'B': // 212A versus V.22 connection
|
||||
if (num > 1) {
|
||||
cmd = AT_CMD_ERR;
|
||||
}
|
||||
else {
|
||||
//cfg->connect_1200=num;
|
||||
}
|
||||
break;
|
||||
case 'D':
|
||||
if (end > start) {
|
||||
strncpy(cfg->dialno, command + start, end - start);
|
||||
cfg->dialno[end - start] = '\0';
|
||||
cfg->dial_type = (char) num;
|
||||
cfg->last_dial_type = (char) num;
|
||||
strncpy(cfg->last_dialno, command + start, end - start);
|
||||
cfg->last_dialno[end - start] = '\0';
|
||||
cfg->memory_dial = FALSE;
|
||||
}
|
||||
else if (num == 'L') {
|
||||
strncpy(cfg->dialno, cfg->last_dialno, strlen(cfg->last_dialno));
|
||||
cfg->dial_type = cfg->dial_type;
|
||||
cfg->memory_dial = TRUE;
|
||||
mdm_write(cfg, cfg->crlf, 2);
|
||||
mdm_write(cfg, cfg->dialno, strlen(cfg->dialno));
|
||||
}
|
||||
else {
|
||||
cfg->dialno[0] = 0;
|
||||
cfg->last_dialno[0] = 0;
|
||||
cfg->dial_type = 0;
|
||||
cfg->last_dial_type = 0;
|
||||
}
|
||||
if (strlen(cfg->dialno) > 0) {
|
||||
mdm_connect(cfg);
|
||||
}
|
||||
else {
|
||||
mdm_off_hook(cfg);
|
||||
}
|
||||
done = TRUE;
|
||||
break;
|
||||
case 'E': // still need to define #2
|
||||
if (num == 0)
|
||||
cfg->echo = FALSE;
|
||||
else if (num == 1)
|
||||
cfg->echo = TRUE;
|
||||
else {
|
||||
cmd = AT_CMD_ERR;
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
if (num == 0) {
|
||||
mdm_disconnect(cfg);
|
||||
}
|
||||
else if (num == 1) {
|
||||
mdm_answer(cfg);
|
||||
}
|
||||
else
|
||||
cmd = AT_CMD_ERR;
|
||||
break;
|
||||
case 'I': // Information.
|
||||
break;
|
||||
case 'L': // Speaker volume
|
||||
if (num < 1 || num > 3)
|
||||
cmd = AT_CMD_ERR;
|
||||
else {
|
||||
//cfg->volume=num;
|
||||
}
|
||||
break;
|
||||
case 'M': // speaker settings
|
||||
if (num > 3)
|
||||
cmd = AT_CMD_ERR;
|
||||
else {
|
||||
//cfg->speaker_setting=num;
|
||||
}
|
||||
break;
|
||||
case 'N': // automode negotiate
|
||||
if (num > 1)
|
||||
cmd = AT_CMD_ERR;
|
||||
else {
|
||||
//cfg->auto_mode=num;
|
||||
}
|
||||
break;
|
||||
case 'P': // defaut to pulse dialing
|
||||
//cfg->default_dial_type=MDM_DT_PULSE;
|
||||
break;
|
||||
case 'Q': // still need to define #2
|
||||
if (num == 0)
|
||||
cfg->send_responses = TRUE;
|
||||
else if (num == 1)
|
||||
cfg->send_responses = FALSE;
|
||||
else if (num == 2) // this should be yes orig/no answer.
|
||||
cfg->send_responses = TRUE;
|
||||
else {
|
||||
cmd = AT_CMD_ERR;
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
strncpy(tmp, command + start, end - start);
|
||||
tmp[end - start] = '\0';
|
||||
cfg->s[num] = atoi(tmp);
|
||||
switch (num) {
|
||||
case 3:
|
||||
cfg->crlf[0] = cfg->s[3];
|
||||
break;
|
||||
case 4:
|
||||
cfg->crlf[1] = cfg->s[4];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AT_CMD_FLAG_QUERY | 'S':
|
||||
sprintf(tmp, "%s%3.3d", cfg->crlf, cfg->s[num]);
|
||||
mdm_write(cfg, tmp, strlen(tmp));
|
||||
break;
|
||||
case 'T': // defaut to tone dialing
|
||||
//cfg->default_dial_type=MDM_DT_TONE;
|
||||
break;
|
||||
case 'V': // done
|
||||
if (num == 0)
|
||||
cfg->text_responses = FALSE;
|
||||
else if (num == 1)
|
||||
cfg->text_responses = TRUE;
|
||||
else {
|
||||
cmd = AT_CMD_ERR;
|
||||
}
|
||||
break;
|
||||
case 'W':
|
||||
if (num > -1 && num < 3)
|
||||
cfg->connect_response = num;
|
||||
else
|
||||
cmd = AT_CMD_ERR;
|
||||
break;
|
||||
case 'X':
|
||||
if (num > -1 && num < 5)
|
||||
cfg->response_code_level = num;
|
||||
else
|
||||
cmd = AT_CMD_ERR;
|
||||
break;
|
||||
case 'Y': // long space disconnect.
|
||||
if (num > 1)
|
||||
cmd = AT_CMD_ERR;
|
||||
else {
|
||||
//cfg->long_disconnect=num;
|
||||
}
|
||||
break;
|
||||
case 'Z': // long space disconnect.
|
||||
if (num > 1)
|
||||
cmd = AT_CMD_ERR;
|
||||
else {
|
||||
// set config0 to cur_line and go.
|
||||
}
|
||||
break;
|
||||
case AT_CMD_FLAG_EXT + 'C':
|
||||
switch (num) {
|
||||
case 0:
|
||||
cfg->dcd_on = TRUE;
|
||||
mdm_set_control_lines(cfg);
|
||||
break;
|
||||
case 1:
|
||||
cfg->dcd_on = FALSE;
|
||||
mdm_set_control_lines(cfg);
|
||||
break;
|
||||
default:
|
||||
cmd = AT_CMD_ERR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case AT_CMD_FLAG_EXT + 'K':
|
||||
// flow control.
|
||||
switch (num) {
|
||||
case 0:
|
||||
dce_set_flow_control(cfg, 0);
|
||||
break;
|
||||
case 3:
|
||||
dce_set_flow_control(cfg, MDM_FC_RTS);
|
||||
break;
|
||||
case 4:
|
||||
dce_set_flow_control(cfg, MDM_FC_XON);
|
||||
break;
|
||||
case 5:
|
||||
dce_set_flow_control(cfg, MDM_FC_XON);
|
||||
// need to add passthrough.. Not sure how.
|
||||
break;
|
||||
case 6:
|
||||
dce_set_flow_control(cfg, MDM_FC_XON | MDM_FC_RTS);
|
||||
break;
|
||||
default:
|
||||
cmd = AT_CMD_ERR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
cfg->cur_line_idx = 0;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
int mdm_handle_char(modem_config *cfg, char ch)
|
||||
{
|
||||
int parbit = ch & 0x80;
|
||||
|
||||
|
||||
ch &= 0x7f; // ignore parity
|
||||
|
||||
if (cfg->echo == TRUE)
|
||||
mdm_write_char(cfg, (ch | parbit));
|
||||
if (cfg->cmd_started == TRUE) {
|
||||
if (ch == (char) (cfg->s[5])) {
|
||||
if (cfg->cur_line_idx == 0 && cfg->echo == TRUE) {
|
||||
mdm_write_char(cfg, 'T');
|
||||
}
|
||||
else {
|
||||
cfg->cur_line_idx--;
|
||||
}
|
||||
}
|
||||
else if (ch == (char) (cfg->s[3])) {
|
||||
// we have a line, process.
|
||||
cfg->cur_line[cfg->cur_line_idx] = 0;
|
||||
strncpy(cfg->last_cmd, cfg->cur_line, sizeof(cfg->last_cmd) - 1);
|
||||
mdm_parse_cmd(cfg);
|
||||
cfg->found_a = FALSE;
|
||||
cfg->cmd_started = FALSE;
|
||||
}
|
||||
else {
|
||||
cfg->cur_line[cfg->cur_line_idx++ % sizeof(cfg->cur_line)] = ch;
|
||||
}
|
||||
}
|
||||
else if (cfg->found_a == TRUE) {
|
||||
if (ch == 't' || ch == 'T') {
|
||||
cfg->cmd_started = TRUE;
|
||||
LOG(LOG_ALL, "'T' parsed in serial stream, switching to command parse mode");
|
||||
cfg->parity |= parbit >> 6;
|
||||
}
|
||||
else if (ch == '/') {
|
||||
LOG(LOG_ALL, "'/' parsed in the serial stream, replaying last command");
|
||||
cfg->cur_line_idx = strlen(cfg->last_cmd);
|
||||
strncpy(cfg->cur_line, cfg->last_cmd, cfg->cur_line_idx);
|
||||
mdm_parse_cmd(cfg);
|
||||
cfg->cmd_started = FALSE;
|
||||
cfg->parity |= parbit >> 6;
|
||||
}
|
||||
else if (ch != 'a' && ch != 'A') {
|
||||
cfg->found_a = FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
else if (ch == 'a' || ch == 'A') {
|
||||
LOG(LOG_ALL, "'A' parsed in serial stream");
|
||||
cfg->found_a = TRUE;
|
||||
cfg->parity = parbit >> 7;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdm_clear_break(modem_config *cfg)
|
||||
{
|
||||
cfg->break_len = 0;
|
||||
cfg->pre_break_delay = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdm_parse_data(modem_config *cfg, char *data, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cfg->cmd_mode == TRUE) {
|
||||
for (i = 0; i < len; i++) {
|
||||
mdm_handle_char(cfg, data[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
line_write(cfg, data, len);
|
||||
if (cfg->pre_break_delay == TRUE) {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (data[i] == (char) cfg->s[2]) {
|
||||
LOG(LOG_DEBUG, "Break character received");
|
||||
cfg->break_len++;
|
||||
if (cfg->break_len > 3) { // more than 3, considered invalid
|
||||
cfg->pre_break_delay = FALSE;
|
||||
cfg->break_len = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG(LOG_ALL, "Found non-break character, cancelling break");
|
||||
// chars past +++
|
||||
mdm_clear_break(cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdm_handle_timeout(modem_config *cfg)
|
||||
{
|
||||
if (cfg->pre_break_delay == TRUE && cfg->break_len == 3) {
|
||||
// pre and post break.
|
||||
LOG(LOG_INFO, "Break condition detected");
|
||||
cfg->cmd_mode = TRUE;
|
||||
mdm_send_response(MDM_RESP_OK, cfg);
|
||||
mdm_clear_break(cfg);
|
||||
}
|
||||
else if (cfg->pre_break_delay == FALSE) {
|
||||
// pre break wait over.
|
||||
LOG(LOG_DEBUG, "Initial Break Delay detected");
|
||||
cfg->pre_break_delay = TRUE;
|
||||
}
|
||||
else if (cfg->pre_break_delay == TRUE && cfg->break_len > 0) {
|
||||
LOG(LOG_ALL, "Inter-break-char delay time exceeded");
|
||||
mdm_clear_break(cfg);
|
||||
}
|
||||
else if (cfg->s[30] != 0) {
|
||||
// timeout...
|
||||
LOG(LOG_INFO, "DTE communication inactivity timeout");
|
||||
mdm_disconnect(cfg);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mdm_send_ring(modem_config *cfg)
|
||||
{
|
||||
LOG(LOG_DEBUG, "Sending 'RING' to modem");
|
||||
cfg->line_ringing = TRUE;
|
||||
mdm_send_response(MDM_RESP_RING, cfg);
|
||||
cfg->rings++;
|
||||
LOG(LOG_ALL, "Sent #%d ring", cfg->rings);
|
||||
if (cfg->cmd_mode == FALSE || (cfg->s[0] != 0 && cfg->rings >= cfg->s[0])) {
|
||||
mdm_answer(cfg);
|
||||
}
|
||||
return 0;
|
||||
}
|
163
src/modem_core.h
Normal file
163
src/modem_core.h
Normal file
@ -0,0 +1,163 @@
|
||||
#ifndef MODEM_CORE_H
|
||||
#define MODEM_CORE_H 1
|
||||
|
||||
#define MDM_RESP_OK 0
|
||||
#define MDM_RESP_CONNECT 1
|
||||
#define MDM_RESP_RING 2
|
||||
#define MDM_RESP_NO_CARRIER 3
|
||||
#define MDM_RESP_ERROR 4
|
||||
#define MDM_RESP_CONNECT_1200 5
|
||||
#define MDM_RESP_NO_DIALTONE 6
|
||||
#define MDM_RESP_BUSY 7
|
||||
#define MDM_RESP_NO_ANSWER 8
|
||||
#define MDM_RESP_CONNECT_0600 9
|
||||
#define MDM_RESP_CONNECT_2400 10
|
||||
#define MDM_RESP_CONNECT_4800 11
|
||||
#define MDM_RESP_CONNECT_9600 12
|
||||
#define MDM_RESP_CONNECT_7200 13
|
||||
#define MDM_RESP_CONNECT_12000 14
|
||||
#define MDM_RESP_CONNECT_14400 15
|
||||
#define MDM_RESP_CONNECT_19200 16
|
||||
#define MDM_RESP_CONNECT_38400 17
|
||||
#define MDM_RESP_CONNECT_57600 18
|
||||
#define MDM_RESP_CONNECT_115200 19
|
||||
#define MDM_RESP_CONNECT_234000 20
|
||||
|
||||
#define MDM_CL_DSR_LOW 0
|
||||
#define MDM_CL_DSR_HIGH 1
|
||||
#define MDM_CL_DCD_LOW 0
|
||||
#define MDM_CL_DCD_HIGH 2
|
||||
#define MDM_CL_CTS_LOW 0
|
||||
#define MDM_CL_CTS_HIGH 4
|
||||
#define MDM_CL_DTR_LOW 0
|
||||
#define MDM_CL_DTR_HIGH 8
|
||||
#define MDM_FC_RTS 1
|
||||
#define MDM_FC_XON 2
|
||||
|
||||
#define MDM_CONN_NONE 0
|
||||
#define MDM_CONN_OUTGOING 1
|
||||
#define MDM_CONN_INCOMING 2
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
#include "nvt.h"
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct line_config {
|
||||
int valid_conn;
|
||||
int fd;
|
||||
int sfd;
|
||||
int is_telnet;
|
||||
int first_char;
|
||||
nvt_vars nvt_data;
|
||||
} line_config;
|
||||
|
||||
typedef struct x_config {
|
||||
int mp[2][2];
|
||||
int cp[2][2];
|
||||
int wp[2][2];
|
||||
char no_answer[256];
|
||||
char local_connect[256];
|
||||
char remote_connect[256];
|
||||
char local_answer[256];
|
||||
char remote_answer[256];
|
||||
char inactive[256];
|
||||
unsigned int direct_conn;
|
||||
char direct_conn_num[256];
|
||||
} x_config;
|
||||
|
||||
typedef struct dce_config {
|
||||
int is_ip232;
|
||||
char tty[256];
|
||||
int first_char;
|
||||
int fd;
|
||||
int dp[2][2];
|
||||
int sSocket;
|
||||
int ip232_is_connected;
|
||||
int ip232_dtr;
|
||||
int ip232_dcd;
|
||||
int ip232_iac;
|
||||
} dce_config;
|
||||
|
||||
typedef struct modem_config {
|
||||
// master configuration information
|
||||
|
||||
// need to eventually change these
|
||||
dce_config dce_data;
|
||||
line_config line_data;
|
||||
x_config data;
|
||||
char config0[1024];
|
||||
char config1[1024];
|
||||
int dce_speed;
|
||||
int dte_speed;
|
||||
int conn_type;
|
||||
int line_ringing;
|
||||
int off_hook;
|
||||
int dsr_active;
|
||||
int dsr_on;
|
||||
int dcd_on;
|
||||
int invert_dsr;
|
||||
int invert_dcd;
|
||||
int allow_transmit;
|
||||
int rings;
|
||||
// command information
|
||||
int pre_break_delay;
|
||||
int found_a;
|
||||
int cmd_started;
|
||||
int cmd_mode;
|
||||
char last_cmd[1024];
|
||||
char cur_line[1024];
|
||||
int cur_line_idx;
|
||||
// dailing information
|
||||
char dialno[256];
|
||||
char last_dialno[256];
|
||||
char dial_type;
|
||||
char last_dial_type;
|
||||
int memory_dial;
|
||||
// modem config
|
||||
int connect_response;
|
||||
int response_code_level;
|
||||
int send_responses;
|
||||
int text_responses;
|
||||
int echo;
|
||||
int s[100];
|
||||
int break_len;
|
||||
int disconnect_delay;
|
||||
char crlf[3];
|
||||
int parity;
|
||||
} modem_config;
|
||||
|
||||
int mdm_init(void);
|
||||
void mdm_init_config(modem_config *cfg);
|
||||
int get_new_cts_state(modem_config *cfg, int up);
|
||||
int get_new_dsr_state(modem_config *cfg, int up);
|
||||
int get_new_dcd_state(modem_config *cfg, int up);
|
||||
int mdm_set_control_lines(modem_config *cfg);
|
||||
void mdm_write_char(modem_config *cfg, char data);
|
||||
void mdm_write(modem_config *cfg, char data[], int len);
|
||||
void mdm_send_response(int msg, modem_config *cfg);
|
||||
int mdm_off_hook(modem_config *cfg);
|
||||
int mdm_answer(modem_config *cfg);
|
||||
int mdm_print_speed(modem_config *cfg);
|
||||
int mdm_connect(modem_config *cfg);
|
||||
int mdm_listen(modem_config *cfg);
|
||||
int mdm_disconnect(modem_config *cfg);
|
||||
int mdm_parse_cmd(modem_config *cfg);
|
||||
int mdm_handle_char(modem_config *cfg, char ch);
|
||||
int mdm_clear_break(modem_config *cfg);
|
||||
int mdm_parse_data(modem_config *cfg, char *data, int len);
|
||||
int mdm_handle_timeout(modem_config *cfg);
|
||||
int mdm_send_ring(modem_config *cfg);
|
||||
|
||||
#include "line.h"
|
||||
#include "shared.h"
|
||||
#include "dce.h"
|
||||
|
||||
#endif
|
165
src/nvt.c
Normal file
165
src/nvt.c
Normal file
@ -0,0 +1,165 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "ip.h"
|
||||
#include "nvt.h"
|
||||
|
||||
int nvt_init_config(nvt_vars *vars)
|
||||
{
|
||||
int i;
|
||||
|
||||
vars->binary_xmit = FALSE;
|
||||
vars->binary_recv = FALSE;
|
||||
for (i = 0; i < 256; i++)
|
||||
vars->term[i] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char get_nvt_cmd_response(char action, char type)
|
||||
{
|
||||
char rc = 0;
|
||||
|
||||
if (type == TRUE) {
|
||||
switch (action) {
|
||||
case NVT_DO:
|
||||
rc = NVT_WILL_RESP;
|
||||
break;
|
||||
case NVT_DONT:
|
||||
rc = NVT_WONT_RESP;
|
||||
break;
|
||||
case NVT_WILL:
|
||||
rc = NVT_DO_RESP;
|
||||
break;
|
||||
case NVT_WONT:
|
||||
rc = NVT_DONT_RESP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (action) {
|
||||
case NVT_DO:
|
||||
case NVT_DONT:
|
||||
rc = NVT_WONT_RESP;
|
||||
break;
|
||||
case NVT_WILL:
|
||||
case NVT_WONT:
|
||||
rc = NVT_DONT_RESP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int parse_nvt_subcommand(int fd, nvt_vars *vars, char *data, int len)
|
||||
{
|
||||
// overflow issue, again...
|
||||
int opt = data[2];
|
||||
char resp[100];
|
||||
char *response = resp + 3;
|
||||
int resp_len = 0;
|
||||
int response_len = 0;
|
||||
char tty_type[] = "VT100";
|
||||
int rc;
|
||||
int slen = 0;
|
||||
|
||||
for (rc = 2; rc < len - 1; rc++) {
|
||||
if (NVT_IAC == data[rc])
|
||||
if (NVT_SE == data[rc + 1]) {
|
||||
rc += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (rc > 5 && (NVT_SB_SEND == data[4])) {
|
||||
switch (opt) {
|
||||
case NVT_OPT_TERMINAL_TYPE:
|
||||
case NVT_OPT_X_DISPLAY_LOCATION: // should not have to have these
|
||||
case NVT_OPT_ENVIRON: // but telnet seems to expect.
|
||||
case NVT_OPT_NEW_ENVIRON: // them.
|
||||
case NVT_OPT_TERMINAL_SPEED:
|
||||
response[response_len++] = NVT_SB_IS;
|
||||
switch (opt) {
|
||||
case NVT_OPT_TERMINAL_TYPE:
|
||||
slen = strlen(tty_type);
|
||||
strncpy(response + response_len, tty_type, slen);
|
||||
response_len += slen;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (response_len) {
|
||||
resp[resp_len++] = NVT_IAC;
|
||||
resp[resp_len++] = NVT_SB;
|
||||
resp[resp_len++] = opt;
|
||||
resp_len += response_len;
|
||||
resp[resp_len++] = NVT_IAC;
|
||||
resp[resp_len++] = NVT_SE;
|
||||
ip_write(fd, resp, resp_len);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int send_nvt_command(int fd, nvt_vars *vars, char action, int opt)
|
||||
{
|
||||
char cmd[3];
|
||||
|
||||
cmd[0] = NVT_IAC;
|
||||
cmd[1] = action;
|
||||
cmd[2] = opt;
|
||||
|
||||
ip_write(fd, cmd, 3);
|
||||
vars->term[opt] = action;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int parse_nvt_command(int fd, nvt_vars *vars, char action, int opt)
|
||||
{
|
||||
char resp[3];
|
||||
|
||||
resp[0] = NVT_IAC;
|
||||
resp[2] = opt;
|
||||
|
||||
switch (opt) {
|
||||
case NVT_OPT_TRANSMIT_BINARY:
|
||||
switch (action) {
|
||||
case NVT_DO:
|
||||
LOG(LOG_INFO, "Enabling telnet binary xmit");
|
||||
vars->binary_xmit = TRUE;
|
||||
break;
|
||||
case NVT_DONT:
|
||||
LOG(LOG_INFO, "Disabling telnet binary xmit");
|
||||
vars->binary_xmit = FALSE;
|
||||
break;
|
||||
case NVT_WILL:
|
||||
LOG(LOG_INFO, "Enabling telnet binary recv");
|
||||
vars->binary_recv = TRUE;
|
||||
break;
|
||||
case NVT_WONT:
|
||||
LOG(LOG_INFO, "Disabling telnet binary recv");
|
||||
vars->binary_recv = FALSE;
|
||||
break;
|
||||
}
|
||||
// fall through to get response
|
||||
|
||||
case NVT_OPT_NAWS:
|
||||
case NVT_OPT_TERMINAL_TYPE:
|
||||
case NVT_OPT_SUPPRESS_GO_AHEAD:
|
||||
case NVT_OPT_ECHO:
|
||||
case NVT_OPT_X_DISPLAY_LOCATION: // should not have to have these
|
||||
case NVT_OPT_ENVIRON: // but telnet seems to expect.
|
||||
case NVT_OPT_NEW_ENVIRON: // them.
|
||||
case NVT_OPT_TERMINAL_SPEED:
|
||||
resp[1] = get_nvt_cmd_response(action, TRUE);
|
||||
break;
|
||||
default:
|
||||
resp[1] = get_nvt_cmd_response(action, FALSE);
|
||||
break;
|
||||
}
|
||||
ip_write(fd, resp, 3);
|
||||
return 0;
|
||||
}
|
54
src/nvt.h
Normal file
54
src/nvt.h
Normal file
@ -0,0 +1,54 @@
|
||||
#ifndef NVT_H
|
||||
#define NVT_H 1
|
||||
|
||||
#define NVT_SE 240
|
||||
#define NVT_NOP 241
|
||||
#define NVT_DM 242
|
||||
#define NVT_SB 250
|
||||
#define NVT_WILL 251
|
||||
#define NVT_WONT 252
|
||||
#define NVT_DO 253
|
||||
#define NVT_DONT 254
|
||||
#define NVT_IAC 255
|
||||
#define NVT_WILL_RESP 251
|
||||
#define NVT_WONT_RESP 252
|
||||
#define NVT_DO_RESP 253
|
||||
#define NVT_DONT_RESP 254
|
||||
|
||||
#define NVT_OPT_TRANSMIT_BINARY 0
|
||||
#define NVT_OPT_ECHO 1
|
||||
#define NVT_OPT_SUPPRESS_GO_AHEAD 3
|
||||
#define NVT_OPT_STATUS 5
|
||||
#define NVT_OPT_RCTE 7
|
||||
#define NVT_OPT_TIMING_MARK 6
|
||||
#define NVT_OPT_NAOCRD 10
|
||||
#define NVT_OPT_TERMINAL_TYPE 24
|
||||
#define NVT_OPT_NAWS 31
|
||||
#define NVT_OPT_TERMINAL_SPEED 32
|
||||
#define NVT_OPT_LINEMODE 34
|
||||
#define NVT_OPT_X_DISPLAY_LOCATION 35
|
||||
#define NVT_OPT_ENVIRON 36
|
||||
#define NVT_OPT_NEW_ENVIRON 39
|
||||
|
||||
#define NVT_SB_IS 0
|
||||
#define NVT_SB_SEND 1
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
typedef struct nvt_vars {
|
||||
int binary_xmit;
|
||||
int binary_recv;
|
||||
char term[256];
|
||||
} nvt_vars;
|
||||
|
||||
|
||||
char get_nvt_cmd_response(char action, char type);
|
||||
int parse_nvt_subcommand(int fd, nvt_vars *vars, char *data, int len);
|
||||
int parse_nvt_command(int fd, nvt_vars *vars, char action, int opt);
|
||||
int nvt_init_config(nvt_vars *vars);
|
||||
int send_nvt_command(int fd, nvt_vars *vars, char action, int opt);
|
||||
|
||||
#endif
|
46
src/phone_book.c
Normal file
46
src/phone_book.c
Normal file
@ -0,0 +1,46 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "phone_book.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define PBSIZE 100
|
||||
|
||||
char phone_book[PBSIZE][2][128];
|
||||
int size = 0;
|
||||
|
||||
int pb_init()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pb_add(char *from, char *to)
|
||||
{
|
||||
LOG_ENTER();
|
||||
if (size < PBSIZE && from != NULL && to != NULL && strlen(from) > 0 && strlen(to) > 0) {
|
||||
// should really trim spaces.
|
||||
strncpy(phone_book[size][0], from, sizeof(phone_book[size][0]));
|
||||
strncpy(phone_book[size][1], to, sizeof(phone_book[size][1]));
|
||||
size++;
|
||||
LOG_EXIT();
|
||||
return 0;
|
||||
}
|
||||
LOG_EXIT();
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *pb_search(char *number)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
LOG_ENTER();
|
||||
for (i = 0; i < size; i++) {
|
||||
if (strcmp(phone_book[i][0], number) == 0) {
|
||||
|
||||
LOG(LOG_INFO, "Found a match for '%s': '%s'", number, phone_book[i][1]);
|
||||
strcpy(number, phone_book[i][1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
LOG_EXIT();
|
||||
return number;
|
||||
}
|
3
src/phone_book.h
Normal file
3
src/phone_book.h
Normal file
@ -0,0 +1,3 @@
|
||||
int pb_init(void);
|
||||
int pb_add(char *from, char *to);
|
||||
char *pb_search(char *number);
|
188
src/serial.c
Normal file
188
src/serial.c
Normal file
@ -0,0 +1,188 @@
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "debug.h"
|
||||
#include "serial.h"
|
||||
|
||||
int ser_get_bps_const(int speed)
|
||||
{
|
||||
int bps_rate = 0;
|
||||
|
||||
LOG_ENTER();
|
||||
|
||||
LOG(LOG_DEBUG, "Checking speed: %d", speed);
|
||||
|
||||
switch (speed) {
|
||||
case 115200:
|
||||
bps_rate = B115200;
|
||||
break;
|
||||
case 57600:
|
||||
bps_rate = B57600;
|
||||
break;
|
||||
case 38400:
|
||||
bps_rate = B38400;
|
||||
break;
|
||||
case 19200:
|
||||
bps_rate = B19200;
|
||||
break;
|
||||
case 9600:
|
||||
bps_rate = B9600;
|
||||
break;
|
||||
case 4800:
|
||||
bps_rate = B4800;
|
||||
break;
|
||||
case 2400:
|
||||
bps_rate = B2400;
|
||||
break;
|
||||
case 1200:
|
||||
bps_rate = B1200;
|
||||
break;
|
||||
case 600:
|
||||
bps_rate = B600;
|
||||
break;
|
||||
case 300:
|
||||
bps_rate = B300;
|
||||
break;
|
||||
case 150:
|
||||
bps_rate = B150;
|
||||
break;
|
||||
case 134:
|
||||
bps_rate = B134;
|
||||
break;
|
||||
case 110:
|
||||
bps_rate = B110;
|
||||
break;
|
||||
case 75:
|
||||
bps_rate = B75;
|
||||
break;
|
||||
case 50:
|
||||
bps_rate = B50;
|
||||
break;
|
||||
case 0:
|
||||
bps_rate = B0;
|
||||
break;
|
||||
default:
|
||||
ELOG(LOG_FATAL, "Unknown baud rate");
|
||||
bps_rate = -1;
|
||||
}
|
||||
LOG_EXIT();
|
||||
return bps_rate;
|
||||
|
||||
}
|
||||
|
||||
int ser_init_conn(char *tty, int speed)
|
||||
{
|
||||
int fd = -1;
|
||||
struct termios tio;
|
||||
int bps_rate = 0;
|
||||
|
||||
LOG_ENTER();
|
||||
|
||||
bps_rate = ser_get_bps_const(speed);
|
||||
|
||||
if (bps_rate > -1) {
|
||||
/* open the device to be non-blocking (read will return immediatly) */
|
||||
LOG(LOG_INFO, "Opening serial device");
|
||||
|
||||
fd = open(tty, O_RDWR | O_NOCTTY);
|
||||
|
||||
if (fd < 0) {
|
||||
ELOG(LOG_FATAL, "TTY %s could not be opened", tty);
|
||||
}
|
||||
else {
|
||||
LOG(LOG_INFO, "Opened serial device %s at speed %d as fd %d", tty, speed, fd);
|
||||
|
||||
/* Make the file descriptor asynchronous (the manual page says only
|
||||
O_APPEND and O_NONBLOCK, will work with F_SETFL...) */
|
||||
fcntl(fd, F_SETFL, FASYNC);
|
||||
|
||||
tio.c_cflag = bps_rate | CS8 | CLOCAL | CREAD | CRTSCTS;
|
||||
|
||||
tio.c_iflag = (IGNBRK);
|
||||
tio.c_oflag = 0;
|
||||
tio.c_lflag = 0;
|
||||
tio.c_cc[VMIN] = 1;
|
||||
tio.c_cc[VTIME] = 0;
|
||||
|
||||
tcflush(fd, TCIFLUSH);
|
||||
cfsetispeed(&tio, bps_rate);
|
||||
cfsetospeed(&tio, bps_rate);
|
||||
tcsetattr(fd, TCSANOW, &tio);
|
||||
LOG(LOG_INFO, "serial device configured");
|
||||
}
|
||||
}
|
||||
|
||||
LOG_EXIT();
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
int ser_set_flow_control(int fd, int status)
|
||||
{
|
||||
struct termios tio;
|
||||
|
||||
if (0 != tcgetattr(fd, &tio)) {
|
||||
ELOG(LOG_FATAL, "Could not get serial port attributes");
|
||||
return -1;
|
||||
}
|
||||
// turn all off.
|
||||
tio.c_cflag &= ~(IXON | IXOFF | CRTSCTS);
|
||||
tio.c_cflag |= status;
|
||||
if (0 != tcsetattr(fd, TCSANOW, &tio)) {
|
||||
ELOG(LOG_FATAL, "Could not set serial port attributes");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int ser_get_control_lines(int fd)
|
||||
{
|
||||
int status;
|
||||
|
||||
|
||||
if (0 > ioctl(fd, TIOCMGET, &status)) {
|
||||
ELOG(LOG_FATAL, "Could not obtain serial port status");
|
||||
return -1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
int ser_set_control_lines(int fd, int state)
|
||||
{
|
||||
unsigned int status;
|
||||
|
||||
|
||||
if (0 > (status = ser_get_control_lines(fd))) {
|
||||
return status;
|
||||
}
|
||||
status &= ~(TIOCM_RTS | TIOCM_DTR);
|
||||
status |= state;
|
||||
if (0 > ioctl(fd, TIOCMSET, &status)) {
|
||||
#ifndef WIN32
|
||||
ELOG(LOG_FATAL, "Could not set serial port status");
|
||||
return -1;
|
||||
#else
|
||||
ELOG(LOG_WARN, "Could not set serial port status, CYGWIN bug?");
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ser_write(int fd, char *data, int len)
|
||||
{
|
||||
log_trace(TRACE_MODEM_OUT, data, len);
|
||||
return write(fd, data, len);
|
||||
}
|
||||
|
||||
int ser_read(int fd, char *data, int len)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = read(fd, data, len);
|
||||
log_trace(TRACE_MODEM_IN, data, res);
|
||||
return res;
|
||||
}
|
17
src/serial.h
Normal file
17
src/serial.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef SERIAL_H
|
||||
#define SERIAL_H 1
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
int ser_get_bps_const(int speed);
|
||||
int ser_init_conn(char *tty, int speed);
|
||||
int ser_set_flow_control(int fd, int status);
|
||||
int ser_get_control_lines(int fd);
|
||||
int ser_set_control_lines(int fd, int state);
|
||||
int ser_write(int fd, char *data, int len);
|
||||
int ser_read(int fd, char *data, int len);
|
||||
|
||||
#endif
|
14
src/shared.c
Normal file
14
src/shared.c
Normal file
@ -0,0 +1,14 @@
|
||||
#include "modem_core.h"
|
||||
|
||||
int sh_init_config(modem_config *cfg)
|
||||
{
|
||||
cfg->data.local_connect[0] = 0;
|
||||
cfg->data.remote_connect[0] = 0;
|
||||
cfg->data.local_answer[0] = 0;
|
||||
cfg->data.remote_answer[0] = 0;
|
||||
cfg->data.no_answer[0] = 0;
|
||||
cfg->data.inactive[0] = 0;
|
||||
cfg->data.direct_conn = FALSE;
|
||||
cfg->data.direct_conn_num[0] = 0;
|
||||
return 0;
|
||||
}
|
6
src/shared.h
Normal file
6
src/shared.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef SHARED_H
|
||||
#define SHARED_H 1
|
||||
|
||||
int sh_init_config(modem_config *cfg);
|
||||
|
||||
#endif
|
146
src/tcpser.c
Normal file
146
src/tcpser.c
Normal file
@ -0,0 +1,146 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "init.h"
|
||||
#include "ip.h"
|
||||
#include "modem_core.h"
|
||||
#include "bridge.h"
|
||||
#include "phone_book.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
|
||||
const char MDM_BUSY[] = "BUSY\n";
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
modem_config cfg[64];
|
||||
int modem_count;
|
||||
int port = 0;
|
||||
|
||||
char all_busy[255];
|
||||
|
||||
pthread_t thread_id;
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
int sSocket = 0;
|
||||
fd_set readfs;
|
||||
int max_fd = 0;
|
||||
int accept_pending = FALSE;
|
||||
|
||||
int res = 0;
|
||||
char buf[255];
|
||||
|
||||
int cSocket;
|
||||
|
||||
log_init();
|
||||
|
||||
LOG_ENTER();
|
||||
|
||||
log_set_level(LOG_FATAL);
|
||||
|
||||
mdm_init();
|
||||
|
||||
pb_init();
|
||||
|
||||
signal(SIGIO, SIG_IGN); /* Some Linux variant term on SIGIO by default */
|
||||
|
||||
modem_count = init(argc, argv, cfg, 64, &port, all_busy, sizeof(all_busy));
|
||||
|
||||
sSocket = ip_init_server_conn(port);
|
||||
|
||||
for (i = 0; i < modem_count; i++) {
|
||||
if (-1 == pipe(cfg[i].data.mp[0])) {
|
||||
ELOG(LOG_FATAL, "Bridge task incoming IPC pipe could not be created");
|
||||
exit(-1);
|
||||
}
|
||||
if (-1 == pipe(cfg[i].data.mp[1])) {
|
||||
ELOG(LOG_FATAL, "Bridge task outgoing IPC pipe could not be created");
|
||||
exit(-1);
|
||||
}
|
||||
if (dce_init_conn(&cfg[i]) < 0) {
|
||||
LOG(LOG_FATAL, "Could not open serial port %s", cfg->dce_data.tty);
|
||||
exit(-1);
|
||||
}
|
||||
cfg[i].line_data.sfd = sSocket;
|
||||
|
||||
rc = pthread_create(&thread_id, NULL, *run_bridge, (void *) &cfg[i]);
|
||||
if (rc < 0) {
|
||||
ELOG(LOG_FATAL, "IP thread could not be started");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
}
|
||||
for (;;) {
|
||||
FD_ZERO(&readfs);
|
||||
max_fd = 0;
|
||||
for (i = 0; i < modem_count; i++) {
|
||||
FD_SET(cfg[i].data.mp[0][0], &readfs);
|
||||
max_fd = MAX(max_fd, cfg[i].data.mp[0][0]);
|
||||
}
|
||||
if (accept_pending == FALSE) {
|
||||
max_fd = MAX(max_fd, sSocket);
|
||||
FD_SET(sSocket, &readfs);
|
||||
}
|
||||
LOG(LOG_ALL, "Waiting for incoming connections and/or indicators");
|
||||
select(max_fd + 1, &readfs, NULL, NULL, NULL);
|
||||
for (i = 0; i < modem_count; i++) {
|
||||
if (FD_ISSET(cfg[i].data.mp[0][0], &readfs)) { // child pipe
|
||||
res = read(cfg[i].data.mp[0][0], buf, sizeof(buf) - 1);
|
||||
if (res > -1) {
|
||||
buf[res] = 0;
|
||||
LOG(LOG_DEBUG, "modem core #%d sent response '%c'", i, buf[0]);
|
||||
accept_pending = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(sSocket, &readfs)) { // IP traffic
|
||||
if (!accept_pending) {
|
||||
LOG(LOG_DEBUG, "Incoming connection pending");
|
||||
// first try for a modem that is listening.
|
||||
for (i = 0; i < modem_count; i++) {
|
||||
if (cfg[i].s[0] != 0 && cfg[i].off_hook == FALSE) {
|
||||
// send signal to pipe saying pick up...
|
||||
LOG(LOG_DEBUG, "Sending incoming connection to listening modem #%d", i);
|
||||
writePipe(cfg[i].data.mp[1][1], MSG_ACCEPT);
|
||||
accept_pending = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// now, send to any non-active modem.
|
||||
for (i = 0; i < modem_count; i++) {
|
||||
if (cfg[i].off_hook == FALSE) {
|
||||
// send signal to pipe saying pick up...
|
||||
LOG(LOG_DEBUG, "Sending incoming connection to non-connected modem #%d", i);
|
||||
writePipe(cfg[i].data.mp[1][1], MSG_ACCEPT);
|
||||
accept_pending = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == modem_count) {
|
||||
LOG(LOG_DEBUG, "No open modem to send to, send notice and close");
|
||||
// no connections.., accept and print error
|
||||
cSocket = ip_accept(sSocket);
|
||||
if (cSocket > -1) {
|
||||
if (strlen(all_busy) < 1) {
|
||||
ip_write(cSocket, (char *) MDM_BUSY, strlen(MDM_BUSY));
|
||||
}
|
||||
else {
|
||||
writeFile(all_busy, cSocket);
|
||||
}
|
||||
close(cSocket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_EXIT();
|
||||
return rc;
|
||||
}
|
35
src/util.c
Normal file
35
src/util.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
int writePipe(int fd, char msg)
|
||||
{
|
||||
char tmp[3];
|
||||
|
||||
tmp[0] = msg;
|
||||
tmp[1] = '\n';
|
||||
tmp[2] = '\0';
|
||||
|
||||
//printf("Writing %c to pipe fd: %d\n",msg,fd);
|
||||
|
||||
return write(fd, tmp, 2);
|
||||
}
|
||||
|
||||
int writeFile(char *name, int fd)
|
||||
{
|
||||
FILE *file;
|
||||
char buf[255];
|
||||
size_t len;
|
||||
size_t size = 1;
|
||||
size_t max = 255;
|
||||
|
||||
if (NULL != (file = fopen(name, "rb"))) {
|
||||
while (0 < (len = fread(buf, size, max, file))) {
|
||||
write(fd, buf, len);
|
||||
}
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
2
src/util.h
Normal file
2
src/util.h
Normal file
@ -0,0 +1,2 @@
|
||||
int writePipe(int fd, char msg);
|
||||
int writeFile(char *name, int fd);
|
Loading…
x
Reference in New Issue
Block a user