housel@en.ecn.purdue.edu (Peter S. Housel) (07/03/89)
echo 'x - dial.h'
sed 's/^X//' <<'**-dial.h-EOF-**' >dial.h
Xtypedef struct {
X void *attr; /* pointer to termio attribute struct */
X int baud; /* transmission data rate */
X int speed; /* 212A modem: low=300, high=1200 */
X char *line; /* device name for out-going line */
X char *telno;/* pointer to tel-no digits string */
X int modem; /* specify modem control for direct lines */
X} CALL;
X
X#define INTRPT -1 /* interrupt occurred */
X#define D_HUNG -2 /* dialer hung (no return from write) */
X#define NO_ANS -3 /* no answer within 10 seconds */
X#define ILL_BD -4 /* illegal baud-rate */
X#define A_PROB -5 /* acu problem (open() failure) */
X#define L_PROB -6 /* line problem (open() failure) */
X#define NO_Ldv -7 /* can't open LDEVS file */
X#define DV_NT_A -8 /* requested device not available */
X#define DV_NT_K -9 /* requested device not known */
X#define NO_BD_A -10 /* no device available at requested baud */
X#define NO_BD_K -11 /* no device known at requested baud */
X
X#define LDEVS "/usr/lib/uucp/L-devices"
X#define DVC_LEN 64
X#define DEVDIR "/dev/"
X#define LOCK "/usr/spool/uucp/LCK.."
X
X/* minix */
X#define O_RDWR 2
X#ifdef BITS8
X#define ANYP BITS8
X#endif
**-dial.h-EOF-**
echo 'x - dialvars.c'
sed 's/^X//' <<'**-dialvars.c-EOF-**' >dialvars.c
Xchar AS, /* True if numbers dialed in ASCII, False for binary digits */
X DI, /* True if modem can dial numbers, False otherwise */
X HC, /* True if modem hangs up when DTR drops, False otherwise */
X TT; /* True if modem uses touchtone by default, False for pulse */
X
Xchar *AT, /* Enter command state when online */
X *CS, /* Command start string */
X *CE, /* Command end string - must be present if CS is */
X *DS, /* Dial command string */
X *DE, /* End of dial command string - must be present if DS is */
X *CO, /* Connection made at primary baud rate */
X *CL, /* Connection made at secondary (lower) baud rate */
X *IS, /* Initialization string - reset modem to onhook and ready */
X *HU; /* Hangup command */
X
Xint AD, /* Delay after AT string before next command */
X BD, /* Highest communications baud rate */
X BL, /* Another, lower baud rate */
X ID; /* Delay time after initialization */
**-dialvars.c-EOF-**
echo 'x - hangup.c'
sed 's/^X//' <<'**-hangup.c-EOF-**' >hangup.c
X#include "modemcap.h"
X#ifdef UNIX_S5
X#include <termio.h>
X#endif
X#ifdef UNIX_V7
X#include <sgtty.h>
X#endif
X
Xextern int merrno;
X
Xhangup (fd)
Xint fd;
X{
X#ifdef UNIX_S5
X struct termio termio;
X struct termio hupcl;
X#endif
X#ifdef UNIX_V7
X struct sgttyb termio;
X struct sgttyb hupcl;
X#endif
X if (HU == (char *) 0 && HC == 0) {
X undial (fd);
X return (0);
X }
X if (AT != (char *) 0) {
X write (fd, AT, strlen (AT));
X if (AD)
X sleep (AD);
X }
X if (HU) {
X if (CS)
X write (fd, CS, strlen (CS));
X write (fd, HU, strlen (HU));
X if (CE)
X write (fd, CE, strlen (CE));
X
X if (IS) {
X write (fd, IS, strlen (IS));
X if (ID)
X sleep (ID);
X }
X undial (fd);
X return (1);
X }
X#ifdef UNIX_S5
X ioctl (fd, TCGETA, &termio);
X ioctl (fd, TCGETA, &hupcl);
X
X hupcl.c_cflag &= ~CBAUD;
X hupcl.c_cflag |= HUPCL;
X
X ioctl (fd, TCSETA, &hupcl);
X sleep (2);
X ioctl (fd, TCSETA, &termio);
X#endif
X#ifdef UNIX_V7
X#define B0 0
X gtty (fd, &termio);
X gtty (fd, &hupcl);
X
X hupcl.sg_ispeed = B0;
X hupcl.sg_ospeed = B0;
X stty (fd, &hupcl);
X sleep (2);
X stty (fd, &termio);
X#endif
X undial (fd);
X return (1);
X}
**-hangup.c-EOF-**
echo 'x - initmodem.c'
sed 's/^X//' <<'**-initmodem.c-EOF-**' >initmodem.c
X#include "modemcap.h"
X
Xstatic char f_names[] = "asditthc";
Xstatic char *f_caps[] = {
X &AS, &DI, &TT, &HC
X};
X
Xstatic char i_names[] = "bdblidad";
Xstatic int *i_caps[] = {
X &BD, &BL, &ID, &AD
X};
X
Xstatic char c_names[] = "cscedsdeiscoclathu";
Xstatic char **c_caps[] = {
X &CS, &CE, &DS, &DE, &IS, &CO, &CL, &AT, &HU
X};
X
Xinitmodem (modem, fd)
Xchar *modem; /* name of modem */
Xint fd; /* channel to modem */
X{
X static char mcapbuf[1024];
X static char area[1024];
X char *ap = area;
X register char *cp;
X register int i, j;
X register char *s;
X char *mgetstr ();
X
X if (mgetent (mcapbuf, modem) != 1)
X return (0);
X
X for (i = 0, cp = f_names;*cp;i++, cp += 2)
X *(f_caps[i]) = mgetflag (cp);
X
X for (i = 0, cp = i_names;*cp;i++, cp += 2) {
X j = mgetnum (cp);
X if (j >= 0)
X *(i_caps[i]) = j;
X else
X *(i_caps[i]) = 0;
X }
X for (i = 0, cp = c_names;*cp;i++, cp += 2)
X *(c_caps[i]) = mgetstr (cp, &ap);
X
X if (IS != (char *) 0) {
X write (fd, IS, strlen (IS));
X if (ID)
X sleep (ID);
X }
X return (1);
X}
**-initmodem.c-EOF-**
echo 'x - makefile.unix'
sed 's/^X//' <<'**-makefile.unix-EOF-**' >makefile.unix
X# Your library directory.
XLIBDIR=/usr/lib
X# Your local command directory.
XLBIN=/usr/local/bin
XOSFLAG=-DUNIX_S5 # For System V machines.
X# OSFLAG=-DUNIX_V7 # For Version 7 machines.
X# For those poor people who need ranlib
X# RANLIB=ranlib $(LIBDIR)/libmodemcap.a
X# Standard Bourne shell.
XSHELL=/bin/sh
X
XCFLAGS=-O
XLDFLAGS=-s
X
XOFILES=mgetent.o mgetstr.o mgetflag.o mgetnum.o mdial.o merror.o \
X initmodem.o hangup.o dial.o
X
XCFILES=mgetent.c mgetstr.c mgetflag.c mgetnum.c mdial.c merror.c \
X initmodem.c hangup.c dial.c
X
XLFILES=libmodemcap.a(mgetent.o)\
X libmodemcap.a(mgetstr.o)\
X libmodemcap.a(mgetflag.o)\
X libmodemcap.a(mgetnum.o)\
X libmodemcap.a(mdial.o)\
X libmodemcap.a(merror.o)\
X libmodemcap.a(initmodem.o)\
X libmodemcap.a(hangup.o)\
X libmodemcap.a(dial.o)
X
Xall: $(LFILES)
X
Xinstall: all call
X cp modemcap.h /usr/include
X cp modemcap /etc/modemcap
X cp modemtype /etc/modemtype
X cp libmodemcap.a $(LIBDIR)
X chmod 644 /usr/include/modemcap.h /etc/modemcap /etc/modemtype $(LIBDIR)/libmodemcap.a
X cp call $(LBIN)/call
X chmod 711 $(LBIN)/call
X
Xcall: call.c libmodemcap.a
X cc $(LDFLAGS) $(CFLAGS) call.c libmodemcap.a -o call
X
Xinitmodem.o: initmodem.c /usr/include/modemcap.h
X
Xmdial.o: mdial.c /usr/include/modemcap.h
X
Xhangup.o: hangup.c /usr/include/modemcap.h
X
Xdial.o: dial.c /usr/include/modemcap.h
X
Xshar: README $(CFILES) makefile modemcap modemtype modemcap.h call.c
X shar README makefile modemcap modemtype modemcap.h $(CFILES) call.c > modem.shar
**-makefile.unix-EOF-**
echo 'x - mdial.c'
sed 's/^X//' <<'**-mdial.c-EOF-**' >mdial.c
X#include "modemcap.h"
X#include <setjmp.h>
X#include <signal.h>
X#include <dial.h>
X
Xstatic jmp_buf env; /* long jump buffer if timeout in read */
Xextern int merrno;
X
Xstatic timeout ()
X{
X longjmp (env, 1);
X}
X
Xmdial (telno, fd)
Xchar *telno;
Xint fd;
X{
X char buf[64]; /* telephone buffer if AS is false */
X char command[80]; /* dial command buffer */
X int i, j; /* index and length of telephone number */
X char c; /* single character for connection verification */
X
X if (! DI)
X return (merrno = A_PROB); /* can't dial phone anyhow */
X
X if (! AS) /* never used any other kind of modem */
X return (merrno = A_PROB);
X else /* normal ascii character phone numbers */
X strcpy (buf, telno);
X
X sprintf (command, "%s%s%s%s%s", CS, DS, buf, DE, CE);
X if (setjmp (env) != 0) {
X signal (SIGALRM, SIG_DFL);
X return (merrno = D_HUNG);
X }
X signal (SIGALRM, timeout);
X alarm (30);
X write (fd, command, strlen (command));
X
X if (CO) { /* verify connection */
X if (setjmp (env) != 0) {
X signal (SIGALRM, SIG_DFL);
X return (merrno = A_PROB);
X }
X signal (SIGALRM, timeout);
X if (TT)
X alarm (60);
X else
X alarm (60 + strlen (telno) / 2);
X
X for (i = 0;CO[i] != 0;) {
X if (read (fd, &c, 1) != 1)
X continue;
X
X if (CO[i] == c)
X i++;
X else
X i = 0;
X }
X return (0);
X }
X return (0);
X}
**-mdial.c-EOF-**
echo 'x - merror.c'
sed 's/^X//' <<'**-merror.c-EOF-**' >merror.c
X#include <stdio.h>
X
Xint merrno;
X
Xchar *_merr_list[] = {
X "No error",
X "Interrupt occurred",
X "Dialer Hung",
X "No answer",
X "Illegal baud rate",
X "ACU Problem",
X "Line Problem",
X "Can't open LDEVS file",
X "Requested device not available",
X "Requested device not known",
X "No device available at requested baud",
X "No device known at requested baud"
X};
X
Xint _msys_nerr = (sizeof (_merr_list) / sizeof (char *));
X
Xmerror (s)
Xchar *s;
X{
X int i = - merrno;
X
X if (0 <= i && i < _msys_nerr)
X fprintf (stderr, "%s: %s\n", s, _merr_list[i]);
X else
X fprintf (stderr, "%s: Error %d\n", s, merrno);
X}
**-merror.c-EOF-**
echo 'x - mgetent.c'
sed 's/^X//' <<'**-mgetent.c-EOF-**' >mgetent.c
X#include <stdio.h>
X
Xchar *__modemcap;
Xchar *MODEMCAP = "/usr/etc/modemcap";
X
Xstatic isent (ent, name)
Xchar *ent;
Xchar *name;
X{
X char buf[16];
X register int i;
X
X while (*ent != ':' && *ent != 0) {
X for (i = 0;*ent != ':' && *ent != '|' && *ent != 0 && i < 15;i++)
X buf[i] = *ent++;
X
X if (*ent == '|')
X ent++;
X
X buf[i] = 0;
X if (strcmp (buf, name) == 0)
X return (1);
X }
X return (0);
X}
X
Xmgetent (bp, name)
Xchar *bp;
Xchar *name;
X{
X char buf[1024];
X register char *cp;
X register FILE *modemcap;
X register int i;
X char *getenv ();
X
X if ((cp = getenv ("MODEMCAP")) != NULL) {
X if (*cp != '/') {
X if (isent (cp, name)) {
X strcpy (buf, cp);
X return (1);
X }
X }
X MODEMCAP = cp;
X }
X if ((modemcap = fopen (MODEMCAP, "r")) == NULL)
X return (-1);
X
X while (fgets (buf, 512, modemcap) != NULL) {
X if (buf[0] == '#') /* skip all comment lines */
X continue;
X
X i = strlen (buf) - 1; /* find last character in line */
X buf[i] = 0; /* remove trailing newline */
X if (i == 0) /* ignore blank lines */
X continue;
X
X while (buf[(i = strlen (buf) - 1)] == '\\') { /* is last character a \\, still more */
X cp = &buf[i]; /* find last character */
X cp[0] = 0; /* nullify, end of this part */
X if (fgets (cp, 512, modemcap) == NULL) /* end of file? ... */
X break; /* ... end of entry */
X
X cp[strlen (cp) - 1] = 0; /* remove trailing newline */
X if (cp[0] == '#') { /* comment line? ... */
X cp[0] = 0; /* remove that line */
X continue; /* go get another line */
X }
X }
X if (isent (buf, name)) {
X __modemcap = bp;
X strcpy (bp, buf);
X fclose (modemcap);
X return (1);
X }
X }
X fclose (modemcap);
X return (0);
X}
**-mgetent.c-EOF-**
echo 'x - mgetflag.c'
sed 's/^X//' <<'**-mgetflag.c-EOF-**' >mgetflag.c
Xextern char *__modemcap;
X
Xmgetflag (id)
Xregister char *id;
X{
X register char *cp = __modemcap;
X
X if (__modemcap == (char *) 0) /* has mgetent() been called? ... */
X return (-1); /* ... no, can't find number */
X
X while (*cp != ':' && *cp != 0) /* find first entry in cap */
X cp++;
X
X if (*cp == 0) /* empty entry??? */
X return (0); /* ... yes, bad modemcap entry */
X else
X cp++; /* point to first character in next */
X
X while (*cp != 0) { /* until entry found or end of entry */
X if (cp[0] == id[0] && cp[1] == id[1]) /* found entry!!! */
X return (1); /* return true */
X else { /* not entry, skip this entire entry */
X while (*cp != ':' && *cp != 0)
X cp++; /* search for end of current entry */
X
X if (*cp != 0)
X cp++; /* skip terminating character */
X }
X }
X return (0);
X}
**-mgetflag.c-EOF-**
echo 'x - mgetnum.c'
sed 's/^X//' <<'**-mgetnum.c-EOF-**' >mgetnum.c
Xextern char *__modemcap;
X
Xmgetnum (id)
Xregister char *id;
X{
X register char *cp = __modemcap;
X
X if (__modemcap == (char *) 0) /* has mgetent() been called? ... */
X return (-1); /* ... no, can't find number */
X
X while (*cp != ':' && *cp != 0) /* find first entry in cap */
X cp++;
X
X if (*cp == 0) /* empty entry??? */
X return (-1); /* ... yes, bad modemcap entry */
X else
X cp++; /* point to first character in next */
X
X while (*cp != 0) { /* until entry found or end of entry */
X if (cp[0] == id[0] && cp[1] == id[1]) { /* found entry!!! */
X if (cp[2] != '#') /* is it a numeric value??? */
X return (-1); /* no, something else */
X
X return (atoi (&cp[3])); /* return value (just after #) */
X } else { /* not entry, skip this entire entry */
X while (*cp != ':' && *cp != 0)
X cp++; /* search for end of current entry */
X
X if (*cp != 0)
X cp++; /* skip terminating character */
X }
X }
X return (-1);
X}
**-mgetnum.c-EOF-**
echo 'x - mgetstr.c'
sed 's/^X//' <<'**-mgetstr.c-EOF-**' >mgetstr.c
Xextern char *__modemcap;
X
Xchar *mgetstr (id, area)
Xregister char *id;
Xregister char **area;
X{
X register char *cp = __modemcap;
X register char *str = *area; /* start of current string */
X
X if (__modemcap == (char *) 0) /* has mgetent() been called? ... */
X return ((char *) 0); /* ... no, can't find string */
X
X while (*cp != ':' && *cp != 0) /* find first entry in cap */
X cp++;
X
X if (*cp == 0) /* empty entry??? */
X return ((char *) 0); /* ... yes, bad modemcap entry */
X else
X cp++; /* point to first character in next */
X
X while (*cp != 0) { /* until entry found or end of entry */
X if (cp[0] == id[0] && cp[1] == id[1]) { /* found entry!!! */
X if (cp[2] != '=') /* is it a string value??? */
X return ((char *) 0); /* no, something else */
X else
X break; /* yes, entry was found */
X } else { /* not entry, skip this entire entry */
X while (*cp != ':' && *cp != 0)
X cp++; /* search for end of current entry */
X
X if (*cp != 0)
X cp++; /* skip terminating character */
X }
X }
X if (*cp == 0) /* end of modem cap entry */
X return ((char *) 0);
X
X cp += 3; /* point to actual string */
X while (*cp != ':' && *cp != 0) { /* for every character in string ... */
X if (*cp == '\\') { /* translate escaped character */
X cp++;
X switch (*cp) {
X case 'n': /* newline */
X **area = '\n';
X (*area)++;
X cp++;
X break;
X case 'r': /* carriage return */
X **area = '\r';
X (*area)++;
X cp++;
X break;
X case 'b': /* backspace */
X **area = '\b';
X (*area)++;
X cp++;
X break;
X case 'f': /* form feed */
X **area = '\f';
X (*area)++;
X cp++;
X break;
X case 't': /* tab */
X **area = '\t';
X (*area)++;
X cp++;
X break;
X case 'E': /* Escape character */
X **area = 033;
X (*area)++;
X cp++;
X break;
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X **area = ((cp[0] - '0') << 6) +
X ((cp[1] - '0') << 3) +
X (cp[2] - '0');
X (*area)++;
X cp += 3;
X break;
X default:
X **area = *cp++;
X (*area)++;
X break;
X }
X } else if (*cp == '^') { /* some control character */
X cp++;
X if (*cp >= '@' && *cp <= '_') {
X **area = *cp - '@';
X (*area)++;
X }
X cp++;
X } else { /* some normal character */
X **area = *cp++; /* put character in area */
X (*area)++;
X }
X }
X *((*area)++) = 0; /* null terminate area and string */
X return (str); /* return pointer to start of string */
X}
**-mgetstr.c-EOF-**
echo 'x - modemcap'
sed 's/^X//' <<'**-modemcap-EOF-**' >modemcap
X#
X# @(#)modemcap 1.0
X#
X# First attempt at a modem capabilities database.
X#
X# Capabilities are:
X#
X# Name Type Meaning
X#
X# as flag Numbers are in ASCII, not binary
X# at string Attention string, forces model into command mode
X# from online mode
X# ad number Delay after AS
X# bd number Highest online baud rate
X# bl number Alternate lower baud rate
X# cs string Command start string
X# ce string Command end string (required if CS is present)
X# co string String from modem on remote connection at BD baud rate
X# cl string String from modem on remote connection at BL baud rate
X# di flag Modem has a dialer
X# ds string Start dial command string
X# de string End dial command string (required if DS is present)
X# is string Initialization string, resets modem to offline,
X# ready to dial
X# id number Delay after IS
X# hc flag Modem hangs up when DTR drops
X# hu string Hangup command
X# tt flag Modem dials touchtone by default (or DS is set
X# that way)
X#
X# All commands, such as DS (dial command) and HU (hang up) will be
X# prefixed by CS and ended with CE. If there is a common prefix
X# and suffix, use this feature. Otherwise, each command will have
X# to have the entire string built in.
X#
Xhy|hayes|Hayes Smartmodem 1200:\
X :as:at=+++:ad#6:bd#1200:bl#300:cs=AT:ce=\r:co=CONNECT:\
X :cl=CONNECT:di:ds=DT :de=:is=ATQ0 V1 E1\r:id#2:\
X :hc:hu=H0 V0 E0 Q1:tt:
Xpr|promodem|Prometheus Promodem 1200:\
X :as:at=+++:ad#6:bd#1200:bl#300:cs=AT:ce=\r:co=CONNECT:\
X :cl=CONNECT:di:ds=DT:de=:is=ATQ0V1E1\r:id#2:\
X :hu=H0V0E0Q1:tt:
Xsi|mk12|Signalman Mark XII:\
X :as:at=+++:ad#6:bd#1200:bl#300:cs=AT:ce=\r:co=CONNECT 1200:\
X :cl=CONNECT:di:ds=DT :de=:is=ATQ0 V1 E1\r:id#2:\
X :hu=H0 V0 E0 Q1:tt:
Xds|dc300|Radio Shack Direct-Connect 300 Modem:\
X :bd#300:bl#110:
**-modemcap-EOF-**
cd ..
echo 'x - newgpkt.c'
sed 's/^X//' <<'**-newgpkt.c-EOF-**' >newgpkt.c
X/* file: newgpkt.c
X * author: Peter S. Housel
X *
X * The |cksum()| routine is taken from UUPC, Copyright 1985, 1986, 1987 by
X * Richard H. Lamb, with (possible) changes Copyright 1987 by Stuart Lynne
X *
X * All other code is Copyright 1989 by Peter S. Housel.
X * Redistribution for any purpose is permitted provided this message
X * is included intact. No warranty of any sort is provided.
X *
X * newgpkt version 1.0 1/5/89
X */
X
X/* This program was written based on the original UUPC 'g' driver,
X * John Gilmore's version of uuslave, and Greg Chesson's protocol
X * description article. The last was especially helpful.
X *
X * This code was written around a severely hacked version of UUPC.
X * The call interface is almost identical to the original, but
X * the internal implementation is quite different. Also, many
X * functions are called that were not available in the original
X * UUPC support functions. It should serve as an adequate framework.
X *
X * The framing strategy requires that a |read()| be interruptable
X * by a |SIGALRM|. No "busy wait" or nonblocking read is required.
X */
X
X#include "dcp.h"
X
X#define MAXPKT 64 /* incredibly conservative... actually 4096 */
X#define SWINDOW 3 /* initial send window size */
X#define RWINDOW 3 /* window size we want to recieve */
X#define SPKTSIZE 64 /* initial send packet size */
X#define RPKTSIZE 64 /* window size we want to recieve */
X
X#define MAXLOST 5 /* max lost packets (closes or such) */
X#define TIMEOUT 5 /* max seconds of before timeout */
X
X#define LOSTPKT -1 /* packet lost, got timeout */
X#define BADPKT -2 /* bad checksum, or data read timed out */
X
X#define ENV_DLE 0 /* framing char at start of envelope */
X#define ENV_K 1 /* packet length specifier */
X#define ENV_C0 2 /* low-order checksum */
X#define ENV_C1 3 /* high-order checksum */
X#define ENV_C 4 /* control byte */
X#define ENV_X 5 /* xor check byte */
X#define ENV_LEN 6 /* overall envelope length */
X
X#define TT_CTRL 0 /* control packet */
X#define TT_DATA 2 /* data packet */
X#define TT_SDATA 3 /* short data packet */
X#define TT_ALTCHAN 1 /* 'alternate channel' - invalid */
X
X#define X_CLOSE 1 /* close down protocol */
X#define X_RJ 2 /* reject recieved packet */
X#define X_SRJ 3 /* selectively reject packet - invalid */
X#define X_RR 4 /* reciever ready */
X#define X_INITC 5 /* third init packet */
X#define X_INITB 6 /* second init packet */
X#define X_INITA 7 /* first init packet */
X
X#define OP_OPEN 1 /* request to open/init protocol */
X#define OP_CLOSE 2 /* request to close protocol */
X#define OP_WRITE 3 /* request to send packet */
X#define OP_READ 4 /* request to read packet */
X
X#define MAGIC (unsigned) 0xAAAA /* checksum magic value */
X
X/* from original dcp - determinie if a <= b < c, for mod 8 seq numbers */
X#define between(a,b,c) (((a)<=(b) && (b)<(c)) \
X || ((c)<(a) && (a)<=(b)) \
X || ((b)<(c) && (c)<(a)))
X
Xunsigned cksum(/* unsigned char *data, int len */);
Xextern char *visib(/* unsigned char *data, int len */);
X
Xstruct {
X short sdata; /* 'is this a short data packet' flag */
X unsigned length; /* length of this packet */
X unsigned char *bufloc; /* location of this data pkt's buffer */
X }
X inpbufs[8], outbufs[8]; /* input/output queues */
X
Xstatic int needack; /* do we need to acknowledge a rcv'd pkt? */
Xstatic int neednack; /* do we need to reject a recieved pkt? */
Xstatic int recv; /* seq. number of last correctly rcv'd pkt */
Xstatic int lastread; /* seq. number of last pkt ret. to caller */
Xstatic int send; /* first packet in output window */
Xstatic int next; /* next pkt to send send <= next < nstuff */
Xstatic int nstuff; /* next loc. to stuff a pkt in output queue */
X /* (last pkt in output window) + 1 */
Xstatic int initpk; /* current init sequence send packet */
Xstatic int skipping; /* skipping out-of-seq packets after RJ */
Xstatic int spktsize; /* send data size (requested by other end) */
Xstatic int swindow; /* send output window size (ditto) */
Xstatic int nlost; /* number of consecutive timeouts */
Xstatic int chanopen = 0; /* 'channel open' flag */
X
Xstatic unsigned char buf[ENV_LEN + RPKTSIZE]; /* packet framing buffer */
Xstatic unsigned char *low, *high; /* framing buffer limits */
X
Xstatic unsigned char *ibufset, *obufset; /* i/o packet buffer sets */
X
X/* |gopenpk()| opens the 'g' packet protocol on the serial line. It
X * initializes its state variables, allocates buffers, etc., then calls
X * |gmachine()| to do the actual protocol negotiation/initialization.
X */
Xgopenpk()
X{
X int i; /* index */
X unsigned char *p, *q; /* pointers into buffer set */
X
X high = low = buf; /* empty input buffer */
X needack = neednack = 0; /* don't need to accept or reject anything */
X initpk = X_INITA; /* send INITA during initialization */
X recv = lastread = 0; /* initial empty read queue, seq=0 */
X send = next = nstuff = 1; /* first in output queue, seq=1 */
X skipping = nlost = 0; /* nothing lost yet, not skipping */
X
X if(gmachine(OP_OPEN) < 0) /* do the open */
X return -1;
X
X /* allocate send and recieve buffers */
X if(NULL == (p = ibufset = (unsigned char *)malloc(8 * RPKTSIZE)))
X return -1;
X if(NULL == (q = obufset = (unsigned char *)malloc(8 * spktsize)))
X return -1;
X
X for(i = 0; i < 8; ++i)
X {
X inpbufs[i].bufloc = p;
X p += RPKTSIZE;
X outbufs[i].bufloc = q;
X q += spktsize;
X }
X
X pktsize = spktsize; /* for dcp compatibility */
X
X return 0;
X}
X
X/* |gclosepk()| closes down the packet protocol using the |OP_CLOSE| operation
X * of |gmachine()|.
X */
Xgclosepk()
X{
X return gmachine(OP_CLOSE);
X}
X
X/* |ggetpkt()| reads one packet and returns it. The data is stored in
X * the buffer pointed to by |cdata|, and the length is stored in |*lenp|.
X * It calls |gmachine()| to get the data, and copies it from the proper input
X * buffer to the user's buffer area. "Short data" packets are handled here,
X * as opposed to within |gmachine()|.
X */
Xint ggetpkt(cdata, lenp)
Xunsigned char *cdata; int *lenp;
X{
X int nextread;
X unsigned char *bufp;
X
X if(!chanopen)
X return -1;
X
X nextread = (lastread + 1) & 7;
X printmsg(M_HIGHPROTO, "waiting for input pkt seq=%d", nextread);
X
X if(gmachine(OP_READ) < 0)
X return -1;
X
X *lenp = inpbufs[nextread].length;
X bufp = inpbufs[nextread].bufloc;
X if(inpbufs[nextread].sdata)
X {
X if(*bufp < 128) /* less than 128 bytes shorter than packet length */
X *lenp -= *bufp++;
X else /* more than 128 bytes shorter */
X {
X *lenp -= (*bufp++ & 127) * 256;
X *lenp -= *bufp++;
X }
X }
X memcpy(cdata, bufp, *lenp);
X lastread = nextread;
X return 0;
X}
X
X/* |gsendpkt()| queues the packet pointed to by |cdata| of length |len|
X * into the packet driver output buffer, and calls |gmachine()| to send
X * it. (|gmachine()| will return when the packet has been transmitted but
X * not necessarily acknowledged, with window size greater than 1.) If
X * |flag| is nonzero, |cdata| is considered a null-terminated string
X * which will be null-padded to the packet size and transmitted.
X */
Xint gsendpkt(cdata, len, flag)
Xunsigned char *cdata; int len, flag;
X{
X unsigned char *destp;
X unsigned diff;
X
X if(!chanopen)
X return -1;
X
X destp = outbufs[nstuff].bufloc;
X if(flag && len < spktsize)
X {
X printmsg(M_HIGHPROTO, "Padded message packet |%s|", cdata);
X strncpy(destp, cdata, spktsize);
X }
X else
X {
X if((diff = spktsize - len) > 127) /* really short packet? */
X {
X *destp++ = (diff >> 8) | 128;
X *destp++ = diff & 255;
X outbufs[nstuff].sdata = 1;
X }
X else if(diff > 0) /* short packet */
X {
X *destp++ = diff;
X outbufs[nstuff].sdata = 1;
X }
X else
X outbufs[nstuff].sdata = 0;
X memcpy(destp, cdata, len); /* copy into buffer */
X }
X printmsg(M_HIGHPROTO, "queued data packet seq=%d len=%d", nstuff, len);
X outbufs[nstuff].length = spktsize;
X nstuff = (nstuff + 1) & 7;
X
X return gmachine(OP_WRITE);
X}
X
X/* |gmachine()| is the heart of the 'g' packet driver. Its basic strategy
X * is:
X * - transmit a packet if necessary
X * - return if possible,
X * - else read a packet and act on it
X * - repeat
X *
X * |OP_OPEN| requests that the channel be opened, and |OP_CLOSE| requests that
X * it be closed. If |why| is |OP_WRITE|, |gmachine()| will return when the
X * last packet in the output queue has been transmitted (but not necessarily
X * acknowledged). |OP_READ| requests will return as soon as a new packet
X * arrives.
X */
Xint gmachine(why)
Xint why;
X{
X int xxx, yyy, len;
X unsigned char *bufp;
X int shortdat;
X int i;
X
X while(1)
X {
X if(OP_CLOSE == why)
X {
X gspack(TT_CTRL, X_CLOSE, 0, (unsigned char *)NULL, 0);
X chanopen = 0;
X printmsg(M_MEDPROTO, "Sending CLOSE request...");
X }
X else if(neednack)
X {
X gspack(TT_CTRL, X_RJ, recv, (unsigned char *)NULL, 0);
X neednack = 0;
X printmsg(M_MEDPROTO, "Sending RJ... recv=%d", recv);
X }
X else if(send != nstuff /* nonzero output queue? */
X && between(send, next, nstuff) /* 'next' in queue? */
X && between(send, next, (send + SWINDOW) & 7)) /* in out win. */
X {
X printmsg(M_MEDPROTO, "Sending data packet %d", next);
X gspack(outbufs[next].sdata ? TT_SDATA : TT_DATA,
X next, recv, outbufs[next].bufloc, outbufs[next].length);
X needack = 0;
X next = (next + 1) & 7;
X if(OP_WRITE == why && next == nstuff) /* go back for more */
X return 0;
X }
X else if(needack)
X {
X gspack(TT_CTRL, X_RR, recv, (unsigned char *)NULL, 0);
X needack = 0;
X printmsg(M_MEDPROTO, "Sending RR... recv=%d", recv);
X }
X else if(OP_OPEN == why)
X {
X if(X_INITB == initpk)
X i = ilog2(RPKTSIZE) - 5; /* INITB contains packet size, */
X else
X i = RWINDOW; /* INITA, INITC contain window size */
X gspack(TT_CTRL, initpk, i, (unsigned char *)NULL, 0);
X }
X
X if(OP_READ == why && recv != lastread)
X return 0;
X
X shortdat = 0;
X bufp = buf + ENV_LEN;
X switch(grpack(&xxx, &yyy, &len))
X {
X case LOSTPKT:
X printmsg(M_MEDPROTO, "Lost packet...");
X if(nlost > MAXLOST)
X return -1;
X next = send; /* retransmit last un-ack'ed pkt, */
X if(OP_READ == why) /* request retransmit */
X neednack = 1;
X skipping = 0;
X break;
X case BADPKT:
X printmsg(M_MEDPROTO, "Bad packet...");
X neednack = 1; /* reject! */
X skipping = 1; /* ignore the rest of the 'window' */
X break;
X case TT_SDATA:
X shortdat = 1;
X /* fall through */
X case TT_DATA:
X send = (yyy + 1) & 7; /* recieve acknowledgement */
X if(xxx != ((recv + 1) & 7))
X {
X printmsg(M_MEDPROTO,
X "Recieved data out of sequence (%d != %d)",
X xxx, (recv + 1) & 7);
X if(!skipping)
X {
X neednack = 1; /* we must have missed one */
X skipping = 1;
X }
X else
X printmsg(M_MEDPROTO, "(Ignoring)");
X }
X else
X {
X recv = xxx; /* this is most recent correct pkt */
X needack = 1; /* we will ack it */
X skipping = 0;
X inpbufs[xxx].length = len;
X inpbufs[xxx].sdata = shortdat;
X memcpy(inpbufs[xxx].bufloc, bufp, len);
X }
X break;
X case TT_CTRL:
X skipping = 0;
X switch(xxx)
X {
X case X_CLOSE:
X printmsg(M_MEDPROTO, "* CLOSE *");
X gsendpk(TT_CTRL, X_CLOSE, 0, NULL, 0);
X chanopen = 0;
X if(OP_CLOSE == why)
X return 0; /* expected? */
X else
X return -1; /* nope */
X case X_RJ:
X printmsg(M_MEDPROTO, "got RJ yyy=%d", yyy);
X next = send = (yyy + 1) & 7;
X break;
X case X_RR:
X printmsg(M_MEDPROTO, "got RR yyy=%d", yyy);
X send = (yyy + 1) & 7;
X break;
X case X_INITC:
X printmsg(M_MEDPROTO, "* INITC *");
X swindow = yyy;
X if(X_INITC == initpk)
X {
X chanopen = 1;
X return 0;
X }
X break;
X case X_INITB:
X printmsg(M_MEDPROTO, "* INITB *");
X spktsize = 32 << yyy;
X if(X_INITB == initpk)
X initpk = X_INITC;
X break;
X case X_INITA:
X printmsg(M_MEDPROTO, "* INITA *");
X swindow = yyy;
X initpk = X_INITB;
X break;
X default:
X printmsg(M_MEDPROTO, "bad control packet: xxx=%d", xxx);
X }
X break;
X default:
X printmsg(M_MEDPROTO, "bad packet type");
X break;
X }
X }
X}
X
X/*
X * |grpack()| is responsible for reading one 'g'-protocol packet from the
X * input communications channel. This includes framing, detecting timeouts,
X * and checksum validation.
X * The basic strategy is to keep a count of how many bytes have currently
X * been read and how many are needed. When enough bytes for a packet header
X * ("envelope") have been read, it is validated. If it is valid, and it
X * has a nonzero data segment, the data portion is read. When it has
X * everything, it does a checksum test and returns, with the control
X * information stored in |*xxxp|, |*yyyp|, and the data segment length stored
X * in |*lenp|.
X */
Xint grpack(xxxp, yyyp, lenp)
Xint *xxxp, *yyyp; int *lenp;
X{
X int need; /* need how many bytes? */
X int env; /* 'have pkt envelope' flag */
X int gotdle; /* do we have envelope hdr? */
X int tt; /* packet type */
X unsigned sum; /* checksum */
X int remain; /* bytes which we stil need */
X int i;
X
X env = gotdle = 0;
X need = ENV_LEN; /* initially, need a header */
X
X alarm(TIMEOUT); /* time out if we don't have a packet */
X timedout = 0;
X
X while(1)
X {
X if(low == high) /* prevent framebuffer overruns */
X low = high = buf;
X printmsg(M_LOWPROTO, "=> l=%d h=%d g=%d", low - buf, high - buf,
X gotdle);
X
X while((remain = need - (high - low)) > 0)
X {
X if(timedout || (i = sread2(high, remain)) < 0)
X {
X alarm(0);
X ++nlost;
X low = high = buf; /* empty out partial packet, if any */
X return env ? BADPKT : LOSTPKT;
X }
X high += i; /* got some data - move upper limit up */
X }
X if(!gotdle)
X {
X while(low < high) /* look for header 'DLE' prefix */
X {
X if(DLE == *low)
X {
X gotdle = 1;
X break;
X }
X else
X ++low;
X }
X continue;
X }
X else if(!env) /* found DLE, but haven't found header yet */
X {
X if(low > buf) /* move envelope to buf beginning */
X {
X register unsigned char *dst = buf;
X while(low < high)
X *dst++ = *low++;
X low = buf;
X high = dst;
X }
X if(buf[ENV_X] != (buf[ENV_K]^buf[ENV_C0]^buf[ENV_C1]^buf[ENV_C])
X || buf[ENV_K] < 1 || buf[ENV_K] > 9) /* valid? */
X {
X ++low;
X gotdle = 0;
X printmsg(M_LOWPROTO, "grpack: rejecting an envelope");
X continue;
X }
X env = 1; /* we have an envelope */
X tt = (buf[ENV_C] >> 6) & 3; /* store away control info */
X *xxxp = (buf[ENV_C] >> 3) & 7;
X *yyyp = (buf[ENV_C] ) & 7;
X if(buf[ENV_K] == 9) /* does it have data? */
X *lenp = 0;
X else
X *lenp = 16 << buf[ENV_K]; /* size = 32 * 2^(k-1) */
X need += *lenp; /* now need that many more */
X printmsg(M_LOWPROTO, "grpack: tt=%d, xxx=%d, yyy=%d, need=%d",
X tt, *xxxp, *yyyp, need);
X continue;
X }
X else
X { /* have everything we need */
X if(*lenp)
X sum = MAGIC - (cksum(buf + ENV_LEN, *lenp) ^ buf[ENV_C]);
X else
X sum = MAGIC - buf[ENV_C];
X if(((sum >> 8) & 0xFF) != buf[ENV_C1]
X || (sum & 0xFF) != buf[ENV_C0])
X {
X alarm(0);
X nlost = 0;
X printmsg(M_LOWPROTO, "grpack: bad checksum");
X low += ENV_LEN; /* we will search bad data seg for a header */
X return BADPKT;
X }
X else
X {
X alarm(0); /* got it all... return */
X nlost = 0;
X low += need;
X if(*lenp)
X printmsg(M_DATA, "|%s|", visib(buf + ENV_LEN, *lenp));
X return tt;
X }
X }
X }
X}
X
X/*
X * gspack simply sends out a packet, by assembling an envelope and writing
X * it, and the data segment, to the communications channel.
X */
Xgspack(tt, xxx, yyy, data, size)
Xint tt, xxx, yyy; unsigned char *data; int size;
X{
X unsigned char envelope[ENV_LEN]; /* packet envelope */
X unsigned sum; /* checksum */
X unsigned char ctrl; /* header control byte */
X int k; /* size = 32 * 2^(k-1) */
X
X ctrl = ((tt & 3) << 6) | ((xxx & 7) << 3) | (yyy & 7);
X
X if(0 == size)
X {
X k = 9; /* no data seg */
X sum = MAGIC - ctrl;
X }
X else
X {
X int tmp = size;
X for(k = -5; tmp != 0; ++k) /* find k */
X tmp >>= 1;
X sum = MAGIC - (cksum(data, size) ^ ctrl);
X }
X envelope[ENV_DLE] = DLE;
X envelope[ENV_K] = k;
X envelope[ENV_C1] = (sum >> 8) & 0xFF;
X envelope[ENV_C0] = sum & 0xFF;
X envelope[ENV_C] = ctrl;
X envelope[ENV_X] = k ^ envelope[ENV_C0] ^ envelope[ENV_C1] ^ ctrl;
X swrite(envelope, ENV_LEN); /* send envelope */
X if(0 != size)
X swrite(data, size); /* send data segment */
X printmsg(M_LOWPROTO, "gspack: tt=%d xxx=%d yyy=%d k=%d sum=%d",
X tt, xxx, yyy, k, sum);
X if(0 != size)
X printmsg(M_DATA, "|%s|", visib(data, size));
X}
X
X/*
X * chksum(data, len) came directly from dcp. It checksums the given data
X * area using the g protocol algorithm.
X */
Xunsigned cksum(data, len)
Xint len; unsigned char *data;
X{
X unsigned int i, j, tmp, chk1, chk2;
X
X chk1 = 0xffff;
X chk2 = 0;
X j = len;
X for (i = 0; i < len; i++)
X {
X if(chk1 & 0x8000)
X {
X chk1 <<= 1;
X chk1++;
X }
X else
X {
X chk1 <<= 1;
X }
X tmp = chk1;
X chk1 += (data[i] & 0xff);
X chk2 += chk1 ^ j;
X if((chk1 & 0xffff) <= (tmp & 0xffff))
X chk1 ^= chk2;
X j--;
X }
X return (chk1 & 0xffff);
X}
X
X/* |ilog2(value)| returns (int)floor(log2(value)).
X */
Xint ilog2(value)
Xunsigned value;
X{
X int i;
X
X if(value == 0)
X return -1;
X for(i = 0; value > 1; ++i)
X value >>= 1;
X
X return i;
X}
**-newgpkt.c-EOF-**