[comp.sys.att] modemon: use a bi-directional modem on your UNIX PC

ostroff@oswego.Oswego.EDU (Boyd Ostroff) (09/26/88)

I keep reading about the problems everyone is having with external modems
on the UNIX PC. I have seen several solutions posted, but they either involved
getting HDB UUCP (how?), having a modem with non-volatile memory, or required 
a number of various background processes to be run from the crontab.  After
rejecting these, I came up with my own.  I feel it is a simple and reliable 
solution.  The enclosed "modemon" is run in place of /etc/getty from the
/etc/inittab and will enable you to use one modem connected to tty000 for 
both incoming and outgoing calls.

Be sure to look at the enclosed README file before installing this on your
system.  I'm posting it in the hope that it will save others from spending
the many frustrating hours of "trial and error" that I had to.....

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
:: Boyd Ostroff, Technical Director :::::: System Operator, "The CallBoard" :::
:::: Dept of Theatre, SUNY Oswego ::::::: (315) 947-6414 300/1200/2400 baud :::
::::: ostroff@oswego.Oswego.EDU :::::::: rutgers!sunybcs!oswego!cboard!sysop ::
:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -rw-r--r--  1 ostroff prog       5707 Sep 25 14:36 README
# -rw-r--r--  1 ostroff prog       1258 Sep 25 14:23 modemon.sh
# -rw-r--r--  1 ostroff prog       5463 Sep 25 19:56 modemon.c
#
echo 'x - README'
if test -f README; then echo 'shar: not overwriting README'; else
sed 's/^X//' << '________This_Is_The_END________' > README
X+-----------------------------------------------------------------------+
X| MODEMON - configure an external modem for use on a Unix PC RS232 port |
X|          by Boyd Ostroff (ostroff@oswego.Oswego.EDU) 9/20/88          |
X+-----------------------------------------------------------------------+
X
XThis program was developed and tested using version 3.51a of the OS; it
X*should* work with other versions, but I haven't tried it.  I am running
Xthe stock, "out-of-the-box" uucp and uucico (not HDB).  My modem is an 
XEverex EMAC MD2400+ with MNP, but it should be pretty simple to modify this 
Xprogram work with your modem.   
X
X
XHOW IT WORKS
X------------
XThree things are necessary to use a modem bi-directionally:
X
X1) For outgoing calls, the getty on the port must be killed and then
Xre-started for incoming calls.  The /usr/bin/getoff.sh and /usr/bin/geton.sh
Xscripts provide this function on the Unix PC by "commenting out" the
Xappropriate line in /etc/inittab.  They are called by uucico and cu.
X
X2) The modem must be configured for outgoing calls.  This is done by
Xcreating an entry in /usr/lib/uucp/modemcap.
X
X3) The modem must be configured for incoming calls - this is the missing
Xfunction.  The short program "modemon" sends commands to the modem
Xand provides some error checking and logging.  A very simple shell script 
Xcalled "modemon.sh" is placed in /etc/inittab instead of the getty for 
Xtty000.  This shell script calls modemon to initialize the modem and then 
Xexecs getty.
X
X
XSETTING HARDWARE SWITCHES
X-------------------------
XFollowing is my modem's switch configuration - yours may be different, but
Xnotice the *function* of each switch. 
X
XSW1 - ON -  DTR must be high for modem to accept input or answer a call
XSW2 - OFF - Result codes sent as words
XSW3 - ON  - Result codes are not sent
XSW4 - ON  - Commands are not echoed
XSW5 - ON -  Auto-answer enabled (but only if DTR is high)
XSW6 - ON -  DCD follows received carrier signal
XSW7 - OFF - RJ11 phone jack
XSW8 - OFF - Enable AT command recognition
XSW9 - OFF - Use Bell 212A protocol
X
X
XINSTALLATION PROCEDURE
X----------------------
XThe easiest way to start is probably to login as install and use the UA
Xto configure tty000 for a Hayes modem.  After that, follow the steps
Xbelow:
X
X1. Look at the #defines at the beginning of the modemon.c program.  As
Xsupplied it will use tty000 at 2400 baud - change this as needed.  Compile
Xthe program by typing "cc -o modemon modemon.c".
X
X
X2. Take a look at the "modemon.sh" shell script. It was written specifically 
Xfor the Everex, so you may need to modify it to suit your needs.  Generally
Xspeaking, you should turn off command responses and echo.  Also note the final 
Xline which exec's /etc/getty.  I am using 2400 baud and also setting a timeout
Xvalue of 30 seconds (-t30). 
X
X
X3. Become root and do the following:
X
X$ cp modemon.sh /etc
X$ chmod 700 /etc/modemon.sh
X$ cp modemon /etc
X$ chmod 700 /etc/modemon
X
X4. Now you'll need to create the character special file /dev/modem.  To do 
Xthis, simply type:
X
X$ mknod /dev/modem c 0 128
X
XThis will allow you to talk to the modem while DCD is still low.
XIf you're not using tty000 for your modem this will need to be modified 
X(I don't have any other ports, so I couldn't test that :-( 
X
X
X5. Before proceeding, test the modemon program.  Make sure that tty000
Xis not in use first, then kill the getty on it by typing "getoff.sh 000".
XTry sending some commands to your modem and observe the response, for
Xexample:
X
X$ /etc/modemon ATZ
Xmodemon: sending "ATZ"
XATZ
XOK
X
XIf nothing happens check your hardware switches and also modem cable - 
Xfor full control you will generally need pins 1 through 8 and 20 all wired 
Xstraight through.
X
XWhen you're satisfied that the program works, type "geton.sh 000".
X
X
X6. Next you need to change your /etc/inittab entry so that init will run 
Xmodemon.sh.  Mine looks like this:
X
X 000:2:respawn:/etc/modemon.sh
X^
X| note blank space at the beginning of the line - important!
X
X
X7. You might need to change your entry in /usr/lib/uucp/modemcap.  This
Xconfigures the modem for dial-out use by cu and uucico.  You usually want
Xto turn on command echo (E1) and responses (Q0) and turn off auto-answer 
X(S0=0) for dial-out use.  In the example below, note that the line starting
Xwith :ta= is the one which does the configuration:
X
X# Everex EMAC MD2400
X# Name=everex
Xev|everex|everex 2400:tr=\r:wp=\r:wk=K:wt=T:wx=0:\
X                   :ta=at e1 q0 s0=0 \\n1\r:tb=ATDW:\
X                   :es=\r:\
X                   :d1#1:d5#9:\
X                   :pl=tad1wpwkwptbphtrd5wpd5wpwxwxwp:
X
XThere should be a corresponding entry in /usr/lib/uucp/L-devices:
X
XACU tty000 everex 2400
X
X
X8. Finally, type "telinit q", which will cause the system to read the
Xnew inittab and then type "kill <pid>", where <pid> is the process id
X(as shown by ps -ef) of the getty running on tty000.  You should be
Xall set!
X
X
X9. Try to log in at various baud rates on your external modem.  If you
Xhave problems, you'll probably have to change the initialization string
Xthat /etc/modemon.sh sends.
X
XTry placing an outgoing call with uucico -x6 and watch the output for
Xerrors; edit /usr/lib/uucp/modemcap as needed to send the correct modem
Xcommands.
X
XWith a little experimentation you should be able to get your modem
Xworking reliably in both directions.
X
X
X::::::::::::::::::::::::::::::::::::::::: 
X::  Boyd Ostroff, Technical Director   ::   System Operator, "The CallBoard"
X:: Department of Theatre, SUNY Oswego  ::   - Serving the performing arts -
X:: Internet: ostroff@oswego.Oswego.EDU ::  (315) 947-6414 300/1200/2400 baud 
X::        Voice: (315) 341-2987        :: UUCP ...sunybcs!oswego!cboard!ostroff
X:::::::::::::::::::::::::::::::::::::::::
X
X
________This_Is_The_END________
if test `wc -l < README` -ne 153; then
  echo 'shar: README was damaged during transit (should have been 153 bytes)'
fi
fi                ; : end of overwriting check
echo 'x - modemon.sh'
if test -f modemon.sh; then echo 'shar: not overwriting modemon.sh'; else
sed 's/^X//' << '________This_Is_The_END________' > modemon.sh
X# modemon.sh - initialize external modem for dial-in use on tty000
X# by Boyd Ostroff (ostroff@oswego.Oswego.EDU) 9/20/88
X
X# this program is run by init, and it execs getty after initializing the modem
X# you must also have the modemon.c program 
X
X# the following line should be added to /etc/inittab:
X#
X# 000:2:respawn:/etc/modemon
X#
X# the meaning of the modem commands are as follows:
X# (this version for Everex MD2400+, edit as needed to suit your requirements)
X#
X# m0	turn off monitor speaker
X# e0	disable command echoing
X# q1	turn off response display
X# s0=2	answer phone on 2nd ring
X# s9=2  drop line .2 seconds after carrier drops
X# \n3	set MNP auto-reliable mode
X# \j0	baud adjust off - modem & computer always talk at 2400 baud
X# \q0	disable xon/xoff between computer & modem
X# \k5   send break directly
X# \t5	set inactivity timer for 5 minutes
X# &t5	prohibit modem from entering test mode
X
Xdate > /usr/adm/modemlog
Xecho 'initializing modem...' >> /usr/adm/modemlog
X
X# first, reset the modem to get to a known state
X/etc/modemon "atz" >> /usr/adm/modemlog
Xsleep 1
X
X# now send the command string
X/etc/modemon "at m0 e0 q1 s0=2 s9=2 \j0 \q0 \k5 \n3 \t5 &t5" >> /usr/adm/modemlog 
X
X# finally, fire up a getty on the port
Xexec /etc/getty -t30 tty000 2400
________This_Is_The_END________
if test `wc -l < modemon.sh` -ne 37; then
  echo 'shar: modemon.sh was damaged during transit (should have been 37 bytes)'
fi
fi                ; : end of overwriting check
echo 'x - modemon.c'
if test -f modemon.c; then echo 'shar: not overwriting modemon.c'; else
sed 's/^X//' << '________This_Is_The_END________' > modemon.c
X/*--------------------------------------------------------------------------*/
X/* modemon.c - send commands to an external modem on Unix PC port tty000    */
X/* by Boyd Ostroff (ostroff@oswego.Oswego.EDU) 9/19/88                      */
X/*--------------------------------------------------------------------------*/
X/* May be freely modified and distributed as you see fit.                   */
X/* Designed to work with the shell script "modemon.sh".  Please read the    */
X/* accompanying README file before using.                                   */
X/*--------------------------------------------------------------------------*/
X
X#include <stdio.h>
X#include <signal.h>
X#include <fcntl.h>
X#include <termio.h>
X
X#define FALSE 0
X#define TRUE 1
X#define ERROR -1
X
X/* change these next four lines as needed to suit your needs */
X
X#define TIMEOUT 2           /* delay before open on /dev/modem will time out */
X#define SLEEPTIME 2         /* delay to wait while modem executes a command */
X#define MODEMPORT "/dev/modem"  /* you must create this - see the README file */
X#define PORTSPEED B2400         /* baud rate as given in termio.h */
X
Xstatic int timeout = FALSE;
Xstatic char *termname;
X
X/*--------------------------------------------------------------------------*/
Xmain(argc,argv)     /* program should be called with one argument, which is */
Xint argc;           /* the command string to be sent to the modem.  Use     */
Xchar *argv[];       /* double quotes (") to protect whitespace.  A carriage */
X{                   /* return will be appended to the command string.       */
Xint line;
Xint pid;
Xchar *cmd;
Xstruct termio original;
X
X    if ((cmd = argv[1]) == NULL)
X        {
X        printf("modemon: no argument given. Usage: modemon \"command\"\n");
X        exit(ERROR);
X        }
X    if (argc > 2)
X        {
X        printf("modemon: too many arguments. Usage: modemon \"command\"\n");
X        exit(ERROR);
X        }
X    printf("modemon: sending \"%s\"\n", cmd);
X    if( (line = ttyopen(MODEMPORT, O_RDWR)) == ERROR)
X        {
X        exit(ERROR);
X        }
X    ioctl(line, TCGETA, &original);         /* save initial port settings */
X    if (set_line(line, PORTSPEED) == ERROR) 
X        {
X        exit(ERROR);
X        }
X    if ((pid = fork()) != ERROR)    /* start a process to read MODEMPORT */ 
X        {
X        if (pid == 0)               /* now we're in the child process */
X            listen(line);           /* this function never returns - */
X        }                           /* it keeps running until killed */
X    send_cmd(line, cmd);
X    kill(pid, SIGTERM);                     /* kill the child process */
X    wait((int *)0);
X    ioctl(line, TCSETAW, &original);        /* restore initial port settings */
X    close(line);
X    exit(0);
X}
X
X/*--------------------------------------------------------------------------*/
Xsend_cmd(line, what)    /* send the command string to the modem */
Xint line;               /* tty file descriptor */
Xchar *what;             /* modem command */
X{
Xint length;
Xint i;
Xunsigned char c = '\r';
X
X    length = strlen(what);
X    for (i=0; i <= length; i++)
X        {
X        write(line, what+i, 1);
X        }
X        write(line, &c, 1);     /* append a carriage return to end of cmd */
X    sleep(SLEEPTIME);           /* give modem time to execute the command  */
X}
X/*--------------------------------------------------------------------------*/
Xstatic int set_timeout()
X{
X    printf("modemon: timeout on opening %s\n", termname);
X    timeout = TRUE;
X}
X
X/*--------------------------------------------------------------------------*/
Xset_line(fd, speed)     /* configure the port so we can talk to the modem */
Xint fd, speed;
X{
Xstruct termio tt;
X
X    if (!isatty(fd))
X        {
X        printf("modemon: not a valid terminal file descriptor\n");
X        return(ERROR);
X        }
X    if( ioctl(fd, TCGETA, &tt) == ERROR)
X        {
X        printf("modemon: can't get current line settings\n");
X        return(ERROR);
X        }                       /* set the terminal control flags as needed */
X    tt.c_iflag = (IXON|IXOFF);  /* xon/xoff flow control enabled */
X    tt.c_oflag = 0;
X    tt.c_lflag = 0;
X    tt.c_cflag &=~CBAUD;
X    tt.c_cflag |= speed;
X    tt.c_cc[VMIN] = 1;
X    tt.c_cc[VTIME] = 0;
X    if (ioctl(fd, TCSETAW, &tt) == ERROR)   /* flush & reset line */
X        {
X        printf("modemon: can't change current line settings\n");
X        return(ERROR);
X        }
X
X
X}
X/*--------------------------------------------------------------------------*/
Xttyopen(filename, flags)    /* open the modem port */
Xchar *filename;             /* will wait for TIMEOUT seconds, then give up */
Xint flags;                  /* see  p214 of UNIX SYSTEM PROGRAMMING */
X{                           /* by Haviland and Salama */
Xint (*sigfn)();
Xint fd = -1;
X
X    termname = filename;
X    timeout = FALSE;
X    sigfn = signal(SIGALRM, set_timeout);
X    alarm(TIMEOUT);
X    fd = open(filename, flags);
X    alarm(0);
X    signal(SIGALRM, sigfn);
X    return(timeout ? -1: fd);
X}
X/*--------------------------------------------------------------------------*/
Xlisten(line)    /* send everything that is read from MODEMPORT to stdout */
Xint line;       /* this is a child process forked off from the original */
X{
Xunsigned char c;
X
X    for(;;)     /* loops forever until killed */
X        {
X        read(line, &c, 1);
X        write(1, &c, 1);
X        }
X}
X/*--------------------------------------------------------------------------*/
________This_Is_The_END________
if test `wc -l < modemon.c` -ne 156; then
  echo 'shar: modemon.c was damaged during transit (should have been 156 bytes)'
fi
fi                ; : end of overwriting check
exit 0