[comp.os.minix] UUPC for Minix part 4/5

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-**