4.4BSD/usr/src/sys/vax/if/ACC/ucb_beta.tar

README   444    540     24       13305  4535335470   5014 README:		changes	-- pst 7/19/89 -- test release
		recently tested under: Ultrix 3.0 and 4.3tahoe

note: the man pages have been updated;  please read them for more info.

		[1]

This version of the driver has had changes made to data structures shared
between acpconfig and the kernel resident software.  It is imperative that
installation of this software is done in sequence.

	(a)	move the driver files to their proper locations:
		cp if_dda* if_x29* if_pi* /sys/vaxif
	(b)	compile acpconfig and generate the new kernel (see note [2])
	(c)	install the new acpconfig in /etc or wherever finer system
		management tools belong and install the new kernel.

The new version of acpconfig has a "generation" date which is displayed if
you simply type "acpconfig."  The date on the acpconfig shipped with this
driver should be 19-Jul-1989.

		[2]

The driver is no longer auto-configuring, as there are just too many different
systems available now.  You must tell it what type of system you are running.
This is done by adding the appropriate "options" line to your kernel
configuration file in the /sys/conf directory.

For example:

options	ACC_BSD=43		for 4.3bsd or 4.3-tahoe system
options	ACC_BSD=42		for 4.2bsd
options ACC_ULTRIX=30		for Ultrix 3.0
options ACC_ULTRIX=22		for Ultrix 2.2
options ACC_ULTRIX=20		for Ultrix 2.0
options ACC_ULTRIX=12		for Ultrix 1.2
options ACC_VMS=4n		for VAX/VMS 4.n	(Multinet or WIN/TCP)
options ACC_VMS=5n		for VAX/VMS 5.n	(Multinet or WIN/TCP)

In addition to the operating system definitons,  if you are using either
the X.29 option or the programmers interface option, you may enable these
options by adding options statements.

options	DDA_RAWOPT		enables the raw "programmer's interface"
options	DDA_PADOPT		enables the X.29 pad/host option

Debugging code is normally unused in a customer environment,  but some
sites may find it useful when using the programmer's interface or when
working on site dependant modifications to the driver.  If you wish to
enable the debugging code,  add the line:

options	DDADEBUG


		[3]

The debug facility for the driver has been completely rewritten.  Debug
messages are now controlled via the acpconfig "-c" command and may be
selectively enabled or disabled.

Messages 0 - 127 are error messages.  By default, they are on.  To toggle
on or off the display of these messages, simply type acpconfig -c <msgnum>.

The following error messages have been added for the X29 module:

[96]  dda%d:(x29) xxstart: unit offline
[97]  dda%d:(x29) xxstart: could not get mbuf
[98]  dda%d:(x29) x29_supr: unexpected message type
[100] dda%d:(x29) Bad decode, call REJECTED VC 0x%x
[101] dda%d:(x29) Call cleared LCN %d (%x %x)
[102] dda%d:(x29) X25 RESET on LCN %d (%x %x)
[104] dda%d:(x29) supervisor error (%x %x %x %x)
[105] dda%d:(x29) x29_dhandle: null mbuf
[106] dda%d:(x29) couldn't get mbuf for QBIT message
[107] dda%d:(x29) x29_supr: answer: line was -1, VC 0x%x
[107] dda%d:(x29) x29_supr: ring: line was -1, VC 0x%x
[107] dda%d:(x29) x29_supr: break: line was -1, VC 0x%x
[107] dda%d:(x29) xx_tp_hangup: line was -1
[108] dda%d:(x29) x29_supr: unexpected answer on LCN %d

Messages 128 - 255 are debug assistance messages.  They are not compiled
in unless DDADEBUG is defined.  If the code has been compiled in,  they
may be enabled (they are OFF by default) by using acpconfig.  The current
manual page for dda(4) has a list of debug messages on the last page.
Add 128 to the number in the square bracket and use acpconfig -c to toggle
the printing of the message.

Example:
	[0] dda%d: ddainit()

	Type "acpconfig -c 128" to toggle this message.

* Do not attempt to use the old acpconfig commands "-v debug" and "-v dbg_unit",
* as they are nonfunctional with this driver.

The following messages are debug messages,  they are off by default, I
have already added 128 to the number.

[224] dda%d:(x29) open line %d flag %o in %s mode
[225] dda%d:(x29) closing line %d
[225] dda%d:(x29) close: tp->t_state = %x
[226] dda%d:(x29) ioctl qbit msg: cmd=%x ACC=%x
[227] dda%d:(x29) xxstart: port %d t_state = %x
[228] dda%d:(x29) xxstart: asked for %d got %d chars
[229] dda%d:(x29) xxstart: mbuf %x len %d
[230] dda%d:(x29) select()
[231] dda%d:(x29) x29_supr()
[231] dda%d:(x29) supr_msg: got call from %X
[232] dda%d:(x29) x29_data: chan=%x cc=%x cnt=%x subcc=%x
[233] dda%d:(x29) received data: <stream>
[234] dda%d:(x29) x29_data: read complete mbuf %x %x
[235] dda%d:(x29) x29_data: chan=%x DDAIOCOK
[236] dda%d:(x29) qbit: <stream>
[237] dda%d:(x29) flow restart [%d] in %x
[237] dda%d:(x29) flow on [%d] in %x of %d
[238] dda%d:(x29) xx_qbit_msg: %d %d %d
[239] dda%d:(x29) xxcntl()
[239] dda%d:(x29) xxcntl: close state: %s
[239] dda%d:(x29) xxcntl: warning: state not data_idle
[240] dda%d:(x29) xxclear: line=%d pgrp=%d state=%d

	[4]

4.3-tahoe support has been added.  However, due to a change in the cpp
(they finally fixed it!),  ioctl command argument definitions have changed.
In releases prior to 4.3-tahoe,  argument definitions looked like:

#define	XIOWRITE	_IOWR(t, 1, struct pi_dblock)

The newer systems require the ioctl defines to have the command surrounded
by apostrophe marks,  like the following line:

#define	XIOWRITE	_IOWR('t', 1, struct pi_dblock)

Since most of our customers are running Ultrix or versions of BSD UNIX older
than 4.3-tahoe,  we ship the PI module with the apostrophes removed.  If you
are running 4.3-tahoe or a newer BSD release,  you should rename the
current if_pivar.h file to if_pivar.h.pretahoe and rename the if_pivar.h.tahoe
file to if_pivar.h.  The only difference in the files is the addition of
the apostrophe as the CPP parameter delimiter.

You will also need to modify the three ioctl definitions that were previously
 placed in /usr/include/sys/ioctl.h if you haven't done so already.
sages 0 - 127 are error messages.  By default, they are on.  To toggle
on or off the display of these messages, simply type acpconfig -c <msgnum>.

The following error messages have been added for the X29 module:

[96]  dda%d:(x29) xxstart: unit offline
[97]  dda%d:(x29) xxstart: could not get mbuf
[98]  dda%d:(x2acpconfig/   755    540     24           0  4535335666   5767 acpconfig/acpconfig.c   444    540     24      213456  4535335476  10221 
#define	VERSION	"13-Nov-1989\n"

/*************************************************************************/
/*									 */
/*									 */
/*	 ________________________________________________________	 */
/*	/							 \	 */
/*     |	  AAA	       CCCCCCCCCCCCCC	 CCCCCCCCCCCCCC	  |	 */
/*     |	 AAAAA	      CCCCCCCCCCCCCCCC	CCCCCCCCCCCCCCCC  |	 */
/*     |	AAAAAAA	      CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |	 */
/*     |       AAAA AAAA      CCCC		CCCC		  |	 */
/*     |      AAAA   AAAA     CCCC		CCCC		  |	 */
/*     |     AAAA     AAAA    CCCC		CCCC		  |	 */
/*     |    AAAA       AAAA   CCCC		CCCC		  |	 */
/*     |   AAAA	 AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |	 */
/*     |  AAAA	  AAAAAAAAAAA CCCCCCCCCCCCCCCC	CCCCCCCCCCCCCCCC  |	 */
/*     | AAAA	   AAAAAAAAA   CCCCCCCCCCCCCC	 CCCCCCCCCCCCCC	  |	 */
/*	\________________________________________________________/	 */
/*									 */
/*	Copyright (c) 1988 by Advanced Computer Communications		 */
/*	720 Santa Barbara Street, Santa Barbara, California  93101	 */
/*	(805) 963-9431							 */
/*									 */
/*									 */
/*  File:		acpconfig.c					 */
/*									 */
/*  Author:		Clare E. Russ					 */
/*									 */
/*  Project:		Installation verification program for ACC	 */
/*			ACP 5100/6100 and ACP 5250/6250 network		 */
/*			interface drivers.   Acpconfig provides a user	 */
/*			interface which supports the configuration of a	 */
/*			network	 interface.				 */
/*									 */
/*  Function:								 */
/*	  Based on socket ioctls, provide user interface to Network	 */
/*	  Interface Drivers for the following ACC ACP devices:		 */
/*									 */
/*		    ACP 5100/6100  'acp' interface			 */
/*		    ACP 5250/6250  'dda' interface			 */
/*		    ACP 625	   'ddn' interface			 */
/*									 */
/*	  The ioctl indicates what type of message is to be sent to the	 */
/*	  front end device (ie, bring up the HDLC line in external	 */
/*	  loopback mode).						 */
/*									 */
/*  Components:		acpconfig.c, ioctl.h				 */
/*									 */
/*									 */
/*  Usage Notes:							 */
/*									 */
/*	   -A filename							 */
/*		Add the contents of the named file to the address	 */
/*		translation table for PDN service			 */
/*									 */
/*	   -a ipaddr x25addr						 */
/*		Add the specified address pair to the address		 */
/*		translation table for PDN service			 */
/*									 */
/*	   -b baud							 */
/*		Note that the baud rate values are different for the	 */
/*		ACP 625.  Table 1 contains the baud rates which apply	 */
/*		to the ACP 6100, ACP 5250/6250 products.  Table 2	 */
/*		contains the baud rates which apply to the ACP 625.	 */
/*									 */
/*	  Table 1:  Nominal baud rates for ACP 5100/6100, ACP 5250/6250	 */
/*									 */
/*			2.00M  2000K  2.0M  1.33M  1333K  1.3M		 */
/*			1.00M  1000K  1.0M  500K   250K	  100K		 */
/*			64K    64000  56K   56000  30K	  19.2K		 */
/*			9.6K   9600   4.8K  4800   2.4K	  2400		 */
/*			1.2K   1200					 */
/*									 */
/*	  Table 2:  Nominal baud rates for ACP 625			 */
/*									 */
/*			316000	153600	 115200	  76800	  76.8K		 */
/*			 57600	 57.6K	  38400	  38.4K	  28800		 */
/*			 28.8K	 19200	  19.2K	   9600	   9.6K		 */
/*			  4800	  4.8K	   2400	   2.4K	   2150		 */
/*			  1760	  1200					 */
/*									 */
/*		An argument of 0 or "external" to the -b option		 */
/*		specifies external clocking for the ACP 5100/6100	 */
/*		and ACP 5250/6250 interfaces. (The ACP 625 does not	 */
/*		support this feature.)	 All other values imply		 */
/*		internal clocking.					 */
/*		The M for megabits/second and K for kilobits/second	 */
/*		are optional.  Note that Table 1 allows for the entry	 */
/*		of 9.6 (with the assumed unit of Kilobits/second), but	 */
/*		also allows for the entry of 9600 (bits/second).  Thus	 */
/*		there is more than one entry for some of the baud rate	 */
/*		values.							 */
/*									 */
/*	   -c msgnum							 */
/*		Toggle the enable status of driver message number msgnum */
/*									 */
/*	   -d ipaddr							 */
/*		Delete the specified address entry from the address	 */
/*		translation table for PDN service			 */
/*									 */
/*	   -D								 */
/*		Delete all address entries form the address translation	 */
/*		table for PDN service					 */
/*									 */
/*	   -e size							    */
/*		set the firmware buffer size to size.  This also resets	 */
/*		the board.  size comes from a table of valid sizes and	 */
/*		may also be "default" to get the default size.		 */
/*									 */
/*	   -h mode							 */
/*		Read driver histogram and print on standard output. Each */
/*		entry is of the form sec.usec. The entries are as	 */
/*		follows:						 */
/*		0    : number of seconds the link was up		 */
/*		1    : starting time					 */
/*		2    : ending time					 */
/*		3    : current value for tmo_data_idle			 */
/*		4-69 : seconds n logical channels were open for		 */
/*		       n = 0 to 64 (126)				 */
/*									 */
/*		If mode is 0 then data is read.	 If mode is 1 then data	 */
/*		is read and the histogram is reinitialized.		 */
/*									 */
/*	   -f parameter status						 */
/*		Control flow control parameter negotiation where	 */
/*		parameter is either "packet" or "window" and		 */
/*		status is either "on" or "off".				 */
/*		Note:  incoming flow control parameter negotiation	 */
/*		if not effected by this option.				 */
/*									 */
/*	   -l								 */
/*	   -ln								 */
/*		List the currently active lcns.  The 'n' suffix		 */
/*		disables address to name lookups.			 */
/*									 */
/*	   -m message							 */
/*		Pass ``message'' to the FEP as a supervisory channel	 */
/*		message.  Message is a sequence of numbers separated	 */
/*		by white space.	 Numbers with leading ``0'' are taken	 */
/*		as octal, other numbers taken as HEX, decimal is not	 */
/*		supported.  Hex and octal may be intermixed, as in	 */
/*		``0140 0 0 2 89 017''.	The message is terminated by the */
/*		end of the argument list or by an argument beginning	 */
/*		with a dash ``-''.  Absolutely no checking is performed; */
/*		the bytes are written to the FEP as a supervisor	 */
/*		message.  The message is limited to MLEN = 112 bytes.	 */
/*									 */
/*	   -N network							 */
/*		Set network type					 */
/*		transpac - french transpac network addressing		 */
/*		default	 - normal x25 network				 */
/*		net15	 - normal network but with 15 digit addrs	 */
/*									 */
/*	   -n circuits							 */
/*		Set the number of logical channels to ``circuits'', which*/
/*		must be less than some implementation defined maximum.	 */
/*									 */
/*	   -q query-type						 */
/*		Query the board or driver and place results on stdout.	 */
/*									 */
/*	   -q 0	 Statistics query to FEP				 */
/*	   -q 1	 Driver operating mode query				 */
/*	   -q 2	 Reserved for ACC debugging				 */
/*									 */
/*	   -r count							 */
/*		Read the specified number of entries from the address	 */
/*		translation table for PDN service			 */
/*									 */
/*	   -r 0	 read all entries from the address translation table	 */
/*									 */
/*	   -s X.25 service						 */
/*		Select DDN standard X.25 service or basic X.25 service	 */
/*		or Public Data Network X.25 service			 */
/*									 */
/*	   -s standard	select standard X.25 service			 */
/*	   -s basic	select basic X.25 service			 */
/*	   -s pdn	select X.25 Public Data Network service		 */
/*									 */
/*	   -t sec	Set idle circuit timeout			 */
/*									 */
/*	   -u down  bring down the link					 */
/*	   -u dte   bring up link, no loopback, DTE			 */
/*	   -u dce   bring up link, no loopback, DCE			 */
/*	   -u ext   bring up link, external loopback mode		 */
/*	   -u int   bring up link, internal loopback mode		 */
/*									 */
/*	   -v variable value						 */
/*		Set the value of a driver variable symbolized by	 */
/*		"variable" to "value" decimal.	"Variables" understood	 */
/*		are "window" and "packet" to set the driver's notion of	 */
/*		negotiable window size, and negotiable packet size.	 */
/*									 */
/*	   -z  reset the specifed front-end device			 */
/*									 */
/*  Compile Notes:							 */
/*									 */
/*	The associated makefile builds the acpconfig executable image	 */
/*	for UNIX or the specified emulation/simulation such as		 */
/*	The Wollongong Group (TWG) software which runs under VAX/VMS.	 */
/*	The makefile must be invoked with the argument which specifies	 */
/*	the target host operating system, otherwise the makefile	 */
/*	defaults to creating an executable image for UNIX.		 */
/*									 */
/*		  make		       (compile for UNIX 4.nBSD)	 */
/*		  make clean						 */
/*		  make print						 */
/*									 */
/*									 */
/*  System Notes:							 */
/*									 */
/*	Create a socket of the AF_INET address family and of type	 */
/*	datagram, SOCK_DGRAM.  Use the socket to send a socket ioctl	 */
/*	to the network driver of the specified interface ('acp0' in	 */
/*	the usage notes).   Depending on the type of message specified	 */
/*	in the command line, send the appropriate socket ioctl to	 */
/*	specify what kind of action is to be taken.			 */
/*									 */
/*	For those drivers which support the Control Interface (CIF)	 */
/*	the message exchange conforms to the CIF which defines paired	 */
/*	command/response Control Interface Messages (CIMs) between the	 */
/*	host and the front end.	  Otherwise, the exchange of messages	 */
/*	is the pre-CIF protocol (i.e., that used  by the ddn interface). */
/*									 */
/*	Assignment of subcodes for the SIOCACPCONFIG ioctl:		 */
/*									 */
/*	1..14		-bRATE for RATE != 0				 */
/*	`0'..`4'	-u N (line control)				 */
/*	`a'		-A and -a commands (PDN)			 */
/*	`b'		-b0 (set external clock)			 */
/*	`c'		-c msgnum					 */
/*	`d'		-d command (PDN)				 */
/*	`e'		-e size						 */
/*	`h'		-h 0						 */
/*	`H'		-h 1						 */
/*	`m'		-m (message command)				 */
/*	`n'		-n circuits (limit lcns)			 */
/*	`p'		-q 0 (statistics query)				 */
/*	`q'		-q 1 (driver query)				 */
/*	`r'		-r command (PDN)				 */
/*	`t'		-t sec (timeout set)				 */
/*	`S'		-s 0						 */
/*	`T'		-s 1						 */
/*	`U'		-s 2						 */
/*	`V'		-v command (set variable)			 */
/*	`z'		-z command (reset)				 */
/*									 */
/*************************************************************************/

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								    %%*/
/*%%		   INCLUDE FILES				    %%*/
/*%%								    %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*   There are alternate path names for The Wollongong Group (TWG)    */
/*   IPTCP and Eunice software which runs under VMS,		      */
/*   and for TGV, Incorporated's MultiNet software which also runs    */
/*   under VMS.							      */

#ifdef	VAXVMS
#  ifdef eunice
#    define WINS
#  else
#    define MULTINET
#  endif
#endif

#ifdef	vax11c
#  define	EXIT_ERR	0
#  define	EXIT_OK		1
#else
#  define	EXIT_ERR	1
#  define	EXIT_OK		0
#endif

#include <sys/param.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <netdb.h>
#include <nlist.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/ioctl.h>

#ifdef	MULTINET
#include <vaxif/if_ddaioctl.h>		/* multinet doesn't fix ioctl.h */
#endif

#ifdef	VAXVMS				/* TWG or TGV */
#include <netinet/in.h>
#include <net/if.h>
#else
#include "/sys/netinet/in.h"
#include "/sys/net/if.h"
#endif

#ifdef	MULTINET
#ifdef	errno
#undef	errno
#endif	errno
#define errno	socket_errno		/* MultiNet runtime errors	*/
#define Perror(s) XPerror(s)		/* Keep different from perror() */
#define ioctl(s,c,d) socket_ioctl(s,c,d)/* MultiNet ioctl() routine	*/
#endif	MULTINET

#define NDDA		1
#define NONE		0		/* used by the -o option */
#define EXTENDED	1		/* used by -o option */

/* 'iface' interface definitions */
#define ACP_INTERFACE	0x01		/* for acp interface */
#define DDN_INTERFACE	0x02		/* for ddn interface */

#define BAUD_VAL	50		/* for baud rate flag, -b */
#define SERVICE_VAL	35		/* for X.25 service flag, -s */
#define ACTUAL_VAL	83		/* for X.25 service flag 'S' */

/* network values */
#define NET_STANDARD	0		/* on standard network */
#define NET_TRANSPAC	1		/* on transpac style network */

/* delay macro from /sys/vax/param.h */

#ifdef vax11c
#define NEW_DELAY(n)	sleep(n / 100000)
#else
#define NEW_DELAY(n)	{ register int N = (n); while (--N > 0); }
#endif


/* The baud_rate structure maps the user-supplied baud rate into the */
/* parameter used in the socket ioctl to the driver.  In some cases, */
/* more than one representation of a value is present to ease the    */
/* interface.  The manual page for acpconfig recommends that the     */
/* user give the baud rate with the assumed unit of Kilobits/second. */

struct	baud  {
	char	*b_rate;
	char	parameter;
}  baud_rate[] = {
	{ "2.00",	1 },		/* these are nominal baud rates */
	{ "2.00M",	1 },
	{ "2000K",	1 },
	{ "2000k",	1 },
	{ "2.0",	1 },
	{ "2.0M",	1 },
	{ "1.33",	2 },
	{ "1.33M",	2 },
	{ "1333K",	2 },
	{ "1333k",	2 },
	{ "1.3",	2 },
	{ "1.3M",	2 },
	{ "1.00",	3 },
	{ "1.00M",	3 },
	{ "1000K",	3 },
	{ "1000k",	3 },
	{ "1.0M",	3 },
	{ "1.0",	3 },
	{ "500",	4 },
	{ "500K",	4 },
	{ "500k",	4 },
	{ "250",	5 },
	{ "250K",	5 },
	{ "250k",	5 },
	{ "100",	6 },
	{ "100K",	6 },
	{ "100k",	6 },
	{ "64",		7 },
	{ "64K",	7 },
	{ "64k",	7 },
	{ "64000",	7 },
	{ "56",		8 },
	{ "56K",	8 },
	{ "56k",	8 },
	{ "56000",	8 },
	{ "30",		9 },
	{ "30K",	9 },
	{ "30k",	9 },
	{ "19.2",	10 },
	{ "19.2K",	10 },
	{ "19.2k",	10 },
	{ "9.6",	11 },
	{ "9.6K",	11 },
	{ "9.6k",	11 },
	{ "9600",	11 },
	{ "4.8",	12 },
	{ "4.8K",	12 },
	{ "4.8k",	12 },
	{ "4800",	12 },
	{ "2.4",	13 },
	{ "2.4K",	13 },
	{ "2.4k",	13 },
	{ "2400",	13 },
	{ "1.2",	14 },
	{ "1.2K",	14 },
	{ "1.2k",	14 },
	{ "1200",	14 },
	{ 0,		0 },
};


/* Table of baud rate values and the associated parameter for the Set	*/
/* System Parameters message, ddninit_msg.  The 'parameter1' is nonzero */
/* for valid baud rate divisors.  The user's manual gives both the	*/
/* actual and nominal baud rates, either one is accepted from the user, */
/* but the nominal baud rate is the figure which is closest to the rate */
/* set on the front end.						*/

struct	baud	ddnbaud_rate[] =	{
	{ "333333",	1 },		/* actual baud rate  */
	{ "316000",	1 },		/* nominal baud rate */
	{ "153846",	2 },		/* actual baud rate  */
	{ "153600",	2 },		/* nominal baud rate */
	{ "114285",	3 },		/* actual baud rate  */
	{ "115200",	3 },		/* nominal baud rate */
	{ "76923",	4 },		/* actual baud rate  */
	{ "76800",	4 },		/* nominal baud rate */
	{ "76.8K",	4 },		/* nominal baud rate */
	{ "76.8k",	4 },		/* nominal baud rate */
	{ "57971",	5 },		/* actual baud rate  */
	{ "57600",	5 },		/* nominal baud rate */
	{ "57.6K",	5 },		/* nominal baud rate */
	{ "57.6k",	5 },		/* nominal baud rate */
	{ "38461",	6 },		/* actual baud rate  */
	{ "38400",	6 },		/* nominal baud rate */
	{ "38.4K",	6 },		/* nominal baud rate */
	{ "38.4k",	6 },		/* nominal baud rate */
	{ "28776",	7 },		/* actual baud rate  */
	{ "28800",	7 },		/* nominal baud rate */
	{ "28.8K",	7 },		/* nominal baud rate */
	{ "28.8k",	7 },		/* nominal baud rate */
	{ "19230",	8 },		/* actual baud rate  */
	{ "19200",	8 },		/* nominal baud rate */
	{ "19.2K",	8 },		/* nominal baud rate */
	{ "19.2k",	8 },		/* nominal baud rate */
	{ "9592",	9 },		/* actual baud rate  */
	{ "9600",	9 },		/* nominal baud rate */
	{ "9.6K",	9 },		/* nominal baud rate */
	{ "9.6k",	9 },		/* nominal baud rate */
	{ "4801",	10 },		/* actual baud rate  */
	{ "4800",	10 },		/* nominal baud rate */
	{ "4.8K",	10 },		/* nominal baud rate */
	{ "4.8k",	10 },		/* nominal baud rate */
	{ "2400",	11 },		/* actual baud rate  */
	{ "2.4K",	11 },		/* actual baud rate  */
	{ "2.4k",	11 },		/* actual baud rate  */
	{ "2150",	12 },		/* nominal baud rate */
	{ "1760",	13 },		/* actual baud rate  */
	{ "1760",	13 },		/* actual baud rate  */
	{ "1200",	14 },		/* nominal baud rate */
	{ "1.2K",	14 },		/* nominal baud rate */
	{ "1.2k",	14 },		/* nominal baud rate */
	{ 0,		0 },
};

#ifndef MULTINET
char	*kmemf = "/dev/kmem";
int	kmem;
#endif	MULTINET


#include <strings.h>
#include <sys/time.h>

#ifdef VAXVMS
#  ifdef WINS
#    include <vaxif/if_ddareg.h>
#    include <vaxif/if_ddavar.h>
#  endif
#  ifdef MULTINET
#    include "[-.kernel.vaxif]if_ddareg.h"
#    include "[-.kernel.vaxif]if_ddavar.h"
#  endif
#else VAXVMS				/* must be unix or ultrix */
#  include "/sys/vaxif/if_ddareg.h"
#  include "/sys/vaxif/if_ddavar.h"
#endif VAXVMS

/* EXTERNAL FUNCTIONS */
extern	char *routename();
extern	off_t lseek();
extern	u_long inet_addr();
extern	char *inet_ntoa();
extern	u_long inet_network();

/*
 *	Weirdness for test-jig, re-map all ioctl and socket calls to go
 *	through driver simulator.
 */

#ifdef	SIMULATION
#define ioctl	fake_unix_ioctl
#define socket	fake_unix_socket
#define main	cf_main
#endif

/*
 *	Print contents of queues and various data structures
 */

char *state_tab[] = {
        "Down",
	"Rstrt",
	"Idle",
	"Call",
	"Open",
	"Clear"
	};
#define NSTATES 5




/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%			     MAIN()				    %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*								      */
/*  Purpose:							      */
/*								      */
/*	For the specified interface, create a socket for a socket     */
/*	ioctl to set the Internet address and configuration.  The     */
/*	ioctl kicks the appropriate driver, the value in ifr_data     */
/*	indicates the type of action to be taken.  ('b' indicates     */
/*	external clock, 1-14 indicates baud rate from the table of    */
/*	possible values, '0' - '4' indicates -u options, and 's','t', */
/*	'u' indicate -s options.)				      */
/*	Enhancement: 'a' indicates addition of an address table	      */
/*	entry, 'd' indicates deletion of an address table entry,      */
/*	and 'r' is a request to read address table entries.	      */
/*	Etc, etc (see comment at top for complete list)		      */
/*								      */
/*  Call:		main( argc, argv)			      */
/*  Arguments:		argc:	argument count			      */
/*			argv:	argument value			      */
/*  Returns:		nothing					      */
/*  Called by:		invoked by privileged user		      */
/*  Calls to:		socket()				      */
/*			perror()				      */
/*			strcpy()				      */
/*			strncpy()				      */
/*			sizeof()				      */
/*			usage()					      */
/*			long_usage()				      */
/*			get_bfrsize_index()			      */
/*			ioctl()					      */
/*			exit()					      */
/*			Perror()				      */
/*								      */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

struct	ifreq ifr;
struct	sockaddr_in sin = { AF_INET };
char	name[30];
int	s;			/* socket descriptor */
int	nflag;			/* control host name lookup (symbolic) */
struct ddactl ddactl;
struct trtab trtab;
struct lg2tab {			/* For use with packet sizes, so just the */
	int base2log;		/* legal packet sizes for X.25 are here */
	int binval;
} lg2tab[] = {
	4,	16,
	5,	32,
	6,	64,
	7,	128,
	8,	256,
	9,	512,
	10,	1024,
	11,	2048,
	12,	4096,
	0,	0,
};

char *fe_bfr_sizes[] =
{
    "default",		/* entry 0: Default Size */
    "256",		/* entry 1: 256 bytes */
    "512",		/* entry 2: 512 bytes */
    "1024",		/* entry 3: 1K bytes (currently same as default) */
    "2048",		/* entry 4: 2K bytes */
    "4096",		/* entry 5: 4K bytes */
    "8192",		/* entry 6: 8K bytes */
    "16384",		/* entry 7: 16K bytes */
    NULL
};

extern int errno;


/*
 * Structure used to hold the results of an nlist library
 * function call.
 */

struct nlist nl[] = {
	{ "_dda_softc" },
#define SOFTC	0
	"",
};


#ifndef VAXVMS
/*
 * Seek into the kernel for a value.
 * Used by the -l option of acpconfig.
 */
klseek(fd, base, off)
int fd;
off_t base;
int off;
{

	if (lseek(fd, base, off) == (off_t)-1 ){
		fprintf(stderr,"acpconfig:  Bad lseek fd=%d,bas=%x,off=%d ",fd,base,off);
		perror("");
		exit(1);
	}
}

klread(fd,buf,size)
int fd;
char *buf;
int size;
{
	if ( read(fd,buf,size) < 0 ){
		perror("Read");
		exit(2);
	}
}
#endif VAXVMS

/*
 *	Routine used in conjunction with the -l option.
 *	This function displays the status of each active
 *	lcn for the given unit.
 */

display(addr,nddach)
off_t addr;
int nddach;  /* number of circuits currently available */
{
	struct dda_softc dda_softc;
	register struct dda_cb *dc;
	struct sockaddr_in sin;
	char *p;
	int i;
	int header;

	if (addr == 0) {
		printf("acpconfig: nlist--symbol not defined\n");
		return;
	}
#ifdef	MULTINET
	klseek(addr);
	klread((char *)&dda_softc, sizeof(dda_softc));
#else	MULTINET
	klseek(kmem, addr, 0);
	klread(kmem, (char *)&dda_softc, sizeof(dda_softc));
#endif	MULTINET
	header = 0;
	for ( i=0; i<= nddach ; i++)
 	{
	    dc = &dda_softc.dda_cb[i];
	    if ((dc->dc_state != LC_IDLE) && (dc->dc_state != LC_DOWN))
	    {
		if (!header) 
		{
	    	    printf("\n\t\t\tACP 6250 / 5250 (dda%d) Status\n", 
			dda_softc.dda_if.if_unit);
		    printf("lcn Qlen Dropped Flags Timer State  Pin Pout Win Wout  IP Addr\n");
		}
		header++;
		printf("%3d ",i);
	    	printf("%4d ",dc->dc_oq.ifq_len);
	    	printf("%6d  ",dc->dc_oq.ifq_drops);
		switch (dc->dc_flags & DC_CLIENTS) {
		    case DC_X29:	p="Pad "; break;
		    case DC_X29W:	p="Host"; break;
		    case DC_RAW:	p="PI  "; break;
		    case DC_IP:		p="IP  "; break;
		    default:		p="bug!"; break;
		}
		printf("%c %s", (dc->dc_flags & DC_OBUSY ? 'B' : ' '), p);
	        printf(" %3d  ", dc->dc_timer);
		if (dc->dc_state > NSTATES)
		    printf ("%4x? ", dc->dc_state);
		else
		    printf("%-5s ", state_tab [dc->dc_state]);
		printf(dc->dc_pktsizein ? "%4d ":" ??? ",1<<dc->dc_pktsizein);
		printf(dc->dc_pktsizeout ?"%4d ":" ??? ",1<<dc->dc_pktsizeout);
		printf(dc->dc_wsizein ? "%3d ":"??? ",dc->dc_wsizein);
		printf(dc->dc_wsizeout ?"%3d ":"??? ",dc->dc_wsizeout);
		if ((dc->dc_flags & DC_CLIENTS) == DC_IP) 
		    printf("  %s",routename(dc->dc_inaddr));
		putchar('\n');
	    }
        }
	if ((!header) && (i == nddach + 1))
	    printf("lcns 0 through %d inactive\n",nddach);

	sin.sin_addr.s_addr = dda_softc.dda_ipaddr.s_addr;
	printf("Our addr: %s", routename(sin.sin_addr));
	(void)putchar('\n');
}

char *
routename(in)
	struct in_addr in;
{
	char *cp = 0;
	static char line[50];
	int lna, net;

	net = inet_netof(in);
	lna = inet_lnaof(in);
	if (!nflag) {
		if (lna == INADDR_ANY) {
			struct netent *np = getnetbyaddr(net, AF_INET);

			if (np)
				cp = np->n_name;
		} else {
			struct hostent *hp;

			hp = gethostbyaddr((char *)&in, sizeof (struct in_addr),
				AF_INET);
			if (hp)
				cp = hp->h_name;
		}
	}
	if (cp)
		(void)strcpy(line, cp);
	else {
		u_char *ucp = (u_char *)&in;
		if (lna == INADDR_ANY)
			(void)sprintf(line, "%u.%u.%u", ucp[0], ucp[1], ucp[2]);
		else
			(void)sprintf(line, "%u.%u.%u.%u", ucp[0], ucp[1],
				ucp[2], ucp[3]);
	}
	return (line);
}




get_bfrsize_index(str)
char *str;
{
	register int i;

	for(i = 0; fe_bfr_sizes[i]; i++)
		if(strcmp(str,fe_bfr_sizes[i]) == 0)
			return(i);
	fprintf(stderr,"Invalid size: %s\nBuffer sizes available are: ");
	for(i = 0; fe_bfr_sizes[i]; i++)
		fprintf(stderr,"%s ",fe_bfr_sizes[i]);
	fprintf(stderr,"\n");
	return(-1);
}

main(argc, argv)
	int argc;
	char *argv[];
{
    register struct baud *p;            /* baud rates */
    int	iface = 0;			/* indicate interface, i.e. acp */
    int tmp, top, offset;
    int unit;
    FILE *fp;
    char line[80], arg1[40], arg2[40];
    char *ap;
    char option_byte1 = 0;
    int o_flag = 0;

    if (argc == 1)
    {
	long_usage();
    }
    if (argc < 3) {
	fprintf(stderr,"\n acpconfig:  invalid number of arguments\n");
	usage();		/* display proper syntax for user */
	}
    s = socket(AF_INET, SOCK_DGRAM, 0);		/* try create socket */
    if (s < 0) {
	perror("acpconfig: socket");
	exit(1);
    }
    argc--, argv++;		/* interface name */
    (void)strcpy(name, *argv);
    (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
    if ((*argv)[2] == 'n')	/* check for 'ddn' interface */
	iface |= DDN_INTERFACE;
    if ((*argv)[0] == 'a')	/* check for 'acp' interface */
	iface |= ACP_INTERFACE;

    if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
	Perror("ioctl (SIOCGIFFLAGS)");
	exit(1);
    }
    unit = (*argv)[3] & 0x0F;
    while (--argc > 0) {
	argv++;
	if(**argv != '-')
	   setifaddr(*argv);		/* set interface address */
	else {
	   ifr.ifr_data = 0;
	   switch((*argv)[1]) 	/* process flag(s) */
	   {	
	   case 'b':			/* set baud rate or ext clocking */
		ap = *argv;
		if (ap[2] == '\0')
		{	/* -b N case */
			argc--, argv++, ap = *argv;
			if (ap == 0)
				ap = "???";
		}
		else
		    ap += 2;		/* -bN case, skip over the "-b" */

		/* ap points to purported number, regardless -bN or -b N */
		if ((*ap == '0') || (strcmp(ap, "external") == 0))
		{
			if (iface&DDN_INTERFACE)
			{
				fprintf (stderr,
					"\nacpconfig %s: -b 0 invalid\n",
						name);
				break;
			}
			arg1[0] = 'b';
			ifr.ifr_data = arg1;
		}
		else 	/* set baud rate, if N is valid */
		{	
			if (iface&DDN_INTERFACE)
				p = ddnbaud_rate;
			else
				p = baud_rate;
			while (p->b_rate)
			{
				if (strcmp (ap, p->b_rate) == 0)
					break;
				p++;
			}
			if (p->parameter)
				ifr.ifr_data = &(p->parameter);
		}
		if (setconfig(ifr.ifr_data, s))
		{
		    fprintf(stderr,"\nacpconfig: -b %s invalid\n", ap);
		    usage();
		}
		break;

	    case 'l':			/* monitor status of lcns */
		if ((*argv)[2] == 'n' && (*argv)[3] == '\0')
		    nflag = 1;		/* "-ln" - numeric */
		else if ((*argv)[2] == '\0')
		    nflag = 0;		/* "-l" - symbolic */
		else
		    usage();		/* other - wrong. */

		if (strlen(*argv) < 3) {
#ifdef VAXVMS
#  ifdef MULTINET
		    if(multinet_kernel_nlist("MULTINET_NETWORK_IMAGE:",nl) ==-1)
#  else  MULTINET	/* must be TWG instead */
		    char *vmsnlist();
		    kmemf = vmsnlist();
		    if(nlist(kmemf, nl) == -1)
#  endif MULTINET
#else VAXVMS
		    if(nlist("/vmunix", nl) == -1)
#endif VAXVMS
		    {
			fprintf(stderr,"acpconfig: could not open /vmunix\n");
			exit(1);
		    }
		    if (nl[SOFTC].n_value == 0) {
			fprintf(stderr, "acpconfig:  No namelist\n");
			exit(1);
		    }
#ifndef VAXVMS
		    kmem = open (kmemf, 0);
		    if (kmem < 0) {
			fprintf(stderr, "acpconfig:  cannot open ");
			perror(kmemf);
			exit(1);
		    }
#endif
		    ddactl.func = 'n';
		    ddactl.drval = 0;
		    ifr.ifr_data = (caddr_t) &ddactl;
		    if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0)
		    {
			    Perror("ioctl (SIOCACPCONFIG) returns");
			    exit(1);
		    }
		    display((off_t)(nl[SOFTC].n_value+unit * sizeof(struct dda_softc)),*(int *)ifr.ifr_data);
		}
		else
		    usage();
		(void)close(kmem);
		break;
	    case 'o':			/* collect X.25 1984 options */
		{
		    char *cp = *argv;
		    if (strlen (cp) > 2) /* handle -oNAME and -o NAME */
			cp += 2;
		    else {
			argc--; argv++; cp = *argv;
			if (cp == 0)  {
			  fprintf (stderr, "acpconfig: -o what?\n");
			  exit(1);
			}
		    }
		    if (strcmp(cp,"none") == 0)
			option_byte1 = NONE;
		    else
			if (strcmp(cp,"extended") == 0)
			    option_byte1 |= EXTENDED;
			else {
			    fprintf(stderr,"acpconfig: -o %s invalid\n",cp);
			    exit(1);
			}
		o_flag++;
		}
		break;
	    case 's':
		/* select DDN standard X.25 service  */
		/* or basic X.25 service, accept -sn */
		/* or -s n  */

		if (iface & ACP_INTERFACE) { 	/* error if 'acp' device */
		    fprintf(stderr,"\n acpconfig:  '-s' flag invalid for");
		    fprintf(stderr," specified interface\n");
		    break;
		}
		ifr.ifr_data = 0; 		/* clear value */
		if (!(*argv)[2]) {
		    offset = 0;
		    argc--;
		    argv++;
		}
		else
		    offset = 2;

		if (((*argv)[offset] >='0' && (*argv)[offset]< '3')
		   && (*argv)[offset + 1] == 0) {
		    (*argv)[offset] += SERVICE_VAL;     /* match 'S','T','U' */
		    ifr.ifr_data = &((*argv)[offset]);
		}
		else
		    if ((strncmp(&(*argv)[offset],"standard",8)==0)
			 && (*argv)[offset + 8] == 0) {
			(*argv)[offset] = ACTUAL_VAL;	/* match 'S' */
			ifr.ifr_data = &((*argv)[offset]);
		    }
		    else
			if ((strncmp(&(*argv)[offset],"basic",5)==0)
			  && (*argv)[offset + 5] == 0) {
			    (*argv)[offset] = ACTUAL_VAL + 1;	/* match 'T' */
			    ifr.ifr_data = &((*argv)[offset]);
			}
			else
			    if ((strncmp(&(*argv)[offset],"pdn",3)==0)
				 && (*argv)[offset + 3]==0) {
				(*argv)[offset] = ACTUAL_VAL + 2; /* 'U' */
				ifr.ifr_data = &((*argv)[offset]);
			    }
			    else
			    {
				fprintf(stderr,
			       "\nacpconfig:  invalid X.25 service\n");
				usage();/* display proper syntax for user */
			    }

		if ((ifr.ifr_data == 0) || setconfig(ifr.ifr_data, s)) {
		    if (errno == EALREADY) {
			fprintf(stderr,"acpconfig: must shut down interface to\
 change modes between DDN and PDN\n");
			exit(1);
		    }
		}
		break;
	
	    case 'u':
		{
		    char *vp = *argv;	/* value to set it to. */
		    char *cp;
		    ifr.ifr_data = NULL;

		    if (vp[2] == '\0')
		    {   argc--;		/* value is next arg; fix pointers */
			argv++;
			vp = *argv;
		    }
		    else
			vp += 2;	/* value at 3rd char of this arg */

		    if (argc > 1) {
			fprintf(stderr, "-u command must be last on line\n");
			exit(1);
		    }

		    if (strcmp(vp, "down") == 0 || *vp == '0')
			ifr.ifr_data = "0";
		    if (strcmp(vp, "dte") == 0 || *vp == '1')
			ifr.ifr_data = "1";
		    if (strcmp(vp, "dce") == 0 || *vp == '2')
			ifr.ifr_data = "2";
		    if (strcmp(vp, "ext") == 0 || *vp == '3')
			ifr.ifr_data = "3";
		    if (strcmp(vp, "int") == 0 || *vp == '4')
			ifr.ifr_data = "4";

		    if (ifr.ifr_data == 0) {
			fprintf(stderr,"acpconfig: -u invalid mode '%s'\n",
				*vp);
			usage();    /* display proper syntax for user */
		    }
		    else {
			if ( setconfig(ifr.ifr_data, s) ) {
			    if (errno == EADDRNOTAVAIL) {
				fprintf(stderr,
					"acpconfig: no local X.25 address \
translation in table; cannot start up PDN mode\n");
				exit(1);
			    }
			    else {
				usage();    /* display proper syntax for user */
			    }
			}
		    }
		}
		break;
	

	    case 'f':		/* control initiation of flow control */
				/* parameters netgotiation. */
		{
		    char *cp;	/* keyword determining which parameter */
				/* to control*/
		    char *vp;	/* value to set it to. */

		    cp = *argv;
		    if (strlen (cp) > 2) /* handle -fNAME VAL and -f NAME VAL */
			cp += 2;
		    else {
			argc--; argv++; cp = *argv;
			if (cp == 0)  {
			  fprintf (stderr, "acpconfig: -f what?\n");
			  exit(1);
			}
		    }
		    argc--; argv++; vp = *argv;
		    if (vp == 0) {
			fprintf (stderr, "acpconfig: -f %s what?\n", cp);
			exit(1);
		    }
		    if (strcmp(cp,"packet") == 0)
			ddactl.msg[0] = 0;
		    else
			if (strcmp(cp,"window") == 0)
			    ddactl.msg[0] = 1;
			else {
			    fprintf(stderr,"acpconfig: -f %s invalid\n",cp);
			    exit(1);
			}
		    if (strcmp(vp,"on") == 0)
			ddactl.drval = 1;
		    else
			if (strcmp(vp,"off") == 0)
			    ddactl.drval = 0;
			else {
			    fprintf(stderr,"acpconfig: -f %s invalid\n",cp);
			    exit(1);
			}
		    ddactl.func = 'f';
		    (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
		    ifr.ifr_data = (caddr_t) &ddactl;
		    if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
			    Perror("ioctl (SIOCACPCONFIG) returns");
			    exit(1);
		    }
		}
		break;
	    case 'h':		/* histogram information */
		{
		    char *cp = *argv;
		    struct timeval databuf[HISTSIZE];
		    register int i;

		    if (strlen (cp) > 2)
			cp += 2;
		    else {
			argc--; argv++; cp = *argv;
			if (cp == 0) {
				fprintf(stderr, "acpconfig: -h what?\n");
				exit(1);
			}
		    }
		    *((char *)databuf) = (*cp == '1') ? 'H' : 'h';
		    ifr.ifr_data = (caddr_t) databuf;
		    (void)strncpy (ifr.ifr_name, name, sizeof(ifr.ifr_name));
		    if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
			    Perror("ioctl (SIOCACPCONFIG) returns");
			    exit(1);
		    }
		    if(*(cp+1) == 'r')
		    {
			printf("%ld.%06ld\t",databuf[H_LINK_UP].tv_sec,databuf[H_LINK_UP].tv_usec);
			printf("%ld.%06ld",databuf[H_START].tv_sec,databuf[H_START].tv_usec);
			printf("\t%ld.%06ld",databuf[H_END].tv_sec,databuf[H_END].tv_usec);
			printf("\t%ld.%06ld",databuf[H_TMO].tv_sec,databuf[H_TMO].tv_usec);
			for(i = 0; i < H_LINK_UP; i++)
				printf("\t%ld.%06ld",databuf[i].tv_sec,databuf[i].tv_usec);
			printf("\n");
		    }
		    else
			    hist_data_out(databuf);
		}
		break;

	    case 'm':		/* Send supervisory message to FEP */
		{
		    char *mp = ddactl.msg;
		    int i;
		    static char *xfmt = "%x";
		    static char *ofmt = "%o";

		    bzero (ddactl.msg, sizeof (ddactl.msg));
		    while (--argc > 0) {
			argv++;
			if (*argv[0] == '-') {	/* hit a non-number */
			    argc++;			/* fix these guys for */
			    argv--;			/* the main loop */
			    break;
			}
			if (mp > &ddactl.msg[sizeof (ddactl.msg)]) {
			    fprintf (stderr,"\nacpconfig: -m message too long\n");
			    exit(1);
			}
			(void)sscanf (*argv, (**argv == '0') ? ofmt : xfmt, &i);
			*mp++ = i;
		    } /* while */
		    ddactl.func = 'm';
		    (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
		    ifr.ifr_data = (caddr_t) &ddactl;
		    if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
			    Perror("ioctl (SIOCACPCONFIG) returns");
			    exit(1);
		    }
		}
		break;

	    case 'n':		/* set SVC limit */
		{
		    char *ap = *argv;

		    if (ap[2] == '\0')
		    {   argc--;		/* value is next arg; fix pointers */
			argv++;
			ap = *argv;
		    }
		    else
			ap += 2;	/* value at 3rd char of this arg */
		    ddactl.func = 'n';
		    ddactl.drval = atoi(ap);
		    (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
		    ifr.ifr_data = (caddr_t) &ddactl;
		    if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0)
		    {
			    Perror("ioctl (SIOCACPCONFIG) returns");
			    exit(1);
		    }
		    else if (*(int *)ifr.ifr_data != 0)
			printf("acpconfig: %s: %d circuits currently available.\n",name,*(int *)ifr.ifr_data);
		}
		break;
	    case 'q':		/* query board or driver for status */
		{
		    char *cp = *argv;
                    int   mode;

		    if (strlen (cp) > 2)
			cp += 2;
		    else {
			argc--; argv++; cp = *argv;
			if (cp == 0) {
			    fprintf(stderr, "acpconfig: -q what?\n");
			    exit(1);
			}
		    }
		    mode = atoi(cp);

		    ddactl.func   = 'q';
		    ddactl.msg[0] = mode;

		    ifr.ifr_data = (caddr_t) &ddactl;
		    (void)strncpy (ifr.ifr_name, name, sizeof(ifr.ifr_name));
		    if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
			Perror("ioctl (SIOCACPCONFIG) returns");
			exit(1);
		    }

	            switch (mode) {
		      case 0:	 fmtstats(ddactl.msg); break;
		      case 1:	 fmtmode(ddactl.msg);  break;
		      case 2:	 fmtsilo(ddactl.msg);  break;
		    }
		}
		break;

	    case 't':		/* set idle circuit timeout limit */
		{
		    char *ap = *argv;

		    if (ap[2] == '\0')
		    {
			argc--;		/* value is next arg; fix pointers */
			argv++;
			ap = *argv;
		    }
		    else
			ap += 2;	/* value at 3rd char of this arg */
		    ddactl.func = 't';
		    ddactl.drval = atoi(ap);
		    (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
		    ifr.ifr_data = (caddr_t) &ddactl;
		    if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0)
		    {
			    Perror("ioctl (SIOCACPCONFIG) returns");
			    exit(1);
		    }
		}
		break;

	    case 'c':		/* disable/enable driver console message */
		{
		    char *ap = *argv;
			    int val;

		    if (ap[2] == '\0')
		    {
			argc--;		/* value is next arg; fix pointers */
			argv++;
			ap = *argv;
		    }
		    else
			ap += 2;	/* value at 3rd char of this arg */
		    if ((ap == NULL) || (*ap == '-') || (*ap == '\0')) {
			fprintf(stderr, "\nacpconfig: no message number specified for -c\n");
			exit(1);
		    }
		    ddactl.func = 'c';
		    val = ddactl.drval = atoi(ap);
		    (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
		    ifr.ifr_data = (caddr_t) &ddactl;
		    if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0)
		    {
			    Perror("ioctl (SIOCACPCONFIG) returns");
			    exit(1);
		    }
		    else
		    {
			printf("acpconfig: %s: Driver message #%d is now %s\n",name,val,*(u_char *)ifr.ifr_data ? "disabled" : "enabled");
		    }
		}
		break;
	    case 'N':		/* set net id */
		{
		    char *vp = *argv;	/* value to set it to. */
		    char *cp;
		    int  net_type = -1;

		    if (vp[2] == '\0')
		    {   argc--;		/* value is next arg; fix pointers */
			argv++;
			vp = *argv;
		    }
		    else
			vp += 2;	/* value at 3rd char of this arg */

		    if (strcmp(vp, "transpac") == 0)
			net_type = NET_TRANSPAC;
		    if (strcmp(vp, "0") == 0 || strcmp(vp, "standard") == 0)
			net_type = NET_STANDARD;
		
		    if (net_type != -1) {		
			ddactl.func  = 'N';
			ddactl.drval = net_type;
			ifr.ifr_data = (caddr_t) &ddactl;
			if (setconfig(&ifr.ifr_data, s))
			    exit(1);
		    }

		    if (net_type == NET_TRANSPAC || strcmp(vp, "net15") == 0) {
						/* allow 15 digit addrs */
			ddactl.func = 'm';
			bzero(ddactl.msg, sizeof (ddactl.msg));
			bcopy("\140\0\0\2\177\17", ddactl.msg, 6);
			ifr.ifr_data = (caddr_t) &ddactl;
			if (setconfig(&ifr.ifr_data, s))
			    exit(1);
		    }
		}
		break;

	    case 'v':		/* set variable */
		{
		    char *cp;	/* keyword determining what to set */
		    char *vp;	/* value to set it to. */
		    int i, val;
		    /*
		     * The ORDERING of this table must match the
		     * cases in the processing for this ioctl
		     * inside the driver (kludgy interface!)
		     */
		     /* debug, dbgunit, and log are obsolete */
		    static char *nametab[] = {
			"log", "debug", "dbgunit", "packet", "window", 0
		    };

		    cp = *argv;
		    if (strlen (cp) > 2) /* handle -vNAME VAL and -v NAME VAL */
			cp += 2;
		    else {
			argc--; argv++; cp = *argv;
			if (cp == 0)  {
			  fprintf (stderr, "acpconfig: -v what?\n");
			  exit(1);
			}
		    }
		    argc--; argv++; vp = *argv;
		    if (vp == 0) {
			fprintf (stderr, "acpconfig: -v %s what?\n", cp);
			exit(1);
		    }
		    for (i = 0; nametab[i] != 0; i++)
			if (0 == strcmp (nametab[i], cp))
			    goto strmatch;
		    fprintf (stderr, "acpconfig: -v: \"%s\" unknown\n", cp);
		    exit(1);

		strmatch:	/* hit the keyword, and "i" is its index */
		    if (i < 3)	/* log, debug: masks: read in hexadecimal */
			(void)sscanf (vp, "%x", &val);
		    else	/* window, packet: counts: read in decimal */
			(void)sscanf (vp, "%d", &val);
		    if (i == 3) {	/* packet size: take base 2 log */
			struct lg2tab *lp;

			/*
			 * The sad thing is that after putting in all this
			 * code to take the base 2 log, I realized that the
			 * FEP takes this argument in BYTES and does the
			 * log internally.  Left the code in, though, since
			 * it's the easiest way to check the argument.
			 * (The driver converts to bytes with a shift).
			 */
			for (lp = lg2tab; lp->base2log; ++lp)
			    if (lp->binval == val) {
				val = lp->base2log;
				goto goodsize;
			    }
			fprintf (stderr, "acpconfig: -v: bad packet size\n");
			exit(1);
		    }

		goodsize:
		    ddactl.drval = val;
		    ddactl.msg[0] = i;
		    ddactl.func = 'v';
		    (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
		    ifr.ifr_data = (caddr_t) &ddactl;
		    if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
			    Perror("ioctl (SIOCACPCONFIG) returns");
			    exit(1);
		    }
		}
		break;

	    case 'e':
		{
		char *cp = *argv;
		char databuf[10];
		register int i;

		if (strlen (cp) > 2)
		    cp += 2;
		else
		{
		    argc--; argv++; cp = *argv;
		}
		databuf[0] = 'e';
		i = get_bfrsize_index(cp);
		if(i == -1)
		    exit(1);  /* get_bfrsize_index will print error message */
		else
		    databuf[1] = i;
		ifr.ifr_data = (caddr_t) databuf;
		if (!setconfig(ifr.ifr_data, s))
		    fprintf(stderr,"\nacpconfig:  buffer reset in progress, wait 1 minute\n");
		break;
		}
	    case 'z':
		/* reset the specified front-end device*/

		ifr.ifr_data = &((*argv)[1]);
		if (!setconfig(ifr.ifr_data, s))
		    fprintf(stderr,"\nacpconfig:  reset in progress, wait 1 minute\n");
		break;

	    case 'x':

		ifr.ifr_data = &((*argv)[1]);
		if (setconfig(ifr.ifr_data, s))
		    fprintf(stderr,"acpconfig: Could not create UCBs\n");
		break;
	
	    case 'A':
		/* add a file of address translation table entries */
		/* accept "-A filename OR "-Afilename"             */

		if (iface & ACP_INTERFACE) { 	/* error if 'acp' device */
		    fprintf(stderr,"acpconfig: '-A' flag invalid for specified \
interface\n");
		    exit(1);
		}
		ifr.ifr_data = 0;	/* clear value */
		if (strlen(*argv) > 2)
			fp = fopen(&((*argv)[2]),"r");
		else {
			argc--, argv++;
			fp = fopen(*argv,"r");
		}
		if (fp==NULL)
		{	fprintf(stderr,"acpconfig: cannot open file '%s'\n",
			  ((*argv)[0] == '-') ? &((*argv)[2]) : *argv);
			exit(1);
		}
		fprintf(stdout,
			"acpconfig: processing file '%s'; please wait...\n",
			  ((*argv)[0] == '-') ? &((*argv)[2]) : *argv);
		while (fgets(line, 80, fp) != NULL)
		{	if (line[0] == '#' || line[0] == '\n')
				continue;
			(void)sscanf (line, "%s %s", arg1, arg2);
/*
			fprintf(stdout,
				"acpconfig: processing translation %s ==> %s\n",
				arg1, arg2);
*/
			if ((top=strlen(arg2)) > (MAXADDRLEN-1) || top < (MINADDRLEN-1))
			{	fprintf(stderr,
				  "acpconfig: bad X.25 address length: '%s'\n",
				  arg2);
				exit(1);
			}
			for (tmp=0; tmp<top; tmp++)
			    if (arg2[tmp] > '9' || arg2[tmp] < '0')
			    {	fprintf(stderr,
				  "acpconfig: invalid X.25 address '%s'\n",
				  arg2);
				exit(1);
			    }
			getaddr(arg1,&sin);
			trtab.func = 'a';
			trtab.ipaddr = sin.sin_addr.s_addr;
			trtab.x25addr[0]=strlen(arg2);
			(void)strncpy((char *)&trtab.x25addr[1], arg2, MAXADDRLEN-1);
			ifr.ifr_data = (caddr_t)&trtab;
			(void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
			if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0)
			{
			    Perror("ioctl (SIOCACPCONFIG) returns");
			    exit(1);
			}
		}
		(void)fclose(fp);
		break;
	

	    case 'a':
		/* add an address translation table entry */
		/* accept "-a inet_addr x25_addr"         */
		/* OR "-ainet_addr x25_addr"              */

		if (iface & ACP_INTERFACE) { 	/* error if 'acp' device */
		    fprintf(stderr,"acpconfig: '-a' flag invalid for specified \
interface\n");
		    exit(1);
		}
		ifr.ifr_data = 0;	/* clear value */
		if (strlen(*argv) > 2)
			getaddr(&((*argv)[2]),&sin);
		else {
			argc--, argv++;
			getaddr(*argv,&sin);
		}
		trtab.func = 'a';
		trtab.ipaddr = sin.sin_addr.s_addr;
		argc--, argv++;
		if ((top=strlen(*argv)) > (MAXADDRLEN-1) || top < (MINADDRLEN-1))
		{	fprintf(stderr,
			  "acpconfig: bad X.25 address length: '%s'\n", *argv);
			exit(1);
		}
		for (tmp=0; tmp<top; tmp++)
		    if ((*argv)[tmp] > '9' || (*argv)[tmp] < '0')
		    {	fprintf(stderr,
			  "acpconfig: invalid X.25 address '%s'\n",
			  *argv);
			exit(1);
		    }
		trtab.x25addr[0]=strlen(*argv);
		(void)strncpy((char *)&trtab.x25addr[1], *argv, MAXADDRLEN-1);
		ifr.ifr_data = (caddr_t)&trtab;
		(void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
		if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
			Perror("ioctl (SIOCACPCONFIG) returns");
			exit(1);
		}
		break;

	    case 'd':
		/* delete an address translation table entry */
		/* accept "-d inet_addr" OR "-dinet_addr"    */
		if (iface & ACP_INTERFACE) { 	/* error if 'acp' device */
		    fprintf(stderr,"acpconfig: '-d' flag invalid for specified \
interface\n");
		    exit(1);
		}

		ifr.ifr_data = 0;	/* clear value */
		if (strlen(*argv) > 2)
			getaddr(&((*argv)[2]),&sin);
		else {
			argc--, argv++;
			getaddr(*argv,&sin);
		}
		trtab.func = 'd';
		trtab.ipaddr = sin.sin_addr.s_addr;
		ifr.ifr_data = (caddr_t)&trtab;
		(void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
		if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
			Perror("ioctl (SIOCACPCONFIG) returns");
			exit(1);
		}
		break;

	    case 'D':
		if (iface & ACP_INTERFACE) { 	/* error if 'acp' device */
		    fprintf(stderr,"acpconfig: '-d' flag invalid for specified \
interface\n");
		    exit(1);
		}

		trtab.func = 'D';
		ifr.ifr_data = (caddr_t)&trtab;
		(void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
		if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
			Perror("ioctl (SIOCACPCONFIG) returns");
			exit(1);
		}
		break;

	    case 'r':
		/* read an address translation table entry */
		/* accept "-r length" OR "-rlength"        */
		if (iface & ACP_INTERFACE) { 	/* error if 'acp' device */
		    fprintf(stderr,"acpconfig: '-r' flag invalid for specified \
interface\n");
		    exit(1);
		}

		ifr.ifr_data = 0;	/* clear value */
		if (strlen(*argv) > 2)
			top = atoi(&((*argv)[2]));
		else {
			argc--, argv++;
			top = atoi(*argv);
		}
		if (top==0)
		    top = 30000;  /* (a big number) */
		for (tmp=0; tmp<top; tmp++) {
		  trtab.ipaddr = (u_long) tmp;
		  trtab.func = 'r';
		  ifr.ifr_data = (caddr_t)&trtab;
		  (void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
		  if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
		    if (errno == EFAULT) {
			fprintf(stdout,"acpconfig: end of address table \
(%d entries max)\n", tmp);
			break;
		    }
		    else {
			Perror("ioctl (SIOCACPCONFIG) returns");
			exit(1);
		    }
		  }
		  /*
		   * The byte ordering "works" on the inet_ntoa() call.
		   * Probably only on VAXen, though (fine for us).
		   */
		  if (trtab.x25addr[0]) {
		    printf("acpconfig: address table entry %d: ", tmp);
		    printf ("%s ==> %01.16s\n",
			inet_ntoa (trtab.ipaddr), &trtab.x25addr[1]);
		  }
		}
		break;

	    default:
		usage();    /* display proper syntax for user */
	    }
	}
     }
/*
 *	Send ioctl for gathered -o options.
 *	Presently (08-31-87) the -o option supports only
 *	one byte of options.  If the number of firmware
 *	supported options reaches 9, then a second byte
 *	will be used.  The driver must be modified if a
 *	second byte is used.  The value of PKT_OPTIONS
 *	must be changed from 0x77 to 0xB7 to notify the
 *	FE that PKT_OPTIONS is a 2-Byte Valued ID.
 */
     if (o_flag) {	/* send ioctl for gathered options */
	ddactl.func = 'o';
	ddactl.msg[0] = option_byte1;
	(void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
	ifr.ifr_data = (caddr_t) &ddactl;
	if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
		Perror("ioctl (SIOCACPCONFIG) returns");
		exit(1);
	}
     }
/*
 *	if simulation, simply return to main loop of test jig
 */
#ifndef	SIMULATION
     exit(0);
#endif
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                        SETIFADDR()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                    */
/*  Purpose:                                                          */
/*                                                                    */
/*      Set the interface address.                                    */
/*                                                                    */
/*  Call:            setifaddr(addr)                                  */
/*  Arguments:       addr:   internet address                         */
/*  Returns:         nothing                                          */
/*  Called by:       main()                                           */
/*  Calls to:        getaddr()                                        */
/*                   strncpy()                                        */
/*                   ioctl()                                          */
/*                   Perror()                                         */
/*                                                                    */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

setifaddr(addr)
	char *addr;
{
	getaddr(addr, (struct sockaddr_in *)&ifr.ifr_addr);
	(void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
	if (ioctl(s, SIOCSIFADDR, (caddr_t)&ifr) < 0) {
		Perror("ioctl (SIOCSIFADDR)");
		exit(1);
	}
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                          GETADDR()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/* THIS ROUTINE IS SLOW AS MOLASSES - - FIX IT SOONER OR LATER !!!    */
/*  Purpose:                                                          */
/*                                                                    */
/*      Get the interface address and stick it in the appropriate     */
/*      data structure.  This routine has four different methods for  */
/*      deriving the address.  If successful return nothing, else     */
/*      print error message and exit.                                 */
/*                                                                    */
/*                                                                    */
/*  Call:            getaddr(s, sin)                                  */
/*  Arguments:       s:    string for address                         */
/*                   sin:    socket address struct                    */
/*  Returns:         nothing for success, else error message exit     */
/*  Called by:       setifaddr()                                      */
/*  Calls to:        gethostbyname()                                  */
/*                   bcopy()                                          */
/*                   getnetbyname()                                   */
/*                   inet_makeaddr()                                  */
/*                   inet_addr()                                      */
/*                   inet_network()                                   */
/*                   fprintf()                                        */
/*                   exit()                                           */
/*                                                                    */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

struct in_addr inet_makeaddr();

getaddr(s, sin)
	char *s;
	struct sockaddr_in *sin;
{
	struct hostent *hp,
#ifdef VAXVMS
			*_gethtbyname();
#else VAXVMS
			*gethostbyname();
#endif VAXVMS
	struct netent *np;
	int val;

	sin->sin_family = AF_INET;
	val = inet_addr(s);
	if (val != -1) {
		sin->sin_addr.s_addr = val;
		return;
	}
#ifdef VAXVMS
	hp = _gethtbyname(s); /* Use host table, not name server */
#else VAXVMS
# ifdef	SIMULATION
	hp = 0;			/* don't find our address by host name */
# else	SIMULATION
	hp = gethostbyname(s);
# endif	SIMULATION
#endif	VAXVMS
	if (hp) {
		sin->sin_family = hp->h_addrtype;
		bcopy(hp->h_addr, (char *)&sin->sin_addr, hp->h_length);
		return;
	}
	np = getnetbyname(s);
	if (np) {
		sin->sin_family = np->n_addrtype;
		sin->sin_addr = inet_makeaddr((int)np->n_net, INADDR_ANY);
		return;
	}
	sin->sin_family = AF_INET;
	val = inet_addr(s);
	if (val != -1) {
		sin->sin_addr.s_addr = val;
		return;
	}
	val = inet_network(s);
	if (val != -1) {
		sin->sin_addr = inet_makeaddr(val, INADDR_ANY);
		return;
	}
	fprintf(stderr, "acpconfig: invalid internet address '%s'\n", s);
	exit(1);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                         SETCONFIG()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                    */
/*  Purpose:                                                          */
/*                                                                    */
/*      Set the configuration parameters by sending socket ioctl      */
/*      to the specified interface driver.                            */
/*                                                                    */
/*                                                                    */
/*  Call:            setconfig(action, s)                             */
/*  Arguments:       action    indicates action to be taken           */
/*                   s         socket descriptor                      */
/*  Returns:         0 for success, nonzero for failure               */
/*                   NOTE:  if no internet address, doesn't return,   */
/*                           program exits                            */
/*  Called by:       main()                                           */
/*  Calls to:        fprintf()                                        */
/*                   strncpy()                                        */
/*                   sizeof()                                         */
/*                   ioctl()                                          */
/*                   Perror()                                         */
/*                                                                    */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

setconfig(action, s)

	char	  *action;
	int 	  s;
{
	extern int errno;
	int saves;

	if (action == 0)
		return(1);
	saves = s;
	(void)strncpy(ifr.ifr_name, name, sizeof (ifr.ifr_name));
	if (ioctl(s, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
		if (errno == EINTR) {
			fprintf(stderr,"\nacpconfig:  command in progress\n");
			NEW_DELAY(3000000);	   /* wait for device pwrup diags */
			if (ioctl(saves, SIOCACPCONFIG, (caddr_t)&ifr) < 0) {
				Perror("ioctl (SIOCACPCONFIG) returns");
				return(1);
			}
			else
				return(0);
		}
		else {
			Perror("ioctl (SIOCACPCONFIG) returns");
			return(1);
		}
	}
	return(0);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                          USAGE()                               %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                    */
/*  Purpose:                                                          */
/*                                                                    */
/*      Display the correct syntax for the acpconfig command line.    */
/*                                                                    */
/*  Call:            usage()                                          */
/*  Returns:         nothing                                          */
/*  Called by:       main()                                           */
/*  Calls to:        fprintf()                                        */
/*                   exit()                                           */
/*                                                                    */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static char *usemsg =
"Usage: acpconfig interface [address] [-A filename] [-a ipaddr x25addr]\n\
\t[-b baud] [-c msgnum] [-d ipaddr] [-D] [-e size] [-f facility status]\n\
\t[-h mode[r]] [-l] [-m message] [-n circuits] [-o option]\n\
\t[-q type] [-r count] [-s X.25_service] [-t secs]\n\
\t[-u mode] [-v variable value] [-z]\n";

usage()
{
       fprintf(stderr, usemsg);
       fprintf(stderr, "\nacpconfig generation %s\n", VERSION);
       exit(1);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                   LONG_USAGE()                                 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                    */
/*  Purpose:                                                          */
/*                                                                    */
/*      Display the correct syntax for the acpconfig command line.    */
/*      This routine is called when no arguments are passed to        */
/*      acpconfig, this generally indicates that the user want to see */
/*      the usage line as opposed to making a mistake in the          */
/*      arguments                                                     */
/*                                                                    */
/*  Call:            usage()                                          */
/*  Returns:         nothing                                          */
/*  Called by:       main()                                           */
/*  Calls to:        fprintf()                                        */
/*                   exit()                                           */
/*                                                                    */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static char *longusemsg =
"Usage: acpconfig interface [address] \n\
[-a ipaddr x25addr]  : add a single IP to X.25 address translation (PDN only)\n\
[-A filename]        : read IP to X.25 addr translations from file (PDN only)\n\
[-b baud]            : set baud rate to baud. 0 designates external clocking\n\
[-c msgnum]          : enable/disable driver console message\n\
[-d ipaddr]          : delete an IP to X.25 translation (PDN only)\n\
[-D]                 : delete all IP to X.25 translations (PDN only)\n\
[-e size]            : set front-end buffer size\n\
[-f facility status] : control initiation of flow control negotiation\n\
                       status is \"on\" or \"off\"; facility is \"packet\" or\n\
                       \"window\"\n\
[-h mode[r]]         : print statistics on logical circuit usage\n\
                       mode is 0 (read) or 1 (read and reset); \"r\"\n\
                       specifies raw mode\n\
[-l[n]]              : display status of each active logical channel\n\
		       the optional \"n\" argument will cause acpconfig\n\
[-m message]         : send specified supervisor message\n\
[-n circuits]        : set number of virtual circuits\n\
[-N net]             : set network type;  values for net are one of:\n\
                       transpac - the French Transpac network\n\
                       net15    - any network using 15 digit addresses\n\
                       default  - standard network configuration\n\
[-o option]          : set or disable extended clear handling; option is\n\
                       \"extended\" or \"none\"\n\
[-q type]            : query driver or front end status; type is 0 for\n\
                       front end, 1 for driver\n\
[-r count]           : read and print address translation table; count\n\
                       is number of translations to read; 0 means all\n\
[-s X.25_service]    : specify DDN standard, DDN basic or PDN service;\n\
                       X.25_service is: \"standard\", \"basic\" or \"pdn\"\n\
[-t secs]            : set the idle circuit timeout to secs seconds\n\
[-u mode]            : set interface mode to:\n\
                       down - disable interface\n\
                       dte  - online as DTE\n\
                       dce  - online as DCE\n\
                       ext  - external loopback\n\
                       int  - internal loopback\n\
[-v variable value]  : set driver variable; variable/values are one of:\n\
                       packet DDD - set X.25 packet size to DDD decimal\n\
                       window DDD - set X.25 window size to DDD decimal\n\
[-z]                 : reset the board\n";

long_usage()
{
       fprintf(stderr, longusemsg);
       fprintf(stderr, "\nacpconfig generation %s\n", VERSION);
       exit(1);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                            PERROR()                               %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*	print error message, based on errno                              */
/*									 */
/*  Call:  		Perror(cmd)                                      */
/*  Arguments:      	cmd:   error message                             */
/*  Returns:  		nothing, exits if no internet address is set     */
/*  Called by:  	main()                                           */
/*  Calls to:   	fprintf()                                        */
/*                	perror()                                         */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

Perror(cmd)
	char *cmd;
{
	extern int errno;

	fprintf(stderr, "acpconfig: ");
	switch (errno)
	{

	case ENXIO:
		fprintf(stderr, "%s: ", cmd);
		fprintf(stderr, "no such interface\n");
		break;

	case EPERM:
		fprintf(stderr, "permission denied\n");
		break;

	case EDESTADDRREQ:
		fprintf(stderr, "%s: ", cmd);
		fprintf(stderr, "no internet address assigned to interface\n");
		usage();		/* display proper syntax for user */
		exit(1);
		break;

	case EINVAL:
		fprintf(stderr, "%s: ", cmd);
		fprintf(stderr, "invalid command\n");
		break;

	case EINTR:
		fprintf(stderr, "%s: ", cmd);
		fprintf(stderr, "device not operational\n");
		break;

	case EINPROGRESS:
		fprintf(stderr, "%s: ", cmd);
		fprintf (stderr, "Can't change parameters with link up\n");
		fprintf (stderr, "Bring the link down and try again\n");
		break;

	case ENOPROTOOPT:
		fprintf (stderr, "%s: ", cmd);
		fprintf (stderr, "Operation not supported by this version of the firmware.\n");
		break;

	default:
		perror(cmd);
	}
}

/*
 * from SAGE::PROJECT:[X25.ACP5250W.HCOMMON]NCP.H on
 */
struct stat_rsp {
	u_char	cmnd, path, var, count; /* message header */	
	u_long  uptime;		/* seconds since startup */
	u_long  elapsed;	/* tenths since last statistics response */
	u_long  frames_sent;	/* frames and... */
	u_long  bytes_sent;	/* bytes transmitted */
	u_long  frames_rcvd;	/* frames and... */
	u_long  bytes_rcvd;	/* bytes received */
	u_short SABMs_sent;	/* similarly for SABMs, */
	u_short SABMs_rcvd;
	u_short DISCs_sent;	/* DISCs */
	u_short DISCs_rcvd;
	u_short FRMRs_sent;	/* FRMRs */
	u_short FRMRs_rcvd;
	u_short RRs_sent;	/* RRs */
	u_short RRs_rcvd;
	u_short REJs_sent;	/* REJs */
	u_short REJs_rcvd;
	u_short RNRs_sent;	/* RNRs */
	u_short RNRs_rcvd;
	u_short crc_errors;	/* CRC errors received */
	u_short bad_frames;	/* invalid frames received */
	u_short rexmitted;	/* I-frames RE-transmitted */
	u_short UAs_sent;	/* UAs transmitted, received */
	u_short UAs_rcvd;
	u_short DMs_sent;	/* DMs transmitted, received */
	u_short DMs_rcvd;
	u_short I_frames_sent;	/* I frames transmitted, received */
	u_short I_frames_rcvd;
};

/*
 * A long time ago in a UNIBUS far, far away, there was a Z80 based
 * front end processor happily talking to its PDP-11 host.  They
 * exchanged many cheerful messages, some of which were statistics
 * queries.  The statistics query was defined in the only logical
 * format, the format shared by both the Z80 and its PDP-11 host.
 * Well, thousands of computer generations passed, and the little
 * Z80 grew up into a big, strong 68000 based front end.  His
 * friendly little PDP-11 host grew up too, into a big honkin'
 * VAX.  They still liked to exchange cheerful messages, though,
 * and some of those were still the same well-worn statistics
 * queries, which are still in the only "logical" data format.
 * So don't ask again why a VAX and a 68000 exchange data in Z80
 * byte ordering!
 */
#define z80toVAX(l)	(((l) << 16) | (((l) >> 16) & 0xFFFF))

#ifdef	__GNU__		/* gnu compiler prefers ANSII C */
#define pr(A) printf("%-10s%10d%10d\n", #A, sp->A##_sent, sp->A##_rcvd)
#else			/* idiot compiler */
#define pr(A) printf("%-10s%10d%10d\n", "A", sp->A/**/_sent, sp->A/**/_rcvd)
#endif

/*
 * internal routines for use by query commands
 */
fmtstats (buf)
    char *buf;
{
	register struct stat_rsp *sp = (struct stat_rsp *) buf;
	struct timeval t;
	char *cp;

	sp->elapsed = z80toVAX (sp->elapsed);
	sp->uptime = z80toVAX (sp->uptime);
	sp->frames_sent = z80toVAX (sp->frames_sent);
	sp->frames_rcvd = z80toVAX (sp->frames_rcvd);
	sp->bytes_sent = z80toVAX (sp->bytes_sent);
	sp->bytes_rcvd = z80toVAX (sp->bytes_rcvd);

	(void)gettimeofday (&t, (struct timezone *)0);
	cp = ctime (&t.tv_sec);
	cp[24] = '\0'; /* get rid of the embedded newline */
	printf ("\n----------------------------------------------\n");
	printf ("Front End Processor statistics (host time: %s)\n", cp);

	t.tv_sec -= sp->uptime;
	cp = ctime (&t.tv_sec);
	cp[24] = '\0';
	printf ("FEP up %d seconds (since %s)\n", sp->uptime, cp);

	t.tv_sec += sp->uptime;
	t.tv_sec -= sp->elapsed;
	cp = ctime (&t.tv_sec);
	cp[24] = '\0';
	printf ("%d seconds since last statistics (at %s)\n", sp->elapsed, cp);
/* ---- debugging stuff
	printf ("cmnd = %x path = %x var = %x count = %x\n",
		sp->cmnd, sp->path, sp->var, sp->count);
   ---- */

	printf ("\n%20s%10s\n", "SENT", "RCVD");
	pr (frames);
	pr (bytes);
	pr (I_frames);
	pr (SABMs);
	pr (DISCs);
	pr (FRMRs);
	pr (RRs);
	pr (REJs);
	pr (RNRs);
	pr (UAs);
	pr (DMs);

	printf ("\n%d CRC errors; %d bad frames; %d frames retransmitted\n",
		sp->crc_errors, sp->bad_frames, sp->rexmitted);
	printf ("--------------- END OF STATISTICS -----------------\n\n");
}

/*
 * This is disgustingly ugly.  The whole interface between acpconfig
 * and the driver has gotten completely out of hand.  Has to do with
 * the fact that we want this program to be compatible with both
 * ULTRIX and BSD and they have different include file structures AND
 * historically many of the structures weren't even IN include files!
 *
 * buf:
 *	dda_state, dda_init, dda_flags, dda_firmrev
 */
fmtmode (buf)
    char *buf;
{
    static char *brd_modes[] = {
	"DISABLED", "COMING UP", "UP", "GOING DOWN"
    };

    printf ("\n>>> Driver status for %s (Interface is %s) <<<\n",
		name, brd_modes[*buf & 3]);
    buf++;

    printf ("\tMode 0x%x ", (u_char)*buf);
    if (*buf & 0x1)
	printf ("<DDN_STANDARD>\n");
    if (*buf & 0x2)
	printf ("<DDN_BASIC>\n");
    if (*buf & 0x4)
	printf ("<PDN>\n");
    if (*buf & 0x40)
	printf ("\t<Packet Size Facility Negotiation Initiation Enabled>\n");
    if (*buf & 0x80)
	printf ("\t<Window Size Facility Negotiation Initiation Enabled>\n");
    buf++;

    if (*buf == 0)
	printf ("\tWarning, DDAF_OK is OFF <--------------\n");
    else
	printf ("\tInterface on-line (DDAF_OK is ON)\n");
    buf++;

    printf ("\tFirmware revision %d.%d installed\n",
	(*buf >> 4) & 0xF, *buf & 0xF);
    buf++;

    printf ("*** End of driver status information ***\n\n");
}

fmtsilo (buf)
unsigned char *buf;
{
    int i, j;

    for (j = 0; j < 256; j += 16) {
	for (i = 0; i < 16; i++) 
	    printf("%02x ", buf[j+i]);
	for (i = 0; i < 16; i++)
	    putchar(isalpha(buf[j+i]) ? buf[j+i] : '.');
	printf("\n");;
    }
}

/* print out data from the histogram request */

double
tval_dbl(tv)
struct timeval *tv;
{
	return((double)tv->tv_sec+(double)tv->tv_usec / (double)1000000.0);
}

hist_data_out(times)
struct timeval times[];
{
	register int i;
	double percents[HISTSIZE];
	double totaltime;
	char *p, *p1;

	totaltime = tval_dbl(times+H_END) - tval_dbl(times+H_START);

	for(i = 0; i <= H_LINK_UP; i++)
		percents[i] = tval_dbl(times+i) / totaltime;

	p1 = p = ctime(&times[H_START].tv_sec);
	while(*p)
	{
		if(*p == '\n')
			*p = ' ';
		*p++;
	}
	printf("START: %s",p1);
	p1 = p = ctime(&times[H_END].tv_sec);
	while(*p)
	{
		if(*p == '\n')
			*p = ' ';
		*p++;
	}
	printf("\t\tEND: %s\n",p1);
	printf("total time: %6.2f seconds",totaltime);
	printf("\ttime up: %ld seconds (%3.2f%%)\n",times[H_LINK_UP].tv_sec,
		percents[H_LINK_UP]*100.0);
	printf("idle circuit timeout: %ld seconds\n",times[H_TMO].tv_sec);
	for(i = 0; i < H_LINK_UP; i++)
	{
		if(percents[i] <= 0.0)
			continue;
		printf("%3d %6.2f\n",i,percents[i]*100.0);
	}
}

/*  Revision History:	

9-AUG-1987	Steph Price	V2.3
	Add new -f option for controlling the initiation of flow control
	parameter negotiation.  The default in the driver is no flow
	control parameter negotiation initiation.  To turn on packet or window
	size negotiation initiation, issue the new acpconfig commands:
		-f packet on
		-f window on
	To turn off initiation:
		-f packet off
		-f window off
	Note that these options are valid only on a per call basis.  If a
	circuit is already open for the requested destination, the status of
	flow control parameter negotiation initiation can't be altered until
	the circuit is cleared either by timing out or by a reset.

	Add "external" argument to the -b option.
	Add new arguments to the -s option.  They are as follows:
		-s standard (equivalent to -s 0)
		-s basic (equivalent to -s 1)
		-s pdn (equivalent to -s 2)
	SERVICE_VAL was modified to accomodate the new arguments.
	Previously the driver expected arguments of s, t, and u to be
	passed for the -s 0, -s 1, and -s 2 options.  Since an argument of v
	is already in use, use of the -s0/standard option will result in the
	use of 'S' as an argument to the driver's ioctl routine.
	Similarly, 'T', 'U', and 'V' will be passed for the -s1/basic,
	-s2/pdn, and -s3/class_b_c options.

	Add the -o option to enable root to select extended clear and extended
	clear confirmation packets as the first step toward X.25 1984
	compatability.

	Add the -l option to enable root to display the status of each active
	lcn.

	Modify reset case to check for returned value before printing a message
	saying that a reset is in progress.

	Fix bug in -n case.  The interface was not given.

25-OCT-87	Stephanie Price
	Modified -l option to used /dev/kmem instead of /dev/mem.  Removed
	mask of high order bit in klseek() routine.
6-NOV-87	Brad Engstrom
	Add -D flag
11-NOV-87	Brad Engstrom
	Add -h and -H histogram flags
18-NOV-87	Brad Engstrom
	Changed -A option handling so that acpconfig does not exit upon seeing
	an address already in use error.
18-DEC-87	Brad Engstrom
	Added -t options to set the idle circuit timeout value to a certain
	number of seconds.
21-APR-88	Brad Engstrom
	Added the -v dbgunit option to the -v flag to set the debug unit
	variable in the driver.
22-APR-88	Brad Engstrom
	Added the -c command to enable or disable driver console messages.  Each
	use of the command for a particular message will toggle the message
	status.
26-APR-88	Brad Engstrom
	Modified the -n command so that a command of the form "-n 0" will
	print the number of circuits currently available.  Also the -l
	command uses this information to print the number of circuits free.
	This allows the new driver to work with old firmware easier.
10-MAY-88	Brad Engstrom
	Made all references to the length of an X.25 address refer to theo
	constants MAXADDRLEN and MINADDRLEN which are defined in if_ddavar.h.
26-JAN-89	Steve Johnson
	Added -N option to support TRANSPAC net.
	Merged in extra TWG support.
15-FEB-89	Paul Traina
	Fixed bug with -N command parsing.
	Implemented an improved form of Brad's changes for Transpac.
17-FEB-89	Paul Traina
	Installed Multinet support.
10-MAR-89	Paul Traina
	Installed simulation support.
13-MAR-89	Paul Traina
	Installed support for undocumented -q 2 command.
31-MAY-89	Paul Traina
	Updated command parsing on -u fixed documentation.
20-JUN-89	Paul Traina
	Obsoleted -v debug and -v dbg_unit commands.  New driver is *NOT*
	compatible with the old crufty debugging format.
26-JUN-89	Paul Traina
	Removed the tolower()'s on -u and -N arguments.
	Reinstalled Charles' changes to 3.1 acpconfig for -ln
	Fixed things so -h w/o an argument doesn't core.
19-Jul-89	Paul Traina
	Changed version date id due to incompatible changes in the dc
	structure located in if_ddavar.h
13-Nov-89	Paul Traina
	Changed query ioctl interface.  Incompatible with old drivers.

End of Revision History
*/
idle circuit timeout: %ld seconds\n",times[H_TMO].tv_sec);
	for(i = 0; i < H_LINK_UP; i++)
	{
		if(percents[i] <= 0.0)
			continue;
		printf("%3d %6.2f\n",i,percents[i]*100.0);
	}
}

/*  Revision History:	

9-acpconfig/makefile   444    540     24        7104  4535335500   7537 ######################################################################
#                                                                    #
#                                                                    #
#   Copyright (c) 1986 by Advanced Computer Communications           #
#   720 Santa Barbara Street, Santa Barbara, California 93101        #
#   (805) 963-9431                                                   #
#                                                                    #
#  filename:  makefile                                               #
#  author:    Clare E. Russ, ACC Santa Barbara                       #
#  purpose:   create executable image of 'acpconfig' configuration   #
#             program to run under UNIX (BSD, TWG, ULTRIX)           #
#                                                                    #
#  use:                                                              #
#        By default, this makefile builds an executable image,       #
#        acpconfig, for a BSD or ULTRIX  system.  If the TWG world   #
#        is desired, it must be defined explicitly in the make       #
#        command line.						     #
#                                                                    #
#           make            (make acpconfig for BSD or ULTRIX)	     #
#           make bsd        (make acpconfig for BSD or ULTRIX)	     #
#           make twg        (make acpconfig for TWG under VAX/VMS)   #
#           make ultrix     (same as "make bsd", now)		     #
#           make clean      (rm -f acpconfig)                        #
#           make print      (print -nh acpconfig.c)                  #
#                                                                    #
#  date:      V0.6    26 June 1986 Clare Russ                        #
#             add ULTRIX V1.2 and ULTRIX V2.0 support to account     #
#             for differences in the internet address data structure #
#             in_addr (see /sys/netinet/in.h)                        #
#                                                                    #
#	      V0.7    16 March 1987 Jeff Berkowitz		     #
#	      Correct the V0.6 change; there is no reason to have an #
#	      ifdef for ULTRIX versus BSD.			     #
#	      V0.8    15 Sept 1987 Stephanie Price		     #
#	      Give full pathname for in.h and if.h because 4.2BSD    #
#	      may not have the same symbolic links as 4.3BSD.        #
#                                                                    #
######################################################################
#
#
#
CFLAGS	=	-O
NAME	=	acpconfig
PRINT	=	print -nh
RM	=	rm -f
#
#  The COMMON_FILES are included regardless of the target environment.
#
COMMON_FILES	=	acpconfig.c \
			/usr/include/sys/types.h \
			/usr/include/sys/socket.h \
			/usr/include/sys/ioctl.h \
			/usr/include/stdio.h \
			/usr/include/errno.h \
			/usr/include/ctype.h \
			/usr/include/netdb.h
#
#  The BSD_FILES are included for the BSD and ULTRIX environments.
#
BSD_FILES	=	/sys/netinet/in.h \
			/sys/net/if.h
#
#  The TWG_FILES are only included for the TWG environment.
#
TWG_FILES	=	/sys/twgtcp/kernel/netinet/in.h \
			/sys/twgtcp/kernel/net/if.h


#
#  Compile an executable image for BSD (4.2 or 4.3) or ULTRIX (1.2 or 2.0)
#
bsd:	${COMMON_FILES} ${BSD_FILES}
	cc ${CFLAGS} -o ${NAME} ${NAME}.c


#
#  This entry exists so we don't have to change the manual, which says
#  to type "make ultrix"
#
ultrix:	bsd


#
#  Compile an executable image for TWG.  Note the -DTWG flag.
#
twg:	${COMMON_FILES} ${TWG_FILES}
	cc -DTWG ${CFLAGS} -o ${NAME} ${NAME}.c

clean:
	${RM} ${NAME}

print:
	${PRINT} ${NAME}.c
rint      (print -nh acpconfig.c)                  #
#                                                                    #
#  date:      V0.6    26 June 1986 Clare Russ                        #
#             add ULTRIX V1.2 and ULTRIX V2.0 support to account     #
#             for differences in the internet address data structure #
#             in_addr (see /sys/netinet/in.h)                        #
#                                   conf/   755    540     24           0  4535574763   4766 conf/ddn   555    540     24         712  4535335503   5507 #! /bin/csh -f
# configure ACP5250/6250 for connection to PSN
set UNIT=0			# change to suit your unit number
set INET_ADDR=X.X.X.X		# change to your site's internet address
alias acp /etc/acpconfig dda$UNIT
acp $INET_ADDR
acp -u down			# required only if interface was enabled
acp -b external			# EXTERNAL clocking; change if necessary
acp -s standard			# DDN standard mode
acp -f window on -f packet on	# enable negotiation
acp -u dte			# normal (DTE) mode
######################################################conf/ddn_external   555    540     24         715  4535335504   7415 #! /bin/csh -f
# configure ACP5250/6250 for external loopback (using loopback connector)
set UNIT=0			# change to suit your unit number
set INET_ADDR=X.X.X.X		# change to your site's internet address
alias acp /etc/acpconfig dda$UNIT
acp $INET_ADDR
acp -u 0			# required only if interface was enabled
acp -b 9600			# set internal clocking
acp -s standard			# DDN standard mode
acp -f window on -f packet on	# enable negotiation
acp -u ext			# external loopback
###################################################conf/ddn_internal   555    540     24         724  4535335515   7411 #! /bin/csh -f
# configure ACP5250/6250 for internal loopback (bypassing serial interface)
set UNIT=0			# change to suit your unit number
set INET_ADDR=X.X.X.X		# change to your site's internet address
alias acp /etc/acpconfig dda$UNIT
acp $INET_ADDR
acp -u 0			# required only if interface was enabled
# acp -b 0			# clock source doesn't matter
acp -s standard			# DDN standard mode
acp -f window on -f packet on	# enable negotiation
acp -u int			# internal loopback
############################################conf/pdn   555    540     24        1443  4535335504   5546 #! /bin/csh -f
# configure ACP5250/6250 for connection to public data network
set UNIT=0			# if necessary, change to suit your system
set INET_ADDR=X.X.X.X		# change to your site's internet address
set X25_ADDR=XXXXXXXXXXXXXX 	# (optionally) change to your X.25 address
set ADDRFILE=/etc/addresses     # set name of address translation table if
                                # one exists
alias acp /etc/acpconfig dda$UNIT
acp $INET_ADDR
acp -u down			# required only if interface was enabled
acp -b external			# clock supplied by modem
acp -o extended			# enable extended clear format (Telenet)
acp -a $INET_ADDR $X25_ADDR	# enter ourselves in translation table
if (-r $ADDRFILE) then
    acp -A $ADDRFILE  # read addresses if file exists and readable
endif
acp -s pdn			# PDN mode
acp -u dte			# DTE
o.h \
			/usr/include/errno.h \
			/usr/include/ctype.h \
			/usr/include/netdb.h
#
#  The BSD_FILES are included for the BSD and ULTRIX environments.
#
BSD_FILES	=	/sys/netinet/in.h \
			/sys/net/if.h
#
#  The TWG_FILES conf/pdn_external   555    540     24        1043  4535335505   7445 #! /bin/csh -f
# configure ACP5250/6250 for external loopback (using loopback connector)
set UNIT=0			# if necessary, change to suit your system
set INET_ADDR=X.X.X.X		# change to your site's internet address
set X25_ADDR=11223344556677 	# (optionally) change to your X.25 address
alias acp /etc/acpconfig dda$UNIT
acp $INET_ADDR
acp -u down			# required only if interface was enabled
acp -b 9600			# set internal clocking
acp -a $INET_ADDR $X25_ADDR	# enter ourselves in translation table
acp -s pdn			# PDN mode
acp -u ext			# external loopback
6250 for external loopback (using loopback connector)
set UNIT=0			# if necessary, change to suit your system
set INET_ADDR=X.X.X.X		# change to your site's internet address
set X25_ADDR=11223344556677 	# (optionally) change to your X.25 address
alias acp /etc/acpconfig dda$UNIT
acp $INET_ADDR
acp -u down			# required only if interface was enabled
acp -b 9600			# set internal clocking
acp -a $INET_ADDR $X25_ADDR	# enter ourselves in translation table
acp -s pdn			# PDN modconf/pdn_internal   555    540     24         777  4535335505   7434 #! /bin/csh -f
# configure ACP5250/6250 for internal loopback (bypassing serial interface)
set UNIT=0			# if necessary, change to suit your system
set INET_ADDR=X.X.X.X		# change to your site's internet address
set X25_ADDR=11223344556677 	# (optionally) change to your X.25 address
alias acp /etc/acpconfig dda$UNIT
acp $INET_ADDR
acp -u down			# required only if interface was enabled
acp -a $INET_ADDR $X25_ADDR	# enter ourselves in translation table
acp -s pdn			# PDN mode
acp -u int			# internal loopback
dconf/MAKEDEV.acc   555    540     24        3032  4535335471   6565 #!/bin/sh -
#
#	@(#)MAKEDEV.local	1.00 (ACC) 5/19/89
#
# Terminal multiplexors:
#	xx*	unibus acp5250/6250 with x29 option
# Special purpose devices:
#	pi*	unibus acp5250/6250 with programmers interface option
#
X29_MAJOR=60
XPI_MAJOR=61

umask 77
for i
do
case $i in

local)
	echo "acceptable arguments are pi, xt, and pad"
	;;

xt*|pad*)
	case $i in
	pad*) name=pad ;;
	xt*)  name=xt  ;;
	esac
	unit=`expr $i : "$name\(.*\)"`
	if [ ! -f x29show ] ; then
		/etc/mknod x29show c $X29_MAJOR 255
	fi
	case $unit in
	0) ch=J ;; 1) ch=K ;; 2) ch=L ;; 3) ch=M ;;
	4) ch=N ;; 5) ch=O ;; 6) ch=P ;; 7) ch=Q ;;
	*) echo bad unit for $name in: $i ;;
	esac
	count=16 ;
	eval `echo $ch $unit $X29_MAJOR $count $name |
	  awk ' { ch = $1; u = $4 * $2; m = $3; cnt = $4; n=$5 } END {
	    for (i = 0; i < cnt; i++)
	      if (n == "pad")
		if (i < 10)
		  printf("/etc/mknod pad%s%x c %d %d; ",ch,i,m,128+u+i);
		else
		  printf("/etc/mknod pad%s%c c %d %d; ",ch,87+i,m,128+u+i);
	      else
	        if (i < 10)
		  printf("/etc/mknod tty%s%x c %d %d; ",ch,i,m,u+i);
	        else
		  printf("/etc/mknod tty%s%c c %d %d; ",ch,87+i,m,u+i); }'`
	;;

pi*)
	class=`expr $i : 'pi\(.*\)'`
	case $class in
	0) offset=0  name=a;;
	1) offset=32 name=b;;
	2) offset=64 name=c;;
	3) offset=96 name=d;;
	*) echo bad unit for pi in: $i;;
	esac
	case $class in
	0|1|2|3)
		umask 0
		eval `echo $offset $name $XPI_MAJOR | awk ' { b=$1; n=$2; m=$3 } END {
			for (i = 0; i < 32; i++)
				printf("/etc/mknod pi%s%02d c %d %d; ", \
					n, i, m, b+i); }'`
		umask 77
		;;

	esac
	;;
esac
done
${RM} ${NAME}

print:
	${PRINT} ${NAME}.c
rint      (print -nh acpconfig.c)                  #
#                                                                    #
#  date:      V0.6    26 June 1986 Clare Russ                        #
#             add ULTRIX V1.2 and ULTRIX V2.0 support to account     #
#             for differences in the internet address data structure #
#             in_addr (see /sys/netinet/in.h)                        #
#                                   conf/pitest.c   444    540     24       23170  4535335503   6533 
/* 	pitest.c		V1.0		      30 Aug 1988        */

/*************************************************************************/
/*                                                                       */
/*                                                                       */
/*       ________________________________________________________        */
/*      /                                                        \       */
/*     |          AAA          CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*     |         AAAAA        CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     |        AAAAAAA       CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |       AAAA AAAA      CCCC              CCCC              |      */
/*     |      AAAA   AAAA     CCCC              CCCC              |      */
/*     |     AAAA     AAAA    CCCC              CCCC              |      */
/*     |    AAAA       AAAA   CCCC              CCCC              |      */
/*     |   AAAA  AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |  AAAA    AAAAAAAAAAA CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     | AAAA      AAAAAAAAA   CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*      \________________________________________________________/       */
/*                                                                       */
/*  	Copyright (c) 1988 by Advanced Computer Communications           */
/*  	720 Santa Barbara Street, Santa Barbara, California  93101       */
/*  	(805) 963-9431                                                   */
/*                                                                       */
/*                                                                       */
/*  File:		pitest.c                                         */
/*                                                                       */
/*  Author:		Brad Engstrom                                    */
/*                                                                       */
/*  Project:		Installation verification program for ACC        */
/*			ACP 5250/6250 Programmers Interface.             */
/*                                                                       */
/*  Function:                                                            */
/*	This program fork a child process, establishes an X.25 connection*/
/*	with it and sends data packets to the child.   The child echoes  */
/*	the packets back to the sender. The sender verifies the packets. */
/*	The program will print PASSED or FAILED to indicate the results  */
/*	of the test.							 */
/*                                                                       */
/*  Components:		pitest.c, /sys/vaxif/if_pivar.h                  */
/*                                                                       */
/*  Revision History:                                                    */
/*                                                                       */
/* 30-AUG-1988  Brad Engstrom:  first generated.                         */
/*************************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <signal.h>
#include <ctype.h>
#include "/sys/vaxif/if_pivar.h"

#define PIPROTOCOL	7

#define CALL	0x0
#define ANSWER 	0x3
#define RING   	0x1
#define CLEAR	0x4

/* PREDEFINED SUPERVISORY MESSAGES */
u_char          callmsg[] =
{CALL, 0x4, 0x0, 0x22,
 0xe, 3, 1, 1, 0, 8, 0, 5, 0, 0, 1, 0, 1, 1, 1,
 0xe, 3, 1, 1, 0, 8, 0, 5, 0, 0, 1, 0, 1, 1, 1,
 1, PIPROTOCOL,
 0,
 0};

u_char		clearmsg[] = {CLEAR, 0x0, 0, 1, 0};


#define DATABUFSIZE 1024
u_char          databuffer[DATABUFSIZE];
u_char          readbuffer[DATABUFSIZE];

#define PACKETSTOSEND	75

main (argc, argv)
int             argc;
char           *argv[];

{
    int             fd1, fd2;

    if (argc != 3)
    {
	fprintf (stderr, "Usage: pitest xxdev1 xxdev2\n");
	return (1);
    }
    if ((fd1 = open (argv[1], O_RDWR)) == -1)
	error (argv[0]);
    if ((fd2 = open (argv[2], O_RDWR)) == -1)
	error (argv[0]);

    if (fork ())
    {
	caller (fd1);
        fprintf(stderr,"pitest: PASSED\n");
    }
    else
	callee (fd2);
    return (0);
}

caller (fd)
int             fd;
{
    int             lcn, i;
    struct pi_dblock pd, rd;
    caddr_t cp;
    char c;

    sleep (5);			/* give callee chance to set up */

    /*
     * RESERVE THE LOGICAL CIRCUIT TO USE 
     */
    if (ioctl (fd, XIOGETLCN, &lcn) == -1)
	error ("caller: XIOGETLCN");
    lcn &= 0xff;

    /*
     * MAKE THE CALL 
     */
    fprintf(stderr,"Making X.25 call...\n");
    callmsg[1] = lcn * 2;
    pd.dataptr = (caddr_t) callmsg;
    pd.length = sizeof (callmsg);
    pd.lcn = 0;
    pd.subfunc = 0;
    pd.flags = 0;
    if (ioctl (fd, XIOWRITE, &pd) == -1)
	error ("caller: XIOWRITE");

    /*
     * READ AND CHECK THAT WE GOT AN ANSWER MESSAGE 
     */
    pd.dataptr = (caddr_t) databuffer;
    pd.length = DATABUFSIZE;
    pd.flags = 0;
    if (ioctl (fd, XIOREAD, &pd) == -1)
	error ("caller: XIOREAD");
    if (pd.lcn != 0 || pd.dataptr[0] != ANSWER)
    {
	fprintf (stderr, "caller: did not get answer message\n");
	exit (1);
    }
    fprintf(stderr,"X.25 connection established\n");

    /*
     * SEND 25 DATA PACKETS, EXPECT THEM TO BE ECHOED 
     */
    fprintf(stderr,"Sending and verifying data packets\n");
    pd.dataptr = (caddr_t) databuffer;
    pd.subfunc = 0;
    pd.flags = 0;
    pd.lcn = lcn;
    /* fill buffer with data */
    for (cp = pd.dataptr, c = '0'; cp < pd.dataptr + DATABUFSIZE; cp++)
    {
	*cp = c;
	if (++c > '9')
	    c = '0';
    }
    srandom (getpid ());
    for (i = 0; i < PACKETSTOSEND; i++)
    {
	pd.length = random () & 0x3ff;
	if (pd.length == 0)
	    pd.length = 1;
	if (ioctl (fd, XIOWRITE, &pd) == -1)
	    error ("caller: XIOWRITE");
	rd.dataptr = (caddr_t) readbuffer;
	rd.length = DATABUFSIZE;
	rd.flags = 0;
	if (ioctl (fd, XIOREAD, &rd) == -1)
	    error ("caller: XIOREAD");
	if (rd.lcn != lcn)
	{
	    fprintf (stderr, "caller: mismatched lcn (%d != %d)\n",rd.lcn,lcn);
	    exit (1);
	}
	if (rd.length != pd.length)
	{
	    fprintf (stderr, "caller: mismatched length\n");
	    exit (1);
	}
	if (bcmp (pd.dataptr, rd.dataptr, rd.length))
	{
	    fprintf (stderr, "caller: data does not match\n");
	    exit (1);
	}
        fprintf(stderr,"+");
    }
    fprintf(stderr,"\n");

    /*
     * CLEAR THE CALL. 
     */
    clearmsg[1] = lcn * 2;
    pd.dataptr = (caddr_t) clearmsg;
    pd.length = sizeof (clearmsg);
    pd.lcn = 0;
    pd.subfunc = 0;
    pd.flags = 0;
    if (ioctl (fd, XIOWRITE, &pd) == -1)
	error ("caller: XIOWRITE");

    /*
     * WAIT FOR CLEAR CONFIRMATION 
     */
    for (;;)
    {
	pd.dataptr = (caddr_t) databuffer;
	pd.length = DATABUFSIZE;
	pd.flags = 0;
	if (ioctl (fd, XIOREAD, &pd) == -1)
	    error ("caller: XIOREAD");
	if (pd.lcn == 0 && pd.dataptr[0] == CLEAR)
		break;
	else
	{
	    fprintf (stderr, "caller: packet was not a clear\n");
	    exit(1);
	}
    }

    /*
     * FREE THE LOGICAL CHANNEL AND RESOURCES 
     */
    if (ioctl (fd, XIOFREELCN, &lcn) == -1)
	error("caller: XIOFREELCN");

    /*
     * CLOSE THE DEVICE 
     */
    close (fd);
}

callee (fd)
int             fd;
{
    struct pi_dblock pd, rd;
    proto_range     pr;
    int             lcn, i;

    /*
     * RESERVE THE LOGICAL CIRCUIT TO USE 
     */
    if (ioctl (fd, XIOGETLCN, &lcn) == -1)
	error ("callee: XIOGETLCN");
    lcn &= 0xff;

    /*
     * TELL THE DRIVER WHICH PROTOCOLS THIS CHANNEL WILL ACCEPT 
     */
    pr.lower = PIPROTOCOL;
    pr.upper = PIPROTOCOL;
    if (ioctl (fd, XIOACCRING, &pr) == -1)
	error ("callee: XIOACCRING");

    /*
     * WAIT FOR A RING TO COME IN AND THEN ANSWER IT. 
     */
    for (;;)
    {
	pd.dataptr = (caddr_t) databuffer;
	pd.length = DATABUFSIZE;
	pd.flags = 0;
	if (ioctl (fd, XIOREAD, &pd) == -1)
	    error ("XIOREAD");
	if (pd.lcn == 0 && pd.dataptr[0] == RING)
	{
	    databuffer[0] = ANSWER;
	    databuffer[1] = lcn * 2;
	    /* databuffer[2] already has data */
	    databuffer[3] = 6;
	    databuffer[4] = 0;	/* length of called addr */
	    databuffer[5] = 0;	/* length of calling addr */
	    databuffer[6] = 1;	/* length of protocol */
	    databuffer[7] = PIPROTOCOL;	/* use protocol 7 */
	    databuffer[8] = 0;	/* length of facilities */
	    databuffer[9] = 0;	/* length of user data */
	    pd.length = 10;
	    pd.flags = 0;
	    pd.lcn = 0;
	    pd.subfunc = 0;
	    if (ioctl (fd, XIOWRITE, &pd) == -1)
		error ("XIOWRITE");
	    break;
	}
	else
	    fprintf (stderr, "callee: packet was not a ring\n");
    }

    /*
     * ECHO 25 PACKETS 
     */
    for (i = 0; i < PACKETSTOSEND; i++)
    {
	rd.dataptr = (caddr_t) readbuffer;
	rd.length = DATABUFSIZE;
	rd.flags = 0;
	if (ioctl (fd, XIOREAD, &rd) == -1)
	    error ("callee: XIOREAD");
	if (rd.lcn == 0)
	{
	    fprintf (stderr, "callee: unexpected supervisor message\n");
	    exit (1);
	}
	if (ioctl (fd, XIOWRITE, &rd) == -1)
	    error ("callee: XIOWRITE");
    }

    /*
     * WAIT FOR THE CLEAR
     */
    for (;;)
    {
	pd.dataptr = (caddr_t) databuffer;
	pd.length = DATABUFSIZE;
	pd.flags = 0;
	if (ioctl (fd, XIOREAD, &pd) == -1)
	    error ("callee: XIOREAD");
	if (pd.lcn == 0 && pd.dataptr[0] == CLEAR)
		break;
	else
	{
	    fprintf (stderr, "callee: packet was not a clear\n");
	    exit(1);
	}
    }

    /*
     * SEND A CLEAR CONFIRMATION
     */
    pd.dataptr[1] = lcn * 2; /* patch up clear message and send it back */
    if (ioctl (fd, XIOWRITE, &pd) == -1)
        error ("callee: XIOREAD");

    /*
     * FREE THE LOGICAL CHANNEL AND RESOURCES 
     */
    if (ioctl (fd, XIOFREELCN, &lcn) == -1)
	error("callee: XIOFREELCN");

    /*
     * CLOSE THE DEVICE 
     */
    close (fd);
}

error(s)
char *s;
{
	perror(s);
	fprintf("pitest: FAILED\n");
	exit(1);
}
ength = 1;
	if (ioctl (fd, XIOWRITE, &pd) == -1)
	    error ("caller: XIOWRITE");
	rd.dataptr = (caddr_t) readbuffer;
	rd.length = DATABUFSIZE;
	rd.flags = 0;
	if (ioctl (fd, XIOREAD, &rd) == -1)
	    error ("caller: XIOREAD");
	if (rd.lcn != lcn)
	{
	    fprintf (stderr, "caller: mismatched lcn (%d != %d)\n",rd.lcn,lcn);
	    exit (1);
	}
	if (rd.length != pd.length)
	{
	    fprintf (stdeconf/bi_data.diff   444    540     24        1667  4535573571   7274 *** bi_data.c.orig	Thu Nov 30 15:54:32 1989
--- bi_data.c	Thu Nov 30 15:54:31 1989
***************
*** 170,175 ****
--- 170,189 ----
  #define bvpsspinit binotconf
  #endif
  
+ #include "dda.h"
+ #if NDDA > 0
+ int ddaprobe();
+ #else
+ #define ddaprobe binotconf
+ #endif
+ 
+ #include "acp.h"
+ #if NACP > 0
+ int acpprobe();
+ #else
+ #define acpprobe binotconf
+ #endif
+ 
  int nNVAXBI = NVAXBI;
  
  #if NVAXBI > 0
***************
*** 186,191 ****
--- 200,206 ----
  int (*cibcaprobes[])() = {  biciinit,0};
  int (*lxprobes[])() = {  lx_init,0};
  int (*xbibprobes[])() = {xbibinit,0};
+ int (*acp7kprobes[])() = { ddaprobe, acpprobe, 0};
  
  struct bisw bisw [] =
  {
***************
*** 235,240 ****
--- 250,258 ----
  	{ BI_XBI,	"xbib",		xbibprobes,	nobireset,
  	  BIF_NOCONF|BIF_SET_HEIE},
  
+ 	{ BI_ACP7K,	"acp7000",	acp7kprobes,	nobireset,	
+           BIF_DEVICE},
+ 
  	{ 0 }
  };
  int nbitypes = sizeof (bisw) / sizeof (bisw[0]);
 25 PACKETS 
     */
    for (i = 0; i < PACKETSTOSEND; i++)
    {
	rd.daconf/bireg.diff   444    540     24        1014  4535573571   6763 *** bireg.h.orig	Thu Nov 30 15:54:10 1989
--- bireg.h	Thu Nov 30 15:54:10 1989
***************
*** 207,213 ****
  #define BI_BDA		0x0000010e
  #define BI_MEM1		0x00000001
  
! 
  
  /* BI control register */
  #define BICTRL_BIICREV  0xff000000	/* BI interface chip revision */
--- 207,213 ----
  #define BI_BDA		0x0000010e
  #define BI_MEM1		0x00000001
  
! #define	BI_ACP7K	0x00008127	/* ACC ACP7000 hardware platform */
  
  /* BI control register */
  #define BICTRL_BIICREV  0xff000000	/* BI interface chip revision */
xprobes[])() = {  lx_init,0};
  int (*xbibprobes[])() = {xbibinit,0};
+ int (*acp7kprobes[])() = { ddaprobe, acpprobe, 0};
  
  struct bisw bisw [] =
  {
***************
*** 235,240 ****
--- 250,258 ----
  	{ BI_XBI,	"xbib",		xbibprobes,	nobireset,
  	  BIF_NOCONF|BIF_SET_HEIE},
  
+ 	{ BI_ACP7K,	"acp7000",	acp7kprobes,	nobireset,	
+           BIF_DEVICE},
+ 
  	{ 0 }
  };
  int nbitypes = sizeof (bisw) / sizeof (bisw[0]);
 25 PACKETS 
     */
    for (i = 0; i < PACKETSTOSEND; i++)
    {
	rd.daconf/README   644    540     24        1170  4535575121   5717 
Some of the files in this directory:

ddn			-- example acpconfig startup scripts for ddn or pdn
ddn_external		   mode IP links
ddn_internal
pdn
pdn_external
pdn_internal

CONFIG			-- modifcations for your config file in /sys/conf
files.vax		-- modifications to /sys/conf/files.vax

MAKEDEV.acc		-- make X.29 and PI devices in /dev directory
conf.c			-- changes to /sys/machine/conf.c for X.29 and PI
pitest.c		-- test the programmer's interface

These files are only needed with the ACP 7000 series product line:

bi_data.diff		-- patches to Ultrix 3.0 /sys/data/bi_data.c
bireg.diff		-- patches to Ultrix 3.0 /sys/vaxbi/bireg.h


 acpprobe, 0};
  
  struct bisw bisw [] =
  {
***************
*** 235,240 ****
--- 250,258 ----
  	{ BI_XBI,	"xbib",		xbibprobes,	nobireset,
  	  BIF_NOCONF|BIF_SET_HEIE},
  
+ 	{ BI_ACP7K,	"acp7000",	acp7kprobes,	nobireset,	
+           BIF_DEVICE},
+ 
  	{ 0 }
  };
  int nbitypes = sizeof (bisw) / sizeof (bisw[0]);
 25 PACKETS 
     */
    for (i = 0; i < PACKETSTOSEND; i++)
    {
	rd.daconf/CONFIG   644    540     24        1001  4535573245   5725 
For an Ultrix 3.0 system, add the appropriate lines to the configuration
file before the "config" line.

# We're running Ultrix 3.0
options		ACC_ULTRIX=30
# Enable the PAD (X.29 interface)
options		DDA_PADOPT
# Enable the RAW (programmers interface)
options		DDA_RAWOPT

After the "config" line,  near the end of the device entries,  add the
following line (assuming you have one 6250/5250 to be installed in the
default location):

device         dda0      at  uba0      csr 0167000   vector ddainta  ddaintb  
For an Ultrix 3.0 system, add the appropriate lines to the configuration
file before the "config" line.

# We're running Ultrix 3.0
options		ACC_ULTRIX=30
# Enable the PAD (X.29 interface)
options		DDA_PADOPT
# Enable the RAW (programmers interface)
options		DDA_RAWOPT

After the "config" line,  near the end of the device entries,  add the
following line (assuming you have one 6250/5250 to be installed in the
default location):

device         dda0      at  uba0      csr 0167000   vector ddainta  ddaintb  conf/conf.c   644    540     24        3506  4535573245   6142 
If you have either the PI or the X.29 (DDA_RAWOPT or DDA_PADOPT) defined,
you will have to add new device driver entries into the file /sys/vax/conf.c.

The following example is for an Ultrix 3.0 system,  the similar changes are
necessary for Ultrix 2.n and 4.3bsd systems.  If you are not using DDA_RAWOPT
and not using DDA_PADOPT,  there is no need to modify conf.c.

Someplace after the definiton of nulldev() and nodev() enter the following
preamble:

#include "dda.h"
#if (NDDA > 0) && defined(DDA_PADOPT)
int xxopen(), xxclose(), xxread(), xxwrite();
int xxioctl(), xxreset(), xxstop(), xxselect();
struct tty xx_tty[];
#else
#define	xxopen	nodev			/* ACP 5250/6250 x29 interface */
#define	xxclose	nodev
#define xxread	nodev
#define	xxwrite	nodev
#define	xxioctl	nodev
#define	xxreset	nulldev
#define	xxstop	nodev
#define	xxselect nodev
#define	xx_tty	NULL
#endif

#if (NDDA > 0) && defined(DDA_RAWOPT)
int piopen(), piclose(), piioctl();
#else
#define	piopen	nodev			/* ACP 5250/6250 pi interface */
#define	piclose	nodev
#define	piioctl	nodev
#endif

At the end of the cdevsw table (character device dispatch table),  add the
following two entries:

	/* ACP5250/6250 x29 interface */
	xxopen,		xxclose,	xxread,		xxwrite,	/*??*/
	xxioctl,	xxstop,		xxreset,	xx_tty,
	xxselect,	nodev,		0,

	/* ACP5250/6250 programmers interface */
	piopen,		piclose,	nodev,		nodev,		/*??*/
	piioctl,	nodev,		nulldev,	NULL,
	nodev,		nodev,		0,

For the sake of sanity,  enter the major device numbers for these devices
in the "??" spaces after each entry.  For example (again, Ultrix 3.0), if
the last device is 59,  the X.29 interface is accessed via major # 60, and
the X.29 interface is accessed via major # 61.

You should also edit the file MAKEDEV.acc that accompanied this distribution
so that it will contain the proper major numbers for each of these interfaces.
        error ("callee: XIOREAD");

    /*
     * FREE THE LOGICAL CHANNEL AND RESOURCES 
     */
    if (ioctl (fd, XIOFREELCN, &lcn) == -1)
	error("callee: XIOFREELCN");

    /*
     *conf/files.vax   644    540     24         416  4535573245   6650 
For Ultrix (versions 2.0, 2.2, 3.0):

Add the following line to the file /sys/conf/files.vax:

vaxif/if_dda.c		optional dda device-driver Notbinary

For Berkeley UNIX,  add the following line to the file /sys/conf/files/vax:

vaxif/if_dda.c		optional dda device-driver
ems.  If you are not using DDA_RAWOPT
and not using DDA_PADOPT,  there is no need to modify conf.c.

Someplace after the definiton of nulldev() and nodev() enter the following
preamble:

#include "dda.h"
#if (NDDA > 0) && defined(DDA_PADOPT)
doc/   755    540     24           0  4535335726   4600 doc/dda.4   444    540     24       56745  4535335500   5527 .TH DDA 4 "19 July 1989"
.nh
.UC 4
.ds ]W "4.3 BSD
.SH NAME
dda \- ACC ACP 5250/6250 Network Interface Driver for X.25
.SH SYNOPSIS
.B "optional device-driver dda"
.br
.B "options ACC_BSD=43"
.br
.B "device dda0 at uba0 csr 0167000 flags 0 vector ddainta ddaintb"
.SH DESCRIPTION
The 
.I dda
device supports virtual circuits between the ACP 5250/6250 and the DDN
IMP for carrying IP datagrams.  The dda driver may also be configured
to support IP traffic on X.25 Public Data Networks.  The ACP 5250/6250
is a microprocessor-based front end that provides X.25 capability.  Via
the ACC supplied ACPCONFIG(8C) program, the user can set the device's
internet address, loopback state, baud rate, and service mode (DDN
standard, DDN basic, or PDN X.25).  By default, the driver supports DDN
standard X.25 service.
.SH CONFIGURATION
In previous versions, the
.I dda
driver configured itself to operate with whatever version of Unix, Ultrix,
or VMS it was compiled under.
However,  since these distinctions have become rather muddy,  the driver
requires an additional "options" command line in the system's kernel
configuration file.
Only one ACC_<operating system> option line is required.  The following
lines may be used:
.PP
.nf
.ta .5i 2.5i
options ACC_BSD=43	4.3bsd or 4.3-tahoe system
options ACC_BSD=42	4.2bsd
options ACC_ULTRIX=30	Ultrix 3.0
options ACC_ULTRIX=22	Ultrix 2.2
options ACC_ULTRIX=20	Ultrix 2.0
options ACC_ULTRIX=12	Ultrix 1.2
options ACC_VMS=4n	VAX/VMS 4.n (Multinet or WIN/TCP)
options ACC_VMS=5n	VAX/VMS 5.n (Multinet or WIN/TCP)
.fi
.PP
In addition to the operating system definitons,  if either the X.29 or PI
options are installed,  they may be enabled using:
.PP
.nf
.ta .5i 2.5i
options DDA_RAWOPT	enables the raw "programmer's interface"
options DDA_PADOPT	enables the X.29 pad/host option
.fi
.PP
Debugging code is normally unused in a customer environment,  but some
sites may find it useful when using the programmer's interface or when
working on site dependant modifications to the driver.
If you wish to enable the debugging code,  add the line:
.PP
.nf
.ta .5i 2.5i
options DDADEBUG
.fi
.SH DIAGNOSTICS
The diagnostic messages below refer to concepts that are explained further
in the ACP 5250/6250 Hardware Installation and User Guide.
The content of the
messages ranges from status information to notification of
unrecoverable errors.  The notation is based on the C Programming
Language's formatted output conventions where %d represents decimal
notation and %x represents unsigned hexadecimal notation.  The messages
preceded by numbers in square brackets may be disabled via the
acpconfig -c option, specifying the message number; the numbers are not
displayed as part of the message.  These messages are normally enabled.
.sp
.PP
The following messages are informational only; no action need be taken.
.PP
[0]
.BR "dda%d: reset"
.br
This message indicates reset of the front end, in response to a
UNIBUS/Qbus reset, or an ACPCONFIG command invoked by the user
(acpconfig dda0 -z).  After a bus reset, if the interface is on the
specified bus adapter, its state is reset.  There is a pause to allow
the powerup diagnostics to run for a short period of time.
If the buffer size has been altered via the acpconfig -e command,
it reverts to the value set in the on-board switches.
.PP
[38]
.BR "dda%d: buffer size reset"
.br
This message indicates that the front end is being reset to change its
buffer size, in response to the acpconfig -e command.
.PP
[14]
.BR "lcn=%d func=%x"
.br
This message provides the logical channel number and the function code
for the errors reported by messages 7 through 13.
.PP
[29]
.BR "dda%d:  link disabled"
.br
This message indicates that the interface has been disabled, in
response to the -u 0 option to ACPCONFIG.
.PP
[31]
.BR "dda%d: Restart (%x %x) received"
.br
[31]
.BR "dda%d: Restart Ack Received"
.br
These messages indicate receipt of an X.25 Restart Indication or
Restart Confirmation packet; these packets are used to initialize the
packet level protocol.  The numbers in parentheses are the Cause and
Diagnostic fields from the Restart Indication packet.
See CCITT Recommendation X.25 for an explanation of cause and
diagnostic codes.
.PP
[32]
.BR "dda%d: (ACP6250 rev %d.%d) link up"
.br
[32]
.BR "dda%d: (ACP5250 rev %d.%d) link up"
.br
This message indicates that the interface is ready to make and accept
calls (X.25 frame level is up and Restart packets have been
exchanged).  The board type (ACP6250/ACP5250) and firmware revision
level are indicated in the message.  The front end will not attempt to
bring the link up until instructed to do so via the -u option to ACPCONFIG.
If this message is displayed when the link is already up,
it indicates that the link has been reset.
.PP
[258]
.BR "dda%d: Accepting call from %d.%d.%d.%d (%s) on lcn %d
.br
This message is disabled by default.
The message reports the internet address and X.25
address of the host originating the call, and the  logical channel
number used by the front end, when the driver accepts an incoming call
from the network.
.PP
[258]
.BR "dda%d: Calling %d.%d.%d.%d (%s) on lcn %d
.br
[258]
.BR "dda%d: lcn %d connected
.br
These messages are disabled by default.
The first message reports the internet address (%d.%d.%d.%d)
and X.25 address (%s) of the destination host,
and the logical channel number used by the front end,
when the driver initiates a call to the network.
The second message indicates that the call has been successfully established.
(If the call fails, message 33 is displayed,
reporting the reason for the failure).
.PP
[258]
.BR "dda%d: Cleared lcn %d
.br
This message is disabled by default.
It indicates that the driver has closed a circuit
after it has been idle for the period defined by the "idle timeout".
.PP
[258]
.BR "dda%d: Cleared lcn %d to %d.%d.%d.%d (%x %x)"
.br
This message is disabled by default.
A circuit was cleared by the remote host or the
network; the Clearing Cause and Diagnostic fields are displayed in
parentheses.
See CCITT Recommendation X.25 for an explanation of cause and
diagnostic codes.
.PP
[256]
.BR "dda%d: abort completed on chan %d
.br
This message is disabled by default.
An outstanding read or write to the indicated
logical channel has been aborted; this occurs when the circuit is
cleared or reset.
.sp
.PP
The following messages are warnings of potential problems.
.PP
[27]
.BR "dda%d: supervisory channel overflow (maxlen=%d)"
br
The output queue for the supervisory path has reached its maximum length.
The queue length for the supervisory path is doubled;
the supervisory message is not dropped.
If this message is repeated, the front end may have stopped accepting commands
from the host, in which case the front end should be reset;
however, this message may also be displayed during error recovery if a
large number of circuits is opened or closed.
.PP
[28]
.BR "dda%d: truncated supervisor message"
.br
A message from the front end over the supervisor path exceeded the
maximum anticipated length; the partial message is discarded.
.PP
[30]
.BR "dda%d: link down"
.br
This message indicates that the front end has reported the link down,
because it has not received any response to its messages, because a
protocol error has occurred, or because the network interface has requested
that the link be disconnected.  If the link has not yet been reported up,
this may indicate that the configuration parameters for the front end and
the network interface are incompatible (i.e., both declared as DCE, or
clock source is incorrectly specified).  The front end will continue to
attempt to bring the link up, unless the user disables the link with the -u 0
option to ACPCONFIG.
.PP
[33]
.BR "dda%d: Call to %d.%d.%d.%d on lcn %d failed (%x %x)"
The front end has placed a call to the X.25 address associated with the IP
address displayed in the message; the network has refused the call with the
Clearing Cause and Diagnostic reported in parentheses.
See CCITT Recommendation X.25 for an explanation of cause and
diagnostic codes.
.PP
[34]
.BR "dda%d: X25 RESET (%x %x) on lcn %d: %d.%d.%d.%d"
.br
An X.25 RESET supervisor message was received from the front end.  The
Resetting Cause and Diagnostic fields are displayed, as well as the
channel number and the internet address of the remote host.
This message may indicate that the front end and network interface
configurations are incompatible (incorrect packet size or window size,
for instance).
See CCITT Recommendation X.25 for an explanation of cause and
diagnostic codes.
.PP
[35]
.BR "dda%d: X25 INTERRUPT (%x) on lcn %d: %d.%d.%d.%d"
.br
An X.25 INTERRUPT supervisor message was received from the front end.
The Interrupt User Data is displayed, as well as the channel number and
the internet address of the remote host.
.PP
[36]
.BR "dda%d: supervisor error (%x %x %x %x)"
.br
A message was received over the supervisor channel which is not
recognized by the driver.
The first four bytes of the message are displayed.
.PP
[37]
.BR "dda%d: Clear request lost -- lcn %d"
.br
The driver has attempted to close an circuit, but the request was not
acknowledged before the clear timeout expired.  The lcn will remain
inactive until the clear request is acknowledged, Restart packets are
exchanged, or the interface is reset.
.PP
[38]
.BR "dda%d: make_x25_call message too large for mbuf (%d bytes)"
.br
The driver attempted to make a call,  but there was too much data to be
passed into the calling mbuf.  This usually occurs because of a
.ul user data
field that is too large.  Attempt the call again with less information
in the user data field.  (Note: this should not occur except when an X.29
call is being placed).
.PP
[258]
.BR "dda%d: Rejecting call from %s on VC 0x%x
.br
An incoming call from the X.25 address shown (%s)
is being rejected because the front end does not have an available channel
on which to accept it.
.PP
[258]
.BR "dda%d: Network cleared VC %x (%x %x)
.br
It should follow the "Rejecting call" message,
and indicates that the specified virtual circuit is again ready for use.
.PP
[257]
.BR "dda%d: all circuits in use"
.br
While attempting to open a new circuit,
the driver has discovered that all circuits are active.
This can be prevented by increasing the number of circuits available
(if possible), or by reducing the amount of time an idle circuit will
be left open (via the -t option to ACPCONFIG).
This message may be suppressed by clearing the LOG_BUSY bit in the
dda_logger variable with the -v option to ACPCONFIG.
.PP
[257]
.BR "dda%d: no circuits available"
.br
While attempting to open a new circuit, the driver has discovered that
no circuits are available; this is usually because the link is being
reset.  This message may be suppressed by clearing the LOG_BUSY bit in
the dda_logger variable with the -v option to ACPCONFIG.
.sp
.PP
The following messages report errors affecting only a single message;
the message is discarded.
.PP
[2]
.BR "dda%d: can't handle af%d"
.br
The driver was requested to transmit a datagram with an address family
other than AF_INET (internetwork:  UDP, TCP, etc.).  The datagram is
discarded.
.PP
[10]
.BR "dda%d: DMA completion error (%x) lcn=%d func=%x"
.br
An I/O interrupt from the ACP 5250/6250 has occurred; the I/O
completion status indicates that its DMA controller detected an error.
The value in parentheses is the contents of the channel error register,
which indicates what type of error occurred.
This message may result from a software error
(invalid address passed to the front end),
or from a hardware error (bus timeout or other bus error).
The lcn and function codes are printed only if message 14 is enabled.
.PP
[22]
.BR "dda%d: couldn't get mbuf for call command."
.br
The driver was given a datagram addressed to a destination to which no
circuit is open, and was unable to allocate the mbuf needed to place a
call.  The datagram is discarded.
.PP
[26]
.BR "dda: couldn't get buffer for ifp header"
.br
The driver received a datagram, but was unable to allocate the mbuf needed
for the interface header.  The datagram is discarded.
.sp
.PP
The following messages report errors affecting the operation of the
interface.
.PP
[1]
.BR "dda%d: failed getting UBA resources"
.br
Insufficient UNIBUS resources existed to initialize the device.  This
is likely to be a shortage of UNIBUS mapping registers.  The driver
will try again if the front end is reset.
.PP
[15]
.BR "dda%d: asynchronous restart, status = %d"
.br
A system interrupt occurred (as opposed to an I/O interrupt).  The
status indicates the status of the ACP 5250/6250 device.  If the ACP
5250/6250 is operational, a system interrupt is unexpected.
.PP
[16]
.BR "dda%d: Diagnostic failure = %d"
.br
A system interrupt occurred (as opposed to an I/O interrupt).
The powerup diagnostics resident in the ACP 5250/6250 detected a
hardware failure.
.PP
[17]
.BR "dda%d: No Microcode Present!"
.br
The device status indicates that the front end is ready to be downloaded.
Since the ACP 5250/6250 is not downloadable,
this may indicate a system configuration error.
.PP
[18]
.BR "dda%d: can't get bfr for acpconfig msg"
.br
While processing a configuration command (via the acpconfig -m option),
the driver was unable to obtain an mbuf to hold the command to the
front end.  The configuration command is ignored.
.PP
[19]
.BR "dda%d: supervisor message too long"
.br
The driver received a configuration command (via the acpconfig -m
option) that is too long; the configuration command is ignored.
.PP
[23]
.BR "dda%d: failed to get supr msg bfr!"
.br
While attempting to send a supervisory message, the driver was unable
to allocate an mbuf to hold the message.
The supervisor command is not sent.
.PP
[25]
.BR "dda%d: couldn't get buffer for read"
.br
The attempt to allocate an mbuf for a read was unsuccessful.
No read is issued for the channel, so no additional messages will be
received from this circuit.
.sp
.PP
The following messages report possible configuration errors.
.PP
[20]
.BR "dda%d: illegal X25 address length!"
.br
In DDN Standard or Basic mode, an incoming call contained an X.25
address whose address length was found to be invalid.  The call is
refused.  This should not happen if the front end is attached to a
DDN X.25 network.
.PP
[21]
.BR "dda%d: illegal X25 address format!"
.br
In DDN Standard or Basic mode, an incoming call contained an X.25
address which does not conform to the DDN X.25 address format.  The
call is refused.  This should not happen if the front end is attached to a
DDN X.25 network.
.sp
.PP
The following messages "can't happen" -- they may indicate logic errors
in the driver or the front end.
.PP
[4]
.BR "dda%d: unknown transfer channel, lcn=%d"
.br
An I/O interrupt from the ACP 5250/6250 has occurred; the type of
interrupt is a transfer request from the front end and the logical
channel is invalid.
.PP
[5]
.BR "dda%d: transfer request lcn %d: no mbuf"
.br
While servicing a transfer request, the driver discovered that it had no
buffer to transmit on the listed channel.  The driver aborts the transfer
(by specifying a byte count of zero).
.PP
[6]
.BR "dda%d: unknown completion channel, lcn=%d"
.br
An I/O interrupt from the ACP 5250/6250 has occurred; the type of
interrupt is an I/O completion and the logical channel is invalid.
.PP
[7]
.BR "dda%d: program error lcn=%d func=%x"
.br
An I/O interrupt from the ACP 5250/6250 has occurred; the I/O
completion status indicates a program error.  Resetting the board with
the -z option to acpconfig(8C) may clear the error.  The lcn and
function codes are printed only if message 14 is enabled.
.PP
[8]
.BR "dda%d: overrun error lcn=%d func=%x"
.br
An I/O interrupt from the ACP 5250/6250 has occurred; the I/O
completion status indicates a data overrun.  A matching pair of I/O
requests specified a larger write request count than the read request
count without specifying stream mode.  The lcn and function codes are
printed only if message 14 is enabled.
.PP
[9]
.BR "dda%d: transfer count = 0 lcn=%d func=%x"
.br
An I/O interrupt from the ACP 5250/6250 has occurred; the I/O
completion status indicates that the host specified a byte count of 0.
Either the driver inadvertently passed a byte count of 0 on an I/O
request or transfer grant, or the driver has intentionally aborted a
transfer request by granting a byte count of 0 (because it no longer has
a buffer).  The lcn and function codes are printed only if message 14
is enabled.
.PP
[11]
.BR "dda%d: listen collision lcn=%d func=%x"
.br
An I/O interrupt from the ACP 5250/6250 has occurred; the I/O
completion status indicates that both sides of a data path in the same
direction have listen requests pending.  Both requests are terminated
with this status code.  The lcn and function codes are printed only if
message 14 is enabled.
.PP
[12]
.BR "dda%d: invalid function lcn=%d func=%x"
.br
An I/O interrupt from the ACP 5250/6250 has occurred; the I/O
completion status indicates that the function specified in a request is
invalid.  The lcn and function codes are printed only if message 14 is
enabled.
.PP
[13]
.BR "dda%d: invalid dpn lcn=%d func=%x"
.br
An I/O interrupt from the ACP 5250/6250 has occurred; the I/O
completion status indicates that the data path number (DPN) specified
in a request is invalid (too large).  The lcn and function codes are
printed only if message 14 is enabled.
.PP
[24]
.BR "dda%d: dequeued NULL mbuf in IP output chain!"
.br
.BR "RESET dda%d MANUALLY: use /etc/acpconfig dda%d -z"
.br
When attempting to start I/O on a channel, the driver discovered that
no data was available to be started.
The interface is disabled, and must be reset before it can be used again.
.sp
.PP
The following error messages are only produced if the X.29 module is
installed and enabled.
.PP
[96]
.BR "dda%d:(x29) xxstart: unit offline"
.br
The unit has gone offline,  data will be flushed and the connection will
be dropped.
.PP
[97]
.BR "dda%d:(x29) xxstart: could not get mbuf"
.br
A memory buffer could not be obtained.  This should never happen unless
the mbuf pool is too small.
.PP
[98]
.BR "dda%d:(x29) x29_supr: unexpected message type
.br
A supervisor message came in that should normally never be processed by
the X29 module.
[100]
.BR "dda%d:(x29) Bad decode, call REJECTED VC 0x%x"
.br
An invalid call request was received,  either the remote PAD is
configured improperly or the front end is not equipped to offer
the X.28 options requested by the remote pad.
.PP
[101]
.BR "dda%d:(x29) Call cleared LCN %d (%x %x)"
.br
A call was cleared unexpectedly.  This could be due to network failure
or remote PAD failure.  No action required.
.PP
[102]
.BR "dda%d:(x29) X25 RESET on LCN %d (%x %x)"
.br
A reset was received.  The driver will take appropriate action.  Possibly
due to link hardware problems.   No action usually required.
.PP
[104]
.BR "dda%d:(x29) supervisor error (%x %x %x %x)"
.br
An invalid command was sent to the front-end supervisor.  This could be
due to the driver losing sync with the front-end.  No action required.
.PP
[105]
.BR "dda%d:(x29) x29_dhandle: null mbuf"
.br
Unable to obtain a memory buffer.  If this happens often, the mbuf pool
may need to be resized, otherwise no action required.
.PP
[106]
.BR "dda%d:(x29) couldn't get mbuf for QBIT message"
.br
We're out of mbufs.  Probably need to resize the mbuf pool.  System
configurations should be checked, and a netstat -m should be issued to
attempt to determine what is being a mbuf hog.
.PP
[107]
.BR "dda%d:(x29) x29_supr: answer: line was -1, VC 0x%x"
.br
[107]
.BR "dda%d:(x29) x29_supr: ring: line was -1, VC 0x%x"
.br
[107]
.BR "dda%d:(x29) x29_supr: break: line was -1, VC 0x%x"
.br
[107]
.BR "dda%d:(x29) xx_tp_hangup: line was -1"
.br
This error should never happen.  The driver attempted to perform an action
on a terminal structure entry,  but no link from the bottom of the stack
to the top of the stack was found.  If you see this error, please contact
ACC customer service,  as you've found a bug in the driver.
.sp
.PP
The following messages are produced by the driver only if debugging is
enabled (by defining the symbol DDADEBUG).  By default, they are disabled,
but they may be enabled using the acpconfig -c <msg> command.
.PP
[always enabled]
.BR "dda%d: write completion timeout lcn %d"
.br
[128]
.BR "dda%d: ddainit()"
.br
[129]
.BR "dda%d: ddaoutput: dst = %d.%d.%d.%d"
.br
[130]
.BR "dda%d: ddaoutput: dst = %d.%d.%d.%d"
.br
[130]
.BR "dda%d: ddaoutput: lcn found = %d"
.br
[131]
.BR "dda%d: ddatimer()"
.br
[132]
.BR "dda%d: acpconfig_msg is %x %x %x"
.br
[132]
.BR "dda%d: ioctl()"
.br
[133]
.BR "dda%d: ddainta()"
.br
[134]
.BR "dda%d: ddaintb()"
.br
[135]
.BR "dda%d: send_config()"
.br
[136]
.BR "dda%d: send_config"
[followed by data]
.br
[137]
.BR "dda%d: locate_x25_lcn()"
.br
[138]
.BR "dda%d: locate_x25_lcn: made call to %d.%d.%d.%d"
.br
[139]
.BR "dda%d: convert_ip_addr: %d.%d.%d.%d ==> %s"
.br
[140]
.BR "dda%d: convert_x25_addr: %s ==> %d.%d.%d.%d"
.br
[141]
.BR "dda%d: make_x25_call: call_bfr"
[followed by data]
.br
[141]
.BR "dda%d: make_x25_call: lcn used = %d"
.br
[142]
.BR "dda%d: dda_start()"
.br
[143]
.BR "dda%d: dda_wrq: chan=%d func=%x"
.br
[144]
.BR "dda%d: dda_rrq()"
.br
[145]
.BR "dda%d: start_chn()"
.br
[146]
.BR "dda%d: start_chn: WRITE on lcn %d func %x"
.br
[147]
.BR "dda%d: dda_data: chan=%d cc=%x cnt=%x"
.br
[147]
.BR "dda%d: dda_data: chan=%d DDAIOCOK"
.br
[147]
.BR "dda%d: received data"
[followed by data]
.br
[148]
.BR "dda%d: dda_supr: chan=%d cc=%x"
.br
[149]
.BR "dda%d: supr_msg"
[followed by data]
.br
[149]
.BR "dda%d: supr_msg: CLEARVC VCN=%x"
.br
[149]
.BR "dda%d: supr_msg: HDLC link up"
.br
[149]
.BR "dda%d: Unexpected RESTART in state %x
.br
[149]
.BR "dda%d: supr_msg: RESTART rcvd, no RESTART pending"
.br
[149]
.BR "dda%d: supr_msg: got call from %d.%d.%d.%d"
.br
[150]
.BR "dda: decode_ring()"
.br
[151]
.BR "dda%d: clear_lcn(%d)"
.br
[152]
.BR "dda%d: send_restart()"
.br
[153]
.BR "dda%d: send_supr"
[followed by data]
.br
[154]
.BR "dda%d: start_supr"
.br
[155]
.BR "dda%d: abort_io on lcn's %d - %d"
.br
[156]
.BR "dda%d: abort_io--invalidating sioq lcn %d"
.br
[156]
.BR "dda%d: abort_io--queueing abort: lcn %d"
.br
[156]
.BR "dda%d: start_chn: aborting chan %d"
.sp
.PP
The following debug messages are relevant only if the X.29 option has
been installed.
.PP
[224]
.BR "dda%d:(x29) open line %d flag %o in %s mode"
.br
[225]
.BR "dda%d:(x29) closing line %d"
.br
[225]
.BR "dda%d:(x29) close: tp->t_state = %x"
.br
[226]
.BR "dda%d:(x29) ioctl qbit msg: cmd=%x ACC=%x"
.br
[227]
.BR "dda%d:(x29) xxstart: port %d t_state = %x"
.br
[228]
.BR "dda%d:(x29) xxstart: asked for %d got %d chars"
.br
[229]
.BR "dda%d:(x29) xxstart: mbuf %x len %d"
.br
[230]
.BR "dda%d:(x29) select()"
.br
[231]
.BR "dda%d:(x29) x29_supr()"
.br
[231]
.BR "dda%d:(x29) supr_msg: got call from %X"
.br
[232]
.BR "dda%d:(x29) x29_data: chan=%x cc=%x cnt=%x subcc=%x"
.br
[233]
.BR "dda%d:(x29) received data: <stream>"
.br
[234]
.BR "dda%d:(x29) x29_data: read complete mbuf %x %x"
.br
[235]
.BR "dda%d:(x29) x29_data: chan=%x DDAIOCOK"
.br
[236]
.BR "dda%d:(x29) qbit: <stream>"
.br
[237]
.BR "dda%d:(x29) flow restart [%d] in %x"
.br
[237]
.BR "dda%d:(x29) flow on [%d] in %x of %d"
.br
[238]
.BR "dda%d:(x29) xx_qbit_msg: %d %d %d"
.br
[239]
.BR "dda%d:(x29) xxcntl()"
.br
[239]
.BR "dda%d:(x29) xxcntl: close state: %s"
.br
[239]
.BR "dda%d:(x29) xxcntl: warning: state not data_idle"
.br
[240]
.BR "dda%d:(x29) xxclear: line=%d pgrp=%d state=%d"
.br
[241]
.BR "dda%d:(x29) x29_init() active=%d"
.br
[242]
.BR "dda%d:(x29) x29_supr: answer: line=%d"
.br
[242]
.BR "dda%d:(x29) x29_supr: answer: line=%d"
.br

.SH "NOTES"
The old logging and debug functionality has been merged in with the
rest of the driver messages.
Messages are simply enabled or disabled
by using the acpconfig "-c" command to toggle the current state of
the message.
The acpconfig options "-v debug" and "-v dbgunit" and "-v log" are no
longer valid.

.SH "SEE ALSO" acpconfig(8C)
If you see this error, pleadoc/acpconfig.8c   444    540     24       75163  4535335472   7102 .TH ACPCONFIG 8C "29 July 1989"
.nh
.UC 8C
.ds ]W "4.3 BSD
.SH NAME
acpconfig \- configure ACC's ACP network interface
.SH SYNOPSIS
.B /etc/acpconfig
.I interface
[
.I address
]
[
.I options
]
.SH DESCRIPTION
The
.I acpconfig
program is used to administer an ACC ACP 625, 5100, 5250, 6100, or 6250
network interface front-end processor.
There are numerous options allowing interface configuration,
maintenance of an address translation table,
console debugging/logging control, and status inquiries.
The options are discussed below.

When bringing up an ACP device, the configuration
parameters can be set for
external or internal transmit clock and baud rate,
internal or external loopback, and DTE or DCE mode.
The configuration parameters are described further in the user manual
that accompanies your ACP interface.
For the ACP 5250 and ACP 6250, you may select DDN standard mode,
DDN basic mode, or PDN (Public Data Network) X.25 service.

When the system is brought up,
.I acpconfig
is normally called from /etc/rc.local to
configure the ACP front end and to define its internet address;
it can also be used later to redefine the interface's
internet address or configuration.
.I acpconfig
is also used as part of an installation verification procedure
when the ACP front end and the network interface driver are installed.
Once a parameter is specified, it applies to subsequent commands unless
explicitly changed.
When PDN service is employed,
.I acpconfig
is used to add to, delete from, and read the contents of
an internet-to-X.25 address translation table used by the driver.

The
.I interface
parameter is a string of the form \*(lqname unit\*(rq (e.g., \*(lqdda0\*(rq) 
which corresponds to the interface as it is defined in your system 
configuration file. 
Note that 
.I acpconfig 
is always invoked with the 
.I interface 
parameter.
The interface parameter name is
\*(lqddn\*(rq for ACP 625,
\*(lqdda\*(rq for ACP 5250/6250, and
\*(lqacp\*(rq for ACP 5100/6100.

The
.I address
is either a host name present in the host name database ( see hosts(5) )
or an internet address expressed in internet standard
dot notation (e.g., 1.0.0.3). See INET(3N).

For each of the following options,
white space (ASCII SPACE or TAB characters) is
.I optional
between the \*(lqminus letter\*(rq flag and the following parameter.
For example, \*(lq-b 0\*(rq and \*(lq-b0\*(rq are equivalent.

All acpconfig options are restricted to the super-user except the
-l option, which requires read access to /dev/kmem.

As stated in the DDN X.25 specification, DDN X.25 provides two types
of service: DDN basic X.25 service and DDN standard X.25 service.
DDN standard X.25 service provides only local DTE to local DCE support
of the X.25 connection.
A reliable transport protocol (i.e., TCP) provides DDN standard X.25
service.
DDN basic X.25 service provides end-to-end call management with significance
as described in CCITT Recommendation X.25.

The
.B -s service
flag is used only for the ACP 5250 and ACP 6250 devices
to select DDN standard mode, DDN basic mode, or PDN X.25 service.
The default for both devices is DDN standard mode.

The use of PDN X.25 service requires that an
address translation table be communicated to the driver, to enable
it to convert between the internet address and the X.25
address of all sources and destinations.
(A conversion algorithm is used for DDN service.)  
The address table must include entries for the local ACP front end and
for all destinations, to establish connections.
Creation and management of the address translation table is accomplished
by the use of the -A, -a, -D, -d, and -r options.

The
.B -b baud_rate
and
.B -u mode
options apply to all interface types (ddn, dda, and acp).
All other options
are specific to the dda (ACP 5250/6250) interface.
.PP
.TP 15
.B -A filename
Add the contents of the named file to the driver's address translation table,
used in PDN mode.
The filename may be any absolute or relative
pathname.  The named file must contain lines which consist of an internet
address followed by white space followed by an X.25 address.
.sp .5v
The internet address may either be in standard \*(lqdot\*(rq notation
(of the form a.b.c.d; see INET(3N)) or the
name of a host from the system host name database.
For example, either \*(lq14.2.3.1\*(rq or
\*(lqhost-name\*(rq is acceptable.  
The network portion of each internet address must be non-zero.
.sp .5v
The X.25 address must be a
string of from twelve to fourteen ASCII decimal digits.
Thus, an acceptable X.25 address might be \*(lq30003010007600\*(rq.
The requirement for a minimum length of 12 may be
changed by suitable alteration of the #define'd value PDNX25AMIN 
in the acpconfig.c source file, followed by recompilation.
.sp .5v
Blank lines, lines beginning with the character \*(lq#\*(rq, and any characters
following white space after the X.25 address field are all ignored.
.sp .5v
Due to the necessity of reading each entry in the file and passing it to the
driver for installation in its translation table, execution of the -A
option may take a noticeable amount of time.  A message is displayed indicating
that a delay will be observed.
.sp .5v
An error message is displayed if insufficient space is available in the
table to add all entries requested.  All entries from the file
which fit are entered.
See the installation section of the User's Guide for a discussion on changing the size of the table.
.sp
.TP 15
.B -a ipaddr x25addr
Add a single entry to the driver's address translation table,
used in PDN mode.  The
.I ipaddr
field must be an internet address
of the format prescribed above for the \*(lq-A\*(rq option (e.g., \*(lq14.2.3.1\*(rq or
\*(lqhost-name\*(rq).  The
.I x25addr
field must be an X.25 address of the format
prescribed above for the \*(lq-A\*(rq option (e.g., \*(lq30002130006578\*(rq).
An error message is displayed if the specified
internet address is already in the table, or if insufficient space is
available in the table to add the requested entry.
A single
.I acpconfig
command may include more than one -a option.
.sp 
.TP 15
.B -b baud_rate
Select baud rate (which sets internal clocking) or external clocking.
The baud rate is set to control the speed of the internally
generated communications circuit clock physically located on the
ACP front-end hardware, the transmitter clock.
(A modem's timing signal is an example of an externally
generated clock source not physically on the ACP front end.)
The -b flag is specified with a nonzero value for baud rate if
internal clocking is desired.
Specifying -b external or -b 0 implies external clocking.
The tables below list valid baud rates used for internal clocking. 
.nf
.sp .5v
ACP 5100/6100, ACP 5250/6250  Baud Rate Parameters*
.ta 1iR 2iR 3iR 4iR
.sp .5v
	1.33M	100K	19.2K	1200
	1.00M	64K	9600
	500K	56K	4800
	250K	30K	2400
.sp .5v
ACP 625 Baud Rate Parameters*
.sp .5v
	316000	57600	9600	1760
	153600	38400	4800	1200
	115200	28800	2400
	76800	19200	2150
.sp .5v
.fi
* These are nominal baud rates.
In some cases, the
actual baud rate differs from the nominal baud rate by a few percent.
Consult your user's manual for further information and descriptions of 
baud rates.
The unit of measure is bps unless otherwise specified:
M = megabits/second, and K = kilobits/second.
The unit of measure does not need to be supplied with the -b
flag, it is optional.
To set the acp0 baud rate to 1.33 megabits/second, enter a command of
the following form:  \*(lq/etc/acpconfig acp0 -b 1.33M -u dte\*(rq.
External clocking is the default for ACP 5100/6100 and ACP 5250/6250.
To bring up the link with external clocking (the -b flag may be omitted),
use a command of the following form: \*(lq/etc/acpconfig dda0 -u dte\*(rq.
.sp .5v
Note that for the ACP 625 device (ddn interface), the clocking
is not settable by software.
Only the baud rate can be set by software.
For the ACP 625, the specification of external or
internal clocking is a hardware configuration option.
The ACP 625 is factory configured to transmit a clock signal
on specified RS-449/422 or RS-232 pins that can be used
if an external clock is not provided.
Refer to the ACP 625 User's Manual (ACC P/N 1500015) for more details.
.sp
.TP 15
.B -c
Control printing of driver error, log, and debug messages.
All error messages are initially enabled; all debug messages are initially
disabled, and the logging of call aborts is enabled.
This command allows the user to
disable (and re-enable) specific messages.
This may be used if unusual situations cause the driver to flood the console
with error messages or if extended debugging is required.
The message number
.I
msgnum
is listed in the
.I
dda (4)
man page for each message produced by the driver.
This command will display the new status of the message (i.e., whether it is
now enabled or disabled).
.sp
.TP 15
.B -D
Delete all address translation table entries.
.sp
.TP 15
.B -d ipaddr
Delete a single entry from the driver's address translation table.
The
.I ipaddr
field must be an internet address
of the format prescribed above for the \*(lq-A\*(rq option (e.g., \*(lq14.2.3.1\*(rq or
\*(lqhost-name\*(rq).  An error message is displayed if the specified
internet address is not in the table.
A single
.I acpconfig
command may include more than one -d option.
.sp
.TP 15
.B -e size
Set front-end buffer size.
This allows the user to tune buffer sizes for the number of virtual circuits
enabled.  This option causes a reset of the front-end.  The available sizes
are powers of two between 256 and 16384.  Specifying too large a value may
cause the front-end to fail.
.sp
.TP 15
.B -f facility status
Control the initiation of flow control parameter negotiation.
The -f flag is specified with a
.I facility
value of "packet" or "window"
and with a
.I status
of "on" or "off".
The default is no flow control parameter negotiation initiation.
To turn on packet and window size negotiation initiation
for unit 0, issue the
.I acpconfig
commands as follows:
.ti +.5i
/etc/acpconfig dda0 -f packet on -f window on
.br
To turn off initiation:
.ti +.5i
/etc/acpconfig dda0 -f packet off -f window off

Note that these options affect only calls made after the
parameter is changed.
If a circuit is already open, flow control parameters
can't be altered until the circuit is cleared.

When the driver initiates a call, it will request negotiation of
packet size or window size if the corresponding facility has been enabled.
For incoming calls, the driver will recognize requests to negotiate, and
will reduce the requested values to the maximum value permitted if necessary.
The maximum value for negotiation may be specified with the -v option to
.I acpconfig.
For versions of the firmware prior to v2.0, negotiation may not be enabled;
the driver will respond to negotiation attempts by setting the packet size
to 128 bytes and the packet window to 2.
.sp
.TP 15
.B -h mode
Print statistics on logical circuit usage. The
.I mode
may be one of 0, 0r, 1 or 1r.
If
.I mode
is 0 then a histogram is printed showing the percent of time
that n logical channels were used.  For example,

.KS
.nf
START: Tue Nov 10 15:56:26 1987 
END: Tue Nov 10 16:04:37 1987 
total time: 490.31 seconds
time up: 490.250000 seconds (99.99%)
  0   13.79
  1   74.67
  2    2.18
  3    9.36
.fi
.KE

This shows statistics recorded from 15:56:26 to 16:04:37.  The
total time data was recorded was 490.31 seconds.   The link was up for
490.25 seconds.  The column shows the number of logical channels and the
percent of time that that many channels were open.  
In the above example there were two logical channels open 2.18 
percent of the total time.

When
.I mode
is 1, the histogram data is printed and the histogram is reset.
If the 
.I r
modifier is attached to either mode then the data is printed in raw form.
The raw data can be piped into a user-supplied program to print out required
statistics.
Raw mode prints a single line, consisting of a set of floating point numbers
each separated by a single space.
The first field contains the number of seconds the link was up (cumulative
since the last reset).
The second and third fields contain timestamps, interpreted as seconds since
Jan 1, 1970; the second field reports when the statistics were last cleared,
and the third field reports the current time.
The fourth field contains the current idle timer value, in seconds.
The remaining fields report the number of seconds
.I n
circuits were open, where
.I n
is the field offset (i.e., the fifth field reports the number of seconds no
circuits were open)
Thus, for 64 circuits, 69 fields will be returned.
.sp
.TP 15
.B -l[n]
Display the status of each active logical channel.
To display for unit 0 use:
.ti +.5i
/etc/acpconfig dda0 -l
.br
or
.ti +.5i
/etc/acpconfig dda0 -ln
.br
For each active logical channel the following information is
given:
.nf
.in +.5i
Interface unit number 
Logical channel number
Output queue length
Number of dropped outgoing packets
Timer value
Client owning channel
Logical channel state
Source/Destination host name
Host name of the interface (or address, if -ln is used)
.in -.5i
.fi
.sp
.TP 15
.B -m message
Send an arbitrary supervisory message to the ACP 5250/6250.
.I message
is a sequence of arguments each of which represents one byte.
Arguments beginning with \*(lq0\*(rq (zero) are interpreted
as octal, others are interpreted as hexadecimal; decimal
is not supported.  The message is terminated by an argument
which begins with a dash or by the end of
.I acpconfig's
argument list.
This option is used primarily to change parameters in the ACP 5250/6250 for
which a less cumbersome method has not yet been provided.
For instance, the command \*(lqacpconfig dda0 -m 60 0 0 3 90 0 1\(rq
changes the \*(lqdefault packet size\*(rq parameter to 256;
the command \*(lqacpconfig dda0 -m 60 0 0 2 7f f\(rq
changes the \*(lqmaximum address length\*(rq parameter to 15.
This option should be used with caution;
correct use of this option requires knowledge
of ACP 5250/6250 supervisory message formats and parameters.
These messages are detailed in chapter 9, and summarized in appendix A,
of the ACP 5250/6250 Hardware Installation and User Guides.
.sp
.TP 15
.B -n circuits
Limit the number of virtual circuits which may be used at any
one time to
.I circuits.
This option requires version 2.1 or later of the firmware.
The default value is 64 circuits.
Version 2.1 supports up to 64 simultaneous circuits;
version 2.2 supports up to 126 simultaneous circuits.
The -n option should not be used once the
link has been enabled.  A reset with the -z 
option of
.I acpconfig
is necessary before changing the number of
virtual circuits.
After the reset completes, the number of circuits
may be changed with the -n option and then the
link must be enabled with the -u option of
.I acpconfig.
.sp
.TP 15
.B -N network
Configure unit to work with a nonstandard X.25 network.  Current acceptable
values for
.I network
are:
.nf
.ta .5i 2.0i
	-n standard	standard network (default)
	-n transpac	the French Transpac network
	-n net15	networks with 15 digit address fields
.fi
.sp
.TP 15
.B -o option
Select 1984 X.25 options.  Currently the only
supported 1984 X.25 option is extended clear and extended clear
confirmation packet handling.  This may be required for operation on
Telenet.
To enable this option for unit 0 enter:
.ti +.5i
/etc/acpconfig dda0 -o extended
.br
To disable this option for unit 0 enter:
.ti +.5i
/etc/acpconfig dda0 -o none
.sp
.TP 15
.B -q type
Query the driver or Front End Processor for status.  If
.I type
is 0, a Statistics Response message is solicited from the
ACP 5250/6250.  The results are written to the standard
output in tabular form.  With the exception of uptime,
statistics are cleared when read.
The Statistics Response is supported only in versions 2.1 and
greater of the ACP 5250/6250 product.

If
.I type
is 1, a driver status query is performed.  This shows
the driver's concept of the link state, driver flags, and
the status of flow control parameter negotiation initiation.
.sp
.TP 15
.B -r count
Read address translation table entries;
the internet address and corresponding X.25 address obtained from the
first
.I count
entries in the driver's address translation table will
be displayed.  If
.I count
is zero, or greater than the maximum size of the table,
all entries are returned, along with
an indication of the maximum possible size of the table.
Internet addresses contained in the address table are displayed
in normal a.b.c.d format.
The order in which entries are displayed is dependent
on the table maintenance algorithm in the driver.
Currently, the table is sorted on internet address
as stored internally (d.c.b.a).
.sp
.TP 15
.B -s X.25_service
Specify DDN X.25 standard, DDN X.25 basic, or PDN X.25 service.
The -s flag is specified with one of the following arguments:
.DS
.ta .5i 2i
	-s 0	DDN standard X.25 service
	-s standard	DDN standard X.25 service

	-s 1	DDN basic X.25 service
	-s basic	DDN basic X.25 service

	-s 2	PDN X.25 service
	-s pdn	PDN X.25 service
.DE
.sp
.TP 15
.B -t seconds
Set the idle circuit timeout.  This value is the amount of time a circuit
may be idle before it is closed by the driver.  The default value is 600
seconds (ten minutes).  Reducing the idle circuit timeout will free idle
circuits sooner and can be used to tune performance.  Setting the timeout
too low will cause circuits to close too soon and increase overhead by
repeatedly clearing and reestablishing calls.
.sp
.TP 15
.B -u mode
Bring up the interface (enable link level) in the specified configuration,
or bring down the interface (disable link level).
A
.I mode
For normal operation, the interface is configured for no loopback;
external and internal loopback configurations are used only for
installation verification.
.nf
.ta .5i 1i
	-u down	bring down the interface
	-u dte	bring up the interface for normal DTE operation without loopback
	-u dce	bring up the interface for normal DCE operation without loopback
	-u ext	bring up the interface in external loopback
	-u int	bring up the interface in internal loopback

.fi
The DTE/DCE mode sets the DTE/DCE address used by the
link-level protocol to reference the interface.
When DTE mode is specified (-u dte), the address is 03.
When DCE mode is specified (-u dte), the address is 01.
With the exception of the address, no other changes are made by
specifying DTE or DCE.
.sp 1v
Note that when external loopback is specified with
\*(lq-u ext\*(rq, you
should specify a baud rate if you are using the loopback connector
supplied with the device,
or should specify external clocking if the
link is being looped back by the modem.
To set external loopback, use a command of the following 
form:  \*(lq/etc/acpconfig dda0 1.0.0.1 -b 9600 -u ext\*(rq.

Use of \*(lq-u down\*(rq disables link level on the front end.
Success is indicated by a \*(lqlink disabled\*(rq message on the console
terminal.
Two disable commands followed by one enable command
is not recommended.
If two disables have been sent (link level disable is aborted)
then a delay (long enough for any outstanding frames to time out;
5 seconds should be sufficient) is 
required before issuing a link enable command.
In the following sequence, note that the line is disabled 
each time the parameters are changed:
.nf
.in +.5i
/etc/acpconfig dda0 -u int
/etc/acpconfig dda0 -u down
/etc/acpconfig dda0 -b 1.33 -u dte
/etc/acpconfig dda0 -u down
/etc/acpconfig dda0 -u int
.in -.5i
.fi

If you try to bring up the interface without first setting the
internet address, an error message is generated
and no further processing occurs.

When PDN X.25 service has been specified (by -s 2/pdn), a -u option
to bring up the
interface (-u dte/dce/int/ext) will not succeed unless an address translation
table entry already exists for the ACP front end interface's internet address.
If the address translation table entry does not exist, an error
message will be displayed.  An address table entry for the device
must be added (via a -a or -A option) before the interface can be brought up.
.sp
.TP 15
.B -v key value
Set a driver internal variable symbolized by
.I key
to
.I value.
Possible key values are
.I log, debug, dbgunit, packet,
and
.I window.
.nf
.ta .5i 2i
	-v packet DDD		Set X.25 packet size to DDD decimal
	-v window DDD		Set X.25 window size to DDD decimal

.fi
The
.I packet
and
.I window
variables control the maximum negotiable
packet and window size supported by the
interface.
These variables can only be set if the
ACP 5250 or ACP 6250 contains firmware
release 2.1 or above.  The link must
first be brought down (use the -u down
flag of
.I acpconfig
), the variables changed,
and the link brought back up.
.sp
.TP 15
.B -z
Reset the front end.
To reset the dda0 device give 
a command of the following form:  \*(lq/etc/acpconfig dda0 -z\*(rq.
A message will appear on the system console telling you
a device reset is in progress.
There is a pause during the reset because the powerup diagnostics
are run for a short period of time.
.sp
.SH EXAMPLE
To specify internet address 1.0.0.1, external clock,
normal DTE operation, and DDN standard X.25 service
use a command of the following 
form: \*(lq/etc/acpconfig dda0 1.0.0.1 -u dte\*(rq.
.SH DIAGNOSTICS
Diagnostic messages appear on your terminal to indicate
that the interface's configuration has been established as specified,
or an error occurred during
.I acpconfig
program execution.
.PP
.BR "usage: acpconfig interface [address] [options]"
.br
The program was invoked with incorrect arguments.
.PP
.BR "acpconfig:  invalid number of arguments".
.br
The
.I acpconfig
program must be invoked with a minimum of 2 arguments,
the first of which is the interface name.
.PP
.BR "acpconfig:  reset in progress"
.br
This command appears in response to an
.I acpconfig
command to reset
a specified device.
.PP
.BR "acpconfig:  command in progress".
.br
The
.I acpconfig
command is in progress, expect a small delay.
Two passes are made with a delay of about 30 seconds
between attempts.  This message indicates that the second attempt
is in progress.
.PP
.BR "acpconfig:  device not operational"
.br
The front end device is not operational.
Repeat the command.
In response to the
.I acpconfig
command to initialize the front-end device,
a check is made to verify that powerup diagnostics have completed.
If the diagnostics have not successfully completed, then there is a
30 second delay followed by a second attempt to initialize the front end.
This message indicates that the device was still not operational at the
time of the second initialization attempt.
.PP
.BR "acpconfig:  no internet address assigned to interface".
.br
The interface must be assigned an internet address before any of the
configuration parameters will be accepted.
You may assign an interface address in the following manner: \*(lqacpconfig 
dda0 1.0.0.1\*(rq.
.PP
.BR "acpconfig:  -b %s invalid".
.br
The baud rate argument for the -b flag must be one of the values listed
above.
.PP
.BR "acpconfig:  '-b 0' invalid for specified interface".
.br
The '-b 0'  and -b external commands (for specifying external
clocking) do not apply to 
the specified interface (i.e., ddn?).
The '-b 0' and -b external flags are only valid for ACC ACP devices
that support software selection of internal or external clocking.
The ACP 625 device does not.
In the case of the ACP 625 ddn interface, the clocking is selected
via strapping options, not software control.
The use of '-b 0' or -b external is valid for the ACP 5100/6100 'acp'
interfaces or the ACP 5250/6250 'dda' interfaces because they support
software selection of internal or external clocking.
For more detail consult the user's manuals.
.PP
.BR "acpconfig:  ioctl (SIOCGIFFLAGS) returns no such interface"
.br
The specified interface does not exist.
This message indicates that one of many possible errors exists:  the
ACP board itself is not installed in the QBUS or UNIBUS, the ACP
board is in a UNIBUS slot which does not have NPR unwrapped, the driver
is not installed, the ACP board is installed with a CSR address which
does not match the one in the system configuration file, or the
name of the interface was misspelled.
.PP
.BR "acpconfig:  ioctl (SIOCACPCONFIG) returns permission denied".
.br
You must have sufficient (root) privilege for this operation.
.PP
.BR "acpconfig:  socket: <error explanation>".
.br
This message indicates that a socket could not be created for the reason given.
.PP
.BR "acpconfig: invalid internet address '%s'".
.br
This message indicates that the value for the internet address
is invalid.
The internet address must be given in \*(lqdot notation\*(rq or be a valid
host name.
.PP
.BR "acpconfig:  invalid mode '%s'".
.br
The mode argument for the -u flag must be down, dte, dce, int, or ext.
.PP
.BR "acpconfig: '%s' flag invalid for specified interface".
.br
The flag identified does not apply to the specified interface (i.e., acp?).
The -s, -A, -a, -d, and -r flags are only valid for ACC ACP devices which
support DDN/PDN X.25 service.
.PP
.BR "acpconfig:  invalid X.25 service".
.br
The X.25 service argument for the -s flag must be a value 0 - 3
or the strings: standard, basic, pdn, or class_b_c.
.PP
.BR "acpconfig: processing file '%s'; please wait...".
.br
Acpconfig is beginning to read address pairs from
the named file, as requested by the -A flag; some delay will be observed while
all addresses are passed to the driver for installation in its address
translation table.
.PP
.BR "acpconfig: cannot open file '%s'".
.br
When attempting to read address translation table entries from a file using
the -A flag, the specified file cannot be opened.
.PP
.BR "acpconfig: bad X.25 address length: '%s'".
.br
An X.25 address supplied with either the -A or -a flag has an invalid length,
either greater than 14 or less than the minimum value PDNX25AMIN,
#define'd in the acpconfig.c source code with the default value of 12.
If necessary, the value of PDNX25AMIN may be changed and
.I acpconfig
recompiled.
.PP
.BR "acpconfig: invalid X.25 address '%s'".
.br
An X.25 address supplied with either the -A or -a flag includes characters
that are not decimal digits, which is invalid.
.PP
.BR "acpconfig: end of address table (%d entries max)".
.br
A request for display of address translation table entries specified more
entries than the table holds; the maximum table size is noted; all current
entries have already been displayed.
.PP
.BR "acpconfig: address table entry %d: %d.%d.%d.%d ==> %s".
.br
This information is displayed for each address translation table entry
requested via the -r flag.
%d.%d.%d.%d is an internet address whose corresponding X.25 address is %s.
.PP
.BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Operation already in progress".
.br
.BR "acpconfig: must shut down interface to change modes between DDN and PDN".
.br
An attempt was made, while the interface was up, to change between DDN and
PDN service via the -s flag.  This is allowed only after bringing the interface 
down via -u down.
.PP
.BR "acpconfig: -u flag must be last".
.br
The -u flag must follow all others in any
.I acpconfig
command, so
all requested options can be set before the interface is brought up (or down).
.PP
.BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Can't assign requested address".
.br
.BR "acpconfig: no local X.25 address translation in table; cannot start up PDN mode".
.br
These two messages indicate that
an attempt was made to start up the interface for PDN service (via
-s 2 then -u), but no address translation table entry exists for this host's
internet address.  Use -A or -a to add such a table entry then try again.
.PP
.BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Address already in use".
.br
An attempt was made to add an entry to the address translation table, but
an entry was already present in the table for the identical internet
address; no action was taken.  Note that if the table is already full
the following message will appear instead.
.PP
.BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Not enough core".
.br
An attempt was made to add an entry to the address translation table, but
no space remained in the table.  The maximum size of the table may be determined
by using the -r 0 flag; increasing the table size may be accomplished only
by rebuilding the kernel (See ACP 5250/6250 Hardware Installation and User Guide installation
chapter for information on changing the table size).  
An alternative is to delete unused entries via the -d flag.
.PP
.BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Bad address".
.br
An attempt was made to delete an internet address from the address translation
table; that internet address was not found in the table; no action was taken.
.PP
.BR "acpconfig: -m message too long"
.br
The bytes following the -m option exceeded
.I acpconfig\c
\'s internal buffering.  An alternative is to break the
long parameter command into two or more commands.
.PP
.BR "acpconfig: -v what?".
.br
An unknown key value or no key value was issued with the
.I acpconfig
-v command.
.PP
.BR "acpconfig: -v: bad packet size".
.br
The packet size argument for the -v flag must be one of the values listed
above.
.PP
.BR "acpconfig: ioctl (SIOCACPCONFIG) returns:
.BR "Can't change parameters with link up".
.BR "Bring the link down and try again".
.br
The value of the target parameter cannot be changed until the link is
brought down via -u down.
.PP
.BR "acpconfig: Operation not supported by this version of the firmware".
.br
Changing packet size, window size,
or SVC limit requires firmware revision 2.1 or greater.
Initiating flow control parameter negotiation, querying the FE for
status, or changing packet options with -o also requires
rev 2.1 or greater.
.PP
.BR "acpconfig:  Bad lseek fd=%d,bas=%x,off=%d ".
.br
The -l option of
.I acpconfig
could not seek into the kernel for
driver structure contents.
.PP
.BR "acpconfig: nlist--symbol not defined".
.br
The nlist program used by the -l option of
.I acpconfig
returned an invalid address.
.PP
.BR "acpconfig:  No namelist"
.br
The nlist program used by the -l option of
.I acpconfig
returned an error indicating that the UNIX kernel's namelist is invalid.
.PP
.BR "acpconfig:  cannot open /dev/kmem".
.br
The process issuing the -l command of
.I acpconfig
must be able to read /dev/kmem.
.PP
.BR "acpconfig: -o what?".
.br
A keyword was not specfified in the command text.
.PP
.BR "acpconfig: -o %s invalid".
.br
The option argument for the -o flag must be one of values listed
above.
.PP
.BR "acpconfig: -f what?".
.br
A keyword was not specfified in the command text.
.PP
.BR "acpconfig: -f %s invalid".
.br
The facility or status argument for the -f flag must be one of values listed
above.
.PP
.SH "SEE ALSO"
rc(8), intro(4N), netstat(1), ioctl(2), socket(2), inet(3N), hosts(5),
ACC ACP devices:  acp(4), dda(4), ddn(4)
.SH "NOTES"
The -u flag is positional, it should be the last flag on the command line.
.PP
The most common user error is forgetting to disable the link (-u down) before
re-enabling it with new configuration parameters.
.PP
The
.BR "-v log, -v debug, "
and
.BR "-v dbgunit"
commands have been obsoleted.  Their functionality has been moved into the
standard message facility.  Use the
.BR "-c"
command to enable or disable the appropriate messagess.
up (or down).
.PP
.BR "acpconfig: ioctl (SIOCACPCONFIG) returns: Can't assign requested address".
.br
.BR "acpconfig: no local X.25 address translation in table; cannot start up PDN mode".
.br
These two messages indicate that
an attempt was made to start up the interface for PDN service (via
-s 2 then -u), but no address translation table entry exists for this host's
internet address.  Use -A odoc/pi.me   444    540     24       46677  4535335502   5652 .po 1i
.he 'ACC'Preliminary'\*(td'
.fo ''%''
.ba 1i
.nr ii 1.25i
.nr si 0.3i
.ps +4
.sp 3
.ce
\fB5250 Programmers Interface\fR
.ps
.sp 2
.sh 1 "Introduction"
.lp
This document describes the preliminary design for the ACC X.25 Programmers
Interface for the 5250 and 6250 boards.  
.lp
The Programmers Interface (PI) is
an extension to the standard [5/6]250 Unix Driver.  It allows user processes
to access the front end (FE) at the same level as the driver.  This can be
used to implement new protocols over X.25.
.lp
Using the Programmer's Interface allows full access to the ACC X.25 Front
End.  All commands are available to the programmer.  The PI and FE handle
packet numbering and packet flow control thus greatly
simplifying X.25 programming. 
.sh 1 "PI Devices"
.lp
Communication with the FE is done through a character device driver.  Character
special devices are opened by user programs.  Packets to/from the FE are
transmitted via \fIioctl(2)\fR calls to the open device.  
.lp
Each open device is referred to as a \fIchannel\fR.  The channel uses
ioctl calls to reserve logical circuits.  Each logical circuit can be used
to establish a virtual circuit and transfer data with a remote host.
The user is responsible for clearing any established call  and
freeing any logical circuits before closing the channel.  Outstanding data 
queued within the driver will be aborted automatically.
.lp
Each channel has two queues associated with it: an input queue and one 
or more output queues.
The input queue holds data that has come in from all logical circuits that
have been associated with the channel as well as supervisory (logical circuit
0) data.  The input queue will hold at most 1 message at a time from 
any given logical circuit.  This does not apply to the supervisory circuit.
Thus the length of this queue should be equal to the sum of 1) the maximum
number of logical circuits a channel will have open simultaneously and 2)
the maximum number of supervisory messages that will be presented to the
channel at one time.
.lp
Channels may be shared between processes using standard UNIX open file
inheritance or by multiple opens of the same minor device.  Currently the
driver does not support the non-blocking and block if in use flags to the
open call.
.lp
There are 256 minor devices for each major device.  The PI interface
consists of a single major device regarless of the number of ACP boards
in the system.  Thus, the minor device numbers are split among the
available boards.  The constant PILINES in if_pi.c defines the number 
of minor devices per board.  In general this number should simply be
256 divided by number of boards present.
.lp
The character
device design allows the PI interface to be ported to many Unix drivers. 
This includes those that do not support a socket interface.  This design also
allows more different process structures for managing data traffic (See
section 5).  Access to the FE is controlled using the permission bits of the
character device entries in the file system.
.sh 1 "Programming"
.lp
The PI allows the user program to send any command supported by the FE.
It is recommended that only those commands that affect a single logical
channel be used.  Global commands may adversely affect the IP 
and X.29 interfaces if they are being used simultaneously.
.lp
Each command or data packet is sent in a single \fIioctl\fR(2) call. 
All available ioctl calls are listed below.  If an invalid command is
sent to the PI ioctl handler then the ioctl call will return -1 and errno
will be set to ENOTTY.
.sp
.ip "\fBXIOGETLCN\fR"
This call will reserve a logical circuit and associate it with the channel
that the ioctl is issued on.  The byte of data used by the ioctl will contain
the lcn of the channel reserved.  A value of 0 indicates that no circuits
are available.
.ip "\fBXIOWRITE\fR" 
The write ioctl uses the following structure to describe the user buffer.  
.(l 
struct pi_dblock {
    caddr_t         dataptr;	/* pointer to user data to be written */
    u_int           length;	/* length of data pointed to by dataptr */
    u_short         lcn;	/* logical channel to send data out on */
    u_char	    func;       /* read status , write function value */
    u_char          subfunc;	/* read substatus, write subfunction value */
    u_short         flags;	/* special flags */
};
.)l
The write ioctl is used to send data or supervisory messages to the FE.
The data is pointed to by \fIdataptr\fR.  The amount of data is given
by \fIlength\fR.  The \fIlcn\fR field gives the logical circuit that the
data is to go out on.  If this value is 0 then the data should be a 
supervisory message.  A channel is permitted to write only on circuit 0 
and those logical circuits that it has reserved for its use. (for more
details see the XIOGETLCN description).
.ip
The \fIfunc\fR field is currently not used.  
It is reserved for future enhancements.
It should always be set to 0.  The \fIsubfunc\fR field is used to set the q-bit
for packets.  The value of the \fIsubfunc\fR field is or-ed into the subfunction
byte in the FE's data request mailbox (See ACP [5/6]250 Hardware
Installation Manual for more information).  To set the q-bit for the packet
the value of \fIsubfunc\fR should be 0x80 (hexadecimal 80) otherwise this
field should always be 0.
.ip
The data is copied from the user buffer to an mbuf and then queued for output.
If the data is less than or equal to (MLEN-1) (111) bytes then a small mbuf is
used.  If the data is 112 to 1K bytes then a page cluster is allocated to
create a large mbuf.  The maximum size for a packet is CLBYTES (1024 on
most systems) bytes.
.ip
The mbuf allocated is queued for output.  If the queue is full then the
process will sleep waiting for it to empty.  If the DB_NONBLOCK bit is
set in \fIflags\fR then the write is non-blocking. The ioctl call
will return -1  and errno will be set to EWOULDBLOCK.
The length of the per logical circuit output queue is defined by DDA_OQMAX
in if_ddavar.h.  This limit applies to all interfaces: IP, x29, and PI.
.ip
The following table gives values returned in errno by the XIOWRITE ioctl
and all possible reasons for the error.
.TS
box,tab(:);
l l.
EINVAL:lcn is <= 0 or greater than max lcns configured.
EINVAL:lcn specified is not associated with this channel.
EINVAL:length specified is <= 0 or greater than CLBYTES.
ENOBUFS:An mbuf or cluster could not be allocated.
EFAULT:dataptr address violation.
EWOULDBLOCK: operation would block.
.TE
.ip "\fBXIOREAD\fR"
The read ioctl uses the same pi_dblock structure as the XIOWRITE ioctl.
The program must fill in the dataptr and length fields.  The flags field
can have the value of \fIDB_NONBLOCK\fR to specify a non-blocking read.
.ip
Upon return the length field will contain the number of bytes actually read.
The lcn field will contain the logical circuit number that the data came in
on.  If the lcn field is 0 then the data should be interpreted as a
supervisory message. Each read will return 1 complete packet.
The buffer provided must be able to accept the largest possible packet.
If supervisory or data packets come in when no user read has been issued then
the packet will be queued on the channel input queue.  
Additional packets will be dropped and an error message printed on the console.
This message indicates that the length of the input queue should probably
be increased.  The length of the input queue is set by PIQLEN in if_pi.c.
.ip
It should always be set to 0.  The \fIsubfunc\fR field is used to set the q-bit
for packets.  The value of the \fIsubfunc\fR field is or-ed into the subfunction
byte in the FE's data request mailbox (See ACP [5/6]250 Hardware
Installation Manual for more information).  To set the q-bit for the packet
the value of \fIsubfunc\fR should be 0x80 (hexadecimal 80) otherwise this
field should always be 0.
.ip
The \fIsubfunc\fR field will contain the C_SBSTAT (completion sub-status)
data contained in the Host Completion Mailbox.  This field indicates 
whether the q-bit was set for the packet. A value of 0x80 (hexadecimal 80)
indicates the q-bit was set otherwise the value of \fIsubfunc\fR will be 0.
.ip
The \fIfunc\fR field will containing the C_STATUS (completion status) data
from the Host Completion Mailbox.  Since the driver always returns a
complete, valid packet this value is uninteresting.
.ip
The following table gives values returned in errno by the XIOREAD ioctl
and all possible reasons for the error.
.TS
box,tab(:);
l l.
EINVAL:the length specified is too small to hold the entire packet.
EFAULT:dataptr address violation.
EWOULDBLOCK:operation would block.
.TE
.ip "\fBXIORPEND\fR"
This ioctl will return the number of messages queued on the channel's
input queue.
.ip "\fBXIORSIG\fR"
This ioctl specifies a signal to be sent to the process when data is
available to be read on this channel.  The program passes an integer
specifying
the signal number as defined in the <sys/signal.h> include file.
.ip "\fBXIOACCRING\fR"
The lower and upper fields specify a range of protocol byte values 
that this channel will accept.  The proto_range structure is used.
.(l
typedef proto_range {
	u_char lower;  
	u_char upper; 
} proto_range; 
.)l
The protocol byte of an incoming call is 
the first byte of the protocol field in the ring packet.
Thus an incoming call with a protocol byte within the requested range
may be routed to this PI device.  If more than one PI device has requested
rings of the same protocol type then the driver will arbitrarily 
(not necessarily fairly) give the ring to one of the devices.  
.ip "\fBXIONORING\fR"
Inform the driver that the device will no longer accept
incoming calls.  This command will not purge any rings that have already been
put in the data queue for the device.
.ip "\fBXIOANYPROTO\fR"
Issuing this ioctl will tell the driver that any calls that have
protocol types not handled by any other channel may be routed to 
this channel.
.ip "\fBXIOCLRCHAN\fR"
This call will flush any input data queued in the driver for the channel
that the ioctl is issued on.
.ip "\fBXIOFREELCN\fR"
This call is the complement of XIOGETLCN.  It is used when the process
is finished with a logical circuit.  The process is responsible for clearing
the call before freeing the circuit.  This call will abort any outstanding
writes.
.ip
The following table gives values returned in errno by the XIOFREELCN ioctl
and all possible reasons for the error.
.TS
box,tab(:);
l l.
EINVAL:lcn is <= 0 or greater than max lcns configured.
EINVAL:lcn specified is not associated with this channel.
.TE
.ip "\fBXIOABORT\fR"
This call will abort any outstanding writes for the logical ciruit specified.
This should be done before sending a CLEAR LOGICAL CIRCUIT supervisory
message to ensure that no data is sent on the circuit after it has been
cleared.
.ip
The following table gives values returned in errno by the XIOABORT ioctl
and all possible reasons for the error.
.TS
box,tab(:);
l l.
EINVAL:lcn is <= 0 or greater than max lcns configured.
EINVAL:lcn specified is not associated with this channel.
.TE
.sh 1 "Interaction with IP and X.29"
.lp
The PI interface can operate simultaneously with IP and X.29 interfaces.
The \fIacpconfig\fR program supplied by ACC is used to configure the FE
and bring up the link.
.lp
The main point of contention between the interfaces is allocation of
virtual circuits.  All interfaces share the same virtual circuit table.
This means that there is a limit of 126 circuits among all types of traffic.
A virtual circuit is reserved each time X.29 device is opened 
as well as each time a PI channel make an XIOGETLCN call.  
IP traffic must use whatever is left over.
Virtual circuits between host pairs are never shared between traffic types.
.lp
It is possible for the PI interface to handle IP or X.29 traffic.  The PI
has precedence over the DDA and X.29 interfaces.
.sh 1 "Usage Examples"
.lp
The following are several ways in which processes could be structured to
process incoming supervisory and data traffic using the PI devices.
.sh 2 "Single Process"
.lp
One process opens many PI devices.
The process knows to open a new channel when all its channels are in use.
Alternatively the process could open one PI device and make multiple
XIOLCNREQ calls to have multiple circuits associated with it.
.sh 2 "Multiple Process"
.lp
Each process opens a channel and manages the communication on that channel.
Each process could open more than one channel if desired.
.sh 2 "Master Process with Children"
.lp
A single master process sits on a channel  with rings enabled.  When a call
comes in the process forks.  The child process handles the channel.  The parent
process opens a new channel. Either the parent or child process can answer the
call.  The master process can also make outgoing calls and fork children to
process the data.  After the child process has been created the parent may
close the channel to free the file descriptor.
.sh 2 "Master Process with Peers"
.lp
A master process sets up connections and informs other (already running)
processes of the name of the PI device to open to process the data.  The
peer processes handle the data.  Communication between peers can be done
using some combination of shared memory, sockets, pipes, files and
signals.
.sh 2 "Server Process"
.lp
One process acts as a server.  It is responsible for all manipulation of
PI devices.  It can have one or many channels open at a time.  Other
processes use sockets to establish connections to the server and to 
send requests and receive replies. Fig. 3 shows this process structure.
It is not required that there be the same number of client processes as
there are open PI devices.  The server can allocate data any way it wishes.
.(b F
.sp 2
.PS
P: [
down
A: circle "process" "1"
B: circle "process" "2"
C: circle "process" "3"
]
move;move
S: box "server"
move right 0.75i
D: box "PI driver" invis
line <-> from 1/4 <S.ne, S.se> to D.l dashed "PI dev" above
line <-> from 1/2 <S.ne, S.se> to D.l dashed
line <-> from 3/4 <S.ne, S.se> to D.l dashed
line <-> from P.A to S.nw chop circlerad chop 0
line <-> from P.B to S.l "socket" above chop circlerad chop 0
line <-> from P.C to S.sw chop circlerad chop 0
.PE
\fBFigure 3. Process structure for a PI server. Dashed lines are PI devices.
Solid lines are sockets.\fR
.sp
.)b
.bp
.sh 1 "Supervisory Command Formats"
.lp
This section describes the commands to be used in the \fBSupr\fR command
types and their formats.
.sh 2 "Call Command Header"
.TS H 
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
  0:   	Command code (00)
  1:   	LCN * 2
  2:   	NA
  3:   	Count
4-r:   	Common body
.sp .5v
.TE
.sh 2 "Ring Command Header"
.TS H 
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
  0:   	Command code (01)
  1:   	NA
  2:   	VCN
  3:   	Count
4-r:   	Common body
.sp .5v
.TE
.sh 2 "Clear Virtual Circuit Command Header"
.TS H 
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
  0:   	 Command code (02)
  1:   	 VCN
  2:   	 Cause code
  3:   	 Count
  4:   	 Diagnostic code
5-r:   	 Common body
.sp .5v
.TE
.sh 2 "Answer Command Header"
.TS H 
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
  0:   	 Command code (03)
  1:   	 LCN * 2
  2:   	 VCN
  3:   	 Count
4-r:   	 Common body
.sp .5v
.TE
.bp
.sh 2 "Clear Logical Channel Command Header"
.TS H 
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
  0:   	Command Code (04)
  1:   	LCN * 2
  2:   	Cause Code
  3:   	Count
  4:   	Diagnostic code
5-r:   	Common body
.sp .5v
.TE
.sh 2 "Call, Ring, Answer, Clear Virtual Circuit, and Clear Logical Channel Commands Common Body"
.TS H
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
   m:   	length of called address (0 to 14 decimal)
 m+1:   	called address in ASCII
   n:   	length of calling address (0 to 14 decimal)
 n+1:   	calling address in ASCII
   o:   	length of protocol (0 to 4)
 o+1:   	protocol
   p:   	length of facilities (0 to 63 decimal)
 p+1:   	facilities
   q:   	length of user data (0 to 124 decimal)
 q+1:   	user data
.sp .5v
.TE
.ce
NOTE
Offset m begins at 4 for Answer, Call, and Ring.
It begins at 5 for Clear Virtual Circuit and Clear Logical Channel.
All length fields are binary values giving the number
of bytes for the field to follow.
.sh 2 "Reset Command"
.TS H
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
0:   	command code (040)
1:   	LCN * 2
2:   	cause code
3:   	count (0 or 1)
4:   	diagnostic code (optional)
.sp .25v
.TE
.sh 2 "Reset Acknowledge"
.TS H 
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
  0:   	 command code (041)
  1:   	 LCN * 2
  2:   	 NA
  3:   	 count (0)
.sp .25v
.TE
.sh 2 "Interrupt Command"
.TS H 
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
  0:   	 command code (042)
  1:   	 LCN * 2
  2:   	 reason code
  3:   	 count (0)
.sp .5v
.TE
.sh 2 "Ready Command"
.TS H 
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
  0:   	 command code (043)
  1:   	 LCN * 2
  2:   	 type code (zero=RNR,
	 nonzero=RR)
  3:   	 count (0)
.sp .5v
.TE
.sh 2 "Interrupt Acknowledge"
.TS H 
box center;
c c
r|l.
.sp .5v
Byte Offset
.sp .5v
_
.TH
  0:   	 command code (044)
  1:   	 LCN * 2
  2:   	 NA
  3:   	 count (0)
.sp .5v
.TE
.bp
.sh 1 "Ioctl Header File and Structures"

.nf
/*
 * Ioctl's have the command encoded in the lower word,
 * and the size of any in or out parameters in the upper
 * word.  The high 2 bits of the upper word are used
 * to encode the in/out status of the parameter; for now
 * we restrict parameters to at most 128 bytes.
 */

/* this structure is used when writing or reading data data */
struct pi_dblock
{
    caddr_t         dataptr;	/* pointer to user data to be written */
    u_int           length;	/* length of data pointed to by dataptr */
    u_short         lcn;	/* logical channel to send data out on */
    u_char	    func;       /* read status , write function value */
    u_char          subfunc;	/* read substatus, write subfunction value */
    u_short         flags;	/* special flags */
};

/* pi_dblock.flags:  The follwing values can be used to set bits */
#define	DB_NONBLOCK	0x01	/* non-blocking read or write */

typedef struct proto_range
{
    u_char          lower;	/* lower bound on protocol range (inclusive) */
    u_char          upper;	/* upper bound on protocol range (inclusive) */
}               proto_range;

/* data write */
#define	XIOWRITE	_IOWR(t, 1, struct pi_dblock)

/* data read */
#define	XIOREAD		_IOWR(t, 2, struct pi_dblock)

/* see if data is ready to be read on a particular channel.  
 * The field will return the number of pending data bytes.
 */
#define	XIORPEND	_IOR(t, 3, int)

/* allow rings on this minor device.  The proto_range structure specifies
 * the lower and upper bounds on the protocol byte of incomming calls
 */
#define	XIOACCRING	_IOW(t, 4, proto_range)

/* specify that a protocol not requested by anyone else will be accepted on
 * this channel.
 */
#define	XIOANYPROTO	_IO(t, 5)

/* reserve an lcn for use by this channel (minor device).  The number of the
 * lcn is returned the the character passed
 */
#define	XIOGETLCN	_IOR(t, 6, u_char)

/* free an lcn specified by the u_char parameter.  This should be called for all
 * lcn's obtained with XIOGETLCN before the channel is closed.
 */
#define	XIOFREELCN	_IOW(t, 11, u_char)

/* clear any data associated with a channel.  The lcn is passed.  Note that
 * this does not clear the circut.  It only flushes queued data stored in the
 * driver
 */
#define	XIOCLRCHAN	_IO(t, 7)

/* disallow incomming calls on this channel */
#define	XIONORING	_IO(t, 9)

/* specify a signal to be sent to the process when there  is data ready to
 * be read on a particular channel.  The u_int specifies the signal to be
 * sent.
 */
#define	XIORSIG		_IOW(t, 10, u_int)

/* abort all output for the given lcn. */
#define	XIOABORT	_IOW(t, 12, u_char)
.fi
ling address (0 to 14 decimal)
 n+1:   	calling address in ASCII
driver/   755    540     24           0  4535574376   5334 driver/if_dda.c   444    540     24      444612  4535335120   7022 /*************************************************************************/
/*									 */
/*									 */
/*	 ________________________________________________________	 */
/*	/							 \	 */
/*     |	  AAA	       CCCCCCCCCCCCCC	 CCCCCCCCCCCCCC	  |	 */
/*     |	 AAAAA	      CCCCCCCCCCCCCCCC	CCCCCCCCCCCCCCCC  |	 */
/*     |	AAAAAAA	      CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |	 */
/*     |       AAAA AAAA      CCCC		CCCC		  |	 */
/*     |      AAAA   AAAA     CCCC		CCCC		  |	 */
/*     |     AAAA     AAAA    CCCC		CCCC		  |	 */
/*     |    AAAA       AAAA   CCCC		CCCC		  |	 */
/*     |   AAAA	 AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |	 */
/*     |  AAAA	  AAAAAAAAAAA CCCCCCCCCCCCCCCC	CCCCCCCCCCCCCCCC  |	 */
/*     | AAAA	   AAAAAAAAA   CCCCCCCCCCCCCC	 CCCCCCCCCCCCCC	  |	 */
/*	\________________________________________________________/	 */
/*									 */
/*	Copyright (c) 1986 by Advanced Computer Communications		 */
/*	720 Santa Barbara Street, Santa Barbara, California  93101	 */
/*	(805) 963-9431							 */
/*									 */
/*									 */
/*  File:		if_dda.c					 */
/*									 */
/*  Project:		DDN-X.25 Network Interface Driver for ACP 5250	 */
/*			and ACP 6250					 */
/*									 */
/*  Function:		This is a network interface driver supporting	 */
/*			the ACP5250/6250 under UNIX versions 4.2, 4.3,	 */
/*			4.3-tahoe, Ultrix versions 1.2 and 2.0, and	 */
/*			under VMS,  TWG WIN/VX and TGV Multinet.	 */
/*									 */
/*  Components:		required: if_dda.c, if_ddareg.h, if_ddavar.h,	 */
/*			  and one of: if_dda_uqbus.c if_dda_bibus.c	 */
/*			optional: if_pi.c, if_pivar.h, if_x29.c,	 */
/*				  if_vmsx29.c				 */
/*									 */
/*************************************************************************/


#include "dda.h"

#if NDDA > 0

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								 %%*/
/*%%		       SYSTEM CONFIGURATION			 %%*/
/*%%								 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#if !defined(ACC_ULTRIX) && !defined(ACC_BSD) && !defined(ACC_VMS)
	ERROR
		an ACC OS specific option must be defined in your config file
	ERROR
#endif

/*
 *	now define the un-set options to zero
 */
#if !defined(ACC_ULTRIX)
#define	ACC_ULTRIX	00
#endif

#if !defined(ACC_BSD)
#define	ACC_BSD		00
#endif

#if !defined(ACC_VMS)
#define ACC_VMS		00
#endif

/*
 * the define DDA_MSGQ enables the message queue.  this adds 2k to the
 * data size of the driver.  It should only be used during driver development
 */

/*#define DDA_MSGQ		/* uncomment this to enable message queue */

/*
 * The following line disables the use of the histogram facilities.  This
 * value (DDA_HISTOGRAM) is automatically undefined for all 4.2 and ULTRIX
 * 1.2 systems which do not support the histogram facilities.
 */

#define DDA_HISTOGRAM		/* comment this out to disable histogram */

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								 %%*/
/*%%		       INCLUDE FILES				 %%*/
/*%%								 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#ifndef SIMULATION		/* real unix system */
#include "../machine/pte.h"	/* page table entries */
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/mbuf.h"
#include "../h/buf.h"
#include "../h/protosw.h"
#include "../h/socket.h"
#include "../h/vmmac.h"
#include "../h/errno.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/kernel.h"
#include "../h/ioctl.h"

#include "../vax/cpu.h"
#include "../vax/mtpr.h"

#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#  if ACC_BSD > 42 || ACC_ULTRIX > 12
#    include "../netinet/in_var.h"
#  endif
#include "../netinet/ip.h"
#include "../netinet/ip_var.h"

#include "../vaxif/if_ddareg.h"
#include "../vaxif/if_ddavar.h"

#else	SIMULATION
#include "machine/pte.h"	/* page table entries */

#include "h/param.h"
#include "h/systm.h"
#include "h/mbuf.h"
#include "h/buf.h"
#include "h/protosw.h"
#include "h/socket.h"
#include "h/vmmac.h"
#include "h/errno.h"
#include "h/dir.h"
#include "h/user.h"
#include "h/kernel.h"
#include "h/ioctl.h"

#include "vax/cpu.h"
#include "vax/mtpr.h"

#include "net/if.h"
#include "net/netisr.h"
#include "net/route.h"
#include "netinet/in.h"
#include "netinet/in_systm.h"
#  if ACC_BSD > 42 || ACC_ULTRIX > 12
#    include "netinet/in_var.h"
#  endif
#include "netinet/ip.h"
#include "netinet/ip_var.h"
#include "if_ddareg.h"
#include "if_ddavar.h"

#  ifndef	SIOCACPCONFIG
#    define	SIOCACPCONFIG	_IOWR(i,40,struct ifreq)
#  endif
#  ifndef	INET
#    define	INET	1
#  endif

extern	struct	ifqueue ipintrq;	/* IP input queue */
#endif	SIMULATION

#if ACC_VMS > 00
#  ifdef eunice
#    define WINS
#  else
#    define MULTINET
#  endif
#endif

#if ACC_VMS > 00
#  ifdef WINS
#    include <vms/adpdef.h>	/* Define Adapters */
#    include <vms/dcdef.h>	/* Define AT$_UBA, adapter type */
#  else MULTINET
#    include "../vaxif/if_ddaioctl.h"	/* not in ioctl.h */
#  endif
#endif 

/* disable histogram functions for BSD 4.2 and ULTRIX 1.2 */

#if ACC_BSD == 42 || ACC_ULTRIX == 12
#  undef DDA_HISTOGRAM
#endif

/* Ultrix doesn't have syslog, so use printf instead.  Since the two
 * functions take different arg list formats, embed the open paren in
 * the defined symbol; provide DDAELOG to close the call while keeping
 * parentheses matched.	 The argument to DDALOG is ignored for printf;
 * for log(), debugging messages use LOG_DEBUG, all others use LOG_ERR.
 */
#if (ACC_BSD > 42 || ACC_VMS > 00) && !defined(SIMULATION)
#  include "syslog.h"
#  define DDALOG(s)	log( s,
#else
#  define DDALOG(s)	printf(
#endif
#define DDAELOG		)

#ifndef	DDADEBUG
#define	PRIVATE		static		/* hide our internal functions */
#else
#define	PRIVATE				/* let the world see them */
#endif

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								 %%*/
/*%%		       GLOBAL FUNCTIONS				 %%*/
/*%%								 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

int		ddaprobe();
int		ddaattach();
int		ddareset();
int		ddainit();
int		ddaoutput();
int		ddatimer();
int		ddaioctl();
int		ddainta();	/* service interrupt "a" from front end */
int		ddaintb();	/* service interrupt "b" from front end */


/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								 %%*/
/*%%		       LOCAL  FUNCTIONS				 %%*/
/*%%								 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void send_config();
PRIVATE struct dda_cb *locate_x25_lcn();
PRIVATE struct dda_cb *find_free_lcn();
PRIVATE boolean	convert_ip_addr();
PRIVATE u_long	convert_x25_addr();
PRIVATE boolean	make_x25_call();
PRIVATE void	dda_start();
PRIVATE void	dda_rrq();
PRIVATE void	dda_wrq();
PRIVATE int	start_chn();
PRIVATE void	dda_data();
PRIVATE void	dda_supr();
PRIVATE void	supr_msg();
PRIVATE boolean	decode_ring();
PRIVATE void	decode_answer();
PRIVATE void	clear_lcn();
PRIVATE void	send_restart();
PRIVATE void	send_supr();
PRIVATE void	start_supr();
PRIVATE void	abort_io();
PRIVATE void	prt_addr();

#ifdef DDA_PAD_OR_RAW
PRIVATE int	dda_decode_type();
#endif

#ifdef DDA_PADOPT
PRIVATE void	x29_data();
PRIVATE void	x29_supr();
PRIVATE void	x29_init();
#endif DDA_PADOPT

#ifdef DDA_RAWOPT
PRIVATE void	pi_data();
PRIVATE void	pi_supr();
PRIVATE void	pi_init();
PRIVATE int	pi_circuit_to_handle_protocol();
#endif DDA_RAWOPT

#ifdef DDADEBUG
PRIVATE void	prt_bytes();
#endif

PRIVATE char    *fmt_x25();

#ifdef DDA_HISTOGRAM
PRIVATE void	hist_init();	/* histogram functions */
PRIVATE void	hist_lcn_state();
PRIVATE void	hist_all_lcns();
PRIVATE void	hist_link_state();
PRIVATE void	hist_read();
PRIVATE int	hist_copyout();

#else DDA_HISTOGRAM		/* make all histogram functions no-op's */
#define hist_init(a,b)
#define hist_lcn_state(a,b,c)
#define hist_all_lcns(a,b)
#define hist_link_state(a,b,c)
#define hist_read(a)
#define hist_copyout(a,b)
#endif DDA_HISTOGRAM


/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								 %%*/
/*%%		       LOCAL  VARIABLES				 %%*/
/*%%								 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int	tmo_data_idle = TMO_DATA_IDLE;	/* idle circuit timeout for
						 * all boards */

PRIVATE int	nddach[4] = {	/* number of channels currently in use */
			NDDACH_DEFAULT, NDDACH_DEFAULT,
			NDDACH_DEFAULT, NDDACH_DEFAULT };

PRIVATE char   *dda_product;	/* name of product, like "ACP5250" */
PRIVATE int	dda_hasmaint;	/* do we have a maintenance board? */

/* the message bits are used in the DMESG macros defined in if_ddavar.h */
/* word 1 and 2 (msgs 0   -  63)  are reserved for the IP interface	*/
/* word 3	(msgs 64  -  95)  are reserved for the PI interface	*/
/* word 4	(msgs 96  -  127) are reserved for the X.29 interface	*/
/* word 5 and 6 (msgs 128 -  191) are reserved for debugging main module*/
/* word 7	(msgs 192 -  223) are reserved for debugging the PI     */
/* word 8	(msgs 224 -  255) are reserved for debugging X29	*/
/* word 9	(msgs 256 -  287) are reserved for call logging		*/
#define NDMESGWORDS	9
#define MAXDMSGS	(NDMESGWORDS * 32)
PRIVATE long	ddamsgs[NDDA][NDMESGWORDS];
/*					       |  |  |  |   |   |   |   |   |
 * default:	all informational messages on, /--/--/--/   |   |   |   |   |
 *		all debug messages off,	--------------------/---/---/---/   |
 *		log busys, but not calls or I/O aborts ---------------------/
 */

/* Must be as large as the larger of (trtab, ddactl, dnload): */
char	dda_iobuf[sizeof(struct ddactl)];

struct dda_softc dda_softc[NDDA];	/* per device infomation */

/*  header for building command to be sent to the front end in	 */
/*  response to ACPCONFIG user command				 */

PRIVATE u_char acpconfig_msg[] = {
	LINE_CNTL,		/* set command code */
	0x00,			/* not used */
	0x00,			/* not used */
	0x00,			/* extension length (set at runtime) */
	0x00,			/* cmd space */
	0x00,
	0x00,
	0x00,
	0x00
};

PRIVATE u_char bfr_size_msg[] =
{
 SET_BFR_SIZE,			/* set command code */
 0x00,				/* not used */
 0x00,				/* not used */
 0x01,				/* extension length */
 0x00,				/* cmd space */
 0x00,
 0x00,
 0x00,
 0x00
};

PRIVATE u_char	ddacb_cmnd[4] = { CALL, 0, 0, 0 };
PRIVATE u_char	ddacb_called_addr[16] = {0};
PRIVATE u_char	ddacb_calling_addr[16] = {0};
PRIVATE u_char	ddacb_facilities[64] = {0};
PRIVATE u_char	ddacb_protocol[5] = {0};
PRIVATE u_char	ddacb_user_data[64] = {0};

#ifdef DDADEBUG
u_char		dda_silo_counter;
u_char		dda_debug_silo[256];
#endif

/* Table of baud rate values and the associated parameter for the Set	*/
/* System Parameters message, ddainit_msg.  The 'parameter1' is nonzero */
/* for valid baud rate divisors.  These are nominal baud rates.		*/

PRIVATE struct baud {
    char	    b_value;
    u_char	    parameter1; /* first byte of baud rate setting  */
    u_char	    parameter2; /* second byte of baud rate setting */
}		ddabaud_rate[] = {
    { 1, 0x02, 0x00 },		/* 2.00M */
    { 2, 0x03, 0x00 },		/* 1.33M */
    { 3, 0x04, 0x00 },		/* 1.00M */
    { 4, 0x08, 0x00 },		/* 500K	 */
    { 5, 0x10, 0x00 },		/* 250K	 */
    { 6, 0x28, 0x00 },		/* 100K	 */
    { 7, 0x3e, 0x00 },		/* 64K	 */
    { 8, 0x47, 0x00 },		/* 56K	 */
    { 9, 0x85, 0x00 },		/* 30K	 */
    { 10, 0xd0, 0x00 },		/* 19.2K */
    { 11, 0xa1, 0x01 },		/* 9600	 */
    { 12, 0x41, 0x03 },		/* 4800	 */
    { 13, 0x83, 0x06 },		/* 2400	 */
    { 14, 0x05, 0x0d },		/* 1200	 */
    { 0, 0, 0 }
};

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								 %%*/
/*%% Address Translation Table for Internet <-> X.25 addresses	 %%*/
/*%%								 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#define DDANATT 32	/* number of addr translation table entries */
PRIVATE int	dda_num_addr_tr[NDDA] = {0};	/* number of address
						 * translations */

 /* currently stored */
PRIVATE struct dda_addr_tr {	/* X.25 PDN address translation table */
    u_long	    ip_addr;			/* internet address */
    u_char	    x25_addr[MAXADDRLEN];	/* X.25 address */
}		dda_addr_tr[NDDA][DDANATT] = {{ 0L, ""}}; /* null */

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								 %%*/
/*%% Aliasing of IP address for 4.2 ==> 4.3 compatibility	 %%*/
/*%% Note: this union is not required in 4.2, since the s_net	 %%*/
/*%% field and its friends are in an include file.  We use it to %%*/
/*%% minimize the number of #ifdef dependencies in the code.	 %%*/
/*%%								 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#ifdef s_net			/* 4.2 */
# undef s_net
# undef s_host
# undef s_lh
# undef s_impno
#endif

union imp_addr {
    struct in_addr  ip;
    struct imp {
	u_char		s_net;
	u_char		s_host;
	u_char		s_lh;
	u_char		s_impno;
    }		    imp;
};

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								 %%*/
/*%%		       GLOBAL ROUTINES				 %%*/
/*%%								 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#ifdef	ACP_BI
#include "if_dda_bibus.c"
#else
#include "if_dda_uqbus.c"
#endif

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%			  DDAIOCTL()				 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*								   */
/*  Purpose:							   */
/*								   */
/*   This routine processes device dependent ioctl's.  Supported   */
/*   ioctls set the host's internet address for this network	   */
/*   interface, or send Set System Parameters Message to the ACP.  */
/*   The logic for setting the interface address must remain	   */
/*   compatible with both ifconfig and acpconfig programs.	   */
/*   If the ioctl comes from the acpconfig program, the front end  */
/*   is not initialized because the user will specify explicitly   */
/*   what parameters are desired.  If the ioctl comes from the	   */
/*   ifconfig program, the fron end is initialized with default	   */
/*   parameters in the ddainit_msg array.			   */
/*								   */
/*  Call:	     ddaioctl(ifp, cmd, data)			   */
/*  Argument:	     ifp:   pointer to the network interface data  */
/*				 structure, ifnet		   */
/*		     cmd:   identifies the type of ioctl	   */
/*		     data:  information for the ioctl		   */
/*  Returns:	     0 for success, or the nonzero error value:	   */
/*				  EINVAL invalid ioctl request	   */
/*  Called by:	      network software, address of this routine is */
/*		      defined in af_inet network interface struct  */
/*  Calls to:	      splimp()					   */
/*		      if_rtinit()				   */
/*		      in_netof()				   */
/*		      in_lnaof()				   */
/*		      ddainit()					   */
/*		      send_config()				   */
/*		      DDALOG()					   */
/*		      splx()					   */
/*								   */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#ifdef MULTINET
volatile int	StatQuery_Completed;	/* Polled for board stat ioctl */
#endif

ddaioctl(ifp, cmd, data)
register struct ifnet *ifp;
int		cmd;
caddr_t		data;
{
    register struct dda_softc *ds = &dda_softc[ifp->if_unit];
    struct ifreq   *ifr = (struct ifreq *) data;

#if defined(DDA_PADOPT) && defined(WINS)
    int		    prealloc_x29();	/* Preallocate UCBs for X29 */
#endif

#if ACC_BSD == 42 || ACC_ULTRIX == 12
    struct sockaddr_in *sin = (struct sockaddr_in *) & ifr->ifr_addr;
#else
    struct ifaddr  *ifa = ds->dda_if.if_addrlist;
#endif

    int		    s;
    int		    error = 0;
    int		    i;
    register struct dda_addr_tr *atp, *btp;
    struct trtab   *tr;
    struct ddactl  *da;
    char	    arg2[MAXADDRLEN], code;

#ifdef DDADEBUG
    if (DDADBCH(4, ifp->if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: ioctl()\n", ifp->if_unit DDAELOG;
    }
#endif DDADEBUG

    /*
     * This may not be necessary here, but under some flavors of BSDish
     * systems (2.0ULTRIX) this routine is apparently called at splimp(). In
     * the case that we are currently processing ioctls issued from acpconfig
     * in /etc/rc, the board may not have come on line yet - so we need to be
     * able to process the B interrupt while in the delay loop below. 
     */
#ifndef MULTINET
    s = spl0();
#endif

    switch (cmd) {
      case SIOCSIFADDR:
	if (!suser())
	    return EACCES;

#if ACC_BSD == 42 || ACC_ULTRIX == 12
	if (ifp->if_flags & IFF_RUNNING)
	    if_rtinit(ifp, -1);	/* delete previous route */
	ifp->if_addr = *(struct sockaddr *) sin;
	ifp->if_net = in_netof(sin->sin_addr);
	ifp->if_host[0] = in_lnaof(sin->sin_addr);
	if (ifp->if_flags & IFF_RUNNING)
	    if_rtinit(ifp, RTF_UP);
	else
	    ddainit(ifp->if_unit);
	ds->dda_ipaddr.s_addr = ((struct sockaddr_in *) & ifp->if_addr)->sin_addr.s_addr;
#else					 /* 4.3 networking */
	if (ifa->ifa_addr.sa_family != AF_INET)
	    return (EINVAL);
	if ((ifp->if_flags & IFF_RUNNING) == 0)
	    ddainit(ifp->if_unit);
	ds->dda_ipaddr = IA_SIN(ifa)->sin_addr;
#endif					/* 4.3 networking */
	break;

      case SIOCACPCONFIG:
	/* process ioctl from acpconfig program */

	code = *(ifr->ifr_data);

	/*********************************************************
	 *							 *
	 *	Commands n, h, q, and r are non-privileged	 *
	 *							 *
	 *********************************************************/

	if (!suser() && code != 'n' && code != 'h' && code != 'q' && code != 'r')
	    return EACCES;

#if ACC_BSD == 42 || ACC_ULTRIX == 12
	sin = (struct sockaddr_in *) & ds->dda_if.if_addr;
	if (in_netof(sin->sin_addr) == 0)
#else 
	if (ds->dda_if.if_addrlist == 0)
#endif 
	{
	    error = EDESTADDRREQ;	/* error, no internet address */
	    goto exit;
	}
	/* for command to set baud rate, look up the value for the  */
	/* baud rate divisor in the ddabaud_rate table, put value   */
	/* in the Set System Parameters message, ddainit_msg        */

	if (code >= 1 && code <= 14) {
	    register struct baud *p;

	    if (error = diags_completed(ds))
		goto exit;
	    for (p = ddabaud_rate; p->b_value; p++) {
		if ((*(ifr->ifr_data) - p->b_value) == 0)
		    break;
	    }
	    /* if internal clock not set, do so */
	    if ((ds->dda_init & DDA_INTCLOCK) == 0) {
		ds->dda_init |= DDA_INTCLOCK;
		acpconfig_msg[MSG_OFFSET] = CLOCK_CNTL;
		acpconfig_msg[MSG_OFFSET + 1] = INTERNAL_CLOCK;
		acpconfig_msg[MSG_OFFSET + 2] = BAUD_CNTL;
		acpconfig_msg[MSG_OFFSET + 3] = p->parameter1;
		acpconfig_msg[MSG_OFFSET + 4] = p->parameter2;
		acpconfig_msg[MSG_LENGTH] = 5;
	    } else {
		acpconfig_msg[MSG_OFFSET] = BAUD_CNTL;
		acpconfig_msg[MSG_OFFSET + 1] = p->parameter1;
		acpconfig_msg[MSG_OFFSET + 2] = p->parameter2;
		acpconfig_msg[MSG_LENGTH] = 3;
	    }

	    if ((p->b_value == 0) || (p->parameter1 == 0))
		error = EINVAL;	/* baud rate value invalid */
	    else
		send_config(ds, acpconfig_msg);	/* send message to front end */
	    goto exit;
	}
	switch (code) {
	  case 'a':		/* Add address translation table entry */
	    if (error = diags_completed(ds))
		goto exit;
	    if (dda_num_addr_tr[ifp->if_unit] >= DDANATT) {	/* table already full */
		error = ENOMEM;
		goto exit;
	    }

	    /*
	     * Copy in user arguments and point "tr" at them.  Then scan the
	     * translation table and either find location to insert or flag
	     * error 
	     */
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct trtab))) {
		error = EFAULT;
		goto exit;
	    }
	    tr = (struct trtab *) dda_iobuf;
	    for (i = 0, atp = &dda_addr_tr[ifp->if_unit][0];
		 i < dda_num_addr_tr[ifp->if_unit]; i++, atp++) {
		if (atp->ip_addr == tr->ipaddr) {
		    if (bcmp((char *) atp->x25_addr,
			     (char *) tr->x25addr, MAXADDRLEN)) {
			error = EADDRINUSE;
			goto exit;
		    } else /* addresses are the same, just ignore ioctl */
			goto exit;
		}
		if (atp->ip_addr > tr->ipaddr)	/* insert entry here */
		    break;
	    }
	    for (btp = &dda_addr_tr[ifp->if_unit][dda_num_addr_tr[ifp->if_unit]];
		 btp > atp; btp--) {	/* open up space for a new entry */
		btp->ip_addr = (btp - 1)->ip_addr;
		bcopy((btp - 1)->x25_addr, btp->x25_addr, MAXADDRLEN);
	    }
	    atp->ip_addr = tr->ipaddr;
	    bcopy(tr->x25addr, atp->x25_addr, MAXADDRLEN);
	    dda_num_addr_tr[ifp->if_unit]++;	/* one more table entry */
	    goto exit;

	  case 'D':
	    if (error = diags_completed(ds))
		goto exit;
	    /* clear table for use by 'r' flag of acpconfig */
	    for (i = 0, atp = &dda_addr_tr[ifp->if_unit][0];
		 i < dda_num_addr_tr[ifp->if_unit]; i++, atp++) {
		atp->ip_addr = 0L;
		atp->x25_addr[0] = 0;
	    }
	    dda_num_addr_tr[ifp->if_unit] = 0;
	    goto exit;

	  case 'd':		/* Delete address translation table entry */
	    if (error = diags_completed(ds))
		goto exit;
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct trtab))) {
		error = EFAULT;
		goto exit;
	    }
	    tr = (struct trtab *) dda_iobuf;
	    error = EFAULT;	/* in case inet address not in table */
	    for (i = 0, atp = &dda_addr_tr[ifp->if_unit][0];
		 i < dda_num_addr_tr[ifp->if_unit]; i++, atp++) {
		if (atp->ip_addr == tr->ipaddr) {
		    error = 0;	/* found it: cancel error */
		    for (; i < dda_num_addr_tr[ifp->if_unit] - 1; i++, atp++) {
			atp->ip_addr = (atp + 1)->ip_addr;
			bcopy((atp + 1)->x25_addr, atp->x25_addr, MAXADDRLEN);
		    }
		    atp->ip_addr = 0L;	/* clear last vacated entry */
		    atp->x25_addr[0] = 0;
		    dda_num_addr_tr[ifp->if_unit]--;	/* one fewer table
							 * entries */
		    break;
		}
	    }
	    goto exit;


	  case 'f':		/* -f facility status */

	    /*
	     * The first byte of the "msg" selects the flow control parameter
	     * and the "drval" field holds the status (on or off). 
	     */
	    if (error = diags_completed(ds))
		goto exit;
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) {
		error = EFAULT;
		goto exit;
	    }
	    if (ds->dda_firmrev < 0x21) {	/* need 2.0 or above ROMs */
		error = ENOPROTOOPT;
		goto exit;
	    }
	    da = (struct ddactl *) dda_iobuf;
	    switch (da->msg[0]) {
	      case 0:		/* packet */
		if (da->drval)
		    ds->dda_init |= DDA_PKTNEG;
		else
		    ds->dda_init &= ~DDA_PKTNEG;
		break;
	      case 1:		/* window */
		if (da->drval)
		    ds->dda_init |= DDA_WNDNEG;
		else
		    ds->dda_init &= ~DDA_WNDNEG;
		break;
	    }
	    goto exit;

	  case 'o':		/* Set options */
	    if (error = diags_completed(ds))
		goto exit;
	    if (ds->dda_firmrev < 0x21) {	/* need 2.1 or above ROMs */
		error = ENOPROTOOPT;
		goto exit;
	    }
	    if (ds->dda_state != S_DISABLED) {	/* must bring link down */
		error = EINPROGRESS;
		goto exit;
	    }
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) {
		error = EFAULT;
		goto exit;
	    }
	    da = (struct ddactl *) dda_iobuf;
	    acpconfig_msg[MSG_OFFSET] = PKT_OPTIONS;
	    acpconfig_msg[MSG_OFFSET + 1] = da->msg[0];
	    acpconfig_msg[MSG_LENGTH] = 2;
#ifdef DDADEBUG
	    if (DDADBCH(4, ifp->if_unit)) {
		DDALOG(LOG_DEBUG) "dda%d: acpconfig_msg is %x %x %x\n",
		    ifp->if_unit, acpconfig_msg[MSG_LENGTH],
		    acpconfig_msg[MSG_OFFSET], acpconfig_msg[MSG_OFFSET + 1] DDAELOG;
	    }
#endif DDADEBUG

	    send_config(ds, acpconfig_msg);
	    goto exit;

	  case 'N':		/* read network id */
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) {
		error = EFAULT;
		goto exit;
	    }
	    da = (struct ddactl *) dda_iobuf;
	    ds->dda_net_id = da->drval;
	    goto exit;

	  case 'r':		/* Read address translation table entry */

	    /*
	     * The value stored in "ipaddr" is not an address, but an index
	     * of a translation table entry to read out.  The x25_addr field
	     * in the input structure is not used. 
	     */
	    if (error = diags_completed(ds))
		goto exit;
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct trtab))) {
		error = EFAULT;
		goto exit;
	    }
	    tr = (struct trtab *) dda_iobuf;
	    i = tr->ipaddr;
	    if (i >= DDANATT) {	/* scanned the whole table */
		error = EFAULT;
		goto exit;
	    }
	    tr->ipaddr = dda_addr_tr[ifp->if_unit][i].ip_addr;
	    bcopy(dda_addr_tr[ifp->if_unit][i].x25_addr, tr->x25addr, MAXADDRLEN);
	    if (copyout(tr, ifr->ifr_data, sizeof(struct trtab)))
		error = EFAULT;
	    goto exit;

#ifdef DDA_HISTOGRAM
	  case 'h':		/* read histogram */
	    if (error = diags_completed(ds))
		goto exit;
	    hist_read(ifp->if_unit);
	    if (hist_copyout(ifp->if_unit, ifr->ifr_data))
		error = EFAULT;
	    goto exit;

	  case 'H':		/* read and reset histogram */
	    if (error = diags_completed(ds))
		goto exit;
	    hist_read(ifp->if_unit);
	    if (hist_copyout(ifp->if_unit, ifr->ifr_data))
		error = EFAULT;
	    else
		hist_init(ifp->if_unit, 1);
	    goto exit;
#endif DDA_HISTOGRAM

	  case 'v':		/* -v variable value */

	    /*
	     * There are two "variables" in the driver which can be set via
	     * ioctl: packet size, and window size.  The "drval" field holds
	     * the value and the first byte of the "msg" selects the variable.
	     * Note that the selector is another little undocumented piece of
	     * the interface between here and the acpconfig program. It is
	     * coupled to the ordering of a little string table inside that
	     * program; new parameters should be added at the end, not the
	     * middle! 
	     */
	    /* No check to see if powerup diags are completed */
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) {
		error = EFAULT;
		goto exit;
	    }
	    da = (struct ddactl *) dda_iobuf;
	    switch (da->msg[0]) {
	      case 0:		/* set logging (obsolete) */
	      case 1:		/* set debug (obsolete) */
	      case 2:		/* set debug unit (obsolete) */
		error = EINVAL;
		break;

		/*
		 * For both packet and window sizes, we check that the link
		 * is currently down.  The new parameters will be sent to the
		 * FEP when the link is next brought up.  See processing for
		 * -u flag. 
		 */
	      case 3:		/* set packetsize */
		if (error = diags_completed(ds))
		    goto exit;
		if (ds->dda_firmrev < 0x21) {	/* need 2.1 or above ROMs */
		    error = ENOPROTOOPT;
		    goto exit;
		}
		if (ds->dda_state != S_DISABLED) {	/* must bring link down */
		    error = EINPROGRESS;
		    goto exit;
		}

		/*
		 * X.25 (1984) section 7.2.2.1.1 says 12 (4096 byte packets)
		 * BBN report 5760 (September 1984) 14.2.1.2 says 10. We just
		 * check for 12. 
		 */
		if (da->drval < 4 || da->drval > 12)
		    error = EINVAL;
		else {
		    int             packetsize = 1 << da->drval;

		    acpconfig_msg[MSG_LENGTH] = 3;
		    acpconfig_msg[MSG_OFFSET] = MAX_PKT_SZ;	/* Max negotiable */
		    /* pkt size */
		    acpconfig_msg[MSG_OFFSET + 1] = packetsize & 0xFF;
		    acpconfig_msg[MSG_OFFSET + 2] = (packetsize >> 8) & 0xFF;
		    send_config(ds, acpconfig_msg);
		}
		break;

	      case 4:		/* set windowsize */
		if (error = diags_completed(ds))
		    goto exit;
		if (ds->dda_firmrev < 0x21) {	/* need 2.0 or above ROMs */
		    error = ENOPROTOOPT;
		    goto exit;
		}
		if (ds->dda_state != S_DISABLED) {	/* must bring link down */
		    error = EINPROGRESS;
		    goto exit;
		}
		if (da->drval < 1 || da->drval > 127)
		    error = EINVAL;
		else {
		    acpconfig_msg[MSG_LENGTH] = 2;
		    acpconfig_msg[MSG_OFFSET] = MAX_PKT_WN;	/* Max negotiable */
		    /* pkt window */
		    acpconfig_msg[MSG_OFFSET + 1] = da->drval;
		    send_config(ds, acpconfig_msg);
		}
		break;
	    }
	    goto exit;

	  case 'm':		/* -m message */
	    if (error = diags_completed(ds))
		goto exit;
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) {
		error = EFAULT;
		goto exit;
	    }
	    da = (struct ddactl *) dda_iobuf;
	    send_config(ds, da->msg);
	    goto exit;

	  case 'n':		/* -n svc_count */
	    if (error = diags_completed(ds))
		goto exit;
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) {
		error = EFAULT;
		goto exit;
	    }
	    if (ds->dda_firmrev < 0x21) {	/* need 2.1 or above ROMs */
		error = ENOPROTOOPT;
		goto exit;
	    }
	    da = (struct ddactl *) dda_iobuf;
	    i = 0;		/* i holds the return value */
	    if (da->drval == 0)
		i = nddach[ifp->if_unit];
	    else if (ds->dda_state != S_DISABLED) {	/* must bring link down */
		error = EINPROGRESS;
		goto exit;
	    } else {
		if (!suser()) {
		    error = EACCES;
		    goto exit;
		}
		if (da->drval < 1 || da->drval > NDDACH)
		    error = EINVAL;
		else {
		    acpconfig_msg[MSG_LENGTH] = 2;
		    acpconfig_msg[MSG_OFFSET] = SVC_LIMIT;
		    acpconfig_msg[MSG_OFFSET + 1] = da->drval;
		    nddach[ifp->if_unit] = da->drval;
		    send_config(ds, acpconfig_msg);
		}
	    }
	    if (copyout(&i, ifr->ifr_data, sizeof(int)))
		error = EFAULT;
	    goto exit;

	  case 'c':		/* -c msgnum  -- dis/enable driver mesg */
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) {
		error = EFAULT;
		goto exit;
	    }
	    da = (struct ddactl *) dda_iobuf;
	    if (da->drval < 0 || da->drval >= MAXDMSGS)
		error = EINVAL;
	    else {
		u_char          new_val;

		DMESGTOG(ifp->if_unit, da->drval);
		new_val = DMESGVAL(ifp->if_unit, da->drval) ? 1 : 0;
		/* 1 means disabled, 0 means enabled */
		if (copyout(&new_val, ifr->ifr_data, sizeof(u_char)))
		    error = EFAULT;
	    }
	    goto exit;

	  case 't':		/* -t sec  -- set data idle timeout */
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) {
		error = EFAULT;
		goto exit;
	    }
	    da = (struct ddactl *) dda_iobuf;
	    if (da->drval < 1)
		error = EINVAL;
	    else
		tmo_data_idle = da->drval / DDA_TIMEOUT;
	    goto exit;

	  case 'q':		/* driver/FE/shm/silo state queries */
	    if (copyin(ifr->ifr_data, dda_iobuf, sizeof(struct ddactl))) {
		error = EFAULT;
		goto exit;
	    }
	    da = (struct ddactl *) dda_iobuf;
	    switch (da->msg[0]) {
	    case 0:		/* front end state query */
		if ((error = diags_completed(ds)) == 0) {
		    int s2 = splimp();

		    /* need 2.0 or above ROMs */
		    if (ds->dda_firmrev < 0x21) {
			error = ENOPROTOOPT;
			splx(s2);	/* We got it and woke up */
			break;
		    }
#ifdef	MULTINET
		    StatQuery_Completed = 0;
		    send_supr(ds, STATQUERY, 0, 0);
		    splx(s2);	/* drop ioctl so we can be scheduled */
		    while (!StatQuery_Completed);
#else	MULTINET
		    send_supr(ds, STATQUERY, 0, 0);
		    sleep(dda_iobuf, PSLEP); /* Interruptible with ^C */
		    splx(s2);	/* We got it and woke up */
#endif	MULTINET

		    if (copyout(dda_iobuf, ifr->ifr_data,
			     sizeof(struct ddactl)))
			error = EFAULT;
		}
		break;
	    case 1:		/* driver state query */
		da->msg[0] = ds->dda_state;
		da->msg[1] = ds->dda_init;
		da->msg[2] = ds->dda_flags;
		da->msg[3] = ds->dda_firmrev;
		if (copyout(dda_iobuf, ifr->ifr_data,
			     sizeof(struct ddactl)))
		    error = EFAULT;
		break;
#ifdef DDADEBUG
	    case 2:		/* debug query */
		if (copyout(dda_debug_silo, ifr->ifr_data, 256))
		    error = EFAULT;
		break;
#endif
#if defined(DDADEBUG) && defined(ACP_BI)
	    case 3:		/* shm/biic query (temporary) */
		{
		    register struct uba_device *ui = ddainfo[ifp->if_unit];
		    dda_dump_shm((SYSGEN_BLOCK *) ds->dda_mapreg);
		    dda_dump_biic_regs((struct biic_regs *) ui->ui_addr);
                }
		break;
#endif
	    default:
		error = EINVAL;
	    }
	    goto exit;

	  case '0':		/* -u 0 */
	    if (error = diags_completed(ds))
		goto exit;
	    acpconfig_msg[MSG_OFFSET] = LINK_DISABLE;
	    acpconfig_msg[MSG_LENGTH] = 1;
	    hist_link_state(ifp->if_unit, ds->dda_state, S_GOING_DOWN);
	    ds->dda_state = S_GOING_DOWN;
	    break;
	  case '1':		/* -u 1 */
	    if (error = diags_completed(ds))
		goto exit;
	    acpconfig_msg[MSG_OFFSET] = LINK_LOOPBACK;
	    acpconfig_msg[MSG_OFFSET + 1] = LOOP_NONE;
	    acpconfig_msg[MSG_OFFSET + 2] = DTE_DCE_MODE;
	    acpconfig_msg[MSG_OFFSET + 3] = DTE;
	    acpconfig_msg[MSG_OFFSET + 4] = LINK_ENABLE;
	    acpconfig_msg[MSG_LENGTH] = 5;
	    ds->dda_state = S_COMING_UP;
	    hist_init(ifp->if_unit, 0);
	    break;
	  case '2':		/* -u 2 */
	    if (error = diags_completed(ds))
		goto exit;
	    acpconfig_msg[MSG_OFFSET] = LINK_LOOPBACK;
	    acpconfig_msg[MSG_OFFSET + 1] = LOOP_NONE;
	    acpconfig_msg[MSG_OFFSET + 2] = DTE_DCE_MODE;
	    acpconfig_msg[MSG_OFFSET + 3] = DCE;
	    acpconfig_msg[MSG_OFFSET + 4] = LINK_ENABLE;
	    acpconfig_msg[MSG_LENGTH] = 5;
	    ds->dda_state = S_COMING_UP;
	    hist_init(ifp->if_unit, 0);
	    break;
	  case '3':		/* -u 3 */
	    if (error = diags_completed(ds))
		goto exit;
	    acpconfig_msg[MSG_OFFSET] = LINK_LOOPBACK;
	    acpconfig_msg[MSG_OFFSET + 1] = LOOP_EXTERNAL;
	    acpconfig_msg[MSG_OFFSET + 2] = LINK_ENABLE;
	    acpconfig_msg[MSG_LENGTH] = 3;
	    ds->dda_state = S_COMING_UP;
	    hist_init(ifp->if_unit, 0);
	    break;
	  case '4':		/* -u 4 */
	    if (error = diags_completed(ds))
		goto exit;
	    acpconfig_msg[MSG_OFFSET] = LINK_LOOPBACK;
	    acpconfig_msg[MSG_OFFSET + 1] = LOOP_INTERNAL;
	    acpconfig_msg[MSG_OFFSET + 2] = LINK_ENABLE;
	    acpconfig_msg[MSG_LENGTH] = 3;
	    ds->dda_state = S_COMING_UP;
	    hist_init(ifp->if_unit, 0);
	    break;
	  case 'b':		/* -b 0 */
	    if (error = diags_completed(ds))
		goto exit;
	    acpconfig_msg[MSG_OFFSET] = CLOCK_CNTL;
	    acpconfig_msg[MSG_OFFSET + 1] = EXTERNAL_CLOCK;
	    acpconfig_msg[MSG_LENGTH] = 2;
	    ds->dda_init &= ~DDA_INTCLOCK;
	    break;
	  case 'S':		/* select DDN standard X.25 service */
	    /* -s 0 or -s standard */
	    if (error = diags_completed(ds))
		goto exit;
	    if (ds->dda_if.if_flags & IFF_UP && ds->dda_init & DDA_PDN) {
		error = EALREADY;
		goto exit;	/* no PDN->DDN mode change if running */
	    }
	    ds->dda_init &= ~(DDA_BASIC | DDA_PDN);
	    ds->dda_init |= DDA_STANDARD;
	    goto exit;
	  case 'T':		/* select DDN basic X.25 service */
	    /* -s 1 or -s basic */
	    if (error = diags_completed(ds))
		goto exit;
	    if (ds->dda_if.if_flags & IFF_UP && ds->dda_init & DDA_PDN) {
		error = EALREADY;
		goto exit;	/* no PDN->DDN mode change if running */
	    }
	    ds->dda_init &= ~(DDA_PDN | DDA_STANDARD);
	    ds->dda_init |= DDA_BASIC;
	    goto exit;
	  case 'U':		/* select X.25 Public Data Network service */
	    /* -s 2 or -s pdn */
	    if (error = diags_completed(ds))
		goto exit;
	    if (ds->dda_if.if_flags & IFF_UP && (ds->dda_init &
					      (DDA_BASIC | DDA_STANDARD))) {
		error = EALREADY;
		goto exit;	/* no DDN->PDN mode change if running */
	    }
	    ds->dda_init &= ~(DDA_BASIC | DDA_STANDARD);
	    ds->dda_init |= DDA_PDN;
	    goto exit;

	  case 'e':		/* set buffer size */
	    /* -e size size is encoded in second byte */
	    if (error = diags_completed(ds))
		goto exit;

	    /*
	     * check to see if we have newer at least version 2.2 roms. 
	     */
	    if (ds->dda_firmrev < 0x22) {
		error = ENOPROTOOPT;
		goto exit;
	    }
	    if (ds->dda_if.if_flags & IFF_UP) {
		error = EALREADY;
		goto exit;	/* no PDN->DDN mode change if running */
	    }
	    bfr_size_msg[MSG_OFFSET] = ifr->ifr_data[1];
	    send_config(ds, bfr_size_msg);
	    bufreset(ifp->if_unit);
	    goto exit;

#ifdef	ACP_BI
	  case 'L':
	    {	struct dda_dnload dl;
		if (copyin(ifr->ifr_data, &dl, sizeof(struct dda_dnload))) {
		    error = EFAULT;
		    goto exit;
		}
		error = dda_dload(ifp->if_unit, &dl);
		goto exit;
	    }
#endif

#if defined(DDA_PADOPT) && defined(WINS)
	  case 'x':		/* Preallocate UCBs for X29 -- VMS only */
	    printf("Preallocated %d PTYs for X29\n", prealloc_x29());
	    goto exit;
#endif

	  case 'z':		/* reset specified front-end device, -z */
/* second parm is supposed to be uban, but ddareset doesn't care about it */
	    ddareset(ifp->if_unit, 0);
	    goto exit;
	  default:
	    error = EINVAL;
	    goto exit;
	}
	if ((*(ifr->ifr_data) != '0') && (ds->dda_init & DDA_PDN) &&
#if ACC_BSD == 42 || ACC_ULTRIX == 12
	    (convert_ip_addr(sin->sin_addr, (u_char *) arg2, ds) == 0)
#else
	    (convert_ip_addr(ds->dda_ipaddr, (u_char *) arg2, ds) == 0)
#endif
	    ) {
	    error = EADDRNOTAVAIL;
	    goto exit;
	}
	send_config(ds, acpconfig_msg);
	break;

      default:
	error = EINVAL;
    }

exit:
#ifndef	MULTINET
    splx(s);
#endif
    return (error);
}


/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAOUTPUT()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*   This routine is called by the network software when it has an */
/*   IP datagram to send out this interface.  An attempt is made   */
/*   to find a LCN which has a virtual circuit open to the         */
/*   indicated host.  If an LCN is found the packet is queued for  */
/*   output on that LCN.                                           */
/*                                                                 */
/*  Call:            ddaoutput(ifp, m0, dst)                       */
/*  Arguments:       ifp:  locates the network interface, ifnet    */
/*                   m0:   locates an mbuf buffer                  */
/*                   dst:  is the socket destination address       */
/*  Returns:         0 for success, or one of following nonzero    */
/*                        error indications:                       */
/*                               ENETDOWN                          */
/*                               EAFNOSUPPORT                      */
/*                               ENOBUFS                           */
/*  Called by:     network software, address of this routine is    */
/*                 defined in the dda_if network interface struct  */
/*  Calls to:      DDALOG()                                        */
/*                 m_freem()                                       */
/*                 splimp()                                        */
/*                 locate_x25_lcn()                                */
/*                 IF_QFULL()                                      */
/*                 IF_DROP()                                       */
/*                 splx()                                          */
/*                 IF_ENQUEUE()                                    */
/*                 dda_start()                                     */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

ddaoutput(ifp, m0, dst)
struct ifnet   *ifp;
struct mbuf    *m0;
struct sockaddr_in *dst;
{
    register struct mbuf *m = m0;
    register struct dda_softc *ds = &dda_softc[ifp->if_unit];
    register struct dda_cb *dc;
    register struct ifqueue *oq;
    struct mbuf    *prev;
    int             s;
    union imp_addr  imp_addr;

#ifdef DDADEBUG
    if (DDADBCH(1, ifp->if_unit)) {
	imp_addr.ip = dst->sin_addr;
	DDALOG(LOG_DEBUG) "dda%d: ddaoutput: dst = %d.%d.%d.%d\n",
	    ifp->if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host,
	    imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG;
    }
#endif DDADEBUG

    if ((ds->dda_if.if_flags & IFF_UP) == 0)
	return (ENETDOWN);

    switch (dst->sin_family) {

#ifdef INET
      case AF_INET:
	break;
#endif INET

      default:
	DMESG(ifp->if_unit, 2,
	      (DDALOG(LOG_ERR) "dda%d: can't handle af%d\n", ifp->if_unit,
	       dst->sin_family DDAELOG));
	m_freem(m0);
	return (EAFNOSUPPORT);
    }

#ifdef DDADEBUG
    if (DDADBCH(2, ifp->if_unit)) {
	imp_addr.ip = dst->sin_addr;
	DDALOG(LOG_DEBUG) "dda%d: ddaoutput: dst = %d.%d.%d.%d\n",
	    ifp->if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host,
	    imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG;
    }
#endif DDADEBUG

    /* Mod to V1.5b ==> V1.5b1 */
    /* In 4.3, the IP code may pass mbuf chains with 0-length mbufs */
    /* This causes "transfer count = 0" messages and might even     */
    /* cause actual garbage data transmission if the mbuf is at the */
    /* end of the chain (we don't think it ever will be, but one    */
    /* can't be too sure...so we scan the chain first).		    */
    /* WE DO ASSUME that there is at least one nonempty mbuf!	    */
    /* (ULTRIX: we don't know, but the code is at worst harmless)   */

    while (m0->m_len == 0) {
	m = m0;
	m0 = m0->m_next;
	m->m_next = 0;
	m_freem(m);
    }
    /* Now we know the first mbuf (at m0)  is not zero length	    */
    prev = m0;
    m = m0->m_next;
    while (m) {
	if (m->m_len == 0) {
	    prev->m_next = m->m_next;
	    m->m_next = 0;
	    m_freem(m);
	    m = prev->m_next;
	} else {
	    prev = m;
	    m = m->m_next;
	}
    }
    m = m0;			/* reset m to beginning of modified chain */

    s = splimp();		/* disable interrrupts */

    /* try to find an LCN */

    if (dc = locate_x25_lcn(ds, dst->sin_addr)) {	/* if found */
#ifdef DDADEBUG
	if (DDADBCH(2, ifp->if_unit)) {
	    DDALOG(LOG_DEBUG) "dda%d: ddaoutput: lcn found = %d\n", ifp->if_unit,
		dc->dc_lcn DDAELOG;
	}
#endif DDADEBUG
	oq = &(dc->dc_oq);	/* point to output queue */
	if (IF_QFULL(oq)) {	/* if q full */
	    IF_DROP(oq);	/* drop the data */
	    m_freem(m);
	    ds->dda_if.if_collisions++;	/* for netstat display */
	    splx(s);
	    return (ENOBUFS);
	}
	IF_ENQUEUE(oq, m);	/* otherwise queue it */
	dda_start(ds, dc);	/* and try to output */
	splx(s);
	return (0);
    } else {			/* if no circuit available */
	IF_DROP(&ifp->if_snd);	/* drop the data */
	m_freem(m);
	splx(s);
	return (EHOSTUNREACH);
    }

}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDATIMER()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  This routine is entered to perform timer management. The       */
/*  LCN table is scanned for active timers (nonzero) which are     */
/*  decremented.  If a timer expires (becomes zero), the proper    */
/*  action is taken.                                               */
/*                                                                 */
/*                                                                 */
/*  Call:              ddatimer(unit)                              */
/*  Arguments:         unit:  ACP device unit number               */
/*  Returns:           nothing                                     */
/*  Called by:         ddainit()                                   */
/*  Calls to:          splimp()                                    */
/*                     send_restart()                              */
/*                     clear_lcn()                                 */
/*                     splx()                                      */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

int
ddatimer(unit)
int             unit;
{
    register struct dda_softc *ds = &dda_softc[unit];
    register struct dda_cb *dc;
    register int    s, lcn;

#ifdef DDADEBUG
    if (DDADBCH(3, unit)) {
	DDALOG(LOG_DEBUG) "dda%d: ddatimer()\n", unit DDAELOG;
    }
#endif DDADEBUG

    ds->dda_if.if_timer = DDA_TIMEOUT;	/* restart timer */

    dc = ds->dda_cb;

    s = splimp();

    /* LCNLINK */
    for (lcn = 0; lcn <= nddach[unit]; lcn++) {	/* scan all LCN's */
#ifdef DDADEBUG
	if (dc->dc_out_t && lcn > 0 && (--(dc->dc_out_t) == 0)) {
	    DDALOG(LOG_DEBUG) "dda%d: write completion timeout lcn %d\n",
		unit, lcn DDAELOG;
	}
#endif
	if (dc->dc_timer && (--(dc->dc_timer) == 0)) {	/* if a timer expired */
	    if (dc->dc_state == LC_RESTART) {	/* if a restart was out */
		send_restart(ds);	/* send another one */
		break;
	    } else {		/* otherwise */
#ifdef DDA_PAD_OR_RAW
		/* if it is not an X.29 connection */
		if ((dc->dc_flags & (DC_X29W | DC_X29 | DC_RAW)) == 0)
#endif
		    clear_lcn(ds, dc);	/* clear the LCN */
	    }
	}
	dc++;
    }
    splx(s);
}


/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                     DIAGS_COMPLETED()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*    This routine checks to see that power up diagnostics have completed*/
/*    It waits for a while if necessary.                                 */
/*  Call:          diags_completed(ds)                                   */
/*  Argument:      ds - pointer to softc structure;                      */
/*  Returns:       0 if board is up, EINTR if it never came on line.     */
/*  Called by:     ddaioctl()                                            */
/*  Calls to:                                                            */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

diags_completed(ds)
struct dda_softc *ds;
{
    int             nretries = 10;

    /*
     * It's overkill to check this on EVERY ioctl, because it only matters if
     * we are going to touch the board.  But the driver has had repeated
     * problems with not checking it when it should have - overkill is
     * preferred.  The delays are here rather then in the acpconfig program
     * due to a bug in acpconfig.  They will only be executed during
     * /etc/rc.local when the board has not had a chance to do the "B"
     * interrupt yet.  At that time the machine will be running essentially
     * single thread so it won't really matter where the delays are.  (ie, if
     * we put the delay into acpconfig and kept calling here 10 times, the
     * machine would not do anything else useful in the meantime - might as
     * well loop here). 
     */
    while (((ds->dda_flags & DDAF_OK) == 0) && nretries-- > 0)
	DELAY(3000000);
    if ((ds->dda_flags & DDAF_OK) == 0)	/* never came on line... */
	return (EINTR);
    else
	return (0);
}



/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   LOCAL  FUNCTIONS                          %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      SEND_CONFIG()                          %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*   Send a Set System Parameters Message to the front end in      */
/*   response to an ACPCONFIG command from the user.               */
/*                                                                 */
/*  Call:          send_config(ds, p)                              */
/*  Argument:      ds:  pointer to ACP device control structure    */
/*		   p: pointer to the message			   */
/*  Returns:           nothing                                     */
/*  Called by:         ddaioctl()                                  */
/*  Calls to:          MGET()                                      */
/*                     DDALOG()                                    */
/*                     mtod()                                      */
/*                     bcopy()                                     */
/*                     sizeof()                                    */
/*                     start_supr()                                 */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
send_config(ds, p)
struct dda_softc *ds;
u_char		 *p;
{
    struct mbuf    *m;
    register u_char *bp;
    int             length;

#ifdef DDADEBUG
    if (DDADBCH(7, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: send_config()\n", ds->dda_if.if_unit DDAELOG;
    }
#endif DDADEBUG

    MGET(m, M_DONTWAIT, MT_DATA);
    if (m == 0) {
	DMESG(ds->dda_if.if_unit, 18,
	      (DDALOG(LOG_ERR) "dda%d: can't get bfr for acpconfig msg\n",
	       ds->dda_if.if_unit DDAELOG));
	return;
    }
    bp = mtod(m, u_char *);	/* point to data section of mbuf */

    length = p[MSG_LENGTH] + MSG_OFFSET;	/* msg length */
    if (length > MLEN - 1) {

	/*
	 * Supervisory messages have to fit in a small mbuf.  The driver
	 * itself is careful never to get in trouble this way, but now that
	 * we have "-m" the user could.  Dropping such a message is not
	 * likely to cause any big problems, and the user can rephrase the
	 * request. 
	 */
	DMESG(ds->dda_if.if_unit, 19,
	      (DDALOG(LOG_ERR) "dda%d: supervisor message too long\n",
	       ds->dda_if.if_unit DDAELOG));
	m->m_next = 0;
	m_freem(m);
	return;
    }
    bcopy(p, bp, length);
    m->m_len = length;		/* set msg length */

#ifdef DDADEBUG
    if (DDADBCH(8, ds->dda_if.if_unit)) {
	prt_bytes(ds->dda_if.if_unit, "send_config", bp, length);
    }
#endif DDADEBUG

    start_supr(ds, m);
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      LOCATE_X25_LCN()                       %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine tries to locate an X25 LCN associated with a    */
/*    remote internet address.  A linear search of the LCN table   */
/*    is made for a matching address.  If the search succeeds, the */
/*    LCN is returned.  If the search fails, the LCN table is      */
/*    searched for an unused table entry.  If an unused table      */
/*    entry is found, an X25 call is generated to the host         */
/*    specified in the destination internet address.  If no LCN is */
/*    available, zero is returned.                                 */
/*                                                                 */
/*  Call:              locate_x25_lcn(ds, ip_addr)                 */
/*  Argument:          ds:   pointer to dev control block struct   */
/*                     ip_addr:  IP address                        */
/*  Returns:           pointer to the dda control block which      */
/*                     contains LCN, else zero for failure         */
/*  Called by:         ddaoutput()                                 */
/*  Calls to:          convert_ip_addr()                           */
/*                     make_x25_call()                             */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE struct dda_cb *
locate_x25_lcn(ds, ip_addr)
struct dda_softc *ds;
struct in_addr  ip_addr;
{
    register int    lcn, maxlcn;
    register struct dda_cb *dc;
    union imp_addr  imp_addr;

#ifdef DDADEBUG
    if (DDADBCH(9, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: locate_x25_lcn()\n",
	    ds->dda_if.if_unit DDAELOG;
    }
#endif DDADEBUG

    imp_addr.ip = ip_addr;	/* DDN X.25 doesn't know net number */

    if (!(ds->dda_init & DDA_PDN)) {
	if ((imp_addr.imp.s_net & 0x80) == 0x00) {	/* class A */
	    imp_addr.imp.s_net = 0;
	    imp_addr.imp.s_lh = 0;
	} else if ((imp_addr.imp.s_net & 0xc0) == 0x80) {	/* class B */
	    imp_addr.imp.s_net = 0;
	    imp_addr.imp.s_host = 0;
	} else {		/* class C, should check for class C */
	    imp_addr.imp.s_net = 0;
	    imp_addr.imp.s_host = 0;
	    imp_addr.imp.s_lh = 0;
	}
    }
    /* LCNLINK */
    maxlcn = nddach[ds->dda_if.if_unit];
    dc = &(ds->dda_cb[1]);	/* scan LCN table for addr match */
    for (lcn = 1; lcn <= maxlcn; lcn++) {
	if ((dc->dc_key.key_addr.s_addr == imp_addr.ip.s_addr)
	&& (dc->dc_state == LC_CALL_PENDING || dc->dc_state == LC_DATA_IDLE))
	    return (dc);	/* return LCN */
	dc++;
    }

    if ((dc = find_free_lcn(ds)) == 0)
	return (0);

    ddacb_user_data[0] = (u_char) 0;		/* we have no user data */

    if (convert_ip_addr(ip_addr, ddacb_called_addr, ds)
	&& make_x25_call(ds, dc, ip_addr, X25_PROTO_IP)) {
						/* addr can be converted */
	dc->dc_inaddr = ip_addr;		/* store dest ip addr */
	dc->dc_key.key_addr.s_addr = imp_addr.ip.s_addr;
						/* store match key */
#ifdef DDADEBUG
	if (DDADBCH(9, ds->dda_if.if_unit)) {
	    imp_addr.ip = ip_addr;
	    DDALOG(LOG_DEBUG)
		"dda%d: locate_x25_lcn: made call to %d.%d.%d.%d\n",
		ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host,
		imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG;

	}
#endif DDADEBUG
	return (dc);		/* and return the LCN */
    } else {
	return (0);		/* give up */
    }
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      FIND_FREE_LCN()                        %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine tries to locate a free X25 LCN.                 */
/*    The LCN table is searched for an unused entry.               */
/*    If no LCN is available, zero is returned.                    */
/*                                                                 */
/*  Call:              find_free_lcn(ds)                           */
/*  Argument:          ds:   pointer to dev control block struct   */
/*  Returns:           pointer to the dda control block which      */
/*                     contains LCN, else zero for failure         */
/*  Called by:         locate_x25_lcn()                            */
/*		       supr_msg()				   */
/*		       xxcntl()					   */
/*  Calls to:          DDALOG()                                    */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE struct dda_cb *
find_free_lcn(ds)
struct dda_softc *ds;
{
    struct dda_cb  *dc;
    register int    lcn, maxlcn, dwnflg = 0;

    /* LCNLINK */
    dc = &(ds->dda_cb[1]);	/* scan LCN table for free entry */
    maxlcn = nddach[ds->dda_if.if_unit];
    for (lcn = 1; lcn <= maxlcn; lcn++) {
#ifdef DDA_PAD_OR_RAW
	if (dc->dc_state == LC_IDLE && (dc->dc_flags & (DC_X29W | DC_X29 | DC_RAW)) == 0)
#else
	if (dc->dc_state == LC_IDLE)
#endif DDA_PAD_OR_RAW
	    break;
	else if (dc->dc_state == LC_RESTART || dc->dc_state == LC_DOWN)
	    dwnflg = 1;
	dc++;
    }

    /* LCNLINK */
    if (lcn > maxlcn) {		/* if we didn't find a free entry */
	if (LOG_BUSY) {
	    if (dwnflg)
		DDALOG(LOG_ERR) "dda%d: no circuits available (link not up)\n",
		    ds->dda_if.if_unit DDAELOG;
	    else
		DDALOG(LOG_ERR) "dda%d: all circuits in use\n",
		    ds->dda_if.if_unit DDAELOG;
	}
	return (0);		/* return empty handed */
    }
    return (dc);
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      CONVERT_IP_ADDR()                      %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    Based on the type of X.25 service, this routine performs     */
/*    one of two functions.  For PDN X.25 service, the address     */
/*    translation table is searched for presence of local X.25     */
/*    address (which was entered by the user via acpconfig).       */
/*                                                                 */
/*    For DDN X.25 service, this routine accepts an internet       */
/*    address and attempts to translate to an equivalent X25       */
/*    address.  This follows the guidelines in the DDN X25         */
/*    interface spec.  The resultant X25 address is stored in the  */
/*    X25 called addr buffer.                                      */
/*                                                                 */
/*    NOTE: Although front end was designed to accept ASCII coded  */
/*    digits for the address fields, we only supply the binary     */
/*    values.  The front end only uses low four bits to extract    */
/*    the binary value from the ASCII digits, so this works out.   */
/*                                                                 */
/*                                                                 */
/*                                                                 */
/*                                                                 */
/*                                                                 */
/*                                                                 */
/*  Call:              convert_ip_addr(ip_addr, x25addr, ds)       */
/*  Argument:          ip_addr:  IP address                        */
/*                     x25addr:  X.25 address                      */
/*                     ds:       &dda_softc[unit]                  */
/*  Returns:           1 for success                               */
/*  Called by:         locate_x25_lcn()                            */
/*                     make_x25_call()                             */
/*                     ddaioctl()                                  */
/*  Calls to:          bcopy()                                     */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE boolean
convert_ip_addr(ip_addr, x25addr, ds)
struct in_addr  ip_addr;
u_char          x25addr[];
struct dda_softc *ds;

{
    register struct dda_addr_tr *atp, *hip, *lop;
    register int    temp;
    union imp_addr  imp_addr;

/****************************************************************/
/* processing for Public Data Network (PDN) X.25 service        */
/* search address translation table for local address           */
/****************************************************************/

    if (ds->dda_init & DDA_PDN) {
	x25addr[0] = 0;		/* clear result X.25 address length */
	lop = &dda_addr_tr[ds->dda_if.if_unit][0];	/* set up for binary
							 * search */
	hip = &dda_addr_tr[ds->dda_if.if_unit][dda_num_addr_tr[ds->dda_if.if_unit]];
	while (lop < hip - 1) {	/* binary search loop */
	    atp = lop + (hip - lop) / 2;
	    if (atp->ip_addr > ip_addr.s_addr)
		hip = atp;
	    else
		lop = atp;
	}
	if (lop->ip_addr == ip_addr.s_addr)
	    bcopy(lop->x25_addr, x25addr, MAXADDRLEN);
    }
/****************************************************************/
/* processing for DDN Standard or Basic X.25 service            */
/* convert IP address to X.25 address                           */
/****************************************************************/

    else {
	int             imp_no, imp_port;


	imp_addr.ip = ip_addr;
	if ((imp_addr.imp.s_net & 0x80) == 0x00) {	/* class A */
	    imp_no = imp_addr.imp.s_impno;
	    imp_port = imp_addr.imp.s_host;
	} else if ((imp_addr.imp.s_net & 0xc0) == 0x80) {	/* class B */
	    imp_no = imp_addr.imp.s_impno;
	    imp_port = imp_addr.imp.s_lh;
	} else {		/* class C */
	    imp_no = imp_addr.imp.s_impno / 32;
	    imp_port = imp_addr.imp.s_impno % 32;
	}

	x25addr[0] = 12;	/* set addr length */
	x25addr[1] = 0;		/* clear DNIC */
	x25addr[2] = 0;
	x25addr[3] = 0;
	x25addr[4] = 0;

	if (imp_port < 64) {	/* Physical:  0000 0 IIIHH00 [SS] *//* s_impno
				 *  -> III, s_host -> HH */
	    x25addr[5] = 0;	/* set flag bit */
	    x25addr[6] = imp_no / 100;
	    x25addr[7] = (imp_no % 100) / 10;
	    x25addr[8] = imp_no % 10;
	    x25addr[9] = imp_port / 10;
	    x25addr[10] = imp_port % 10;
	} else {		/* Logical:   0000 1 RRRRR00 [SS]	 *//* s
				 * _host * 256 + s_impno -> RRRRR */
	    temp = (imp_port << 8) + imp_no;
	    x25addr[5] = 1;
	    x25addr[6] = temp / 10000;
	    x25addr[7] = (temp % 10000) / 1000;
	    x25addr[8] = (temp % 1000) / 100;
	    x25addr[9] = (temp % 100) / 10;
	    x25addr[10] = temp % 10;
	}

	x25addr[11] = 0;	/* clear rest of addr */
	x25addr[12] = 0;
    }

#ifdef DDADEBUG
    if (DDADBCH(11, ds->dda_if.if_unit)) {
	imp_addr.ip = ip_addr;
	DDALOG(LOG_DEBUG) "dda%d: convert_ip_addr: %d.%d.%d.%d ==> %s\n",
	    ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host,
	    imp_addr.imp.s_lh, imp_addr.imp.s_impno,
	    fmt_x25(&x25addr[1], (int) x25addr[0]) DDAELOG;
    }
#endif DDADEBUG

    return (x25addr[0] ? 1 : 0);
}




/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      CONVERT_X25_ADDR()                     %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine accepts an X25 address and attempts to          */
/*    translate to an equivalent internet address.  For DDA this   */
/*    follows the guidelines in the DDA X25 interface spec.  The   */
/*    resultant internet address is returned to the caller.        */
/*                                                                 */
/*  Call:              convert_x25_addr(x25addr, ds)               */
/*  Argument:          x25addr:  X.25 address                      */
/*                     ds:       &dda_softc[unit]                  */
/*                     dc: pointer to allocated dda_cb structure   */
/*  Returns:           IP address for success, else zero for fail  */
/*  Called by:         supr_msg()                                  */
/*  Calls to:          DDALOG()                                    */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE          u_long
convert_x25_addr(x25addr, ds, dc)
u_char          x25addr[];
struct dda_softc *ds;
struct dda_cb  *dc;

{
    register int    cnt, temp;
    union imp_addr  imp_addr;
    register struct dda_addr_tr *atp;

    dc->dc_inaddr.s_addr = imp_addr.ip.s_addr = 0L;
    if (ds->dda_init & DDA_PDN) {
	for (atp = &dda_addr_tr[ds->dda_if.if_unit][0];
	     atp < &dda_addr_tr[ds->dda_if.if_unit][dda_num_addr_tr[ds->dda_if.if_unit]]; atp++) {
	    if (bcmp((char *) atp->x25_addr, (char *) x25addr, x25addr[0] + 1) == 0) {
		/* set key address and print address up */
		dc->dc_inaddr.s_addr = imp_addr.ip.s_addr = atp->ip_addr;
		break;
	    }
	}
    } else {
	int             imp_no, imp_port;
	struct in_addr  my_addr;

	my_addr = ds->dda_ipaddr;
	if (((cnt = x25addr[0]) < MINADDRLEN - 1) || (cnt > MAXADDRLEN - 1)) {
	    DMESG(ds->dda_if.if_unit, 20,
		  (DDALOG(LOG_ERR) "dda%d: illegal X25 address length!\n",
		   ds->dda_if.if_unit DDAELOG));
	    return (0L);
	}
	switch (x25addr[5] & 0x0f) {
	  case 0:		/* Physical:  0000 0 IIIHH00 [SS]	 */
	    imp_no =
		((int) (x25addr[6] & 0x0f) * 100) +
		((int) (x25addr[7] & 0x0f) * 10) +
		((int) (x25addr[8] & 0x0f));


	    imp_port =
		((int) (x25addr[9] & 0x0f) * 10) +
		((int) (x25addr[10] & 0x0f));
	    break;
	  case 1:		/* Logical:   0000 1 RRRRR00 [SS]	 */
	    temp = ((int) (x25addr[6] & 0x0f) * 10000)
		+ ((int) (x25addr[7] & 0x0f) * 1000)
		+ ((int) (x25addr[8] & 0x0f) * 100)
		+ ((int) (x25addr[9] & 0x0f) * 10)
		+ ((int) (x25addr[10] & 0x0f));

	    imp_port = temp >> 8;
	    imp_no = temp & 0xff;
	    break;
	  default:
	    DMESG(ds->dda_if.if_unit, 21,
		  (DDALOG(LOG_ERR) "dda%d: illegal X25 address format!\n",
		   ds->dda_if.if_unit DDAELOG));
	    return (0L);
	}

	dc->dc_inaddr = imp_addr.ip = my_addr;	/* use local net number */
	if ((imp_addr.imp.s_net & 0x80) == 0x00) {	/* class A */
	    imp_addr.imp.s_net = 0;	/* mask net number */
	    imp_addr.imp.s_lh = 0;	/* mask logical host */
	    imp_addr.imp.s_host = imp_port;
	    ((union imp_addr *) & dc->dc_inaddr.s_addr)->imp.s_host = imp_port;
	    imp_addr.imp.s_impno = imp_no;
	    ((union imp_addr *) & dc->dc_inaddr.s_addr)->imp.s_impno = imp_no;
	} else if ((imp_addr.imp.s_net & 0xc0) == 0x80) {	/* class B */
	    imp_addr.imp.s_net = 0;
	    imp_addr.imp.s_lh = imp_port;
	    ((union imp_addr *) & dc->dc_inaddr.s_addr)->imp.s_lh = imp_port;
	    imp_addr.imp.s_host = 0;
	    imp_addr.imp.s_impno = imp_no;
	    ((union imp_addr *) & dc->dc_inaddr.s_addr)->imp.s_impno = imp_no;
	} else {		/* class C */
	    imp_addr.imp.s_impno = (imp_no << 5) + imp_port;
	    ((union imp_addr *) & dc->dc_inaddr.s_addr)->imp.s_impno = imp_addr.imp.s_impno;
	    imp_addr.imp.s_lh = 0;
	    imp_addr.imp.s_host = 0;
	    imp_addr.imp.s_net = 0;
	}
    }

#ifdef DDADEBUG
    if (DDADBCH(12, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: convert_x25_addr: %s ==> %d.%d.%d.%d\n",
	    ds->dda_if.if_unit, fmt_x25(&x25addr[1], (int) x25addr[0]),
	    imp_addr.imp.s_net, imp_addr.imp.s_host, imp_addr.imp.s_lh,
	    imp_addr.imp.s_impno DDAELOG;
    }
#endif DDADEBUG

    return (imp_addr.ip.s_addr);
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      MAKE_X25_CALL()                        %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine places an X25 call using the X25 Call Msg       */
/*    buffer.  The calling LCN is placed in the appropriate state  */
/*    and a timer is started.  Based on dda_init flag, implement   */
/*    DDN standard or basic service.  (If PDN mode is set, then    */
/*    the logic for basic service is followed.)                    */
/*                                                                 */
/*  Call:              make_x25_call(ds, dc, ip_addr, proto	   */
/*				     udlen, ud) 		   */
/*  Arguments:         ds:  pointer to device control structure    */
/*                     dc:  pointer to the Logical Channel control */
/*                            block structure                      */
/*                     ip_addr: callee's ip address                */
/*                     proto: protocol identifier byte             */
/*		       udlen: user data length			   */
/*		       ud:    user data				   */
/*  Returns:           one for success, zero for failure           */
/*  Called by:         locate_x25_lcn()                            */
/*  Calls to:          MGET()                                      */
/*                     mtod()                                      */
/*                     convert_ip_addr()                           */
/*                     bcopy()                                     */
/*                     IF_ENQUEUE()                                */
/*                     start_supr()                                */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE boolean
make_x25_call(ds, dc, ip_addr, proto, udlen, ud)
register struct dda_softc *ds;
register struct dda_cb *dc;
struct in_addr  ip_addr;
u_char          proto;
u_char		udlen;
u_char		*ud;
{
    register struct mbuf *m_callbfr;
    register u_char *cb;
    union imp_addr  imp_addr;

#if ACC_BSD == 42 || ACC_ULTRIX == 12
    struct sockaddr_in *our_addr;
#endif 

#ifdef DDADEBUG
    if (DDADBCH(13, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: make_x25_call: lcn used = %d\n",
	    ds->dda_if.if_unit, dc->dc_lcn DDAELOG;
    }
#endif DDADEBUG

    MGET(m_callbfr, M_DONTWAIT, MT_DATA);	/* try to get call cmnd
						 * buffer */
    if (m_callbfr == 0) {
	DMESG(ds->dda_if.if_unit, 22,
	      (DDALOG(LOG_ERR) "dda%d: couldn't get mbuf for call command\n",
	       ds->dda_if.if_unit DDAELOG));
	return (0);
    }
    cb = mtod(m_callbfr, u_char *);

    if (ds->dda_net_id == TRANSPAC) {
	ddacb_calling_addr[0] = 0;	/* send a 0 length calling address */
    } else {
#if ACC_BSD == 42 || ACC_ULTRIX == 12
	our_addr = (struct sockaddr_in *) & (ds->dda_if.if_addr);
	(void) convert_ip_addr(our_addr->sin_addr, ddacb_calling_addr, ds);
#else
	(void) convert_ip_addr(ds->dda_ipaddr, ddacb_calling_addr, ds);
#endif
    }

    ddacb_protocol[0] = 4;
    ddacb_protocol[1] = proto;	/* protocol type */
    ddacb_protocol[2] = 0;
    ddacb_protocol[3] = 0;
    ddacb_protocol[4] = 0;

    /*
     * CCITT standard facilities must precede DDN specific facilities See BBN
     * report 5476 section 2.1.2.  Firmware preceding rev 0x20 does not
     * support packet size / window negotiation. 
     */
    ddacb_facilities[0] = 0;	/* initialize facilities length */
    if (ds->dda_firmrev >= 0x21) {
	ddacb_facilities[0] = 0;
	if (ds->dda_init & DDA_PKTNEG) {
	    int             n = ddacb_facilities[0];	/* length so far */

	    ddacb_facilities[n + 1] = X25_FACIL_PKTSIZE;
	    ddacb_facilities[n + 2] = PKTSIZE_LARGE;
	    ddacb_facilities[n + 3] = PKTSIZE_LARGE;
	    ddacb_facilities[0] += 3;
	}
	if (ds->dda_init & DDA_WNDNEG) {
	    int             n = ddacb_facilities[0];	/* length so far */

	    ddacb_facilities[n + 1] = X25_FACIL_WINSIZE;
	    ddacb_facilities[n + 2] = WINSIZE_LARGE;
	    ddacb_facilities[n + 3] = WINSIZE_LARGE;
	    ddacb_facilities[0] += 3;
	}
    }
    if ((ds->dda_init & (DDA_BASIC | DDA_PDN)) == 0) {	/* DDN standard mode,
							 * tell callee */
	int             n = ddacb_facilities[0];	/* length so far */

	ddacb_facilities[0] += 4;	/* additional facility bytes */
	ddacb_facilities[n + 1] = DDN_FACIL_MARKER; /* end of CCITT stuff, */
	ddacb_facilities[n + 2] = DDN_FACIL_MARKER; /* and start DDN local */
	ddacb_facilities[n + 3] = X25_FACIL_DDA;    /* DDA standard mode */
	ddacb_facilities[n + 4] = FAC_DDASTD;
    }

    ddacb_cmnd[0] = CALL;	/* set command code */
    ddacb_cmnd[1] = dc->dc_lcn << 1;	/* set channel id */
    ddacb_cmnd[2] = 0;
    ddacb_cmnd[3] = (ddacb_called_addr[0] + 1) +	/* tally cmnd ext len */
	(ddacb_calling_addr[0] + 1) +
	(ddacb_protocol[0] + 1) +
	(ddacb_facilities[0] + 1) +
	(ddacb_user_data[0] + 1);

    if ((unsigned) ddacb_cmnd[3] + 4 > MLEN) {
	DMESG(ds->dda_if.if_unit, 38, (DDALOG(LOG_ERR)
	    "dda%d: make_x25_call message too large for mbuf (%d bytes)\n",
	    ds->dda_if.if_unit, (unsigned) ddacb_cmnd[3] + 4 DDAELOG));
	return 0;	/* failure */
    }

    m_callbfr->m_len = ddacb_cmnd[3] + 4;

    /* copy command header */
    bcopy(ddacb_cmnd, cb, 4);
    cb += 4;

    /* copy called address */
    bcopy(ddacb_called_addr, cb, ddacb_called_addr[0] + 1);
    cb += (ddacb_called_addr[0] + 1);

    /* copy calling address */
    bcopy(ddacb_calling_addr, cb, ddacb_calling_addr[0] + 1);
    cb += (ddacb_calling_addr[0] + 1);

    /* copy protocol */
    bcopy(ddacb_protocol, cb, ddacb_protocol[0] + 1);
    cb += (ddacb_protocol[0] + 1);

    /* copy facilities */
    bcopy(ddacb_facilities, cb, ddacb_facilities[0] + 1);
    cb += (ddacb_facilities[0] + 1);

    /* copy user data */
    bcopy(ddacb_user_data, cb, ddacb_user_data[0] + 1);
    cb += (ddacb_user_data[0] + 1);

    hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_CALL_PENDING);
    dc->dc_state = LC_CALL_PENDING;	/* set state */
    dc->dc_timer = TMO_CALL_PENDING;	/* start call timeout */

#ifdef DDADEBUG
    if (DDADBCH(13, ds->dda_if.if_unit)) {
	prt_bytes(ds->dda_if.if_unit, "make_x25_call: call_bfr",
		  mtod(m_callbfr, u_char *), m_callbfr->m_len);
    }
#endif DDADEBUG
    if (LOG_CALLS) {
	imp_addr.ip = ip_addr;
	DDALOG(LOG_ERR) "dda%d: Calling %d.%d.%d.%d (%s) on lcn %d\n",
	    ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host,
	    imp_addr.imp.s_lh, imp_addr.imp.s_impno,
	    fmt_x25(&ddacb_called_addr[1], (int) ddacb_called_addr[0]),
	    dc->dc_lcn
	    DDAELOG;
    }
    start_supr(ds, m_callbfr);
    return (1);
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDA_START()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine attempts to start output of data queued on a    */
/*    specific LCN.  If the LCN was not already busy and data is   */
/*    available for output, the data is copied into the LCN's I/O  */
/*    buffer and a write request queued to the ACP device.  Data   */
/*    is passed in mbuf(s) from IP to ddaoutput(), ddaoutput()     */
/*    queues the data, and the data is dequeued here.              */
/*                                                                 */
/*  Call:              dda_start(ds, dc)                           */
/*  Arguments:         ds:  pointer to device control structure    */
/*                     dc:  pointer to the Logical Channel control */
/*                            block structure                      */
/*  Returns:           nothing                                     */
/*  Called by:         ddaoutput()                                 */
/*                     x25_init()                                  */
/*                     make_x25_call()                             */
/*                     supr_msg()                                  */
/*                     send_supr()                                 */
/*                     dda_data()                                  */
/*                     dda_supr()                                  */
/*  Calls to:          IF_DEQUEUE()                                */
/*                     dda_wrq()                                   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
dda_start(ds, dc)
register struct dda_softc *ds;
register struct dda_cb *dc;
{
    register struct mbuf *m;
    register struct hdx_chan *hc = &dc->dc_wchan;
    register int    s;

#ifdef DDADEBUG
    if (DDADBCH(14, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: dda_start()\n", ds->dda_if.if_unit DDAELOG;
    }
#endif DDADEBUG

    /*
     * If output isn't active, attempt to start sending a new packet. 
     */

    if ((dc->dc_flags & DC_OBUSY) || (dc->dc_oq.ifq_len == 0) ||
	((dc->dc_lcn != 0) && (dc->dc_state != LC_DATA_IDLE))) {
	return;
    }
    if (dc->dc_lcn != 0)
	dc->dc_timer = tmo_data_idle;
/*
 *  Raise priority whenever touching dc_oq because
 *  the mbufs on this queue may be asynchronously
 *  freed upon receipt of a line status msg, restart,
 *  clear, or reset.
 */
    s = splimp();
    IF_DEQUEUE(&dc->dc_oq, m);
    splx(s);
    if (m == 0) {		/* XXX this is a bug catcher XXX */

	DMESG(ds->dda_if.if_unit, 24,
	 (DDALOG(LOG_ERR) "dda%d: dequeued NULL mbuf in IP output chain!\n",
	  ds->dda_if.if_unit DDAELOG));
	DMESG(ds->dda_if.if_unit, 24,
	      (DDALOG(LOG_ERR) "RESET dda%d MANUALLY: use /etc/acpconfig dda%d -z\n",
	       ds->dda_if.if_unit, ds->dda_if.if_unit DDAELOG));

	ds->dda_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
	hist_link_state(ds->dda_if.if_unit, ds->dda_state, S_DISABLED);
	ds->dda_state = S_DISABLED;
	dda_disable(ds->dda_if.if_unit);
	return;
    }
    s = splimp();
    hc->hc_mbuf = m;
    hc->hc_curr = m;
#ifdef DDA_PAD_OR_RAW		/* crufty kludge to get the Qbit */
    if (dc->dc_flags & (DC_X29 | DC_X29W | DC_RAW)) {	/* raw or x29? */
	if (m->m_len < (MLEN - 1))	/* small mbuf? */
	    hc->hc_sbfc = m->m_dat[MLEN - 1];	/* ok, get the subfunc byte */
	else
	    hc->hc_sbfc = 0;	/* subfunc must be zero for large buffers */
    } else
	hc->hc_sbfc = 0;	/* subfunc must be zero for ip buffers */
#else
    hc->hc_sbfc = 0;
#endif
    splx(s);
    dc->dc_flags |= DC_OBUSY;
    dda_wrq(ds, hc, 0);		/* write request to ACP */
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDA_DATA()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine is called when a data channel I/O completes.    */
/*    If the completion was for a write, an attempt is made to     */
/*    start output on the next packet waiting for output on that   */
/*    LCN.  If the completion was for a read, the received packet  */
/*    is sent to the IP input queue (if no error) and another read */
/*    is started on the LCN.                                       */
/*                                                                 */
/*  Call:              dda_data(ds, hc, cc, cnt)                   */
/*  Argument:          ds:  device control block                   */
/*                     hc:  half duplex channel control block      */
/*                     cc:   Mailbox I/O completion status         */
/*                     cnt:  byte count                            */
/*  Returns:           nothing                                     */
/*  Called by:         ddainta()                                   */
/*  Calls to:          m_freem()                                   */
/*                     dda_start()                                 */
/*                     IF_QFULL()                                  */
/*                     IF_DROP()                                   */
/*                     IF_ENQUEUE()                                */
/*                     schednetisr()                               */
/*                     dda_rrq()                                   */
/*                     dda_wrq()                                   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
dda_data(ds, hc, cc, cnt)
register struct dda_softc *ds;
register struct hdx_chan *hc;
int             cc, cnt;
{
    register struct dda_cb *dc = &(ds->dda_cb[hc->hc_chan / 2]);
    struct ifqueue *inq = &ipintrq;	/* IP input queue */

/* note that this routine is a weird case in which Ultrix 2.0 behaves like
 * a 4.2 system rather than a 4.3 system.  This is reflected in the structure
 * of conditional compilation segments.
 */
#if ACC_BSD > 42			/* 4.3bsd or newer */
    register struct mbuf *m, *mb;
    struct ifnet   *ifp;
#else					/* 4.2, or all flavors of Ultrix */
    register struct mbuf *m;
#endif

#ifdef DDADEBUG
    if (DDADBCH(18, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: dda_data: chan=%d cc=%x cnt=%x\n",
	    ds->dda_if.if_unit, hc->hc_chan, cc, cnt DDAELOG;
    }
#endif DDADEBUG

#if ACC_BSD > 42
    ifp = &ds->dda_if;
#endif

    if (hc->hc_chan & 0x01) {	/* was it read or write? *//* write, fire up
				 * next output */
#ifdef DDADEBUG
	dc->dc_out_t = TMO_OFF;	/* turn off output completion timer */
#endif
	hc = &dc->dc_wchan;
	if ((hc->hc_func != DDAABT) && (hc->hc_curr = hc->hc_curr->m_next))
	    dda_wrq(ds, hc, 0);
	else {
	    m_freem(hc->hc_mbuf);
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	    if (hc->hc_func == DDAABT) {
		hc->hc_func &= ~DDAABT;
		hc->hc_inv &= ~INVALID_MBUF;
	    } else
		ds->dda_if.if_opackets++;
	    dc->dc_flags &= ~DC_OBUSY;
	    dda_start(ds, dc);
	}
    } else {			/* read, process rcvd packet */
	hc = &dc->dc_rchan;

#ifdef DDADEBUG
	if (DDADBCH(19, ds->dda_if.if_unit)) {
	    u_char         *p;

	    p = mtod(hc->hc_curr, u_char *);
	    prt_bytes(ds->dda_if.if_unit, "received data", p, (cnt < 64 ? cnt : 64));
	}
#endif DDADEBUG

	if (cc == DDAIOCOK) {	/* Queue good packet for input */
#ifdef DDADEBUG
	    if (DDADBCH(19, ds->dda_if.if_unit)) {
		DDALOG(LOG_DEBUG) "dda%d: dda_data: chan=%d DDAIOCOK\n",
		    ds->dda_if.if_unit, hc->hc_chan DDAELOG;
	    }
#endif DDADEBUG
	    ds->dda_if.if_ipackets++;
	    hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_DATA_IDLE);
	    if (dc->dc_state == LC_DATA_IDLE)
		dc->dc_timer = tmo_data_idle;
	    hc->hc_curr->m_len += cnt;	/* update byte count */
	    m = hc->hc_mbuf;	/* que mbuf chain */

#if ACC_BSD > 42
	    /* Prepend ifp pointer for 4.3 */
	    MGET(mb, M_DONTWAIT, MT_DATA);
	    if (mb == 0) {
		DMESG(ds->dda_if.if_unit, 26,
		(DDALOG(LOG_ERR) "dda%d: couldn't get mbuf for ifp header\n",
		 ds->dda_if.if_unit DDAELOG));
		m_freem(m);
		return;
	    }
	    *(mtod(mb, struct ifnet **)) = ifp;
	    mb->m_len = sizeof(ifp);
	    mb->m_next = m;

	    if (IF_QFULL(inq)) {
		IF_DROP(inq);
		m_freem(mb);
	    } else {
		IF_ENQUEUE(inq, mb);
		schednetisr(NETISR_IP);
	    }
#else
	    if (IF_QFULL(inq)) {
		IF_DROP(inq);
		m_freem(m);
	    } else {
		IF_ENQUEUE(inq, m);
		schednetisr(NETISR_IP);
	    }
#endif
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	} else if (cc == DDAIOCOKP) {	/* good completion, more data pending */
	    hc->hc_curr->m_len += cnt;
	} else {
	    m_freem(hc->hc_mbuf);
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	}
	/* hang a new data read */
	dda_rrq(ds, hc);
    }
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDA_SUPR()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine is called when a supervisor I/O completes.      */
/*    If the completion was for a write, an attempt is made to     */
/*    start output on the next supervisor command waiting for      */
/*    output.  If the completion was for a read, the received      */
/*    supervisor message is processed and another read is started. */
/*                                                                 */
/*  Call:              dda_supr(ds, hc, cc)                        */
/*  Argument:          ds:  device control block                   */
/*                     hc:  half duplex channel control block      */
/*                     cc:   Mailbox I/O completion status         */
/*  Returns:           nothing                                     */
/*  Called by:         ddainta()                                   */
/*  Calls to:          dda_start()                                 */
/*                     mtod()                                      */
/*                     supr_msg()                                  */
/*                     m_free()                                    */
/*                     dda_rrq()                                   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
dda_supr(ds, hc, cc, cnt)
register struct dda_softc *ds;
struct hdx_chan *hc;
int             cc;
int             cnt;
{
    register struct dda_cb *dc = &(ds->dda_cb[hc->hc_chan / 2]);
    u_char         *p;

#ifdef DDADEBUG
    if (DDADBCH(20, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: dda_supr: chan=%d cc=%x\n",
	    ds->dda_if.if_unit, hc->hc_chan, cc DDAELOG;
    }
#endif DDADEBUG

    /* an odd-numbered channel indicates a write */
    /* the supr msg is assumed to be in 1 mbuf   */

    if (hc->hc_chan & 0x01) {
	m_freem(hc->hc_mbuf);
	hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	dc->dc_flags &= ~DC_OBUSY;
	dda_start(ds, dc);
    }
    /* otherwise, process the read */

    else {
	if (cc == DDAIOCOK) {
	    p = mtod(hc->hc_curr, u_char *);	/* point to data in mbuf */
#ifdef DDA_PAD_OR_RAW
	    switch (dda_decode_type(ds, p)) {
#  ifdef DDA_PADOPT
	      case 1:
#    ifdef DDADEBUG
		if (DDADBCH(20, ds->dda_if.if_unit)) {
		    printf("dda%d: dda_supr(): case 1: chan = %x, p = %x\n",
			   ds->dda_if.if_unit, hc->hc_chan, *p);
		}
#    endif DDADEBUG
		x29_supr(ds, p);
		break;
#  endif
#  ifdef DDA_RAWOPT
	      case 2:
#    ifdef DDADEBUG
		if (DDADBCH(20, ds->dda_if.if_unit)) {
		    printf("dda%d: dda_supr(): case 2: chan = %x, p = %x\n",
			   ds->dda_if.if_unit, hc->hc_chan, *p);
		}
#    endif DDADEBUG
		hc->hc_curr->m_len += cnt;
		pi_supr(ds, hc->hc_curr);
		/* don't free mbuf here */
		hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
		dda_rrq(ds, hc);/* hang a new supr read */
		return;
#  endif
	      default:
		supr_msg(ds, p);/* process supervisor message */
		break;
	    }
#else DDA_PAD_OR_RAW
	    supr_msg(ds, p);	/* process supervisor message */
#endif DDA_PAD_OR_RAW
	} else if (cc == DDAIOCOKP) {
	    DMESG(ds->dda_if.if_unit, 28,
		  (DDALOG(LOG_ERR) "dda%d: truncated supervisor message\n",
		   ds->dda_if.if_unit DDAELOG));
	}
	m_freem(hc->hc_mbuf);
	hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	dda_rrq(ds, hc);	/* hang a new supr read */
    }
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      SUPR_MSG()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*       This routine processes received supervisor messages.      */
/*       Depending on the message type, the appropriate action is  */
/*       taken.                                                    */
/*                                                                 */
/*  Call:              supr_msg(ds, p)                             */
/*  Arguments:         ds:  pointer to dev control block struct    */
/*                     p:   pointer to a character array           */
/*                              containing the supervisor message  */
/*  Returns:           nothing                                     */
/*  Called by:         dda_supr()                                  */
/*  Calls to:          DDALOG()                                    */
/*                     IF_DEQUEUE()                                */
/*                     m_freem()                                   */
/*                     send_restart()                              */
/*                     send_supr()                                 */
/*                     dda_start()                                 */
/*                     decode_ring()                               */
/*                     decode_answer()                             */
/*                     convert_x25_addr()                          */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
supr_msg(ds, p)
struct dda_softc *ds;
u_char          p[];

{
    register struct dda_cb *dc;
    register int    lcn;
    register int    maxlcn;
    union imp_addr  imp_addr;

#ifdef DDADEBUG
    if (DDADBCH(21, ds->dda_if.if_unit)) {
	prt_bytes(ds->dda_if.if_unit, "supr_msg", p, (int) (4 + p[3]));
    }
#endif DDADEBUG

    maxlcn = nddach[ds->dda_if.if_unit];	/* obtain SVC limit */
    switch (p[0]) {
      case LINE_STATUS:	/* link status msg */
	if (p[2] == LINK_UP) {	/* if link came up */
#ifdef DDADEBUG
	    if (DDADBCH(21, ds->dda_if.if_unit)) {
		DDALOG(LOG_DEBUG) "dda%d: supr_msg: HDLC link up\n",
		    ds->dda_if.if_unit DDAELOG;
	    }
#endif DDADEBUG
	    send_restart(ds);	/* send restart msg */
	    ds->dda_state = S_COMING_UP;
	} else {		/* if link went down */
	    ds->dda_if.if_flags &= ~IFF_UP;	/* ? should call if_down() ? */
	    hist_link_state(ds->dda_if.if_unit, ds->dda_state, S_DISABLED);
	    ds->dda_state = S_DISABLED;
	    dc = ds->dda_cb;
	    /* LCNLINK */
	    for (lcn = 0; lcn <= maxlcn; lcn++) {	/* for all LCN's */
		dc->dc_inaddr.s_addr = 0;	/* forget dest address */
		dc->dc_key.key_addr.s_addr = 0;
		dc->dc_wsizein = dc->dc_wsizeout = 0;
		dc->dc_pktsizein = dc->dc_pktsizeout = 0;
		dc->dc_state = LC_DOWN;	/* set state */
		dc->dc_timer = TMO_OFF;	/* stop timer */
		dc++;
	    }
	    hist_all_lcns(ds->dda_if.if_unit, LC_DOWN);
	    abort_io(ds->dda_if.if_unit, ALL_CHANS);
#ifdef DDA_PADOPT
	    x29_init(ds->dda_if.if_unit, 1);
#endif
	    if (p[2] == LINK_DISABLED)	/* link disabled */
		DMESG(ds->dda_if.if_unit, 29,
		      (DDALOG(LOG_ERR) "dda%d:  link disabled\n",
		       ds->dda_if.if_unit DDAELOG));
	    else
		DMESG(ds->dda_if.if_unit, 30,
		      (DDALOG(LOG_ERR) "dda%d:  link down\n", ds->dda_if.if_unit DDAELOG));
	}
	break;

      case RESTART:		/* restart received */
	if (ds->dda_cb[0].dc_state != LC_RESTART) {	/* if not restarting */

#ifdef DDADEBUG
	    if (DDADBCH(21, ds->dda_if.if_unit)) {
		DDALOG(LOG_DEBUG)
		    "dda%d: supr_msg: RESTART rcvd, no RESTART pending\n",
		    ds->dda_if.if_unit DDAELOG;
	    }
#endif DDADEBUG
	    send_supr(ds, RSTRT_ACK, 0, 0);	/* send restart ack */
	}
	/* fall thru */
      case RSTRT_ACK:		/* restart ack */
	if ((ds->dda_state == S_COMING_UP) || (ds->dda_state == S_LINK_UP)) {
	    if (p[0] == RSTRT_ACK) {
		DMESG(ds->dda_if.if_unit, 31,
		      (DDALOG(LOG_ERR) "dda%d: Restart Ack received\n",
		       ds->dda_if.if_unit DDAELOG));
	    } else {		/* restart. print cause and diagnostic. */
		DMESG(ds->dda_if.if_unit, 31,
		      (DDALOG(LOG_ERR) "dda%d: Restart (%x %x) received\n",
		       ds->dda_if.if_unit, p[2], p[3] ? p[4] : 0 DDAELOG));
	    }

	    ds->dda_if.if_flags |= IFF_UP;
	    hist_link_state(ds->dda_if.if_unit, ds->dda_state, S_LINK_UP);
	    ds->dda_state = S_LINK_UP;
	    dc = ds->dda_cb;
	    /* LCNLINK */
	    for (lcn = 0; lcn <= maxlcn; lcn++) {	/* for all LCN's */
		dc->dc_state = LC_IDLE;	/* set state */
		dc->dc_timer = TMO_OFF;	/* stop timer */
		dc->dc_inaddr.s_addr = 0;	/* forget address */
		dc->dc_key.key_addr.s_addr = 0;
		dc->dc_wsizein = dc->dc_wsizeout = 0;
		dc->dc_pktsizein = dc->dc_pktsizeout = 0;
		dc++;
	    }
	    hist_all_lcns(ds->dda_if.if_unit, LC_IDLE);
	    abort_io(ds->dda_if.if_unit, ALL_CHANS);
	    DMESG(ds->dda_if.if_unit, 32,
		  (DDALOG(LOG_ERR) "dda%d: (%s rev %d.%d) link up\n",
		   ds->dda_if.if_unit, dda_product,
	      (ds->dda_firmrev >> 4) & 0xF, ds->dda_firmrev & 0xF DDAELOG));
#ifdef DDA_PAD_OR_RAW
	    x29_init(ds->dda_if.if_unit, 1);

	    /*
	     * wake up all processes that tried to open a x29 device but
	     * slept because the board was not up 
	     */
	    wakeup(&ds->dda_state);
#endif DDA_PAD_OR_RAW
	} else
#ifdef DDADEBUG
	if (DDADBCH(21, ds->dda_if.if_unit)) {
	    DDALOG(LOG_ERR) "dda%d:  Unexpected RESTART in state %x\n",
		ds->dda_if.if_unit, ds->dda_state DDAELOG;
	}
#endif DDADEBUG
	break;

      case ANSWER:		/* call answered */
	lcn = p[1] / 2;
	dc = &(ds->dda_cb[lcn]);
	if (dc->dc_state == LC_CALL_PENDING) {	/* if a call pending */
	    hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_DATA_IDLE);
	    decode_answer(p, dc);
	    dc->dc_state = LC_DATA_IDLE;	/* set state */
	    dc->dc_timer = tmo_data_idle;	/* start timer */
	    dda_start(ds, dc);	/* try to send data */
	}
	if (LOG_CALLS) {
	    DDALOG(LOG_ERR) "dda%d: lcn %d connected\n",
		ds->dda_if.if_unit, lcn DDAELOG;
	}
	break;

      case RING:		/* incoming call */
	/* if ring looks ok, and we find a free LCN to assign */
	if (decode_ring(p) && (dc = find_free_lcn(ds))) {
	    dc->dc_key.key_addr.s_addr =
		convert_x25_addr(ddacb_calling_addr, ds, dc);
#ifdef DDADEBUG
	    if (DDADBCH(21, ds->dda_if.if_unit)) {
		imp_addr.ip = dc->dc_inaddr;
		DDALOG(LOG_DEBUG)
		    "dda%d: supr_msg: got call from %d.%d.%d.%d\n",
		    ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host,
		    imp_addr.imp.s_lh, imp_addr.imp.s_impno DDAELOG;
	    }
#endif DDADEBUG
	    hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_DATA_IDLE);
	    dc->dc_state = LC_DATA_IDLE;	/* set state */
	    dc->dc_timer = tmo_data_idle;	/* start timer */
	    dc->dc_pktsizein = 0;
	    dc->dc_pktsizeout = 0;
	    dc->dc_wsizein = 0;
	    dc->dc_wsizeout = 0;
	    send_supr(ds, ANSWER, (int) dc->dc_lcn * 2,
		      (int) p[2]);		/* send answer */
	    if (LOG_CALLS) {
		imp_addr.ip = dc->dc_inaddr;
		DDALOG(LOG_ERR)
		    "dda%d: Accepting call from %d.%d.%d.%d (%s) on lcn %d\n",
		    ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host,
		    imp_addr.imp.s_lh, imp_addr.imp.s_impno,
		    fmt_x25(&ddacb_calling_addr[1],
		    (int) ddacb_calling_addr[0]), dc->dc_lcn DDAELOG;
	    }
	} else {		/* if no free LCN's */
	    send_supr(ds, CLEARVC, p[2], 0);	/* clear call */
	    if (LOG_CALLS) {
		DDALOG(LOG_ERR) "dda%d: Rejecting call from %s on VC 0x%x\n",
		    ds->dda_if.if_unit,
		    fmt_x25(&ddacb_calling_addr[1], ddacb_calling_addr[0]),
		    p[1] DDAELOG;
	    }
	}
	break;

      case CLEARLC:		/* clear by LCN */

	/*
	 * This could mean one of three things: If we have a call request
	 * outstanding, this message means the call has failed. If we have a
	 * clear request outstanding, this message completes the cleanup; the
	 * channel is now available for reuse. If we have a call active, this
	 * message means the other side is closing the circuit. 
	 */
	lcn = p[1] / 2;		/* get LCN */
	dc = &(ds->dda_cb[lcn]);
	if (dc->dc_state != LC_CLR_PENDING) {	/* if no clear pending */
	    send_supr(ds, CLEARLC, p[1], 0);	/* ack the clear */
	}
	if (dc->dc_state == LC_CALL_PENDING	/* if call is cleared */
	    && (LOG_CALLS || DMESGVAL(ds->dda_if.if_unit, 33) == 0)) {
	    imp_addr.ip = dc->dc_inaddr;
	    DDALOG(LOG_ERR)
		"dda%d: Call to %d.%d.%d.%d on lcn %d failed (%x %x)\n",
		ds->dda_if.if_unit, imp_addr.imp.s_net, imp_addr.imp.s_host,
		imp_addr.imp.s_lh, imp_addr.imp.s_impno, dc->dc_lcn, p[2], p[4]
		DDAELOG;

	} else if (LOG_CALLS) {
	    if (dc->dc_state == LC_CLR_PENDING) {	/* did we clear it? *//* y
							 * es, IP address is
							 * already gone.  Say
							 * channel is free.  */
		DDALOG(LOG_ERR) "dda%d: Cleared lcn %d\n",
		    ds->dda_if.if_unit, dc->dc_lcn DDAELOG;
	    } else {		/* cleared by net, print more info */
		imp_addr.ip = dc->dc_inaddr;
		DDALOG(LOG_ERR)
		    "dda%d: Cleared lcn %d to %d.%d.%d.%d (%x %x)\n",
		    ds->dda_if.if_unit, dc->dc_lcn, imp_addr.imp.s_net,
		    imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno,
		    p[2], p[4] DDAELOG;
	    }
	}
	hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_IDLE);
	/* LCNLINK delete */
	dc->dc_state = LC_IDLE;	/* set state */
	dc->dc_timer = TMO_OFF;	/* stop timer */
	dc->dc_inaddr.s_addr = 0;	/* forget address */
	dc->dc_key.key_addr.s_addr = 0;
	dc->dc_wsizein = dc->dc_wsizeout = 0;
	dc->dc_pktsizein = dc->dc_pktsizeout = 0;
	abort_io(ds->dda_if.if_unit, lcn);
	if (LOG_CALLS) {
	    printf("dda%d: Cleared LCN %d cause code %x diag code %x\n",
		   ds->dda_if.if_unit, dc->dc_lcn, p[2], p[4]);
	}
	break;

      case CLEARVC:		/* clear by VCN */

	send_supr(ds, CLEARVC, p[1], 0);	/* send clear ack */
	if (LOG_CALLS) {
	    DDALOG(LOG_ERR) "dda%d: Network cleared VC %x (%x %x)\n",
		ds->dda_if.if_unit, p[1], p[2], p[4] DDAELOG;
	}
#ifdef DDADEBUG
	else if (DDADBCH(21, ds->dda_if.if_unit)) {
	    DDALOG(LOG_DEBUG) "dda%d: supr_msg: CLEARVC VCN=%x\n",
		ds->dda_if.if_unit, p[1] DDAELOG;
	}
#endif DDADEBUG
	break;

      case RESET:		/* X25 reset */
	lcn = p[1] / 2;		/* get LCN */
	dc = &(ds->dda_cb[lcn]);
	send_supr(ds, RESET_ACK, p[1], 0);	/* send reset ack */
	abort_io(ds->dda_if.if_unit, lcn);
	imp_addr.ip = dc->dc_inaddr;
	DMESG(ds->dda_if.if_unit, 34,
	      (DDALOG(LOG_ERR)
	       "dda%d: X25 RESET (%x %x) on lcn %d: %d.%d.%d.%d\n",
	       ds->dda_if.if_unit, p[2], p[4], lcn, imp_addr.imp.s_net,
	       imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno
	       DDAELOG));
	break;

      case INTERRUPT:		/* X25 interrupt */
	lcn = p[1] / 2;		/* get LCN */
	dc = &(ds->dda_cb[lcn]);
	imp_addr.ip = dc->dc_inaddr;
	DMESG(ds->dda_if.if_unit, 35,
	      (DDALOG(LOG_ERR)
	       "dda%d: X25 INTERRUPT (%x) on lcn %d: %d.%d.%d.%d\n",
	       ds->dda_if.if_unit, p[2], lcn, imp_addr.imp.s_net,
	       imp_addr.imp.s_host, imp_addr.imp.s_lh, imp_addr.imp.s_impno
	       DDAELOG));
	break;

      case STATRESP:		/* Statistics Response from FEP */

	/*
	 * Copy the whole message into a static buffer, dda_iobuf. The buffer
	 * is viewed as a (struct ddactl).  Wake up the ioctl thread which
	 * will copy the message out for acpconfig. 
	 */
	{
	    struct ddactl  *da = (struct ddactl *) dda_iobuf;

	    bcopy(p, da->msg, max(4 + p[3], sizeof(da->msg)));
#ifdef MULTINET
	    StatQuery_Completed = 1;
#else
	    wakeup(dda_iobuf);
#endif
	    break;
	}

      default:
	DMESG(ds->dda_if.if_unit, 36,
	      (DDALOG(LOG_ERR) "dda%d: supervisor error (%x %x %x %x)\n",
	       ds->dda_if.if_unit, p[0], p[1], p[2], p[3] DDAELOG));
    }
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                   DECODE_ANSWER()                           %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*        This routine looks at the answer message from the FE     */
/*  and decodes it to find the negtiated packet and window sizes   */
/*  if they are present.                                           */
/*                                                                 */
/*  Call:              decode_answer(p, dc)	                   */
/*  Argument:          p: pointer to mbuf data for ANSWER message  */
/*                     dc: pointer to relavant lcn structure       */
/*  Returns:           nothing                                     */
/*  Called by:         supr_msg()                                  */
/*  Calls to:                                                      */
/*                     DDALOG()                                    */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
decode_answer(p, dc)
u_char         *p;
struct dda_cb  *dc;
{
    register u_char *cp;
    int             i, faclen;

    dc->dc_pktsizein = 0;
    dc->dc_pktsizeout = 0;
    dc->dc_wsizein = 0;
    dc->dc_wsizeout = 0;
    cp = p + 4;			/* skip over code, lcn, vcn and count in
				 * answer message */
    /* cp now points to length of called address */
    cp += *cp + 1;		/* skip over called address and length byte */
    /* cp now points to length of calling address */
    cp += *cp + 1;		/* skip over calling address and length byte */
    /* cp now points to length of protocol */
    cp += *cp + 1;		/* skip over protocol and protocol length
				 * byte */
    /* cp now points to the facilities length */

    faclen = *cp++;
    /* cp now points to start of facilities */
    for (i = 0; i < faclen;) {
	switch (*cp & 0xc0) {
	  case 0x00:		/* single octet parameter field */
	    i += 2;
	    cp += 2;
	    break;
	  case 0x40:		/* double octet parameter field */
	    switch (*cp) {
	      case X25_FACIL_PKTSIZE:	/* 0x42, packet size */
		dc->dc_pktsizein = *(cp + 1);
		dc->dc_pktsizeout = *(cp + 2);
		break;
	      case X25_FACIL_WINSIZE:	/* 0x43, window size */
		dc->dc_wsizein = *(cp + 1);
		dc->dc_wsizeout = *(cp + 2);
		break;
	    }
	    i += 3;
	    cp += 3;
	    break;
	  case 0x80:		/* triple octet parameter field */
	    i += 4;
	    cp += 4;
	    break;
	  case 0xc0:		/* variable-length parameter field */
	    cp++;
	    i += 2 + *cp;
	    cp += 1 + *cp;
	    break;
	    /* Note: No other cases (i.e., default) possible */
	}
    }
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DECODE_RING()                          %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine parses and validates the incoming call message. */
/*                                                                 */
/*  Call:              decode_ring(p)                              */
/*  Argument:          p:   pointer to the message                 */
/*  Returns:           1 for success, else 0 for failure           */
/*  Called by:         supr_msg()                                  */
/*  Calls to:          bcopy()                                     */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE boolean
decode_ring(p)
register u_char *p;
{
    register int    cnt;

#ifdef DDADEBUG
    if (DDADBCH(22, 0)) {	/* no easy access to unit, assume unit 0 */
	DDALOG(LOG_DEBUG) "dda: decode_ring()\n" DDAELOG;
    }
#endif DDADEBUG

    p += 3;			/* skip to cmnd ext length */
    if (*p++ < 5)		/* is count appropriate */
	return (0);		/* return false if not */

    /* called address */
    if ((cnt = *p + 1) > 16)	/* is called addr len legal? */
	return (0);		/* return false if not */
    bcopy(p, ddacb_called_addr, cnt);	/* copy field */
    p += cnt;

    /* calling address */
    if ((cnt = *p + 1) > 16)	/* is calling addr len legal? */
	return (0);		/* return false if not */
    bcopy(p, ddacb_calling_addr, cnt);	/* copy field */
    p += cnt;

    /* protocol part of user data */
    if ((cnt = *p + 1) > 5)	/* is protocol len legal? */
	return (0);		/* return false if not */
    bcopy(p, ddacb_protocol, cnt);	/* copy field */
    p += cnt;

    /* facilities */
    if ((cnt = *p + 1) > 64)	/* is facilities len legal? */
	return (0);		/* return false if not */
    bcopy(p, ddacb_facilities, cnt);	/* copy field */
    p += cnt;

    /* ignore rest of user data for now */

#ifdef	DDA_PAD_OR_RAW
    if (ddacb_protocol[0] == 0)
	return (0);
#else DDA_PAD_OR_RAW
    if ((ddacb_protocol[0] == 0) || (ddacb_protocol[1] != X25_PROTO_IP))
	return (0);		/* bad if not IP */
#endif DDA_PAD_OR_RAW

#ifndef DDA_PAD_OR_RAW
    return (1);			/* looks ok */
#else
#  ifdef DDA_RAWOPT
    return (1);			/* anything is ok if we're PI interface */
#  else
    if (ddacb_protocol[1] == X25_PROTO_IP || ddacb_protocol[1] == X25_PROTO_X29)
	return (1);		/* looks ok */
    else
	return (0);		/* bad if not IP or X29 */
#  endif
#endif DDA_PAD_OR_RAW
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      CLEAR_LCN()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine clears an X25 circuit and releases any buffers  */
/*    queued for transmission.                                     */
/*                                                                 */
/*  Call:              clear_lcn(ds, dc)                           */
/*  Argument:          ds:   pointer to dev control block struct   */
/*                     dc:  pointer to the Logical Channel control */
/*                            block structure                      */
/*  Returns:           nothing                                     */
/*  Called by:         ddatimer()                                  */
/*  Calls to:          IF_DEQUEUE()                                */
/*                     m_freem()                                   */
/*                     send_supr()                                 */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
clear_lcn(ds, dc)
struct dda_softc *ds;
struct dda_cb  *dc;
{
    register struct mbuf *m;
    register int    s;

#ifdef DDADEBUG
    if (DDADBCH(23, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: clear_lcn(%d)\n", ds->dda_if.if_unit,
	    dc->dc_lcn DDAELOG;
    }
#endif DDADEBUG

    if (dc->dc_state == LC_CLR_PENDING) {	/* Unfortunately, we can't
						 * display the destination's
						 * IP address, as we cleared
						 * it when we entered
						 * clear-pending state (to
						 * prevent new data from
						 * being queued to this
						 * channel). */
	DMESG(ds->dda_if.if_unit, 37,
	      (DDALOG(LOG_ERR) "dda%d: Clear request lost -- lcn %d\n",
	       ds->dda_if.if_unit, dc->dc_lcn DDAELOG));
	return;
    }
    hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_CLR_PENDING);
    dc->dc_state = LC_CLR_PENDING;	/* set state */
    dc->dc_timer = TMO_CLR_PENDING;	/* start clear timer */
    dc->dc_inaddr.s_addr = 0;	/* clear associated address */
    dc->dc_key.key_addr.s_addr = 0;
    dc->dc_wsizein = dc->dc_wsizeout = 0;
    dc->dc_pktsizein = dc->dc_pktsizeout = 0;
/*
 *  Raise priority whenever dc_oq is touched.
 */
    s = splimp();
    while (dc->dc_oq.ifq_len) {	/* drop any pending data */
	IF_DEQUEUE(&dc->dc_oq, m);
	m_freem(m);
    }
    splx(s);
    send_supr(ds, CLEARLC, (int) dc->dc_lcn * 2, 0);	/* send clear msg */
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      SEND_RESTART()                         %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine marks all LCNs as being in a restarting state   */
/*    and sends a restart command to X25.                          */
/*                                                                 */
/*  Call:              send_restart(ds)                            */
/*  Argument:          ds:   pointer to dev control block struct   */
/*  Returns:           nothing                                     */
/*  Called by:         ddatimer()                                  */
/*                     supr_msg()                                  */
/*  Calls to:          IF_DEQUEUE()                                */
/*                     m_freem()                                   */
/*                     send_supr()                                 */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
send_restart(ds)
struct dda_softc *ds;
{
    register struct dda_cb *dc;
    register int    lcn;
    register int    maxlcn;

#ifdef DDADEBUG
    if (DDADBCH(24, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: send_restart()\n", ds->dda_if.if_unit DDAELOG;
    }
#endif DDADEBUG

    dc = ds->dda_cb;
    /* LCNLINK */
    maxlcn = nddach[ds->dda_if.if_unit];
    for (lcn = 0; lcn <= maxlcn; lcn++) {	/* for all LCN's */
	dc->dc_state = LC_RESTART;	/* set state */
	dc->dc_timer = TMO_RESTART;	/* start restart timeout */
	dc->dc_inaddr.s_addr = 0;	/* forget address */
	dc->dc_key.key_addr.s_addr = 0;
	dc->dc_wsizein = dc->dc_wsizeout = 0;
	dc->dc_pktsizein = dc->dc_pktsizeout = 0;
	dc++;
    }
    hist_all_lcns(ds->dda_if.if_unit, LC_RESTART);
    abort_io(ds->dda_if.if_unit, ALL_CHANS);
    send_supr(ds, RESTART, 0, 0);	/* send restart msg */
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      SEND_SUPR()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine is used to send short (4 bytes only) supervisor */
/*    commands, except that longer ANSWER messages may be sent.    */
/*                                                                 */
/*  Call:              send_supr(ds, cmd, p1, p2)                  */
/*  Argument:          ds:   pointer to dev control block struct   */
/*                     cmd:  type of command                       */
/*                     p1:   2nd byte of supervisor message        */
/*                     p2:   3rd byte of supervisor message        */
/*  Returns:           nothing                                     */
/*  Called by:         supr_msg()                                  */
/*                     clear_lcn()                                 */
/*                     send_restart()                              */
/*  Calls to:          MGET()                                      */
/*                     DDALOG()                                    */
/*                     mtod()                                      */
/*                     IF_ENQUEUE()                                */
/*                     dda_start()                                 */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
send_supr(ds, cmd, p1, p2)
struct dda_softc *ds;
int             cmd, p1, p2;
{
    struct mbuf    *m;
    register u_char *cp;
    u_char         *savcp, *fp, *svcp;
    int             i, faclen;

    MGET(m, M_DONTWAIT, MT_DATA);

    if (m == 0) {
	DMESG(ds->dda_if.if_unit, 23,
	      (DDALOG(LOG_ERR) "dda%d: failed to get supr msg bfr!\n",
	       ds->dda_if.if_unit DDAELOG));
	return;
    }
    cp = savcp = mtod(m, u_char *);

    /* build supervisor message */

    *cp++ = (u_char) cmd;
    *cp++ = (u_char) p1;
    *cp++ = (u_char) p2;
    *cp++ = 0;

    m->m_len = 4;

    if (cmd == ANSWER) {
	register struct dda_cb *dc;

	/* for answer messages p1 is (lcn * 2) */
	dc = &(ds->dda_cb[p1 / 2]);
	*cp++ = 0;		/* zero length called address */
	*cp++ = 0;		/* zero length calling address */
	*cp++ = 0;		/* zero length protocol */

	/* check and copy facilities */
	faclen = 0;
	svcp = cp++;
	for (i = 0, fp = &ddacb_facilities[1]; i < ddacb_facilities[0];) {
	    switch (*fp & 0xc0) {
	      case 0x00:	/* single octet parameter field */
		i += 2;
		fp += 2;
		break;
	      case 0x40:	/* double octet parameter field */

		/*
		 * Note that this code can in some cases attempt to negotiate
		 * the packet size or window away from the default, which
		 * appears to violate the X.25 spec. In fact, the FEP
		 * examines these values and bounds them between the
		 * requested value and the default value thus satisfying X.25 
		 */
		switch (*fp) {
		  case X25_FACIL_PKTSIZE:	/* 0x42, packet size */
		    *cp++ = X25_FACIL_PKTSIZE;
		    if (ds->dda_firmrev < 0x21) {
			*cp++ = PKTSIZE_DEF;	/* Set incoming and outgoing */
			*cp++ = PKTSIZE_DEF;	/* packet size to default */
			dc->dc_pktsizein = dc->dc_pktsizeout = PKTSIZE_DEF;
		    } else {
			*cp++ = *(fp + 1);	/* Answer with requested */
			*cp++ = *(fp + 2);	/* facilities */
			dc->dc_pktsizeout = *(fp + 1);
			dc->dc_pktsizein = *(fp + 2);
		    }
		    faclen += 3;
		    break;
		  case X25_FACIL_WINSIZE:	/* 0x43, window size */
		    *cp++ = X25_FACIL_WINSIZE;
		    if (ds->dda_firmrev < 0x21) {
			*cp++ = WINSIZE_DEF;	/* Set incoming and outgoing */
			*cp++ = WINSIZE_DEF;	/* window size to default */
			dc->dc_wsizein = dc->dc_wsizeout = WINSIZE_DEF;
		    } else {
			*cp++ = *(fp + 1);	/* Answer with requested */
			*cp++ = *(fp + 2);	/* facilities */
			dc->dc_wsizeout = *(fp + 1);
			dc->dc_wsizein = *(fp + 2);
		    }
		    faclen += 3;
		    break;
		}
		i += 3;
		fp += 3;
		break;
	      case 0x80:	/* triple octet parameter field */
		i += 4;
		fp += 4;
		break;
	      case 0xc0:	/* variable-length parameter field */
		fp++;
		i += 2 + *fp;
		fp += 1 + *fp;
		break;
		/* Note: No other cases (i.e., default) possible */
	    }
	}

	if (faclen) {		/* Found facilities to negotiate! */
	    *svcp = faclen;	/* facility length <- faclen */
	    *cp++ = 0;		/* user data length <- 0 */
	    *(savcp + 3) = cp - savcp - 4;	/* set supv message length */
	    m->m_len = cp - savcp;	/* set mbuf message length */
	}
    }				/* (end of answer message case) */
# ifdef DDADEBUG
    if (DDADBCH(25, ds->dda_if.if_unit)) {
	prt_bytes(ds->dda_if.if_unit, "send_supr", savcp, m->m_len);
    }
#endif DDADEBUG
    start_supr(ds, m);
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%				START_SUPR()			       %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*	Start i/o on the supervisor channel, checking for queue full.	 */
/*	Added to revision 2.0 so that "queue full" checking would be	 */
/*	applied uniformly to all supervisory channel output.		 */
/*                                                                       */
/*  Call:          start_supr(ds, m)                                     */
/*  Argument:      ds:  softc structure for board			 */
/*		   m:	mbuf holding message				 */
/*  Returns:       nothing                                               */
/*  Called by:     send_supr(), send_config(), make_x25_call()		 */
/*  Calls to:      DDALOG(), dda_start(), IF_ENQUEUE()			 */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
start_supr(ds, m)
struct dda_softc *ds;
struct mbuf    *m;
{
    register int    s;


#ifdef DDADEBUG
    if (DDADBCH(27, ds->dda_if.if_unit))
	DDALOG(LOG_DEBUG) "dda%d: start_supr\n", ds->dda_if.if_unit DDAELOG;
#endif DDADEBUG

    if (IF_QFULL(&(ds->dda_cb[0].dc_oq))) {
	DMESG(ds->dda_if.if_unit, 27,
	(DDALOG(LOG_ERR) "dda%d: supervisory channel overflow (maxlen=%d)\n",
	 ds->dda_if.if_unit, ds->dda_cb[0].dc_oq.ifq_maxlen DDAELOG));
	ds->dda_cb[0].dc_oq.ifq_maxlen += ds->dda_cb[0].dc_oq.ifq_maxlen;
    }
/*
 *  Raise priority whenever you touch dc_oq.  
 *  We do not want to be interrupted in the middle of adding
 *  an mbuf to the output queue because the interrupt may indicate
 *  a condition that will cause the mbuf to be freed.
 *  (The mbufs are freed on receipt of a line status msg, restart,
 *  clear, or reset.)
 */
    s = splimp();
#ifdef DDA_PAD_OR_RAW
    m->m_dat[MLEN - 1] = 0;
#endif
    IF_ENQUEUE(&(ds->dda_cb[0].dc_oq), m);
    splx(s);
    dda_start(ds, &(ds->dda_cb[0]));
}


/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%				ABORT_IO()   		               %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*	Abort outstanding I/O upon receipt of a line status message, 	 */
/*	restart, clear, or reset.                                        */
/*	The contents of the output queue (dc_oq) is cleared for each     */
/*	lcn;  all I/O queued on either the read or write queue           */
/*	(dc_rchan and dc_wchan) is marked invalid; all I/O queued on     */
/*	the sioq is marked invalid;                                      */
/*                                                                       */
/*  Call:          abort_io()        			                 */
/*  Argument:      none						         */
/*  Returns:       nothing                                               */
/*  Called by:                                                           */
/*  Calls to:      IF_DEQUEUE()                                          */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
PRIVATE void
abort_io(unit, lcn)
int             unit, lcn;
{
    register struct dda_cb *dc;
    register struct dda_softc *ds = &dda_softc[unit];
    register struct hdx_chan *hc;
    register struct mbuf *m;
    register int    lchan;
    register int    s;
    register struct hdx_chan *ptr;
    int             start, end;

    /* set up range of lcns affected */
    if (lcn == ALL_CHANS) {
	start = 1;
	end = nddach[unit];
    } else
	start = end = lcn;
#ifdef DDADEBUG
    if (DDADBCH(28, unit))
	DDALOG(LOG_DEBUG) "dda%d: abort_io on lcn's %d - %d\n",
	    unit, start, end DDAELOG;
#endif DDADEBUG
    s = splimp();
/*
 * Invalidate writes on the sioq for specified channel(s)
 */
    if (ptr = ds->dda_sioq.sq_head)
	for (; ptr; ptr = ptr->hc_next)	/* scan sioq */
	    if ((ptr->hc_chan & 0x01) &&
		((lcn == ALL_CHANS) || (lcn == ptr->hc_chan >> 1))
		&& (ptr->hc_chan != 1)) {
#ifdef DDADEBUG
		if (DDADBCH(28, unit))
		    DDALOG(LOG_DEBUG)
			"dda%d: abort_io--invalidating sioq lcn %d\n",
			unit, ptr->hc_chan >> 1 DDAELOG;
#endif DDADEBUG
		ptr->hc_inv |= INVALID_MBUF;
	    }
/*
 * For each selected lcn, clear the output queue and
 * add an hdx struct to the sioq that will generate an
 * abort.
 */
    for (lchan = start; lchan <= end; lchan++) {	/* for selected LCNs */
	dc = &dda_softc[unit].dda_cb[lchan];
	hc = &dc->dc_wchan;
	while (dc->dc_oq.ifq_len) {
	    IF_DEQUEUE(&dc->dc_oq, m);
	    m_freem(m);
	}

	if (hc->hc_mbuf && !(hc->hc_inv & INVALID_MBUF)) {
	    if (dc->dc_flags & DC_OBUSY) {	/* output pending */
#ifdef DDADEBUG
		if (DDADBCH(28, unit))
		    DDALOG(LOG_DEBUG)
			"dda%d: abort_io--queueing abort: lcn %d\n",
			unit, lchan DDAELOG;
#endif DDADEBUG

		hc->hc_inv |= INVALID_MBUF;
		hc->hc_func = DDAABT;
/*
 * Add to the sioq
 */
		dda_wrq(ds, hc, DDAABT);
	    }
	}
    }
    splx(s);
}

#ifdef DDADEBUG


/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                            PRT_BYTES()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*    This routine is used to print a label, followed by the contents of */
/*	a buffer in hex, 16 bytes per line.  Each line is preceded by	 */
/*	the device name and unit number.				 */
/*                                                                       */
/*  Call:          prt_bytes(unit, label, bp, cnt)			 */
/*  Argument:      unit: dda unit number to be displayed		 */
/*		   label: pointer to string to be displayed		 */
/*		   bp:  pointer to the buffer to be dumped		 */
/*                 cnt: number of bytes in buffer			 */
/*  Returns:       nothing                                               */
/*  Called by:     dda_data()                                            */
/*                 dda_supr()                                            */
/*                 supr_msg()                                            */
/*  Calls to:      DDALOG()                                              */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
PRIVATE void
prt_bytes(unit, label, bp, cnt)
int             unit;
char           *label;
u_char         *bp;
int             cnt;
{
    char            hexbuf[50];	/* (worst case: 3 * 16 + 1 = 49 bytes) */
    char           *p;
    int             i;
    static char     hex[] = "0123456789abcdef";

    DDALOG(LOG_DEBUG) "dda%d: %s\n", unit, label DDAELOG;
    while (cnt > 0) {
	i = (cnt > 16) ? 16 : cnt;
	cnt -= i;
	p = hexbuf;
	while (--i >= 0) {
	    *p++ = ' ';
	    *p++ = hex[*bp >> 4];
	    *p++ = hex[*bp++ & 0x0f];
	}
	*p++ = '\0';
	DDALOG(LOG_DEBUG) "dda%d: %s\n", unit, hexbuf DDAELOG;
    }
}

#endif



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                            FMT_X25()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*    This routine is used to format an X.25 address for inclusion in	 */
/*    an error message.  The previous return value is invalidated each	 */
/*    time the function is called, as it is stored in a static buffer	 */
/*  Note:          The X.25 address is apparently sometimes stored in    */
/*                 BCD, and other times (PDN mode) in ASCII.  So we mask */
/*                 off the high order bits to make ourselves immune.	 */
/*  Call:          fmt_x25(bp, cnt)                                      */
/*  Argument:      bp:  pointer to the string                            */
/*                 cnt: number of bytes (usually from address[0])        */
/*  Returns:       pointer to an internal buffer containing the string;	 */
/*		   string is 1 to 15 digits, null-terminated.		 */
/*  Called by:     make_x25_call()                                       */
/*                 supr_msg()                                            */
/*                 convert_x25_addr()					 */
/*  Calls to:      none							 */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
PRIVATE char *
fmt_x25(bp, cnt)
register u_char *bp;
register int    cnt;
{
    char           *p;
    static char     x25buf[20];	/* worst case is 15 digits plus trailing null */

    /* (Don't put this on the stack!) */
    p = x25buf;
    if (cnt >= sizeof(x25buf))
	cnt = sizeof(x25buf) - 1;	/* (oops!) */
    while (cnt--)
	*p++ = (*bp++ & 0x0f) + '0';
    *p++ = '\0';
    return (x25buf);
}

#ifdef DDA_HISTOGRAM
/*----------------------- HISTOGRAM SUPPORT ---------------------------------*/


/* the histogram array */
struct timeval  histogram[NDDA][HISTSIZE];

/* these two structures save the time of the last change in the state of the
 * lcn table or the board status.
 */

struct timeval  last_lcn_time[NDDA] = {0L, 0L};
struct timeval  last_brd_time[NDDA] = {0L, 0L};

/* h_lcn_level: the current number of active lcns */
int             h_lcn_level[NDDA] = {0};

/*#define DDA_HIST_DEBUG 1     /* set this to debug history features */


/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                           HIST_INIT()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*    This routine initializes the histogram facility when coming up or  */
/*    after a reset.                                                     */
/*  Call:          hist_init(unit,reset)                                 */
/*  Argument:      unit - board number to initialize.                    */
/*                 reset - set to 1 to force an init.                    */
/*  Returns:       nothing.                                              */
/*  Called by:     ddaioctl()                                            */
/*  Calls to:      microtime()                                           */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
hist_init(unit, reset)
int             unit;
int             reset;
{
    int             s;
    register int    i;
    struct dda_cb  *dc;

    if (last_lcn_time[unit].tv_sec != 0L && !reset)
	return;			/* histogram for this unit already enabled */
    bzero(histogram[unit], sizeof(struct timeval) * HISTSIZE);
    h_lcn_level[unit] = 0;
    dc = dda_softc[unit].dda_cb;
    s = splimp();
    for (i = 0; i < NDDACH + 1; i++) {
	if (dc++->dc_state == LC_DATA_IDLE)
	    h_lcn_level[unit]++;
    }
    splx(s);
    microtime(&histogram[unit][H_START]);
#ifdef DDA_HIST_DEBUG
    DDALOG(LOG_DEBUG) "hist_init: starting at level %d\n",
	h_lcn_level[unit] DDAELOG;
#endif
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                     HIST_LCN_STATE()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*    This routine changes the histogram depending on how the state of   */
/*    a channel has changed.                                             */
/*  Call:          hist_lcn_state(unit, old_state, new_state)            */
/*  Argument:      old_state: the old state of the lcn.                  */
/*                 new_state: the state the lcn is changing to.          */
/*                 unit: unit this applies to                            */
/*  Returns:       nothing.                                              */
/*  Called by:                                                           */
/*  Calls to:      timevalsub(), timevaladd(), microtime()               */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
hist_lcn_state(unit, old_state, new_state)
int             unit;
u_char          old_state;
u_char          new_state;
{
    struct timeval  tv, tmpv;

    /*
     * this structure for determining state transitions is much more general
     * than is necessary right now.  However it allows easy changes to the
     * state transition table for the histogram so I will leave it in until
     * it settles down 
     */
    switch (old_state) {
      case LC_DATA_IDLE:
	switch (new_state) {
	  case LC_DATA_IDLE:
	    break;
	  default:		/* all other states */
	    microtime(&tv);
	    tmpv = tv;
	    timevalsub(&tv, &last_lcn_time[unit]);
#ifdef DDA_HIST_DEBUG
	    DDALOG(LOG_DEBUG) "hist_lcn_state: adding %ld.%ld to level %d--\n",
		tv.tv_sec, tv.tv_usec, h_lcn_level[unit] DDAELOG;
#endif
	    timevaladd(&histogram[unit][h_lcn_level[unit]], &tv);
	    last_lcn_time[unit] = tmpv;
	    if (--h_lcn_level[unit] < 0)	/* safety net for driver
						 * errors */
		h_lcn_level[unit] = 0;
	    break;
	}
	break;
      default:
	switch (new_state) {
	  case LC_DATA_IDLE:
	    microtime(&tv);
	    tmpv = tv;
	    timevalsub(&tv, &last_lcn_time[unit]);
#ifdef DDA_HIST_DEBUG
	    DDALOG(LOG_DEBUG) "hist_lcn_state: adding %ld.%ld to level %d++\n",
		tv.tv_sec, tv.tv_usec, h_lcn_level[unit] DDAELOG;
#endif
	    timevaladd(&histogram[unit][h_lcn_level[unit]], &tv);
	    last_lcn_time[unit] = tmpv;
	    if (++h_lcn_level[unit] > NDDACH)	/* safety net for driver
						 * errors */
		h_lcn_level[unit] = NDDACH;
	    break;
	  default:		/* all other states */
	    break;
	}
	break;
    }
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                     HIST_ALL_LCNS()                               %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*    This routine changes the histogram when the state of all the lcns  */
/*    are changed as a group.                                            */
/*  Call:          hist_lcn_state(unit, state)                           */
/*  Argument:      state: state that all lcn are going to.  Currently not*/
/*                        used.                                          */
/*                 unit: unit this applies to                            */
/*  Returns:       nothing.                                              */
/*  Called by:                                                           */
/*  Calls to:      timevalsub(), timevaladd(), microtime()               */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
hist_all_lcns(unit, state)
int             unit, state;
{
    struct timeval  tmpv, tv;

#ifdef lint
    state = state;
#endif
    if (last_brd_time[unit].tv_sec == 0L
	|| last_lcn_time[unit].tv_sec == 0L)
	return;			/* see if we have initialized yet */
    microtime(&tv);
    tmpv = tv;
    timevalsub(&tv, &last_lcn_time[unit]);
#ifdef DDA_HIST_DEBUG
    DDALOG(LOG_DEBUG) "hist_all_lcns: adding %ld.%ld to level %d\n",
	tv.tv_sec, tv.tv_usec, h_lcn_level[unit] DDAELOG;
#endif
    timevaladd(&histogram[unit][h_lcn_level[unit]], &tv);
    last_lcn_time[unit] = tmpv;
    h_lcn_level[unit] = 0;
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                     HIST_LINK_STATE()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*    This routine changes the histogram depending on how the state of   */
/*    the link has changed.                                              */
/*  Call:          hist_link_state(old_state, new_state)                 */
/*  Argument:      old_state: the old state of the link.                 */
/*                 new_state: the state the link is changing to.         */
/*                 unit: unit this applies to                            */
/*  Returns:       nothing.                                              */
/*  Called by:                                                           */
/*  Calls to:      timevalsub(), timevaladd(), microtime()               */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
hist_link_state(unit, old_state, new_state)
int             unit;
u_char          old_state;
u_char          new_state;
{
    struct timeval  tv, tmpv;

    /*
     * this structure for determining state transitions is much more general
     * than is necessary right now.  However it allows easy changes to the
     * state transition table for the histogram so I will leave it in until
     * it settles down 
     */
    switch (old_state) {
      case S_LINK_UP:
	switch (new_state) {
	  case S_LINK_UP:
	    break;
	  default:		/* all other states */
#ifdef DDA_HIST_DEBUG
	    DDALOG(LOG_DEBUG) "hist_link_state: link down\n" DDAELOG;
#endif
	    microtime(&tv);
	    tmpv = tv;
	    timevalsub(&tv, &last_lcn_time[unit]);
	    timevaladd(&histogram[unit][h_lcn_level[unit]], &tv);
	    tv = tmpv;
	    timevalsub(&tv, &last_brd_time[unit]);
	    timevaladd(&histogram[unit][H_LINK_UP], &tv);
	    last_brd_time[unit].tv_sec = 0L;
	    break;
	}
	break;
      default:			/* all other states */
	switch (new_state) {
	  case S_LINK_UP:
#ifdef DDA_HIST_DEBUG
	    DDALOG(LOG_DEBUG) "hist_link_state: link up\n" DDAELOG;
#endif
	    microtime(&last_brd_time[unit]);

	    /*
	     * reset last_lcn_time so 0 entry will not accumulate the time
	     * that we were down 
	     */
	    last_lcn_time[unit] = last_brd_time[unit];
	    break;
	  default:
	    break;
	}
	break;
    }
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                         HIST_READ()                               %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*    This routine prepares the histogram table for reading by making    */
/*    all entries current.                                               */
/*  Call:          hist_read(unit)                                       */
/*  Argument:      unit : board to use.                                  */
/*  Returns:       nothing                                               */
/*  Called by:     ddaioctl()                                            */
/*  Calls to:      timevalsub(), timevaladd(), microtime()               */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
hist_read(unit)
int             unit;
{
    struct timeval  tmpv, tv;

    microtime(&tv);
    tmpv = tv;
    histogram[unit][H_END] = tmpv;
    histogram[unit][H_TMO].tv_sec = tmo_data_idle * DDA_TIMEOUT;
    histogram[unit][H_TMO].tv_usec = 0L;
    if (last_brd_time[unit].tv_sec) {
	timevalsub(&tv, &last_lcn_time[unit]);
#ifdef DDA_HIST_DEBUG
	DDALOG(LOG_DEBUG) "hist_read: adding %ld.%ld to level %d\n",
	    tv.tv_sec, tv.tv_usec, h_lcn_level[unit] DDAELOG;
#endif
	timevaladd(&histogram[unit][h_lcn_level[unit]], &tv);
	last_lcn_time[unit] = tmpv;
	tv = tmpv;
	timevalsub(&tv, &last_brd_time[unit]);
	timevaladd(&histogram[unit][H_LINK_UP], &tv);
	last_brd_time[unit] = tmpv;
    }
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                         HIST_COPYOUT()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */
/*  Purpose:                                                             */
/*                                                                       */
/*    This routine prepares the histogram table for reading by making    */
/*    all entries current.                                               */
/*  Call:          hist_copyout(unit, to)                                */
/*  Argument:      unit : board to use.                                  */
/*                 to   : address in user space to copy to.              */
/*  Returns:       return value from copyout                             */
/*  Called by:     ddaioctl()                                            */
/*  Calls to:      copyout()                                             */
/*                                                                       */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
hist_copyout(unit, to)
int             unit;
caddr_t         to;
{
    return ((copyout(histogram[unit], to, sizeof(struct timeval) * HISTSIZE)));
}

#endif DDA_HISTOGRAM

#ifdef DDA_PAD_OR_RAW

#if ACC_BSD > 42
#  include "uba.h"
#  include "bk.h"
#  include "conf.h"
#  include "proc.h"
#  include "tty.h"
#  include "map.h"
#  include "vm.h"
#  include "bkmac.h"
#  include "clist.h"
#  include "file.h"
#  include "uio.h"
#endif

#if ACC_BSD == 42 || ACC_ULTRIX > 00
#  include "bk.h"
#  include "../h/conf.h"
#  include "../h/proc.h"
#  include "../h/tty.h"
#  include "../h/map.h"
#  include "../h/vm.h"
#  if ACC_ULTRIX > 12
#    include "uba.h"
#  endif
#  include "../h/bk.h"
#  ifdef SIMULATION
#    include "Clist.h"
#  else
#    include "../h/clist.h"
#  endif
#  include "../h/file.h"
#  include "../h/uio.h"
#endif

PRIVATE int
dda_decode_type(ds, p)
struct dda_softc *ds;
u_char         *p;
{
    register u_char *cp;
    int             i, usrlen;

#ifdef DDADEBUG
    if (DDADBCH(20, ds->dda_if.if_unit)) {
	printf(" dda_decode_type():  p[0]= %x ", *p);
    }
#endif DDADEBUG

    switch (p[0]) {
      case LINE_STATUS:	/* link status msg */
      case RESTART:		/* restart received */
      case RSTRT_ACK:		/* restart ack */
      case STATRESP:		/* Statistics Response from FEP */
      case CLEARVC:		/* clear by VCN */
	return (0);
      case RESET:		/* X25 reset */
	return (1);
      case ANSWER:
      case CLEARLC:
      case INTERRUPT:
      case INTR_ACK:
	i = p[1] / 2;		/* get lcn */
	if (ds->dda_cb[i].dc_flags & (DC_X29 | DC_X29W))
	    return (1);
	else if (ds->dda_cb[i].dc_flags & (DC_RAW))
	    return (2);
	else
	    return (0);
    }
    if (p[0] != RING) {		/* let standard dda handle it */
	return (0);
    }
    cp = p + 4;			/* skip over code, lcn, vcn and count in
				 * (ring?) answer message */
    /* cp now points to length of called address */
    cp += *cp + 1;		/* skip over called address and length byte */
    /* cp now points to length of calling address */
    cp += *cp + 1;		/* skip over calling address and length byte */
    /* cp now points to length of protocol */
    if (*cp == 0)
	return (0);

    usrlen = *cp++;
    if (usrlen) {
#ifdef DDA_RAWOPT
	if (pi_circuit_to_handle_protocol(*cp))
	    return (2);
#endif
#ifdef DDADEBUG
	if (DDADBCH(20, ds->dda_if.if_unit)) {
	    printf(" dda_decode_type():  return value = %x ", *cp);
	}
#endif DDADEBUG
	switch (*cp) {
	  case X25_PROTO_IP:
	    return (0);
	  case X25_PROTO_X29:
	    return (1);
	  default:
	    return (2);
	}
    } else
	return (0);
}
#endif DDA_PAD_OR_RAW

#ifdef SIMULATION
#  ifdef DDA_PADOPT
#    include "if_x29.c"
#  endif
#  ifdef DDA_RAWOPT
#    include "if_pi.c"
#  endif
#else
#  ifdef DDA_PADOPT
#    if ACC_VMS > 00
#      include "../vaxif/if_vmsx29.c"
#    else
#      include "../vaxif/if_x29.c"
#    endif
#  endif
#  ifdef DDA_RAWOPT
#    include "../vaxif/if_pi.c"
#  endif
#endif

#ifdef DDA_MSGQ
u_char          ddamsgq[MSGQSIZE];
PRIVATE u_char  *mqptr = 0;

#define MSGQEND	(ddamsgq+MSGQSIZE)

dda_mqstr(s)
char           *s;
{
    if (mqptr == 0)
	mqptr = ddamsgq;
    while (*s) {
	*mqptr++ = *s++;
	if (mqptr >= MSGQEND)
	    mqptr = ddamsgq;
    }
    *mqptr = '\0';
}

dda_mqnum(num, type)
int             num, type;
{
    if (mqptr == 0)
	mqptr = ddamsgq;
    if ((mqptr + sizeof(int) + 2) >= MSGQEND)
	mqptr = ddamsgq;
    *mqptr++ = type;
    *((int *) mqptr) = num;
    mqptr += sizeof(int);
    *mqptr = '\0';
}

#endif DDA_MSGQ

/* link in support for steve's test-jig */
#ifdef	SIMULATION
#include "if_dda_sim.c"
#endif

/*
		Revision History:

18-Dec-87: V3.0 - Brad Engstrom
	Added the -t flag to acpconfig and the 't' case in ddaioctl to allow
	setting of the idle circuit timeout.
	The constant TMO_DATA_IDLE was changed to a variable called
	tmo_data_idle.
11-Mar-88: V3.0 - Brad Engstrom
	Modified the history routine to return the current value of the
	timeout. Also fixed bug so that level 0 records amount of time 0
	circuits were in use only when link is up.
11-Mar-88: V3.0 - Brad Engstrom
	Changed handling of supervisor channel overflows to double the max q
	length each time it overflows.  This Will prevent a flood of console
	messages while still notifying the user that there has been an
	overflow.
21-Mar-88: V3.0 - Brad Engstrom
	Fixed bug in writing the facilities field for packet and window size
	negotiation.  This was in the routine make X.25 call.  Previously
	constants were used to index into the facilities buffer now offsets
	from the current facilities length are used.
12-Apr-88: V3.0 - Brad Engstrom
	Added ability to handle class b and class c addressing.  The changes
	affect locate_x25_lcn, convert_x25_addr, and convert_ip_addr.  The
	modifications came from fixes sent to Wollongong by Lars Poulson.
12-Apr-88: V3.0 - Brad Engstrom
	Made modifications so the driver will work under Ultrix or BSD. In
	cases where there are differences between 4.3 and 4.2 bsd (shown by
	#ifdef BSD4_3) Ultrix 1.2 is exactly like a 4.2 system. Ultrix 2.0 is
	like 4.3 in most cases. New macros were added to distinquish between
	systems.  These are BSD4_2 and BSD43_OR_ULTRIX20.
13-Apr-88: V3.0 - Brad Engstrom
	ddareset() was called from ddaintb without arguments.  This could
	cause ddareset to return without doing anything. Proper arguments were
	inserted.  In ddaioctl the priority level s may be used without being
	set.  This was fixed.
18-Apr-88: V3.0 - Brad Engstrom
	Added the use of a key field in the dda_cb structure.  Previously the
	dc_inaddr field was used both for printing the ip address (-l command)
	and for searching for circuits that were open to a destination.  Using
	this for a cicuit matching address means that the network and local
	host fields needed to be masked off, thus making this field less
	usefull for printing.  Now two fields are used dc_inaddr is used for
	printing.  dc_key is used for circuit matching.  In PDN mode the
	full ip address is used as the key.  In DDN mode just the imp number
	and host(port) number are used.
18-Apr-88: V3.0 - Brad Engstrom
	Made histogram facilities a compile time option.  The histogram is
	enabled if DDA_HISTOGRAM is defined.  The facilities are always
	disabled when using 4.2 or ULTRIX 1.2 as the kernel does not have the
	proper support routines available.
22-Apr-88: V3.0 - Brad Engstrom
	Added new option to -v command to set the dda_db_unit variable.
22-Apr-88: V3.0 - Brad Engstrom
	Added the DMESG macro and the msgbits array to allow selective
	disabling of driver error messages.  To enable or disable an error
	message the -c command of acpconfig is used. The msgbits array holds
	info about whether each message is enabled or disabled.  Setting a bit
	to 1 disables a message.  Clearing a bit to 0 enables a message.
	All messages start as enabled.
22-Apr-88: V3.0 - Brad Engstrom
	Added check for DDAMAINT_BRD in probe routine.  If DDAMAINT_BRD is
	defined then assume we are using a maintenence board so don't try to
	find the firmware id because it won't be there. Fake info that was
	supposed to be contained in the firmware id.
25-Apr-88: V3.0 - Brad Engstrom
	Added check in locate_x25_lcn to see if state of lc is LC_CALL_PENDING
	or LC_DATA_IDLE in the loop that looks for an already open lc.  This
	will prevent an address of 0.0.0.0 from matching a circuit that is not
	in use.  If the address is invalid then the imp will kick it out.
26-Apr-88: V3.0 - Brad Engstrom
	Changed the -n command case so that a command of the form "-n 0" will
	return the number of channels currently available.  This will be used
	by the -l command and possible by the -h command to determine the
	number of available circuits.
10-May-88: V3.0 - Brad Engstrom
	Made all occurences of the length of and X.25 address refer to the
	constants MAXADDRLEN and MINADDRLEN defined in if_ddavar.h.  These
	constants include the 1 byte  for encoding the length.
02-Jun-88: V3.0 - Brad Engstrom
	Change the check for the firmware revision level to 2.2 for the -e
	command.  This command will crash [56]250s that don't have at least
	v2.2 firmware.
12-Jul-88: V3.0 - Brad Engstrom
	Deleted case for class_b_c addressing.
20-Jul-88: V3.0 - Brad Engstrom
	Fixed bug in parsing facilities that would cause the kernel to hang.
	The bug was not incrmenting pointers when an urecognized 2 octet
	facility was seen.  Fixes were applied to send_supr() and
	decode_answer()
30-Aug-88: V4.0 - Brad Engstrom
	Modified driver to support X.29 and a programmers interface.  Includes
	files if_x29.c, if_pi.c, and if_pivar.h
30-Aug-88: V4.0 - Brad Engstrom
	Added support for debug logging under the control of the DDA_MSGQ
	define.   Information is extracted using the new -p command of
	acpconfig.
30-Aug-88: V4.0 - Brad Engstrom
	Modified start_chan to check the ready bit before touching the
	comregs.  Also modified dda_rrq and dda_wrq to raise ipl before
	touching the sioq.  These changes fixed a bug where the FE was losing
	I/O requests.
20-Oct-88: V4.0 - Steve Johnson
	Added SIMULATION #ifdef for simulation support
08-Jan-89: V4.1 - Steve Johnson
	MERGE 4.0 and 3.1
10-Oct-88: V3.1 - Charles Carvalho
	Replace prt_x25 with fmt_x25, which returns a pointer to a formatted
	message instead of printing its data; this allows error messages to be
	output with a single call to DDALOG (or syslog).  Move prt_addr
	inline, for same reason.  Add IP address to some error messages;
	trim excess text from some error messages.  Allocate channels
	for incoming calls from lowest channel up; we do linear searches of
	the lcn table, so it's to our advantage to use the lowest numbers for
	all active circuits. (The lcn is not related to the virtual circuit
	number, so there is no need to allocate incoming channels from the
	top down.)  Modify prt_bytes to take unit number and descriptive
	string to be printed along with the buffer and byte count; it now
	formats up to 16 bytes at a time and prints a full line with each call
	to DDALOG rather than calling DDALOG for each byte.
17-Oct-88: V3.1 - Charles Carvalho
	Add definitions for DDALOG and DDAELOG, which translate into a call to
	DDALOG() or log().
26-Oct-88: V3.1 - Charles Carvalho
	Change index for 'v' ioctl to preserve compatibility with previous 
	versions.  Restrict maximum window size to 127, not 128.
7-Nov-88: V3.2 - Charles Carvalho
	Fix check for no free circuits when processing RING
17-Feb-89: V4.3.0 - Paul Traina
	Added TGV changes for Multinet.
8-Mar-89: V4.3.1 - Steve Johnson
	Installed 'Q' ioctl to support obtaining an internal trace log used
	for debugging only -- not documented for general user.  acpconfig
	dda0 -q 2 dumps 256 bytes from the dda_debug_silo[] array
13-Mar-89: V4.3.2 - Paul Traina
	Updated Multinet support.
17-Apr-89: V4.3.3 - Steve Johnson
	Split bus and simulation related code out to included files for first
	shot at 7000 and tahoe design.  Don't reset timeout counter in
	dda_data() unless link really is in idle state.
28-Apr-89: V4.3.4 - Paul Traina
	Modified changes of 17-Apr-89, added minimal tahoe support until
	driver modified to use 4.3uba transfers.
	Fixed timeout fix of 17-Apr-89 to do what was intended.
	Fixed code dealing with maintenance board, reformatted with indent
	to repair readablility.
09-May-89: V4.3.5 - Paul Traina
	Minimal tahoe support completed,  based on BSD4_3TAHOE define which
	must be uncommented manually.  Finalizing for ECO.
24-May-89: V4.3.6 - Paul Traina
	Ultrix 3.0 support added.  Revised 4.3 tahoe support for automatic
	invocation.
	*** NOTE: one of the three OS defines (ACC_BSD, ACC_ULTRIX, ACC_VMS)
	    in if_dda.c must be set to a non-zero value for the driver to
	    compile.
	Attempting multiple-os support based upon weird variables from include
	files is not acceptable with the latest proliferation of OS versions.
20-Jun-89: V4.3.7 - Paul Traina
	Removed crufty old debug stuff and integrated it with the log-message
	code.  Now X29 and PI modules can be debuged properly (no #if 0's!).
22-Jun-89:	  - Paul Traina
	Diddled ring-decode logic to check for proper ring packet decoding
	before attempting to find a free lcn.  This will make it easier to deal
	with the race condition with find_free_lcn().
	Modified ACC os specific equates to be set as options in the config
	file.  This way, most users won't ever edit if_dda.c.
18-Jul-89:	  - Paul Traina
	Driver will no longer return errors if duplicate address-translation
	entries are made.  Errors will only happen if a redefiniton is
	attempted.
	Moved dc_key.ttyline out of union, creating dc_line.
26-Jul-89:	  - Paul Traina f/Brad Engstrom
	Added support for called user-data field (two new params to
	make_x25_call) to support extended pad mode in the X.29 module.
01-Aug-89:	  - Paul Traina
	Made ddamsgs uninitialized -- it gets inited in ddaattach now.
03-Aug-89:	  - Paul Traina
	Changed hist_copyout definition to PRIVATE.
15-Aug-89:	  - Paul Traina
	Made dda_softc and dda_iobuf non-private.
18-Aug-89:	  - Paul Traina
	Somehow, ddareset was removed from the 'z' ioctl.
28-Aug-89:	  - Paul Traina
	Changed make_x25_call so that it checks length of data to be stuffed
	into the mbuf before actually copying data in.  Removed udlen and
	ud parameters to the routine, as the public areas will be plugged
	with data before being called.  (May need to splimp()).
22-Sep-89:	  - Paul Traina
	The order of the 'v' ioctl parameters was screwed up.  This caused
	window and packet size setting to fail.
23-Oct-89:	  - Paul Traina
	Added further support for Steve's yetchy simulation.  Updated main
	module to work with BI version of dda board.
29-Oct-89:	  - Paul Traina
	Acpconfig inconsistancy (again): removed the 'p', and 'Q' ioctls.
	Since all of these are queries, I placed them under the 'q' ioctl
	with a new switch.  Some day we should just scrap the whole mess
	and design a proper ioctl interface.
11-Nov-89:	  - Paul Traina
	Moved rrq/wrq routines into bus modules because we can do several
	queue reads and writes when working with the BI.
*/
#endif NDDA > 0
 prints a full line with each call
	to DDALOG rather than calling DDALOG for each byte.
17-Oct-88: V3.1 - Charles Carvdriver/if_dda_uqbus.c   444    540     24      151731  4535572350  10246 /*************************************************************************/
/*                                                                       */
/*                                                                       */
/*       ________________________________________________________        */
/*      /                                                        \       */
/*     |          AAA          CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*     |         AAAAA        CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     |        AAAAAAA       CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |       AAAA AAAA      CCCC              CCCC              |      */
/*     |      AAAA   AAAA     CCCC              CCCC              |      */
/*     |     AAAA     AAAA    CCCC              CCCC              |      */
/*     |    AAAA       AAAA   CCCC              CCCC              |      */
/*     |   AAAA  AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |  AAAA    AAAAAAAAAAA CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     | AAAA      AAAAAAAAA   CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*      \________________________________________________________/       */
/*                                                                       */
/*  	Copyright (c) 1986 by Advanced Computer Communications           */
/*  	720 Santa Barbara Street, Santa Barbara, California  93101       */
/*  	(805) 963-9431                                                   */
/*                                                                       */
/*                                                                       */
/*  File:		if_dda_uqbus.c                                   */
/*			Unibus & Q22bus support routines for dda	 */
/*                                                                       */
/*  Project:		DDN-X.25 Network Interface Driver for ACP 5250   */
/*			and ACP 6250                                     */
/*                                                                       */
/*  revision history at the end of if_dda.c				 */
/*************************************************************************/


#ifndef	SIMULATION
#include "../vaxif/if_uba.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
#else
#include "vaxif/if_uba.h"
#include "vaxuba/ubareg.h"
#include "vaxuba/ubavar.h"
#endif

PRIVATE u_short	ddastd[] = {0767000, 0}; /* standard addresses */

struct uba_device *ddainfo[NDDA];	/* ptrs to device info */

struct uba_driver ddadriver =	/* device driver info */
{
 ddaprobe,			/* device probe routine */
 0,				/* slave probe routine */
 ddaattach,			/* device attach routine */
 0,				/* "dmago" routine */
 ddastd,			/* device address */
 "dda",				/* device name */
 ddainfo			/* ptr to device info ptrs */
};

/* figure out if this machine is a QBUS machine */
#if defined(MVAX) || defined(VAX3400) || defined(VAX3600) || defined(DS5400)
#define	QBUS
#endif

/*
 * The following definitions declare the structure of the UNIBUS/QBUS
 * mapped pages (which are 512 bytes long).  In previous versions we
 * assumed that the bus pages were the same size as the memory pages,
 * but the mips PMAX cpu uses 4k memory pages but 512 byte bus pages.
 */

#define	BUS_NBPG	512		/* bytes/unibus mapped page */
#define	BUS_PGOFSET	(BUS_NBPG-1)	/* offset into bus page */
#define	BUS_PGSHIFT	9		/* number of bits to shift for page */

/*
 * If we're not running on a PMAX cpu, we don't need to do write buffer
 * pipeline flushes nor do we need to declare I/O space pointers to
 * be volatile.
 */

#ifndef	mips
#define	wbflush()
#define	volatile	register
#endif

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAPROBE()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/* Purpose:                                                        */
/*                                                                 */
/*  This routine probes the device to obtain the UNIBUS interrupt  */
/*  vector.  Since the ACP is a soft vector device, we obtain an   */
/*  unused vector from the uba structure and return that.  The ACP */
/*  is given the vector and the board is reset.  In order to save  */
/*  the vector in the device info structure, we place it in a      */
/*  static temporary where the attach routine can find it and save */
/*  it in the device info structure.  This is necessary because    */
/*  probe only provides a pointer to the device and we have no     */
/*  idea which unit is being referenced.  This works in 4.2BSD     */
/*  because the attach routine is called immediately after a       */
/*  successful probe.                                              */
/*                                                                 */
/*  Call:          ddaprobe(reg, ui)                               */
/*  Argument:      reg:  caddr_t address in virtual memory of the  */
/*                        control-status register                  */
/*                 ui:   pointer to device data structure, used    */
/*                       for TWG environment                       */
/*  Returns:       length of register structure for ACP device     */
/*  Called by:     network software, part of autoconfiguration on  */
/*                 the VAX, the address of this routine is one of  */
/*                 the fields of the uba_driver structure          */
/*  Calls to:      none                                            */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static int      savevec;	/* static variable for vector */
static int      savefirmrev;	/* firmware rev has same problem as vector */

ddaprobe(reg, ui)
caddr_t         reg;
struct uba_device *ui;		/* TWG VAX/VMS ONLY! */
{
	/* In 2.0 ULTRIX and newer, br and cvec are global (ubavar.h) */
	/* while multinet needs them external */

#if !defined(MULTINET) && ACC_ULTRIX < 20
    register int    br, cvec;	/* r11, r10 value-result */
#endif

#ifdef MULTINET
    extern int      br, cvec;	/* must define external */
#endif

    volatile struct ddaregs *addr = (struct ddaregs *) reg;

#ifdef lint
    br = 0; cvec = br; br = cvec;
#  if ACC_VMS == 00
    ui = ui;
#  endif
#endif

#if ACC_VMS > 00
    cvec = savevec = ui->ui_flags & 0x1f8;	/* flags from config file */
#else
    cvec = savevec = ((uba_hd[numuba].uh_lastiv - 8) & ~7);
    uba_hd[numuba].uh_lastiv = cvec;
#endif

    /* return a vector aligned on a */
    /* QUADWORD boundary */
    /* cvec is the interrupt vector */
    /* address on the UNIBUS */
#ifdef QBUS
    br = 0x17;			/* bus level for MicroVAX */
#else
    br = 0x15;			/* bus level for VAX */
#endif

    /* check that the device is really a 5250/6250 and save away */
    /* the firmware revision level for version dependent processing */
    /* If we just booted, the diagnostics may still be running as we */
    /* probe the device - it's still OK to read the ID and VERSION */
    /* numbers, which come valid within milliseconds after power is */
    /* applied to the board - so say the firmware gurus */

    dda_hasmaint = !(addr->csr & CSR_MAINT);
    savefirmrev = addr->sys_vers;

    switch (addr->sys_id) {
      case 0x8:
	dda_product = "ACP6250";
	break;
      case 0x28:
	dda_product = "ACP5250";
	break;
      case 0x2C:
	dda_product = "ACP5250-W";
	break;
      default:
	if (!dda_hasmaint)
	    return 0;
	savefirmrev = 0xff;
	dda_product = "ACPx250";
	break;
    }

    /* clear communications registers */

    addr->req_flags = 0;	/* I/O request flags            */
    addr->cmp_flags = 0;	/* I/O completion flags         */
    addr->xfr_flags = 0;	/* transfer request/grant flags */
    addr->req_chan = 0;		/* FDX channel number           */
    addr->req_adx = 0;		/* address bits 17-16           */
    addr->req_addr = 0;		/* address bits 15-00           */
    addr->req_cnt = 0;		/* byte count                   */
    addr->req_fcn = 0;		/* I/O function                 */
    addr->req_sbf = 0;		/* I/O subfunction              */
    addr->cmp_chan = 0;		/* FDX channel number           */
    addr->cmp_unused = 0;	/* address bits 17-16           */
    addr->cmp_cnt = 0;		/* byte count                   */
    addr->cmp_stat = 0;		/* I/O status                   */
    addr->cmp_sbst = 0;		/* I/O substatus                */
    addr->xfr_chan = 0;		/* FDX channel number           */
    addr->xfr_adx = 0;		/* address bits 17-16           */
    addr->xfr_addr = 0;		/* address bits 15-00           */
    addr->xfr_cnt = 0;		/* byte count                   */
    addr->sys_stat = 0;		/* system status                */

    addr->sys_vect = cvec >> 2;	/* pass interrupt vector to ACP */
    addr->csr = CSR_RESET;	/* reset the board */
    addr->csr |= CSR_IENB;	/* enable status intr */
    return (sizeof(struct ddaregs));
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAATTACH()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  This routine attaches the device to the network software.  The */
/*  network interface structure is filled in.  The device will be  */
/*  initialized when the system is ready to accept packets.  The   */
/*  dda_init initialization/service flag is zeroed, DDN standard   */
/*  X.25 service is implemented by default unless otherwise        */
/*  specified by the user via the acpconfig program.               */
/*                                                                 */
/*  Call:           ddaattach(ui)                                  */
/*  Argument:       ui:  ptr to the uba_device data structure      */
/*  Returns:        nothing                                        */
/*  Called by:      network software, part of network system       */
/*                  configuration, identification to the network   */
/*                  software,  the address of this routine is one  */
/*                  of the fields of the uba_driver structure      */
/*  Calls to:       if_attach()                                    */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

ddaattach(ui)
struct uba_device *ui;
{
    register struct dda_softc *ds = &dda_softc[ui->ui_unit];
    static long initmsgs[] = { 0, 0, 0, 0, -1, -1, -1, -1, -2 };

    ds->dda_init = DDA_STANDARD;/* init/service flag <- default  */
    ds->dda_vector = savevec;	/* save vector from probe() */
    ds->dda_firmrev = savefirmrev;	/* save firmware rev level */
    ds->dda_net_id = 0;		/* default */
    ds->dda_if.if_unit = ui->ui_unit;	/* set unit number */
    ds->dda_if.if_name = "dda";	/* set device name */
    ds->dda_if.if_mtu = DDAMTU;	/* set max msg size */
    ds->dda_if.if_init = ddainit;	/* set init routine addr */
    ds->dda_if.if_ioctl = ddaioctl;	/* set ioctl routine addr */
    ds->dda_if.if_output = ddaoutput;	/* set output routine addr */
    ds->dda_if.if_reset = ddareset;	/* set reset routine addr */
    ds->dda_if.if_watchdog = ddatimer;	/* set timer routine addr */

    bcopy((char *) initmsgs, (char *) ddamsgs[ui->ui_unit], sizeof(initmsgs));

    if_attach(&ds->dda_if);	/* attach new network device */
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDARESET()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*      Reset of interface after UNIBUS reset.  If interface is on */
/*      specified uba, reset its state.  Free mbufs if there is    */
/*      queued output data.                                        */
/*                                                                 */
/*  Call:              ddareset(unit, uban)                        */
/*  Arguments:         unit:   ACP device unit number              */
/*		       uban:   Unibus Adapter # (do not use!)	   */
/*  Returns:           nothing                                     */
/*  Called by:         network software, address of routine is     */
/*                     defined in dda_if network interface struct  */
/*  Calls to:          DDALOG()                                    */
/*                     IF_DEQUEUE()                                */
/*                     m_freem()                                   */
/*                                                                 */
/*  NOTE: the uban parameter is NOT USED, and may be garbage under */
/*	  some circumnstances.  It is there because the kerenel    */
/*	  expects to pass two parameters to ddareset()		   */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

ddareset(unit, uban)
int             unit, uban;
{
    volatile struct ddaregs *addr;
    register struct uba_device *ui;
    register struct dda_cb *dc;
    register struct dda_softc *ds = &dda_softc[unit];
    register int    lcn;

    ui = (struct uba_device *) ddainfo[unit];
    if (unit >= NDDA || (ui == 0 || ui->ui_alive == 0)) {
        DMESG(unit, 0, (DDALOG(LOG_ERR) "dda%d: ddareset: invalid unit\n", unit DDAELOG));
	return;
    }

    DMESG(unit, 0, (DDALOG(LOG_ERR) "dda%d: reset\n", unit DDAELOG));

    ds->dda_if.if_flags &= ~IFF_UP;
    hist_link_state(unit, ds->dda_state, S_DISABLED);
    ds->dda_state = S_DISABLED;

    addr = (struct ddaregs *) ui->ui_addr;

    addr->cmp_flags = 0;	/* I/O completion flags         */
    addr->xfr_flags = 0;	/* transfer request/grant flags */
    addr->req_chan = 0;		/* FDX channel number           */
    addr->req_adx = 0;		/* address bits 17-16           */
    addr->req_addr = 0;		/* address bits 15-00           */
    addr->req_cnt = 0;		/* byte count                   */
    addr->req_fcn = 0;		/* I/O function                 */
    addr->req_sbf = 0;		/* I/O subfunction              */
    addr->cmp_chan = 0;		/* FDX channel number           */
    addr->cmp_unused = 0;	/* address bits 17-16           */
    addr->cmp_cnt = 0;		/* byte count                   */
    addr->cmp_stat = 0;		/* I/O status                   */
    addr->cmp_sbst = 0;		/* I/O substatus                */
    addr->xfr_chan = 0;		/* FDX channel number           */
    addr->xfr_adx = 0;		/* address bits 17-16           */
    addr->xfr_addr = 0;		/* address bits 15-00           */
    addr->xfr_cnt = 0;		/* byte count                   */
    addr->sys_stat = 0;		/* system status                */
    addr->req_flags = 0;	/* clear handshake flags, mailbox     */

    /* pass interrupt vector to ACP */
    addr->sys_vect = dda_softc[unit].dda_vector >> 2;

    addr->csr = CSR_RESET;	/* reset the board                    */
    dda_softc[unit].dda_flags = 0;	/* clear ACP operational flag  */

    ds->dda_init &= ~DDA_INTCLOCK;	/* reset internal-clocking-set bit */
    nddach[unit] = NDDACH_DEFAULT;	/* reset SVC limit */
    dc = dda_softc[unit].dda_cb;/* flush any queued output data */
    /* LCNLINK */
    for (lcn = 0; lcn <= NDDACH; lcn++) {	/* for all LCN's ... */
	dc->dc_inaddr.s_addr = 0;	/* clear remote internet addr */
	dc->dc_key.key_addr.s_addr = 0;	/* must save for x29 */
	dc->dc_wsizein = dc->dc_wsizeout = 0;
	dc->dc_pktsizein = dc->dc_pktsizeout = 0;
	dc->dc_state = LC_IDLE;	/* init LCN state */
	dc->dc_timer = TMO_OFF;	/* turn LCN timer off */
	dc->dc_flags = 0;
#ifdef DDADEBUG
	dc->dc_out_t = TMO_OFF;	/* turn FE completion timer off */
#endif
	dc++;
    }
    hist_all_lcns(unit, LC_IDLE);
#ifdef DDA_RAWOPT
    pi_init(unit, 1);
#endif
#ifdef DDA_PADOPT
    x29_init(unit, 1);
#endif
    addr->csr |= CSR_IENB;
    wbflush();			/* flush write cache pipeline */
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAINIT()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine initializes the interface for operation.  The   */
/*    device control blocks are initialized, UNIBUS resources are  */
/*    allocated and an initialization message is sent to the ACP.  */
/*                                                                 */
/*    Note that interrupt "b" is enabled here to avoid a possible  */
/*    race condition at power up time - it was previously done in  */
/*    the probe and reset routines.                                */
/*                                                                 */
/*  Call:             ddainit(unit)                                */
/*  Argument:         unit:  ACP device unit number                */
/*  Returns:          nothing                                      */
/*  Called by:        network software, address of this routine is */
/*                    defined in dda_if network interface struct   */
/*                    ddaioctl()                                   */
/*                    ddaintb()                                    */
/*  Calls to:         in_netof() return the network number from    */
/*                               internet address                  */
/*                    spl6()                                       */
/*                    uballoc()                                    */
/*                    ddatimer()                                   */
/*                    btoc()                                       */
/*                    splimp()                                     */
/*                    dda_rrq()                                    */
/*                    splx()                                       */
/*                    if_rtinit()                                  */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

ddainit(unit)
int             unit;
{
    volatile struct ddaregs *addr;
    register struct dda_softc *ds = &dda_softc[unit];
    register struct dda_cb *dc;
    register struct uba_device *ui;

#if ACC_BSD == 42 || ACC_ULTRIX == 12
    struct sockaddr_in *sin;
#endif

    int             lcn, s;

    ui = (struct uba_device *) ddainfo[unit];
    addr = (struct ddaregs *) ui->ui_addr;

#ifdef DDADEBUG
    if (DDADBCH(0, unit)) {
	DDALOG(LOG_DEBUG) "dda%d: ddainit()\n", unit DDAELOG;
    }
#endif DDADEBUG

    /* if we have no internet addr  if device not operational don't init yet */

#if ACC_BSD == 42 || ACC_ULTRIX == 12
    sin = (struct sockaddr_in *) & ds->dda_if.if_addr;
    if ((in_netof(sin->sin_addr) == 0) || ((ds->dda_flags & DDAF_OK) == 0))
#else
    if (ds->dda_if.if_addrlist == (struct ifaddr *) 0 ||
       ((ds->dda_flags & DDAF_OK) == 0))
#endif
	return;

    /* enable intrpt "b" */
    addr->csr |= CSR_IENB;

    if ((ds->dda_if.if_flags & IFF_RUNNING) == 0) {
	dc = ds->dda_cb;	/* setup ptr to first LCN cntl block */

	/* LCNLINK */
	for (lcn = 0; lcn <= NDDACH; lcn++) {	/* for all LCN's ... */
	    dc->dc_lcn = lcn;	/* record LCN */
	    dc->dc_inaddr.s_addr = 0;	/* clear remote internet addr */
	    dc->dc_key.key_addr.s_addr = 0;
	    dc->dc_wsizein = dc->dc_wsizeout = 0;
	    dc->dc_pktsizein = dc->dc_pktsizeout = 0;
	    dc->dc_state = LC_DOWN;	/* init LCN state */
	    dc->dc_timer = TMO_OFF;	/* turn LCN timer off */
#ifdef DDADEBUG
	    dc->dc_out_t = TMO_OFF;	/* turn FE completion timer off */
#endif

	    /* init LCN output queue */

	    s = splimp();
	    dc->dc_oq.ifq_head = (struct mbuf *) 0;
	    dc->dc_oq.ifq_tail = (struct mbuf *) 0;
	    splx(s);
	    dc->dc_oq.ifq_len = 0;
	    dc->dc_oq.ifq_maxlen = DDA_OQMAX;
	    dc->dc_oq.ifq_drops = 0;

	    /* init HDX channels */

	    dc->dc_rchan.hc_next = (struct hdx_chan *) 0;
	    dc->dc_rchan.hc_chan = lcn * 2;
	    dc->dc_wchan.hc_next = (struct hdx_chan *) 0;
	    dc->dc_wchan.hc_chan = (lcn * 2) + 1;

	    dc->dc_rchan.hc_mbuf = (struct mbuf *) 0;
	    dc->dc_rchan.hc_curr = (struct mbuf *) 0;
	    dc->dc_wchan.hc_mbuf = (struct mbuf *) 0;
	    dc->dc_wchan.hc_curr = (struct mbuf *) 0;

	    dc->dc_flags = 0;	/* initialize flags */

	    dc++;		/* point at next cntl blk */
	}
	hist_all_lcns(unit, LC_DOWN);


	/* allocate UNIBUS mapping registers, uballoc() */
	/* returns ubinfo, CLBYTES = (CLSIZE * NBPG)    */
	/* rev 2.0 and above: we are now attempting to handle interrupt */
	/* B by resetting the board and bringing it back on line.  So   */
	/* have to deal with the possibility that we already did this.  */
	/* It's more serious to let mapping registers "leak" than mbufs */
	/* (see above) and it's easy to handle too so THIS we do check. */

#ifndef MULTINET
	s = spl6();
#endif
	if (ds->dda_mapreg == 0)	/* don't have one yet, allocate */
	    ds->dda_mapreg = uballoc(ui->ui_ubanum,
				     (char *) &dda_softc[unit], CLBYTES, 0);
#ifndef MULTINET
	splx(s);
#endif

	if (ds->dda_mapreg == 0) {
	    DMESG(unit, 1,
		  (DDALOG(LOG_ERR)
		   "dda%d: failed getting UBA resources for lcn %d\n",
		   unit, lcn DDAELOG));
	    ds->dda_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
	    hist_link_state(unit, ds->dda_state, S_DISABLED);
	    ds->dda_state = S_DISABLED;
	    return;
	}
	/* leave the UNIBUS mapping register */
#ifdef	UBAI_MR	 /* if the macro exists, use it, because it's always right */
	ds->dda_mapreg = UBAI_MR(ds->dda_mapreg);
#else
	ds->dda_mapreg = (ds->dda_mapreg >> BUS_PGSHIFT) & BUS_PGOFSET;
#endif

	ds->dda_sioq.sq_head = (struct hdx_chan *) 0;
	ds->dda_sioq.sq_tail = (struct hdx_chan *) 0;

	ds->dda_if.if_flags |= IFF_RUNNING;
    }
    s = splimp();
    dc = ds->dda_cb;		/* setup ptr to first LCN cntl block */
    for (lcn = 0; lcn <= nddach[unit]; lcn++) {	/* issue reads on all LCNs */
	dda_rrq(ds, &(dc->dc_rchan));
	dc++;
    }
    splx(s);

    ddatimer(unit);		/* start timers */

#if ACC_BSD == 42 || ACC_ULTRIX == 12
    if_rtinit(&ds->dda_if, RTF_UP);	/* initialize the routing table */
    /* for network, give address of ifnet structure and RTF_UP   */
    /* which means route is useable */
#endif

#ifdef DDA_RAWOPT
    pi_init(unit, 0);		/* initialize progammer interface */
#endif
#ifdef DDA_PADOPT
    x29_init(unit, 0);
#endif
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAINTA()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This is the interrupt handler for I/O interrupts (interrupt  */
/*    "a") from the ACP device.  The I/O mailboxes are scanned for */
/*    handshake events to process. Three types of interrupts are   */
/*    processed:  Transfer Request, I/O request acknowledge, and   */
/*    I/O completion.  The interrupting HDX channel and interrupt  */
/*    type are obtained.  If the interrupt is Transfer Request,    */
/*    quit if there is no data, otherwise supply values for the    */
/*    Transfer Request Mailbox.  Note the mapping of the system    */
/*    page table entry, pte, and the UNIBUS Mapping Register,      */
/*    ddamapreg.  The mapping algorithm is different for the TWG   */
/*    Eunice/IPTCP environments because Sysmap is not present as   */
/*    it is in UNIX 4.2 BSD.  If interrupt is an I/O request       */
/*    acknowledge the next I/O request is passed to the ACP        */
/*    device.  If the interrupt is an I/O completion, check for    */
/*    errors, if ok process according to whether supervisory or    */
/*    data channel.                                                */
/*                                                                 */
/*  Call:              ddainta(unit)                               */
/*  Arguments:         unit:  ACP device unit number               */
/*  Returns:           nothing                                     */
/*  Called by:         network software, address of this routine   */
/*                     is defined in af_inet network interface     */
/*                     data structure                              */
/*  Calls to:          DDALOG()                                    */
/*                     btop() - byte to page w/o rounding ( >> 9 ) */
/*                     btoc()                                      */
/*                     start_chn()                                 */
/*                     dda_data()                                  */
/*                     dda_supr()                                  */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#ifdef WINS
extern struct pte *mmg$gl_sptbase;	/* base of system page table */
extern struct ADP *ioc$gl_adplist;	/* Adapter control blocks */
#endif WINS

#ifdef	MULTINET
extern struct pte *vms_sptbase;
#define mmg$gl_sptbase vms_sptbase
#endif	MULTINET

ddainta(unit)
int             unit;
{
    volatile struct ddaregs *addr = (struct ddaregs *) ddainfo[unit]->ui_addr;
    register struct dda_softc *ds = &dda_softc[unit];
    register struct hdx_chan *hc;
    register struct uba_device *ui;
    register struct uba_hd *uh = &uba_hd[ddainfo[unit]->ui_ubanum];
    struct pte     *pte;	/* page table entry pointers */
    int             chan, cc, subcc, cnt, npf, uadr, pgoff;
    register int    page_to_map;

#if ACC_VMS > 00
    int             temp;
    register struct ADP *adp;	/* TWG or TGV, UNIBUS adapter */
    int            *io;
#else
    struct pte     *io;		/* page table entry pointers */
#endif

#ifdef DDADEBUG
    if (DDADBCH(5, unit)) {
	DDALOG(LOG_DEBUG) "dda%d: ddainta()\n", unit DDAELOG;
    }
#endif DDADEBUG

    ui = (struct uba_device *) ddainfo[unit];
    addr = (struct ddaregs *) ui->ui_addr;

/***********************************************************************/
/*   Check Transfer Request Mailbox  (note VAXVMS dependent code)      */
/***********************************************************************/

    if (addr->xfr_flags & FLAGS_RDY) {	/* Transfer Request Mailbox */
#ifdef DDA_MSGQ
	dda_mqstr("(tx)");
#endif

	/*
	 * Get logical channel information. 
	 */
	if ((chan = addr->xfr_chan) > nddach[unit]) {
	    DMESG(unit, 4,
	       (DDALOG(LOG_ERR) "dda%d: unknown transfer channel, lcn=%d\n",
		unit, chan DDAELOG));
	    return;
	}
	if (addr->xfr_flags & FLAGS_DIR)
	    hc = &(ds->dda_cb[chan].dc_wchan);	/* write channel */
	else
	    hc = &(ds->dda_cb[chan].dc_rchan);	/* read channel */

	cnt = addr->xfr_cnt;	/* Transfer Request byte count */

	if (hc->hc_mbuf == 0) {
	    DMESG(unit, 5,
	       (DDALOG(LOG_ERR) "dda%d: transfer request lcn %d: no mbuf\n",
		unit, chan DDAELOG));
	    addr->xfr_cnt = 0;	/* Abort the transfer */
	    return;
	}
	pgoff    = (int) hc->hc_addr & PGOFSET;

#if ACC_VMS > 00
	/* System page table entry for VMS */
	temp = (((int) hc->hc_addr & (~PG_V & ~(NBPG - 1))) / NBPG);
	pte = (struct pte *) ((int) mmg$gl_sptbase + (4 * temp));

	/*
	 * Find the adapter 
	 */
#  ifdef WINS
	adp = ioc$gl_adplist;
	while (adp) {
	    if ((adp->adp$w_adptype == AT$_UBA) &&
		(adp->adp$b_number == ui->ui_ubanum))
		break;
	    adp = adp->adp$l_link;
	}
	if (!adp)
	    return;		/* adp not found */
	io = (int *) ((int) adp->adp$l_csr + 0x800 + (4 * (ds->dda_mapreg)));
#  endif
#  ifdef MULTINET
	adp = (struct ADP *) 0;
	while (1) {
	    char          **vector;
	    int             number, csr;

	    /*
	     * Get the next adapter 
	     */
	    adp = (struct ADP *) vms_next_uba(adp, &vector, &number, &csr, 0);
	    if (!adp)
		return;

	    /*
	     * Is this the one we are looking for? 
	     */
	    if (number == ui->ui_ubanum) { /* yes */
		io = (int *) ((int) csr + 0x800 + (4 * (ds->dda_mapreg)));
		break;
	    }
	}
#  endif
#else	/* not VMS */
#  ifdef kvtopte	/* if the macro exists, use it for compatibility */
			/* this will be in 4.3tahoe and newer */
	pte = kvtopte(hc->hc_addr);
	io  = &uh->uh_mr[ds->dda_mapreg];
#  else
#    ifdef svtopte	/* under ultrix, kvtopte is svtopte */
			/* but the map registers are in the right place */
	pte = svtopte(hc->hc_addr);
	io  = &uh->uh_uba->uba_map[ds->dda_mapreg];
#    else  /* not 4.3tahoe or a new ultrix flavor */
	pte = &Sysmap[btop((int) hc->hc_addr & ~PG_V)];
	io = &uh->uh_uba->uba_map[ds->dda_mapreg];
#    endif /* not 4.3tahoe or a new ultrix flavor */
#  endif
#endif

	/* pte now points to system memory page table entry */
	/* io points to UNIBUS/QBUS mapping register */

#ifndef SIMULATION
	/* calculate number of page frames */
	npf = (hc->hc_cnt + BUS_PGOFSET) >> BUS_PGSHIFT;

	/* page_to_map is all but nine bits of the physical address
	   in other words, the 512-byte page the bus wants us to map */

	page_to_map = ((pte++)->pg_pfnum << (PGSHIFT - BUS_PGSHIFT)) |
		      (pgoff >> BUS_PGSHIFT);

	while (npf--) {
	    /* UBAMR_MRV = mapping register valid */
	    *(int *) io++ = page_to_map++ | UBAMR_MRV;

	    /* when page_to_map crosses a memory page boundary, we
	       need to get the new physical address from the next pte
	       (on a vax, this happens every time, on a mips, only when
	       the lower 3 bits go from 111 to 000) */	

	   if (page_to_map & (PGOFSET >> BUS_PGSHIFT) == 0)
		/* we know page offset is now going to be zero, so don't
		   bother adding it in (like we had to do above) */
		page_to_map = (pte++)->pg_pfnum << (PGSHIFT - BUS_PGSHIFT);
	}
	*(int *) io++ = 0;	/* invalidate last UMR */

	/* supply values for Transfer Request Mailbox */

	addr->xfr_chan = chan;	/* data path number */

	uadr = (ds->dda_mapreg << BUS_PGSHIFT) + (pgoff & BUS_PGOFSET);

#ifdef	QBUS
	addr->xfr_adx = (uadr & 0x3f0000) >> 16; /* ext address bits 23-16  */
#else	QBUS
	addr->xfr_adx = (uadr &  0x30000) >> 16; /* ext address bits 18-16  */
#endif	QBUS
#else	SIMULATION
	uadr = (int) hc->hc_addr;	/* just use the direct address */
	addr->xfr_adx = (uadr & 0xFF0000) >> 16; /* ext ms address bits */
#endif	SIMULATION

	addr->xfr_addr = uadr & 0xffff;	/* grnt transfer address bits 15-0 */

	hc->hc_cnt -= cnt;
	hc->hc_addr += cnt;

	addr->xfr_flags = (addr->xfr_flags & ~FLAGS_RDY) | FLAGS_DON;
	addr->csr |= CSR_INTRA;	/* enable interrupt "a" */
    }

    /***********************************************************************/
    /*   Check I/O Request Mailbox                                         */
    /***********************************************************************/

    if (addr->req_flags & FLAGS_DON) {	/* I/O Request Mailbox */
#ifdef DDA_MSGQ
	dda_mqstr("(rx)");
#endif

	addr->req_flags &= ~FLAGS_DON;

	/* dequeue old request by copying link to queue head */
	/* and start next I/O request if queue is not empty */

	if (ds->dda_sioq.sq_head = ds->dda_sioq.sq_head->hc_next)
	    start_chn(ds);
    }
    /***********************************************************************/
    /*   Check I/O Completion Mailbox                                      */
    /***********************************************************************/

    if (addr->cmp_flags & FLAGS_RDY) {	/* I/O Completion Mailbox */
#ifdef DDA_MSGQ
	dda_mqstr("(cx)");
#endif

	/*
	 * Get logical channel information. 
	 */
	if ((chan = addr->cmp_chan) > nddach[unit]) {
	    DMESG(unit, 6,
	     (DDALOG(LOG_ERR) "dda%d: unknown completion channel, lcn=%d\n",
	      unit, chan DDAELOG));
	    return;
	}
	if (addr->cmp_flags & FLAGS_DIR)
	    hc = &(ds->dda_cb[chan].dc_wchan);	/* write channel */
	else
	    hc = &(ds->dda_cb[chan].dc_rchan);	/* read channel */

	cc = addr->cmp_stat;	/* Mailbox I/O completion status */
	subcc = addr->cmp_sbst;	/* Mailbox I/O completion substatus */
	cnt = addr->cmp_cnt;	/* Mailbox I/O completion byte count */

#ifdef	mips
	/* if it was a read completion, invalidate the mbuf data portion */
	/* NOTE: the direction check has been commented out because it 
	   seemed like we got it backwards and forgot to check it later.
	   This is now believed to be correct, but is left commented out until
	   a beta site checks it for us. */
	/* if (!(hc->hc_chan & 1)) */ {
		int phys_begin = svtophy(mtod(hc->hc_cur, u_char *));
		clean_dcache(PHYS_TO_K0(phys_begin), cnt);
	}
#endif

	switch (cc) { 	/* check for unsuccessful I/O completion status */
	  case DDAIOCABT:	/* probably VCN flush */
	    if (LOG_ABT)
		DDALOG(LOG_ERR) "dda%d: abort completed on chan %d\n",
		    unit, hc->hc_chan DDAELOG;
	    break;

	  case DDAIOCERR:
	    DMESG(unit, 7,
		  (DDALOG(LOG_ERR) "dda%d: program error ", unit DDAELOG));
	    goto daterr;

	  case DDAIOCOVR:
	    DMESG(unit, 8,
		  (DDALOG(LOG_ERR) "dda%d: overrun error ", unit DDAELOG));
	    goto daterr;

	  case DDAIOCUBE:
	    DMESG(unit, 9,
	      (DDALOG(LOG_ERR) "dda%d: transfer count = 0 ", unit DDAELOG));
	    goto daterr;

	  case DDAIODMAE:
	    DMESG(unit, 10,
		  (DDALOG(LOG_ERR) "dda%d: DMA completion error (%x) ",
		   unit, addr->cmp_sbst DDAELOG));
	    goto daterr;

	  case DDAIOLCOL:
	    DMESG(unit, 11,
		(DDALOG(LOG_ERR) "dda%d: listen collision ", unit DDAELOG));
	    goto daterr;

	  case DDAIOFUNC:
	    DMESG(unit, 12,
		(DDALOG(LOG_ERR) "dda%d: invalid function ", unit DDAELOG));
	    goto daterr;

	  case DDAIODPN:
	    DMESG(unit, 13,
		  (DDALOG(LOG_ERR) "dda%d: invalid dpn ", unit DDAELOG));
	    goto daterr;

    daterr:
	    DMESG(unit, 14,
	    (DDALOG(LOG_ERR) "lcn=%d func=%x\n", chan, hc->hc_func DDAELOG));
	    if (hc->hc_func & DDARDB)
		ds->dda_if.if_ierrors++;
	    else
		ds->dda_if.if_oerrors++;
	}

	/* was it supervisor or data traffic? */

	if (chan > DDA_SUPR) {
#ifdef DDA_PADOPT
	    if (ds->dda_cb[chan].dc_flags & DC_X29)
		x29_data(ds, hc, cc, cnt, subcc);
	    else
#endif
#ifdef DDA_RAWOPT
	    if (ds->dda_cb[chan].dc_flags & DC_RAW)
		pi_data(ds, hc, cc, cnt, subcc);
	    else
#endif
		dda_data(ds, hc, cc, cnt);
	} else
	    dda_supr(ds, hc, cc, cnt);

	/*
	 * Ack the interrupt.  Fix the Mailbox Ready and Done bits:  set DON
	 * bits, and clear RDY bits so mailbox may be reused. 
	 */
	addr->cmp_flags = (addr->cmp_flags & ~FLAGS_RDY) | FLAGS_DON;
	addr->csr |= CSR_INTRA;	/* enable interrupt "a" */
    }
}



/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAINTB()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*   Service interrupt "b", system interrupt, from the ACP device. */
/*   If the ACP device is operational, interrupt is unexpected,    */
/*   disable the device.  If the interrupt indicates a powerup     */
/*   diagnostic failure, disable the device.  Otherwise, set ACP   */
/*   flag for device operational, enable interrupt a, enable DMA,  */
/*   and perform initialization tasks.                             */
/*                                                                 */
/*  Call:             ddaintb(unit)                                */
/*  Argument:         unit: DDA device unit number                 */
/*  Returns:          nothing                                      */
/*  Called by:        network software, address of this routine is */
/*                    defined in af_inet network interface struct  */
/*  Calls to:         DDALOG()                                     */
/*                    ddainit()                                    */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

ddaintb(unit)
int             unit;
{
    volatile struct ddaregs *addr;
    struct uba_device *ui;
    register struct dda_softc *ds = &dda_softc[unit];
    register struct dda_cb *dc;
    register u_char diag_stat;
    register int    lcn;
    register struct mbuf *m;
    register struct hdx_chan *hc;

    ui = (struct uba_device *) ddainfo[unit];
    addr = (struct ddaregs *) ui->ui_addr;
#ifdef DDADEBUG
    if (DDADBCH(6, unit)) {
	DDALOG(LOG_DEBUG) "dda%d: ddaintb()\n", unit DDAELOG;
    }
#endif DDADEBUG

    if (ds->dda_flags & DDAF_OK) {

	/*
	 * Change to rev2.0 and above driver: attempt to handle an unexpected
	 * B interrupt sanely by reinitializing the world. We turn off all
	 * the flavors of UP and OK flags and call ddareset().  This will
	 * restart the diagnostics and enable interrupt B.  After the
	 * diagnostics run, we'll come back here to the B interrupt handler
	 * and with DDAF_OK off will enter the "else" below.  With luck the
	 * board will report success and we'll call ddainit() to bring the
	 * link back on line. 
	 */

	DMESG(unit, 15,
	      (DDALOG(LOG_ERR) "dda%d: asynchronous restart, status = %d\n",
	       unit, addr->sys_stat DDAELOG));
	ds->dda_flags = 0;
	ds->dda_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
	hist_link_state(unit, ds->dda_state, S_DISABLED);
	ds->dda_state = S_DISABLED;
	ddareset(unit, ui->ui_ubanum);
    } else {
	diag_stat = addr->sys_stat;
	if ((diag_stat & DDASTAT_ERR) != 0) {
	    DMESG(unit, 16,
		  (DDALOG(LOG_ERR) "dda%d: Diagnostic failure = %d\n",
		   unit, addr->sys_stat DDAELOG));
	    addr->csr = 0;
	} else if (diag_stat == DDASTAT_NMC) {
	    DMESG(unit, 17,
		  (DDALOG(LOG_ERR) "dda%d: No Microcode Present!\n", unit DDAELOG));
	    addr->csr = 0;
	} else if (diag_stat == DDASTAT_OK) {

/*
 * For each lcn, clear the output queue and free hdx read and write mbufs
 */
	    for (lcn = 0; lcn <= NDDACH; lcn++) {	/* for all LCNs */
		dc = &dda_softc[unit].dda_cb[lcn];
		while (dc->dc_oq.ifq_len) {
		    IF_DEQUEUE(&dc->dc_oq, m);
		    if (m)
			m_freem(m);
		}

		hc = &dc->dc_rchan;
		if (hc->hc_mbuf) {
		    m_freem(hc->hc_mbuf);
		    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
		}
		hc = &dc->dc_wchan;
		if (hc->hc_mbuf) {
		    m_freem(hc->hc_mbuf);
		    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
		}
	    }
	    ds->dda_flags |= DDAF_OK;
	    addr->csr |= (CSR_IENA | CSR_DMAEN);
	    ddainit(unit);
	}
    }
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDA_WRQ()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    Process write requests.  Put I/O request values in           */
/*    half-duplex control channel structure:  set function code    */
/*    for write to ACP with Transfer Grant set.  If there are no   */
/*    more mbufs in chain, mark DDAEOS for end of stream.  Set     */
/*    count from data length (byte count) in mbuf, and subfunction */
/*    is zero.  If the COMREGs are busy, queue for start later.    */
/*                                                                 */
/*  Call:            dda_wrq(ds, hc, abt)                          */
/*  Argument:        ds:   pointer to device control block struct  */
/*                   hc:   pointer to half-duplex channel cntl blk */
/*		     abt:  indication of whether request is an     */
/*			   abort request.			   */
/*  Returns:         nothing                                       */
/*  Called by:         dda_start()                                 */
/*                     dda_data()                                  */
/*  Calls to:          mtod()                                      */
/*                     start_chn()                                 */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
dda_wrq(ds, hc, abt)
struct dda_softc *ds;
register struct hdx_chan *hc;
u_char          abt;
{
    register struct mbuf *m;
    register int    s;


    /* set channel info */
    s = splimp();
    m = hc->hc_curr;
    hc->hc_addr = mtod(m, u_char *);	/* point to data in mbuf */
    hc->hc_cnt  = m->m_len;		/* byte count            */
    splx(s);

    /* set Transfer Request Write, mark DDAEOS if end of stream */
    /* note: non-unibus products ignore the address invalid (DDAXFR) bit */

    if (abt)
	hc->hc_func = DDAABT;
    else if (m->m_next == 0)
	hc->hc_func = DDAWRT + DDAEOS + DDAXFR;
    else
	hc->hc_func = DDAWRT + DDASTR + DDAXFR;

#ifdef DDADEBUG
    if (DDADBCH(15, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: dda_wrq: chan=%d func=%x\n",
	    ds->dda_if.if_unit, hc->hc_chan, hc->hc_func DDAELOG;
    }
#endif DDADEBUG

    s = splimp();

    /*
     * If ACP comm regs busy, queue start i/o for later. 
     */
    if (ds->dda_sioq.sq_head) {
	(ds->dda_sioq.sq_tail)->hc_next = hc;
	ds->dda_sioq.sq_tail = hc;
	hc->hc_next = 0;
	splx(s);
	return;
    }
    /* start i/o on channel now */

    ds->dda_sioq.sq_head = hc;
    ds->dda_sioq.sq_tail = hc;
    hc->hc_next = 0;
    splx(s);
    (void) start_chn(ds);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDA_RRQ()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    Process read requests.  Quit if attempt to get an mbuf is    */
/*    unsuccessful.  Put I/O request values in half-duplex control */
/*    channel structure:  set function code for read from ACP with */
/*    Transfer Grant set, set count from data length (byte count)  */
/*    in mbuf, and subfunction is zero.                            */
/*                                                                 */
/*  Call:            dda_rrq(ds, hc)                               */
/*  Argument:        ds:   pointer to device control block struct  */
/*                   hc:   pointer to half-duplex control chan     */
/*  Returns:         nothing                                       */
/*  Called by:       ddainit()                                     */
/*                   dda_data()                                    */
/*                   dda_supr()                                    */
/*  Calls to:        MGET()                                        */
/*                   DDALOG()                                      */
/*                   start_chn()                                   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
dda_rrq(ds, hc)
struct dda_softc *ds;
register struct hdx_chan *hc;
{
    register struct mbuf *m;
    register int    s;

#ifdef DDADEBUG
    if (DDADBCH(16, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: dda_rrq()\n", ds->dda_if.if_unit DDAELOG;
    }
#endif DDADEBUG

    MGET(m, M_DONTWAIT, MT_DATA);
    if (m == 0) {
	DMESG(ds->dda_if.if_unit, 25,
	      (DDALOG(LOG_ERR) "dda%d:  couldn't get buffer for read\n",
	       ds->dda_if.if_unit DDAELOG));
	return;
    }
    m->m_len = 0;		/* new mbuf, doesn't contain data yet */

    /* hc_mbuf set to zero during initialization */

    s = splimp();
    if (hc->hc_mbuf == 0) {
	hc->hc_mbuf = m;
	hc->hc_curr = m;
    } else {
	hc->hc_curr->m_next = m;
	hc->hc_curr = m;
	m->m_next = 0;
    }
    splx(s);

    hc->hc_func = DDARDB + DDASTR + DDAXFR;
    hc->hc_sbfc = 0;

    /* hc_cnt determines size of read request, addr->req_cnt */
    /* MLEN is 112 bytes, the data portion of a small mbuf   */

    hc->hc_cnt = MLEN;		/* just got the mbuf */

    hc->hc_addr = mtod(m, u_char *);	/* point to mbuf data */

    s = splimp();

    /*
     * If ACP comm regs busy, queue start i/o for later. 
     */
    if (ds->dda_sioq.sq_head) {
	(ds->dda_sioq.sq_tail)->hc_next = hc;
	ds->dda_sioq.sq_tail = hc;
	hc->hc_next = 0;
	splx(s);
	return;
    }
    /* start i/o on channel now */

    ds->dda_sioq.sq_head = hc;
    ds->dda_sioq.sq_tail = hc;
    hc->hc_next = 0;
    splx(s);
    (void) start_chn(ds);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      START_CHN()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine copies ACP I/O requests into the ACP            */
/*    Communications Registers (COMREGs) and notifies the ACP.     */
/*    If the channel number is odd, indicating write, then the     */
/*    direction flag is set to indicate a transfer from the host   */
/*    to the front end.                                            */
/*                                                                 */
/*  Call:              start_chn(ds)                               */
/*  Argument:          ds:  pointer to device control block struct */
/*  Returns:           nothing                                     */
/*  Called by:         ddainta()                                   */
/*                     dda_rrq()                                   */
/*                     dda_wrq()                                   */
/*  Calls to:          none                                        */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
start_chn(ds)
struct dda_softc *ds;
{
    volatile struct ddaregs *addr;
    register struct hdx_chan *hc;
    register int    s;
    struct uba_device *ui;

    ui = (struct uba_device *) (ddainfo[ds->dda_if.if_unit]);
    addr = (struct ddaregs *) ui->ui_addr;

#ifdef DDADEBUG
    if (DDADBCH(17, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: start_chn()\n", ds->dda_if.if_unit DDAELOG;
    }
#endif DDADEBUG

/*
 *  Scan sioq for invalid writes
 */

    s = splimp();

    hc = (struct hdx_chan *) (ds->dda_sioq.sq_head);
    if (hc && ((addr->req_flags & FLAGS_RDY) == 0)) {
	for (; hc; hc = hc->hc_next) {	/* scan sioq */
	    if ((hc->hc_chan & 0x01) && (hc->hc_chan != 1)
		&& (hc->hc_inv & INVALID_MBUF)) {
		if (ds->dda_cb[hc->hc_chan >> 1].dc_flags & DC_OBUSY)
		    goto send;	/* send an abort */
		else {
		    if (hc->hc_mbuf) {	/* free pending request */
			m_freem(hc->hc_mbuf);
			hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
			hc->hc_inv &= ~INVALID_MBUF;
			ds->dda_sioq.sq_head = ds->dda_sioq.sq_head->hc_next;
		    }
/*
 * Restart Output
 */

		    /*
		     * not needed ds->dda_cb[hc->hc_chan>>1].dc_flags &=
		     * ~DC_OBUSY; 
		     */

		    /*
		     * this should be changed to drop ipl before calling
		     * dda_start 
		     */
		    dda_start(ds, &ds->dda_cb[hc->hc_chan >> 1]);
		}
	    } else {		/* Read or Write request is valid */

		/*
		 * Set up comm regs. 
		 */


	send:
		addr->req_chan = hc->hc_chan >> 1;
		addr->req_cnt = hc->hc_cnt;
		addr->req_fcn = hc->hc_func;
		addr->req_sbf = hc->hc_sbfc;

		if (hc->hc_chan & 1) {	/* write */
#ifdef DDADEBUG
		    struct dda_cb  *dc = &ds->dda_cb[hc->hc_chan >> 1];

		    if (DDADBCH(17, ds->dda_if.if_unit))
			DDALOG(LOG_DEBUG)
			    "dda%d: start_chn: WRITE on lcn %d func %x\n",
			    ds->dda_if.if_unit, hc->hc_chan >> 1,
			    hc->hc_func DDAELOG;

		    if (dc->dc_lcn)	/* don't start timer on lcn 0 */
			dc->dc_out_t = TMO_RESTART;	/* Wait 90 sec for
							 * completion */
#endif
		    addr->req_flags = FLAGS_RDY | FLAGS_DIR;
		} else
		    addr->req_flags = FLAGS_RDY;
#ifdef DDADEBUG
		if (DDADBCH(28, ds->dda_if.if_unit)) {
		    if (hc->hc_func == DDAABT)
			DDALOG(LOG_DEBUG) "dda%d: start_chn: aborting chan %d\n",
			    ds->dda_if.if_unit, hc->hc_chan DDAELOG;
		}
#  ifdef DDA_MSGQ
		dda_mqstr("(sc ");
		dda_mqnum(hc->hc_chan >> 1, MQHEX);
		dda_mqstr(" ");
		dda_mqnum(addr->req_flags, MQHEX);
		dda_mqstr(" ");
		dda_mqnum(hc->hc_cnt, MQHEX);
		dda_mqstr(" ");
		dda_mqnum(hc->hc_func, MQHEX);
		dda_mqstr(" ");
		dda_mqstr(")");
#  endif DDA_MSGQ
#endif DDADEBUG
		addr->csr |= CSR_INTRA;	/* interrupt FE */
		splx(s);
		return;
	    }
	}
    }
    splx(s);
    return (1);			/* no valid requests found */
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      BUFRESET()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*      Reset of interface after UNIBUS reset.  If interface is on */
/*      specified uba, reset its state.  Free mbufs if there is    */
/*      queued output data.                                        */
/*                                                                 */
/*  Call:              bufreset(unit)                              */
/*  Arguments:         unit:   ACP device unit number              */
/*  Returns:           nothing                                     */
/*  Called by:         network software, address of routine is     */
/*                     defined in dda_if network interface struct  */
/*  Calls to:          DDALOG()                                    */
/*                     IF_DEQUEUE()                                */
/*                     m_freem()                                   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
bufreset(unit)
int             unit;
{
    volatile struct ddaregs *addr;
    register struct uba_device *ui;
    register struct dda_cb *dc;
    register struct dda_softc *ds = &dda_softc[unit];
    register int    lcn;

    ui = (struct uba_device *) ddainfo[unit];
    if (unit >= NDDA || (ui == 0 || ui->ui_alive == 0))
	return;

    DMESG(unit, 38, (DDALOG(LOG_ERR) "dda%d: buffer size reset\n", unit DDAELOG));

    ds->dda_if.if_flags &= ~IFF_UP;
    hist_link_state(unit, ds->dda_state, S_DISABLED);
    ds->dda_state = S_DISABLED;

    addr = (struct ddaregs *) ui->ui_addr;

    addr->cmp_flags = 0;	/* I/O completion flags         */
    addr->xfr_flags = 0;	/* transfer request/grant flags */
    addr->req_chan = 0;		/* FDX channel number           */
    addr->req_adx = 0;		/* address bits 17-16           */
    addr->req_addr = 0;		/* address bits 15-00           */
    addr->req_cnt = 0;		/* byte count                   */
    addr->req_fcn = 0;		/* I/O function                 */
    addr->req_sbf = 0;		/* I/O subfunction              */
    addr->cmp_chan = 0;		/* FDX channel number           */
    addr->cmp_unused = 0;	/* address bits 17-16           */
    addr->cmp_cnt = 0;		/* byte count                   */
    addr->cmp_stat = 0;		/* I/O status                   */
    addr->cmp_sbst = 0;		/* I/O substatus                */
    addr->xfr_chan = 0;		/* FDX channel number           */
    addr->xfr_adx = 0;		/* address bits 17-16           */
    addr->xfr_addr = 0;		/* address bits 15-00           */
    addr->xfr_cnt = 0;		/* byte count                   */
    addr->sys_stat = 0;		/* system status                */
    addr->req_flags = 0;	/* clear handshake flags, mailbox     */

    /* pass interrupt vector to ACP */
    addr->sys_vect = dda_softc[unit].dda_vector >> 2;

    dda_softc[unit].dda_flags = 0;	/* clear ACP operational flag  */

    ds->dda_init &= ~DDA_INTCLOCK;	/* reset internal-clocking-set bit */
    nddach[unit] = NDDACH_DEFAULT;	/* reset SVC limit */
    /* LCNLINK */
    dc = dda_softc[unit].dda_cb;/* flush any queued output data */
    for (lcn = 0; lcn <= NDDACH; lcn++) {	/* for all LCN's ... */
	dc->dc_inaddr.s_addr = 0;	/* clear remote internet addr */
	dc->dc_key.key_addr.s_addr = 0;
	dc->dc_wsizein = dc->dc_wsizeout = 0;
	dc->dc_pktsizein = dc->dc_pktsizeout = 0;
	dc->dc_state = LC_IDLE;	/* init LCN state */
	dc->dc_timer = TMO_OFF;	/* turn LCN timer off */
	dc->dc_flags = 0;
#ifdef DDADEBUG
	dc->dc_out_t = TMO_OFF;	/* turn FE completion timer off */
#endif
	dc++;
    }
    hist_all_lcns(unit, LC_IDLE);
#ifdef DDA_RAWOPT
    pi_init(unit, 1);
#endif
#ifdef DDA_PADOPT
    x29_init(unit, 1);
#endif
    addr->csr |= CSR_IENB;
    wbflush();				/* flush write pipeline cache */
}

/*
 * Disable all interrrupts and forget about board
 */

PRIVATE void
dda_disable(unit)
{
    volatile struct ddaregs *addr;
    register struct uba_device *ui;

    ui = (struct uba_device *) ddainfo[unit];
    addr = (struct ddaregs *) (ui->ui_addr);
    addr->csr = 0;
    wbflush();				/* flush write pipeline cache */
}

/*

Revision History:

13-Jul-1989: PST	Used PRIVATE convention on functions
01-Aug-1989: PST	Added initialization of ddamsgs in attach routine.
01-Sep-1989: PST	Ignore uban parameter to ddareset.
14-Nov-1989: PST	Added support for MIPS PMAX architecture and
			fixed a QBUS bug (we were not using all 22 addr bits).
			Moved rrq and wrq routines here.
30-Nov-1989: PST	Moved unibus defines here.
01-Dec-1989: PST	Fixed typos.
*/
Returns:           nothing             driver/if_ddareg.h   444    540     24       16373  4535335127   7513 /*************************************************************************/
/*									 */
/*									 */
/*	 ________________________________________________________	 */
/*	/							 \	 */
/*     |	  AAA	       CCCCCCCCCCCCCC	 CCCCCCCCCCCCCC	  |	 */
/*     |	 AAAAA	      CCCCCCCCCCCCCCCC	CCCCCCCCCCCCCCCC  |	 */
/*     |	AAAAAAA	      CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |	 */
/*     |       AAAA AAAA      CCCC		CCCC		  |	 */
/*     |      AAAA   AAAA     CCCC		CCCC		  |	 */
/*     |     AAAA     AAAA    CCCC		CCCC		  |	 */
/*     |    AAAA       AAAA   CCCC		CCCC		  |	 */
/*     |   AAAA	 AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |	 */
/*     |  AAAA	  AAAAAAAAAAA CCCCCCCCCCCCCCCC	CCCCCCCCCCCCCCCC  |	 */
/*     | AAAA	   AAAAAAAAA   CCCCCCCCCCCCCC	 CCCCCCCCCCCCCC	  |	 */
/*	\________________________________________________________/	 */
/*									 */
/*	Copyright (c) 1986 by Advanced Computer Communications		 */
/*	720 Santa Barbara Street, Santa Barbara, California  93101	 */
/*	(805) 963-9431							 */
/*									 */
/*									 */
/*  File:		ddareg.h					 */
/*			When this file is copied to the /sys/vaxif	 */
/*			directory, it is renamed 'if_ddareg.h'.		 */
/*									 */
/*  Project:		UNIX 4.n BSD DDA-X.25 Network Interface Driver	 */
/*			for ACP 5250 and ACP 6250			 */
/*									 */
/*  Function:		This file contains definitions of the hardware	 */
/*			interface of the ACP 5250/6250.			 */
/*									 */
/*  Revision History at end of file					 */
/*									 */
/*************************************************************************/


/* ACP device Communication Register layout */


#ifdef SIMULATION
/* device registers */
struct ddaregs {
	u_short	csr;			/* control and status register */
					/* I/O request mailbox */
	u_short req_chan;		/*   FDX channel number */
	u_char	req_flags;		/*   mailbox flags */
	u_char	req_adx;		/*   address bits 17-16 */
	u_short req_addr;		/*   address bits 15-00 */
	u_short req_cnt;		/*   byte count */
	u_char	req_sbf;		/*   I/O subfunction */
	u_char	req_fcn;		/*   I/O function */
					/* I/O completion mailbox */
	u_short cmp_chan;		/*   FDX channel number */
	u_char	cmp_flags;		/*   mailbox flags */
	u_char	cmp_unused;		/*   address bits 17-16 */
	u_short cmp_cnt;		/*   byte count */
	u_char	cmp_sbst;		/*   I/O substatus */
	u_char	cmp_stat;		/*   I/O status */
					/* Transfer request mailbox */
	u_short xfr_chan;		/*   FDX channel number */
	u_char	xfr_flags;		/*   mailbox flags */
	u_char	xfr_adx;		/*   address bits 17-16 */
	u_short xfr_addr;		/*   address bits 15-00 */
	u_short xfr_cnt;		/*   byte count */
					/* System status mailbox */
	u_char	sys_id;			/*   system identification */
	u_char	sys_vers;		/*   system version number */
	u_char  sys_stat;		/*   system status */
	u_char	sys_vect;		/*   interrupt vector base */
};
#else
/* device registers */
struct ddaregs {
	u_short	csr;			/* control and status register */
					/* I/O request mailbox */
	u_short req_chan;		/*   FDX channel number */
	u_char	req_adx;		/*   address bits 17-16 */
	u_char	req_flags;		/*   mailbox flags */
	u_short req_addr;		/*   address bits 15-00 */
	u_short req_cnt;		/*   byte count */
	u_char	req_fcn;		/*   I/O function */
	u_char	req_sbf;		/*   I/O subfunction */
					/* I/O completion mailbox */
	u_short cmp_chan;		/*   FDX channel number */
	u_char	cmp_unused;		/*   address bits 17-16 */
	u_char	cmp_flags;		/*   mailbox flags */
	u_short cmp_cnt;		/*   byte count */
	u_char	cmp_stat;		/*   I/O status */
	u_char	cmp_sbst;		/*   I/O substatus */
					/* Transfer request mailbox */
	u_short xfr_chan;		/*   FDX channel number */
	u_char	xfr_adx;		/*   address bits 17-16 */
	u_char	xfr_flags;		/*   mailbox flags */
	u_short xfr_addr;		/*   address bits 15-00 */
	u_short xfr_cnt;		/*   byte count */
					/* System status mailbox */
	u_char	sys_vers;		/*   system version number */
	u_char	sys_id;			/*   system identification */
	u_char	sys_vect;		/*   interrupt vector base */
	u_char  sys_stat;		/*   system status */
};
#endif

/* defines for CSR */

#define	CSR_BIT15	0x8000
#define	CSR_BIT14	0x4000
#define	CSR_MAINT	0x2000
#define	CSR_HALT	0x1000
#define	CSR_IBPEND	0x0800
#define	CSR_IAPEND	0x0400
#define	CSR_IBREQ	0x0200
#define	CSR_IAREQ	0x0100
#define	CSR_BIT7	0x0080
#define	CSR_BIT6	0x0040
#define	CSR_INTRB	0x0020		/* ACP CPU Interrupt A Request */
#define	CSR_INTRA	0x0010		/* ACP CPU Interrupt B Request */
#define	CSR_IENB	0x0008		/* enable UNIBUS interrupt b   */
#define	CSR_IENA	0x0004		/* enable UNIBUS interrupt a   */
#define	CSR_DMAEN	0x0002
#define	CSR_RESET	0x0001

/* mailbox handshake flags, these flags are used with the req_flags, */
/* cmp_flags, and xfr_flags to indicate current state of events      */

#define FLAGS_RDY	0x80		/* indicates ready */
#define FLAGS_DON	0x40		/* indicates done */
#define FLAGS_DIR	0x20		/* indicates write (host to ACP) */

/* I/O request function code definitions */

#define DDARDB		0x01		/* read from ACP */
#define DDAWRT		0x02		/* write to ACP */
#define DDASTR		0x10		/* stream flag */
#define DDAEOS		(0x20|DDASTR)	/* end of stream flag */

#define DDAABT		0x04		/* abort flag */
#define DDAXFR		0x40		/* indicates transfer request   */
				/* The UNIBUS address in req_addr is    */
				/* invalid.  The ACP device must issue  */
				/* a Transfer Request in order to       */
				/* obtain the UNIBUS address            */
#define DDASWP		0x80		/* Swap host high/low bytes     */
				/* The ACP device views UNIBUS memory   */
				/* as if it were MC68000 memory:  the   */
				/* MS byte of a word is even-addressed  */
				/* byte and the LS byte is the odd-     */
				/* addressed byte.                      */

#define FCN_MASK	0x07

#define DDA_BITS \
"\10\20UER\17NXM\16PER\15ZRUN\14ZGO\10MBLK\7SRV\6MAIN\5DMA\4WRT\3IEN\2RST\1NMI"

/*  Host Request Mailbox Completion Status  */

#define DDAIOCOK	0x01	/* successful completion */
#define DDAIOCOKP 	0x02	/* successful completion, more data pending */
#define DDAIOCABT 	0xff	/* i/o aborted */
#define DDAIOCERR 	0xfe	/* program error */
#define DDAIOCOVR 	0xfd	/* overrun error */
#define DDAIOCUBE 	0xfc	/* Transfer count = 0 */
				/* Either program error (byte count on I/O   */
				/* request equals 0) or driver error (driver */
				/* granted byte count = 0 in response to     */
				/* Transfer Request)                         */
#define DDAIODMAE 	0xfb	/* DMA completion error:  Completion sub-    */
				/* status equals error bits from the ACP DMA */
				/* status register                           */
#define DDAIOLCOL 	0xf9	/* Listen Collision:  Both sides of a DPN in */
				/* the same direction have Listen requests   */
				/* pending.  Both requests are terminated    */
				/* with this status code.                    */
#define DDAIOFUNC 	0xf8	/* Invalid function:  The function specified */
				/* in a request is invalid.                  */
#define DDAIODPN  	0xf7	/* Invalid DPN:  The DPN specified in a      */
				/* request is out of the range handled by    */
				/* CRI (Communications Register Interface)   */

#ifdef	__GNU__
#define ddaregs ddaregs volatile
#endif	__GNU__

/*
Revision History:

26-Mar-1986: V1.0 Clair Russ
		First generated.
??-???-1988: V1.1 Steve Johnson
		Added code for simulation
19-Mar-1989: V4.3.1 Paul Traina
		Added Multinet / GCC support for structure padding
28-May-1989: V4.3.6 Paul Traina
		Cosmetic changes only
*/
X channel number */
	u_char	cmp_unused;		/*   address bits 17-16 */
	u_char	cmp_flags;		/*   mailbox flags */
	u_short cmp_cnt;		/*   byte count */
	u_char	cmp_stat;		/*   I/O status */
	u_char	cmp_sbst;		/*   I/O substatus */
					/* Transfer request mailbox *driver/if_ddavar.h   444    540     24       61453  4535335126   7524 /*************************************************************************/
/*									 */
/*									 */
/*	 ________________________________________________________	 */
/*	/							 \	 */
/*     |	  AAA	       CCCCCCCCCCCCCC	 CCCCCCCCCCCCCC	  |	 */
/*     |	 AAAAA	      CCCCCCCCCCCCCCCC	CCCCCCCCCCCCCCCC  |	 */
/*     |	AAAAAAA	      CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |	 */
/*     |       AAAA AAAA      CCCC		CCCC		  |	 */
/*     |      AAAA   AAAA     CCCC		CCCC		  |	 */
/*     |     AAAA     AAAA    CCCC		CCCC		  |	 */
/*     |    AAAA       AAAA   CCCC		CCCC		  |	 */
/*     |   AAAA	 AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |	 */
/*     |  AAAA	  AAAAAAAAAAA CCCCCCCCCCCCCCCC	CCCCCCCCCCCCCCCC  |	 */
/*     | AAAA	   AAAAAAAAA   CCCCCCCCCCCCCC	 CCCCCCCCCCCCCC	  |	 */
/*	\________________________________________________________/	 */
/*									 */
/*	Copyright (c) 1986 by Advanced Computer Communications		 */
/*	720 Santa Barbara Street, Santa Barbara, California  93101	 */
/*	(805) 963-9431							 */
/*									 */
/*									 */
/*  File:		ddavar.h					 */
/*			When this file is copied to the /sys/vaxif	 */
/*			directory, it is renamed 'if_ddavar.h'.		 */
/*									 */
/*  Project:		UNIX 4.n BSD DDA-X.25 Network Interface Driver	 */
/*			for ACP 5250 and ACP 6250			 */
/*									 */
/*  Function:		This file contains definitions used to control	 */
/*			and track the status of the ACP 5250/6250.	 */
/*			The values for the Set System Parameter Messages */
/*			are located here.  Note that the values are the	 */
/*			same as those for ACP 625 for compatibility.	 */
/*									 */
/*  Revision History at end of file					 */
/*************************************************************************/


#if defined(DDA_PADOPT) || defined(DDA_RAWOPT)
#	define DDA_PAD_OR_RAW
#endif

#define NDDACH		126		/* maximum number of channels */
#define NDDACH_DEFAULT	64		/* default number of channels */

/* path numbers (also called logical channel numbers, or lcns) */
#define DDA_SUPR	0x00		/* supervisory path for control */
#define ALL_CHANS	0xff		/* denotes all LCNs */

/* the following 2 defines (MAXADDRLEN and MINADDRLEN) give the max and min
 * length X25 addresses.  These numbers include 1 byte to hold the length
 * of the address.  Thus the normal value of MAXADDRLEN is 15, for 14-digit
 * X.25 addresses; MAXADDRLEN can never be greater than 16 (15-digit addr).
 */
#define MAXADDRLEN	16		/* max length of an X.25 address */
#define MINADDRLEN	1		/* min length of an X.25 address */

#define TRANSPAC	1		/* network type for TRANSPAC (def=0) */

#define DDAMTU		1006		/* maximum IP msg length */
#define DDA_OQMAX	8		/* max IP msgs on LCN output q */
#define DDA_TIMEOUT	10		/* dda timer interval, in seconds */

/* definitions for the DC_FLAGS byte of the dda_cb structure */
/* bits in dc_flags */
#define DC_OBUSY	0x01
#define	DC_CLIENTS	0x0e
#define	DC_IP		0x00	/* this channel is being used for IP traffic */
#define DC_X29		0x02	/* this channel is being used for X29 traffic */
#define DC_X29W		0x04	/* this channel waiting for x29 connection */
#define DC_RAW		0x08	/* this is a raw X25 channel */
#define DC_IPEND	0x10	/* input pending */

/* bits in hc_sbfc (which is the wrong place) */
#define INVALID_MBUF	0x01		/* Used to clear outstanding I/O */

typedef unsigned char	byte;
typedef int		boolean;

/* X25 LCN state definitions (in dc_state) */
#define LC_DOWN		   0		/* X25 circuit down		*/
#define LC_RESTART	   1		/* X25 circuit restarting	*/
#define LC_IDLE		   2		/* X25 circuit not in use	*/
#define LC_CALL_PENDING	   3		/* X25 circuit call pending	*/
#define LC_DATA_IDLE	   4		/* X25 circuit open		*/
#define LC_CLR_PENDING	   5		/* X25 circuit clear pending	*/

/* Timeout definitions (in dc_timer and dc_out_t) */
#define TMO_OFF		   0			/* timer off		*/
#define TMO_RESTART	 ( 90/DDA_TIMEOUT)	/* restart timeout	*/
#define TMO_CALL_PENDING (180/DDA_TIMEOUT)	/* call timeout		*/
#define TMO_DATA_IDLE	 (600/DDA_TIMEOUT)	/* idle circuit timeout */
#define TMO_CLR_PENDING	 (380/DDA_TIMEOUT)	/* clear timeout	*/

/* Link status codes (third byte of LINE_STATUS message) */
#define LINK_DOWN	0x00		/* Link layer is down		*/
#define LINK_UP		0x01		/* Link layer is up		*/
#define LINK_DISABLED	0x02		/* Link layer is disabled	*/

/* The following parameter modification commands such as BAUD_CNTL,  */
/* or WATCHDOG, are one-byte values containing size and ID followed  */
/* by 0-2 bytes of parameter information.  The number of bytes of    */
/* parameter information is specified in the most significant 2 bits */
/* of the command, the other 6 bits are the ID.	 A 00, 01, or 10     */
/* specify respectively 0, 1, or 2 bytes of parameter information    */
/* follow.							     */ 

/* Line control codes (in body of LINE_CNTL message) */
#define LINK_DISABLE	0x00		/* Disable link layer		*/
#define LINK_ENABLE	0x01		/* Enable link layer		*/
#define LINK_LOOPBACK	0x42		/* Link layer loopback mode	*/
#define	  LOOP_NONE	0x00		/*   Loopback off		*/
#define	  LOOP_EXTERNAL 0x01		/*   Loopback external		*/
#define	  LOOP_INTERNAL 0x03		/*   Loopback internal		*/
#define DTE_DCE_MODE	0x43		/* DTE/DCE Mode Parameter	*/
#define	  DTE		0x00		/*   operate as DTE		*/
#define	  DCE		0x01		/*   operate as DCE		*/
#define DTE_ADDRESS	0x44		/* DTE Address Parameter	*/
#define	  DTE_ADRVAL	0x03		/*   DTE Address value		*/
#define DCE_ADDRESS	0x45		/* DCE Address Parameter	*/
#define	  DCE_ADRVAL	0x01		/*   DCE Address value		*/
#define IFRAME_TIMEO	0x46		/* I-Frame Timeout Parameter	*/
#define	  IFRAME_TOVAL	0x03		/*   I-Frame Timeout value 3s	*/
#define POLL_TIMEO	0x47		/* Poll Timeout Parameter	*/
#define	  POLL_TOVAL	0x03		/*   Poll Timeout value 3s	*/
#define ADM_TIMEO	0x48		/* ADM Timeout Parameter	*/
#define	  ADM_TOVAL	0x03		/*   ADM Timeout value 3s	*/
#define RETRY_LIMIT	0x4a		/* Retry Limit Parameter	*/
#define	  RETRY_VAL	0x14		/*   20 (decimal) retries	*/
#define WATCHDOG	0x4b		/* Watchdog Timeout Parameter	*/
#define	  WATCHDG_VAL	0x03		/*   Watchdog Timeout value 3s	*/
#define BAUD_CNTL	0xa9		/* Baud Rate Parameter		*/
#define CLOCK_CNTL	0x6a		/* Select Clock Source		*/
#define	  EXTERNAL_CLOCK  0x00		/* clock generated externally	*/
#define	  INTERNAL_CLOCK  0x01		/* clock generated internally	*/
#define IDLE_POLL	0x4d		/* Idle Poll Parameter		*/
#define	  IDLE_POLL_ON	0x01		/*   Idle Polling on		*/
#define	  IDLE_POLL_OFF 0x00		/*   Idle Polling off		*/
#define FRAME_WINDOW	0x4e		/* Frame Window Parameter	*/
#define	  FWINDW_VAL	0x07		/*   Frame Window value 7	*/
#define PKT_WINDOW	0x4f		/* Packet Window Parameter	*/
#define	  PWINDW_VAL	0x02		/*   Packet Window value 2	*/
#define PKT_SIZE	0x90		/* Packet Size Parameter	*/
#define	  PVAL_BYTE1	0x08		/*   least significant byte	*/
#define	  PVAL_BYTE2	0x00		/*   most significant byte	*/
#define MAX_PKT_SZ	0xbe		/* Max Packet Size		*/
#define MAX_PKT_WN	0x7c		/* Max Packet Window Size	*/
#define PKT_OPTIONS	0x77		/* Supported 1984 options	*/
#define HIGH_THRESH	0x51		/* High Buffer Threshold Param	*/
#define	  HTRSH_VAL	0x08		/*   High Threshold value	*/
#define LOW_THRESH	0x52		/* Low Buffer Threshold Param	*/
#define	  LTRSH_VAL	0x08		/*   Low Threshold value	*/
#define QUEUED_BUFS	0x53		/* Max Number of Queued Buffers */
#define	  QBUF_VAL	0x08		/*   Queued Buffer value	*/
#define QUEUED_IFRAMES	0x54		/* Max Number Queued I-Frames	*/
#define	  QIFRAME_VAL	0x08		/*   Queued I-Frame value	*/
#define FRAME_SIZE	0x95		/* Maximum Frames Size Parametr */
#define	  FRAME_SIZE1	0x95		/*   least significant byte	*/
#define	  FRAME_SIZE2	0x01		/*   most significant byte	*/
					/*   for value of 405 (decimal) */
#define LCGN		0x56		/* Logical Channel Group Number */
#define	  LCGN_VAL	0x00		/*   lcgn value			*/

#define SVC_LIMIT	0x57		/* Switched Virtual Circuit	*/
#define	  SVC_VAL	0x20		/*   SVC value 32 (decimal)	*/

#define DDAF_OK		0x0001		/* ACP operation flag		*/
#define DDASTAT_OK	0x00		/* ACP system status returned	*/
					/* on interrupt "b"		*/
#define DDASTAT_ERR	0x80
#define DDASTAT_NMC	0x7F

/* flag values for ds->dda_init */
#define INIT_OK		0x10		/* ok to call x25_init()	*/
#define DDA_STANDARD	0x01		/* DDN standard X.25 service	*/
#define DDA_BASIC	0x02		/* DDN basic X.25 service	*/
#define DDA_PDN		0x04		/* Public Data Network X.25	*/
#define DDA_INTCLOCK	0x08		/* internal clocking is set	*/
#define DDA_CLASS_A_B	0x20		/* Standard service/table lookup */
#define DDA_PKTNEG	0x40		/* Packet size negotiation flag */
#define DDA_WNDNEG	0x80		/* Window size negotiation flag */

/* the following offsets are for ddainit_msg, the set systems parameters */
/* message								 */

#define LOOP_OFFSET	6	/* set system parms, loopback	  */
#define DTE_OFFSET	8	/* set system parms, dte/dce mode */
#define BAUD_OFFSET	10	/* set system parms, dte/dce mode */
#define CLOCK_OFFSET	18	/* set system parms, dte/dce mode */
#define DOWN_OFFSET	19	/* set system parms, line down	  */

#define MSG_LENGTH	3	/* offset for message length	  */
#define MSG_OFFSET	4	/* offset for start of parameters */
#define MSGS_BAUD	3	/* msg size for baud rate parms	  */

/* X25 supervisor message codes */
#define CALL		0x00		/* outgoing call		*/
#define RING		0x01		/* incoming call		*/
#define CLEARVC		0x02		/* clear by VCN			*/
#define ANSWER		0x03		/* answer call			*/
#define CLEARLC		0x04		/* clear by LCN			*/
#define RESET		0x20		/* reset LCN			*/
#define RESET_ACK	0x21		/* reset ack			*/
#define INTERRUPT	0x22		/* X25 interrupt		*/
#define READY		0x23		/* flow control ready		*/
#define INTR_ACK	0x24		/* interrupt ack		*/
#define RESTART		0x40		/* X25 restart			*/
#define RSTRT_ACK	0x41		/* restart ack			*/
#define SYS_STATUS	0x42		/* system status msg		*/
#define LINE_CNTL	0x60		/* link control cmnd		*/
#define LINE_STATUS	0x61		/* link status resp		*/
#define SET_BFR_SIZE	0x62		/* set firmware buffer size	*/
#define STATQUERY     0217	      /* statistics query	      */
#define STATRESP      0216	      /* statistics response	      */

/* X25 facilities */
#define X25_FACIL_PKTSIZE 0x42		/* CCITT packet size negotiation*/
#define X25_FACIL_WINSIZE 0x43		/* CCITT window size negotiation*/
#define DDN_FACIL_MARKER 0		/* two of these mark DDN private*/
#define X25_FACIL_DDA	0x04		/* DDA mode facility		*/
#define FAC_DDASTD	0x01		/*   DDA standard mode		*/
#define PKTSIZE_LARGE	12		/* lg2 (4096) for calls		*/
#define WINSIZE_LARGE	7		/* Large (7) window for calls	*/
#define PKTSIZE_DEF	7		/* Default packet size		*/
#define WINSIZE_DEF	2		/* Default window size		*/

/* X25 protocols */
#define X25_PROTO_IP	0xcc		/* X25 IP protocol type code	*/
#define X25_PROTO_X29	0x01		/* X29 over X25 protocol type code */

/* DMESG(unit,value,statement) e.g. DMESG(0,27,printf("error message")) */
/* v should be a constant (no side effects)				*/
/* (v) >> 5 == (v) / 32	 - find which word the bit is in.		*/
/* (v) & 0x1f == (v) % 32 - find the number of the bit to check		*/
/* DMESGSET - set a msg bit to 1					*/
/* DMESGCLR - set a msg bit to 0					*/
/* DMESGTOG - toggle a msg bit						*/
/* DMESGVAL - the value of a message bit. 0 or non-zero			*/
#define DMESGSET(u,v)	(ddamsgs[(u)][(int)((v)>>5)] |=  (1 << ((v) & 0x1f)))
#define DMESGCLR(u,v)	(ddamsgs[(u)][(int)((v)>>5)] &= ~(1 << ((v) & 0x1f)))
#define DMESGTOG(u,v)	(ddamsgs[(u)][(int)((v)>>5)] ^=  (1 << ((v) & 0x1f)))
#define DMESGVAL(u,v)	(ddamsgs[(u)][(int)((v)>>5)] &   (1 << ((v) & 0x1f)))
#define DMESG(u,v,s)	(DMESGVAL(u,v) ? 0 : (s))

#ifdef	VAXVMS				/* always enable debugging under VMS */
#define DDADEBUG
#endif	VAXVMS

#ifdef DDADEBUG
#define	DDADBCH(n, unit) (!DMESGVAL(unit, n + 128)) /* first 128 are !debug*/
#endif

/*   macros to test for call logging -- only use unit 0 space for now */

#define	LOG_BUSY	(!DMESGVAL(0, 256)) /* "all circuits in use" 
					     * and "no circuits available" */
#define	LOG_CALLS	(!DMESGVAL(0, 257)) /* calls and clears */
#define	LOG_ABT		(!DMESGVAL(0, 258)) /* I/O aborts */

/* values in dda_state */
#define S_DISABLED	0	/* link is disabled */
#define S_COMING_UP	1	/* enable issued, waiting for response */
#define S_LINK_UP	2	/* link operational */
#define S_GOING_DOWN	3	/* disable issued, waiting for response */

struct trtab			 /* This stuff should ALSO be in ddareg.h */
{				 /* Address Translation Table IOCTL Data */
    unsigned char  func;
    unsigned char  x25addr[MAXADDRLEN];
    unsigned long  ipaddr;
};

struct ddactl		/* used for -m (message), -q (query), -n (svc_count) */
{
    unsigned char func;
    unsigned char nothing[3];
    int drval;
    char msg[MLEN];
};

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%									%%*/
/*%%	The download structure is the template for communication	%%*/
/*%%	with the ACP7000 when it is running diagnostic roms.  The	%%*/
/*%%	will initially place the SYSGEN_VALID entry in the BIIC GPR0	%%*/
/*%%	when in the probe routine.  The IOCTL mechanism has been	%%*/
/*%%	extended to support a *very* limited download feature which	%%*/
/*%%	enables us to load programs into the 7000 and then execute	%%*/
/*%%	them.  Versabug has been modified to examine GPR0 for the	%%*/
/*%%	SYSGEN_DLOAD value.  GPR1 will contain the physical address of  %%*/
/*%%	our standard sysgen block.  The driver will then place messages	%%*/
/*%%	in the request queue.  These messages will be read by the 7000	%%*/
/*%%	diagnostic firmware and placed into memory as needed.  After	%%*/
/*%%	all code has been loaded,  a final "EXEC" command may be sent	%%*/
/*%%	to the 7000 giving an execution start address.  The 7000 will	%%*/
/*%%	then execute the system code (which should wait for GPR0 to	%%*/
/*%%	contain the standard SYSGEN_VALID pattern before continuing.	%%*/
/*%%									%%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#ifdef	ACP_BI
#define SYSGEN_VALID	0x12345678	/* normal operation mode */
#define	SYSGEN_DLOAD	0x31415927	/* download operation mode */

#define	DN_LCMD_SETUP	0	/* reset board, alloc buffers, setup shm */
#define	DN_LCMD_FEOP	1	/* send command and data to front end */
#define	DN_LCMD_CLEANUP	2	/* dealloc buffers and restore shm to norm */

#define	DN_TYPE_ID	1	/* identification record */
#define	DN_TYPE_VER	2	/* version record */
#define	DN_TYPE_COPY	3	/* copyright record */
#define	DN_TYPE_DATA	4	/* data record */
#define	DN_TYPE_XFR	5	/* start address transfer record */

struct dda_dnload		/* code download structure */
{
    unsigned char   func;	/* ioctl function code (will be 'L') */
    unsigned char   lcommand;	/* driver load command (setup/op/cleanup) */
    unsigned short  padding;	/* null padding for dest addr */
    unsigned short  len;	/* length of entire record */
    unsigned short  type;	/* type of record passed */
    unsigned int    dest;	/* destination address */
    char           *data;	/* address of data record in user space */
};
#endif	ACP_BI

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%								 %%*/ 
/*%% Histogram support declarations and defines.  Slots 0-NDDACH %%*/
/*%% record the amount of time n channels were being used.  The	 %%*/ 
/*%% following define uses of locations that come after the	 %%*/ 
/*%% first NDDACH entries.					 %%*/ 
/*%%								 %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 


#define H_LINK_UP	(NDDACH+1)	/* entry that records the time link is*/
					/* up 0-126 */
#define H_START		(NDDACH+2)	/* starting time */
#define H_END		(NDDACH+3)	/* ending time */
#define H_TMO		(NDDACH+4)	/* current value of idle timer */
#define HISTSIZE	(H_TMO+1)	/* size of the histogram table */

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								 %%*/
/*%%   Information for each device unit is maintained in an	 %%*/
/*%%   array of structures named dda_softc[].  The array is	 %%*/
/*%%   indexed by unit number.	Each entry includes the network	 %%*/
/*%%   interface structure (dda_if) used by the routing code to	 %%*/
/*%%   locate the interface,  an array of Logical Channel	 %%*/
/*%%   control blocks which maintain information about each of	 %%*/
/*%%   the Logical Channels (LCNs) through which X.25 virtual	 %%*/
/*%%   calls are established, a queue of I/O requests pending	 %%*/
/*%%   for the ACP, the UNIBUS interrupt vector for the unit	 %%*/
/*%%   and misc flags.	The Logical Channel Control blocks	 %%*/
/*%%   maintain information about the state of each LCN, a	 %%*/
/*%%   queue of outbound data, Half Duplex Channel (HDX) blocks	 %%*/
/*%%   used for queuing I/O requests to the ACP and an ifuba	 %%*/
/*%%   structure which records the UNIBUS resources being held	 %%*/
/*%%   by the LCN.						 %%*/
/*%%								 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

struct sioq			 /* Start I/O queue head */
{
    struct hdx_chan *sq_head;	 /* queue head */
    struct hdx_chan *sq_tail;	 /* queue tail */
};

struct hdx_chan			 /* HDX channel block */
{
    struct hdx_chan *hc_next;	/* link to next HDX channel */
    struct mbuf *hc_mbuf;	/* mbuf chain */
    struct mbuf *hc_curr;	/* current mbuf in chain */
    unsigned char *hc_addr;	/* address bits 15-00 */
    unsigned short hc_cnt;	/* byte count */
    unsigned char  hc_func;	/* I/O function */
    unsigned char  hc_sbfc;	/* I/O subfunction */
    unsigned char  hc_chan;	/* HDX channel number */
    unsigned char  hc_inv;	/* place to store various bits */
#ifdef vax11c
    /* VAX C compiler (and GCC running in VAXC native mode) does not pad
	structures to longword boundaries, so we must pad any structures
	that are passed between ACPCONFIG and the driver */
    unsigned char  _vaxcfill[2]; /* pad structure to longword boundary */
#endif
};

union dc_key
{
	struct in_addr key_addr;
	unsigned long key_val;
};

struct dda_cb			 /* Logical Channel control block */
{
#if defined(DDA_PADOPT) && defined(VAXVMS)
    int ptyucb;			/* to store the np unit control blk addr */
    int ttyflags;		/* flags for the VMS x29 driver */
#endif
    int		dc_line;	/* index into tty structure / minor number */
    struct in_addr dc_inaddr;	/* remote Internet address */
    union  dc_key  dc_key;	/* circuit destination key */
    unsigned char  dc_lcn;	/* LCN number */
    unsigned char  dc_state;	/* LCN state */
    unsigned short dc_timer;	/* LCN timer */
    struct ifqueue dc_oq;	/* LCN output queue */
    struct hdx_chan dc_rchan;	/* LCN read HDX channel */
    struct hdx_chan dc_wchan;	/* LCN write HDX channel */
    short  dc_next;		/* LCN next index. Long so padding will work */
    unsigned char dc_wsizeout;	/* negotiated outgoing window size */
    unsigned char dc_wsizein;	/* negotiated ingoing window size */
    unsigned char dc_pktsizeout;/* negotiated outgoing packet size */
    unsigned char dc_pktsizein; /* negotiated ingoing packet size */
    unsigned short dc_flags;	/* misc flags, DC_OBUSY */
    unsigned short dc_out_t;	/* DEBUG output completion timer per lcn */
#ifdef vax11c
    /* VAX C compiler (and GCC running in VAXC native mode) does not pad
	structures to longword boundaries, so we must pad any structures
	that are passed between ACPCONFIG and the driver */
    unsigned char _vaxcfill[2]; /* pad structure to longword boundary */
#endif
};

struct dda_softc		 /* device control structure */
{
    struct ifnet dda_if;	 /* network-visible interface */
    struct dda_cb dda_cb[NDDACH + 1];	/* Logical Channel cntl blks */
    struct sioq dda_sioq;	 /* start I/O queue */
    int	  	   dda_vector;	/* UNIBUS interrupt vector */
    int	    	   dda_mapreg;	 /* UNIBUS mapping register */
    unsigned char  dda_flags;		 /* ACP operational flag (intr b) */
    unsigned char  dda_init;		 /* flag for init, X.25 service */
    unsigned char  dda_state;		 /* state of link (see below) */
    unsigned char  dda_firmrev;	 /* firmware revision level byte */
    struct in_addr dda_ipaddr;	/* local IP address */
    int	    dda_net_id;		/* net type -- for TRANSPAC */
};

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%								 %%*/
/*%%   The dc_flags field in the dda_cb structure is used to	 %%*/
/*%%   indicate that output is busy, DC_OBUSY.	In ddainit(),	 %%*/
/*%%   the flag is set to zero.	 In dda_start(), return if the	 %%*/
/*%%   flag is set DC_OBUSY; otherwise if output isn't active	 %%*/
/*%%   an attempt is made to send another packet.  The packet	 %%*/
/*%%   is dequeued, and the flag is set for DC_BUSY on.	 In	 %%*/
/*%%   both dda_data and dda_supr, if a write completion is	 %%*/
/*%%   indicated, DC_BUSY is turned off before firing up a write %%*/
/*%%   via a call to dda_start().				 %%*/
/*%%								 %%*/
/*%%   In order to modify packet level parameters like packet	 %%*/
/*%%   window, packet level must be restarted.	Doing this	 %%*/
/*%%   reliably requires the driver know four possible states	 %%*/
/*%%   for the FEP: DISABLED (down and not attempting to bring	 %%*/
/*%%   the link up); COMING UP (processed a "-u N" for N != 0,	 %%*/
/*%%   but have not exchanged restarts yet); UP (data transfer	 %%*/
/*%%   state); GOING DOWN (processed "-u 0", but the other end	 %%*/
/*%%   has not yet agreed).  The dda_state variable tracks these %%*/
/*%%   four states.  No other data structure provides this level %%*/
/*%%   of resolution.  In particular, IFF_UP is always on when	 %%*/
/*%%   dda_state is UP, but sometimes on when dda_state is	 %%*/
/*%%   COMING UP or GOING DOWN.					 %%*/
/*%%								 %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#ifdef DDA_MSGQ
#define MSGQSIZE	2048
#define MQHEX		1	/* marks the start of a hex number */
#define MQDEC		2	/* marks the start of a decimal number */
#define MQEND		0	/* marks the end of message queue data */
#define MSGQNAME	"_ddamsgq"	/* the text name of the message q */
#endif


/*

Revision History:

26-Mar-1986: V1.0 - First generated.
		 Clare Russ.
05-Sep-1986: V1.1 - Add definitions for modified ACPCONFIG interface
		 Clare Russ.
27-Oct-1986: V1.2 - Change TMO_DATA_IDLE from 30 to 600 sec.
		 Lars Poulsen and Randy Graves.
30-Jan-1987: V1.3 - Added DDASTAT_NMC
		 Jeff Berkowitz and Stephanie Price
30-Mar-1987: V1.4 - Added new #defines for X.25 facilities
		 Jeff Berkowitz
09-Sep-1987: V2.1 - Moved all major structure and constant
		 declarations from the driver to this file.
		 Added new #defines for X.25 options and facilities
		 Note that you must now edit this file to turn on
		 debugging and logging.
		 Stephanie Price
18-Mar-1988: V3.0 Brad Engstrom
		 Added four fields to the dda_cb structure. These fields
		 are used to track the negotiated packet and window size
		 for both incoming and outgoing directions. This data
		 will be displayed by the -l option of acpconfig.
12-Apr-1988: V3.0 Brad Engstrom
		 Moved all initialized variables to if_dda.c.  This will
		 allow this file to work under BSD or ULTRIX because the
		 ULTRIX C compiler gags on variables declared in header
		 files.
15-Apr-1988: V3.0 Brad Engstrom
		 Added a key field for doing searches for a
		 matching destination.	This is used to see if there is
		 already an open circuit to a particular destination.
15-Apr-1988: V3.0 Brad Engstrom
		 Added a next pointer to the dda_cb structure.	This
		 will allow lcn structures to be linked into free and
		 active lists.	The pointer is actully just an offset to
		 the next element in the list.	This allows programs such
		 as acpconfig to traverse a copy of the structure in
		 memory.
15-Apr-1988: V3.0 Brad Engstrom
		 Got rid of conditional compilation of dc_out_t field
		 in the dda_cb structure.  This will always be included
		 in the structure, but will only be used if DDA_DEBUG is
		 on. This releaves problems of padding size when adding
		 new fields to the dda_cb structure.
22-Apr-1988: V3.0 Brad Engstrom
		 Added new macro DMESG.	 This is used to conditionally
		 print driver console messages.
22-Apr-1988: V3.0 Brad Engstrom
		 Added a new define DDAMAINT_BRD.  If this is defined
		 then it is assumed that the maintenance board is being
		 used so don't do checks for the board id in the probe
		 routine.
10-May-1988: V3.0 Brad Engstrom
		 Added the constant MAXADDRLEN which is the maximum lenth
		 of an X.25 address. Also added	 MINADDRLEN to specify
		 the minimum X25 address length.
26-Oct-1988: V3.1 Charles Carvalho
		Added documentation.
15-Aug-1988: V4.0 Brad Ensgtrom
		added support for X.29 and Programmers Interface
02-Sep-1988: V4.0 Brad Ensgtrom
		added new field hc_inv to the hc structure.  Someone used
		the hc_sbfc field to store the invalid bit.  Now that the
		PI will use the subfunction field the invalid bit must be
		stored someplace else.
09-Jan-1989: V4.1 SAJ -- Merge 4.0, 3.1
		relaxed MAXADDRLEN & MINADDRLEN to allow for transpac
		installed padding changes from TGV for vax11c
17-Feb-1989: Paul Traina
		Merged SAJ's changes for DDA_DEBUG
28-May-1989: Paul Traina
		Changed structure padding for TGV
20-Jun-1989: Paul Traina
		Eliminated old debug logic... call loging is next on the hit
		list.  New driver is not compatible with old acpconfig!
18-Jul-1989: Paul Traina
		Moved dc_key.ttyline out of union, creating dc_line.  This
		is to stop it from getting clobbered on restarts et al.
25-Oct-1989: Paul Traina
		Added download structure for ACP7000 downloading and code
		execution.
*/
ture provides this level %%*/
/*%%   of resolution.  In particular, IFF_UP is always on when	 %%*/
/*%%   dda_state is UP, but sometimes on when dda_state is	 %%*/
/*%%   COMING UP or GOING DOWN.					 %%*/
/*%%			driver/if_pi.c   444    540     24      132531  4535335167   6707 /*************************************************************************/
/*                                                                       */
/*                                                                       */
/*       ________________________________________________________        */
/*      /                                                        \       */
/*     |          AAA          CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*     |         AAAAA        CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     |        AAAAAAA       CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |       AAAA AAAA      CCCC              CCCC              |      */
/*     |      AAAA   AAAA     CCCC              CCCC              |      */
/*     |     AAAA     AAAA    CCCC              CCCC              |      */
/*     |    AAAA       AAAA   CCCC              CCCC              |      */
/*     |   AAAA  AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |  AAAA    AAAAAAAAAAA CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     | AAAA      AAAAAAAAA   CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*      \________________________________________________________/       */
/*                                                                       */
/*  	Copyright (c) 1989 by Advanced Computer Communications           */
/*  	720 Santa Barbara Street, Santa Barbara, California  93101       */
/*  	(805) 963-9431                                                   */
/*                                                                       */
/*                                                                       */
/*  Files:		if_pi.c, if_pivar.c                              */
/*			ACP_PI (Programmer Interface) for 4.3bsd and	 */
/*			Ultrix 2.0 (and newer)				 */
/*                                                                       */
/*  Author:		??? (Steve or Charles)				 */
/*                                                                       */
/*  Project:		Programmers Interface for 6250 software		 */
/*                                                                       */
/*  Function:		To enable network connections on ACP_PI to	 */
/*			communicate with UNIX.				 */
/*									 */
/* Configuration Entry:							 */
/*									 */
/*      device dda0 at uba? csr 0166740 vector ddainta ddaintb		 */
/*									 */
/* 									 */
/*  Revision History at end of file					 */
/*************************************************************************/

/*************************************************************************/
/*									 */
/* Usage Notes:								 */
/*									 */
/*      - make devices in /dev for those pi				 */
/*        devices which you want in your configuration			 */
/* 									 */
/* System Notes:							 */
/* 									 */
/*       Refer to the installation instructions, readme.txt, which	 */
/*       are included on the pi driver distribution medium.		 */
/*									 */
/* Design Overview:							 */
/*									 */
/* Data flows in two directions through the PI interface.  This section	 */
/* covers how data gets in and out of the driver.			 */
/*									 */
/* OUTBOUND DATA (user process -> FE):					 */
/*									 */
/* 1) The user process issues an ioctl(2) call with pi_dblock  structure */
/*    which gives the address and length of the user data as well as	 */
/*    the lcn that the data is to go out on.				 */
/* 2) The piioctl routine in the case XIOWRITE checks the validity of    */
/*    the specified lcn the calls pi_write.				 */
/* 3) The pi_write routine allocates a small mbuf.  If the user data	 */
/*    is longer than MLEN (112) bytes then a page cluster is allocated	 */
/*    to convert the small mbuf into a large mbuf.  The user data is	 */
/*    then copied to the mbuf.  The mbuf is put on the output queue for  */
/*    the lcn (dc->dc_oq) using the IF_ENQUEUE macros.  If the queue is  */
/*    full then the process will sleep on dc->dc_oq.  The wakeup will    */
/*    come from the pi_data routine when it sees a write completion.     */
/*    After the data is queue the dda_start is called to try to send the */
/*    data.								 */
/* 4) The dda_start routine dequeues the data from the per lcn ouput     */
/*    data queue and hooks the mbuf onto the write side of the hdx_chan  */
/*    structure for the lcn. The routine dda_wrq is called passing this  */
/*    structure.							 */
/* 5) The routine dda_wrq takes the hc structure and puts it on the      */
/*     global output queue ds->dda_sioq. It then calls start_chn().	 */
/* 6) start_chn() set up the comregs and request an A interrupt.  The    */
/*    A interrupt routine will handle the transfer grant and write 	 */
/*    completion.							 */
/*									 */
/* INBOUND DATA (FE -> user process):					 */
/*									 */
/* A) interrupt side----						 */
/*    1) Initially a read is posted for every logical circuit.  When     */
/*	 data comes in the interrupt A routine will handle doing the	 */
/*	 transfer grant. When the read completes then the type of	 */
/*	 channel is determined (IP,X.29 or PI).  For PI data the routine */
/*	 pi_data() is called with the mbuf and read completion status.   */
/*    2) In the routine pi_data() if the read completion status is       */
/*	 DDAIOCOK then the the packet is complete so the routine	 */
/*	 pi_queue_data is called to queue the data for the user process  */
/*	 to read.  If the status is DDAIOCOKP then the packet has not	 */
/*	 been completely read.  In this case the routine dda_rrq is	 */
/*	 called to allocate a new mbuf and chain it to the end of the	 */
/*	 mbufs that have already been filled from this packet.  When the */
/*	 packet is exhausted then the whole chain of mbufs will be 	 */
/*	 passed to pi_queue_data.					 */
/*    3) The routine pi_queue_data uses a small array of pi_qmem	 */
/*	 structures to form a queue of input data.  Each of these	 */
/*	 structures has a pointer to an mbuf.  This may point to a	 */
/*	 single mbuf or a chain of mbufs depending on how many mbufs it	 */
/*	 took to read the packet.  Pi_queue_data will copy the important */
/*	 information to the pi_qmem structure and then wakeup any	 */
/*	 processes that have slept waiting for data to come in.		 */
/*									 */
/* B) system call side----						 */
/*    1) The user process issues the XIOREAD ioctl with a pi_dblock	 */
/*	structure describing the target buffer.				 */
/*    2) the piioctl routine using the XIOREAD case calls the routine	 */
/*       pi_rem_qdata to get the next data item.  If there are no data	 */
/*       items ready then pi_rem_qdata will return -1  and the process	 */
/*       will sleep waiting for data to come in.  The wakeup will be done*/
/*       in pi_queue_data.  When a data item is dequeued from a channel	 */
/*       that is not the supervisor channel then a new read is issued on */
/*       that logical circuit.  This means that at most 1 data item will */
/*       be present in the input queue for a particular data circuit.    */
/*	 This excludes the supervisory circuit 0.			 */
/*    3) the pi_rem_qdata routine will dequeue  an mbuf and copy the	 */
/*	 data to the user buffer along with other information such as	 */
/*	 the lcn and read completions statuses.  If several mbufs were	 */
/*	 used to hold a single packet then the entire chain will be	 */
/*	 scanned and all the data placed in the user buffer.		 */
/*************************************************************************/

#ifdef SIMULATION
#include "if_pivar.h"
#else
#include "../vaxif/if_pivar.h"
#endif

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   LOCAL  FUNCTIONS                          %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void    pi_init ();
PRIVATE void    pi_init_single ();
PRIVATE void    pi_clear_piq ();
PRIVATE void    pi_clear_single ();
PRIVATE void    pi_free_all_lcns ();
PRIVATE void    pi_queue_data ();
PRIVATE int     pi_rem_qdata ();
PRIVATE int     pi_find_chan_for_ring ();
PRIVATE int     pi_write ();

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   LOCAL  VARIABLES                          %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/* PILINES: number of channels (minor device numbers) allocated per board */
/* PIQLEN: max number of messages that can be queued for a channel */
#define PILINES		32
#define PIQLEN		8

#define NPILINES        (NDDA * PILINES)

#ifdef DDADEBUG
int pi_placemark = 0;
#define PI_PLACEMARK(n)	(pi_placemark = (n))
#else
#define PI_PLACEMARK(n)
#endif

/* the pi_qmem structure is used to hold essential information about
 * incomming data until the user process has a chance to issue a read.
 * These structures form a queue of input data. All fields should be 0
 * when the structure is not actively part of the queue.
 */
struct pi_qmem
{
    struct mbuf    *mb;
    u_char          stat;
    u_char          substat;
    u_char          lcn;
};


/* the pi_info structure describes a data channel which corresponds to a
 * minor device.  The values in the protocols fields are only valid when
 * the PI_ACCEPT_RING bit is set in flags.  The 0 element is the lowest
 * protocol, The 1 element is the highest.
 */

struct pi_info
{
    u_short         flags;
    u_int           pgrp;	/* process group */
    u_int           signal;	/* signal to use upon data ready */
    struct mbuf    *msav;	/* place to hang mbuf waiting to go out */
    struct pi_qmem  pi_q[PIQLEN];	/* queue of pending input data */
    u_char          firstq, lastq;	/* index of first and last queue
					 * members */
    u_char          pi_qlen;	/* length of the input queue */
    u_char          protocols[2];	/* low and high protocol range */
};

/* bits for the flags field */
#define PI_ACCEPT_RING	0x1
#define PI_ACCEPT_ANY   0x2

struct pi_info  pi_info[NPILINES];	/* tty structures */

/* this macro gives the channel (minor device number) associated with
 * an active lcn.
 */
#define CHANNEL(lcn,ds)		((ds)->dda_cb[(lcn)].dc_line)

/*
 * macro to translate a device number to the unit (i.e. ACP_PI)
 * with which it is associated,  M001 use V7 major and minor macros
 */

#define PI_UNIT(x) (minor(x) / PILINES)	/* ACP_PI controlling this line */
#define PI_LINE(x) (minor(x) % PILINES)	/* Line number */

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   GLOBAL ROUTINES                           %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       PIIOCTL()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Process ioctl request.                                         */
/*                                                                 */
/*  Call:           piioctl(dev, cmd, data, flag)                  */
/*  Argument:       dev:   device                                  */
/*                  cmd:   ioctl command                           */
/*                  data:  pointer to data                         */
/*                  flag:  ignored                                 */
/*  Returns:        0 for sucess, else nonzero error code          */
/*  Called by:      kernel software software,  this routine is in  */
/*                  the cdevsw table                               */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

piioctl (dev, cmd, data, flag)
dev_t           dev;
caddr_t         data;
{
    struct dda_cb  *dc;
    struct dda_softc *ds;
    int             l, unit, maxlcn;

    l = PI_LINE (dev);
    unit = PI_UNIT (dev);
    maxlcn = nddach[unit];
    ds = &dda_softc[unit];
    switch (cmd)
    {
    case XIOWRITE:
	{
	    struct pi_dblock *pd;

	    pd = (struct pi_dblock *) data;
	    if (pd->lcn > maxlcn)
	    {
		DMESG(unit, 66,
		    (DDALOG(LOG_ERR)
			 "XIOWRITE: invalid lcn %d\n", pd->lcn
		     DDAELOG));
		return (EINVAL);
	    }
	    if (pd->lcn && (CHANNEL (pd->lcn, ds) != l))
	    {
		DMESG (unit, 67, 
		    (DDALOG(LOG_ERR)
			"XIOWRITE: lcn %d not associated with channel %d.\n",
			 pd->lcn, l
		    DDAELOG));
		return (EINVAL);
	    }
	    return (pi_write (ds, l, pd));
	}
    case XIOREAD:
	{
	    int             ret;
	    struct pi_dblock *pd;

	    pd = (struct pi_dblock *) data;
	    while (ret = pi_rem_qdata (l, pd))
	    {
		if (ret > 0)
		    goto newread;
		else
		if (pd->flags & DB_NONBLOCK)
		    return (EWOULDBLOCK);	/* read already posted */
		else
		{
#ifdef DDA_MSGQ
		    dda_mqstr ("(r sl) ");
#endif
		    sleep (pi_info[l].pi_q, TTIPRI);
#ifdef DDA_MSGQ
		    dda_mqstr ("(r wo) ");
#endif
		}
	    }
    newread:
#ifdef DDA_MSGQ
	    dda_mqstr ("(r go) ");
#endif
	    if (pd->lcn)	/* issue new read for the lcn */
		dda_rrq (ds, &ds->dda_cb[pd->lcn].dc_rchan);
	    return (ret);
	}
    case XIORPEND:
	{
	    int             s;

	    s = splimp ();
	    *((int *) data) = pi_info[l].pi_qlen;
	    splx (s);
	    break;
	}
    case XIOACCRING:
	{
	    struct proto_range *pr;
	    int             s;

	    pr = (proto_range *) data;
	    s = splimp ();
	    pi_info[l].flags |= PI_ACCEPT_RING;
	    pi_info[l].protocols[0] = pr->lower;
	    pi_info[l].protocols[1] = pr->upper;
	    splx (s);
	    break;
	}
    case XIOANYPROTO:
	pi_info[l].flags |= PI_ACCEPT_ANY;
	break;
    case XIOFREELCN:
	{
	    u_char          lcn;

	    lcn = *((u_char *) data);
	    if (lcn > maxlcn)
		return EINVAL;
	    dc = &ds->dda_cb[lcn];
	    if (CHANNEL (lcn, ds) != l)
		return EINVAL;
	    else
	    {
		abort_io (unit, lcn);
		dc->dc_state = LC_IDLE;
		dc->dc_line = -1;
		dc->dc_inaddr.s_addr = 0;	/* forget address */
		dc->dc_key.key_addr.s_addr = 0;
		dc->dc_wsizein = dc->dc_wsizeout = 0;
		dc->dc_pktsizein = dc->dc_pktsizeout = 0;
		dc->dc_flags = 0;
	    }
	    break;
	}
    case XIOABORT:
	{
	    u_char          lcn;

	    lcn = *((u_char *) data);
	    if (lcn > maxlcn)
		return EINVAL;
	    dc = &ds->dda_cb[lcn];
	    if (CHANNEL (lcn, ds) != l)
		return EINVAL;
	    else
		abort_io (ds->dda_if.if_unit, lcn);
	    break;
	}
    case XIOGETLCN:

	/*
	 * find a free lcn.  Init the line field for this minor device. stuff
	 * lcn number in struct. init the circuit state. 
	 */
	if (dc = find_free_lcn (ds))
	{
	    dc->dc_flags = DC_RAW;
	    dc->dc_state = LC_DATA_IDLE;
	    dc->dc_line = l;
	    *data = dc->dc_lcn;
	}
	else
	    *data = 0;
	break;
    case XIOCLRCHAN:
	pi_clear_piq (ds, &pi_info[l]);
	break;
    case XIONORING:
	pi_info[l].flags &= ~(PI_ACCEPT_ANY | PI_ACCEPT_RING);
	break;
    case XIORSIG:
	pi_info[l].signal = *((int *) data);
	break;
	/* case XIOABORTIO: do an abort-io on the channel */
    default:
	return (ENOTTY);
    }
    return (0);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       PIOPEN()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Open a line.                                                   */
/*                                                                 */
/*  Call:           piopen(dev, flag)                              */
/*  Argument:       dev:   device                                  */
/*                  flag:  indicates type of open, "nonblocking"   */
/*                         "or block if in use"                    */
/*  Returns:        0 for success, else nonzero error code         */
/*  Called by:      kernel software software,  this routine is in  */
/*                  the cdevsw table                               */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

piopen (dev, flag)
dev_t           dev;
int             flag;
{
    int             unit, d;

    unit = PI_UNIT (dev);
    d = PI_LINE (dev);

    if (d >= NPILINES)
	return (ENXIO);

    /* wait for interface to come up */
    while (dda_softc[unit].dda_state != S_LINK_UP)
	sleep (&dda_softc[unit].dda_state, TTIPRI);

    if (pi_info[d].pgrp == 0)
	pi_info[d].pgrp = u.u_procp->p_pid;
    return (0);
}


/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       PICLOSE()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Close a line.                                                  */
/*                                                                 */
/*  Call:           piclose(dev, flag)                             */
/*  Argument:       dev:   device                                  */
/*                  flag:  unused                                  */
/*  Returns:        nothing                                        */
/*  Called by:      kernel software software,  this routine is in  */
/*                  the cdevsw table                               */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

piclose (dev, flag)
dev_t           dev;
int             flag;
{
    pi_clear_single (&dda_softc[PI_UNIT (dev)], &pi_info[PI_LINE (dev)], 0);
    pi_free_all_lcns (PI_UNIT (dev), PI_LINE (dev));
}


/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   LOCAL  FUNCTIONS                          %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      PI_SUPR()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*       This routine processes received supervisor messages.      */
/*       Depending on the message type, the appropriate action is  */
/*       taken.                                                    */
/*                                                                 */
/*  Call:              pi_supr(ds, mb)                             */
/*  Arguments:         ds:  pointer to dev control block struct    */
/*                     mb:  pointer to mbuf 			   */
/*                              containing the supervisor message  */
/*  Returns:           nothing                                     */
/*  Called by:         dda_supr()                                  */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_supr (ds, mb)
struct dda_softc *ds;
struct mbuf    *mb;

{
    u_char         *p;
    register int    lcn;
    register int    chan;
    int             unit;

    unit = ds->dda_if.if_unit;
#ifdef DDADEBUG
    if (DDADBCH (21, unit))
	DDALOG(LOG_ERR) "dda%d:(pi) pi_supr()\n", unit DDAELOG;
#endif DDADEBUG
    p = mtod (mb, u_char *);
    switch (p[0])
    {
    case LINE_STATUS:		/* link status msg */
    case RESTART:		/* restart received */
    case RSTRT_ACK:		/* restart ack */
    case STATRESP:		/* Statistics Response from FEP */
    case CLEARVC:		/* clear by VCN */
	DMESG (unit, 64,
	    (DDALOG(LOG_ERR)
		"dda%d:(pi) pi_supr: unexpected msg type 0x%x\n",
		unit, p[0]
	    DDAELOG));
	break;

    case RING:			/* incoming call */
	if ((chan = pi_find_chan_for_ring (p)) >= 0)
	    pi_queue_data (unit, chan, 0, 0, 0, mb);
	else			/* if no willing channel's */
	{
	    /* reject the call */
	    send_supr (ds, CLEARVC, p[2], 0);	/* clear call */
	    if (LOG_CALLS)
		DDALOG(LOG_ERR)
		    "dda%d:(pi) Call REJECTED VC 0x%x\n", unit, p[1]
		DDAELOG;
	}
	break;
    case ANSWER:		/* call answered */
    case CLEARLC:		/* clear by LCN */
    case RESET:		/* X25 reset */
    case INTERRUPT:		/* X25 interrupt */
    case INTR_ACK:
	lcn = p[1] / 2;		/* get LCN */
	pi_queue_data (unit, CHANNEL(lcn, ds), 0, 0, 0, mb);
	break;
    default:
	DMESG(unit, 65, 
	    (DDALOG(LOG_ERR)
		"dda%d:(pi) supervisor error (%x %x %x %x)\n",
		unit, p[0], p[1], p[2], p[3]
	    DDAELOG));
    }
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       PI_DATA()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine is called when a data channel I/O completes.    */
/*    If the completion was for a write, an attempt is made to     */
/*    start output on the next packet waiting for output on that   */
/*    LCN.  If the completion was for a read, the received packet  */
/*    is sent to the pi input queue (if no error).                 */
/*                                                                 */
/*  Call:              pi_data(ds, hc, cc, cnt, subcc)             */
/*  Argument:          ds:  device control block                   */
/*                     hc:  half duplex channel control block      */
/*                     cc:   Mailbox I/O completion status         */
/*                     cnt:  byte count                            */
/*		       subcc: Mailbox I/O completion substatus     */
/*  Returns:           nothing                                     */
/*  Called by:         ddainta()                                   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_data (ds, hc, cc, cnt, subcc)
register struct dda_softc *ds;
register struct hdx_chan *hc;
int             cc, cnt, subcc;
{
    register struct dda_cb *dc = &(ds->dda_cb[hc->hc_chan / 2]);
    int             unit;

    unit = ds->dda_if.if_unit;
#ifdef DDADEBUG
    if (DDADBCH (18, unit))
	DDALOG(LOG_ERR)
	    "dda%d:(pi) pi_data: chan=0x%x  cc=0x%x  cnt=0x%x subcc=0x%x\n",
	    unit, hc->hc_chan, cc, cnt, subcc
	DDAELOG;
#endif DDADEBUG

#ifdef DDA_MSGQ
	dda_mqstr("(dt");
	dda_mqnum(hc->hc_chan,MQHEX); dda_mqstr(" ");
	dda_mqnum(cc,MQHEX); dda_mqstr(" ");
	dda_mqnum(cnt,MQHEX); dda_mqstr(" ");
	dda_mqstr(")");
#endif
    if (hc->hc_chan & 0x01)	/* was it read or write? */
    {				/* write, fire up next output */
#ifdef DDADEBUG
	dc->dc_out_t = TMO_OFF;	/* turn off output completion timer */
#endif
	if ((hc->hc_func != DDAABT) && (hc->hc_curr = hc->hc_curr->m_next))
	    dda_wrq (ds, hc, 0);
	else
	{
	    m_freem (hc->hc_mbuf);
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	    if (hc->hc_func == DDAABT)
	    {
		hc->hc_func &= ~DDAABT;
		hc->hc_sbfc &= ~INVALID_MBUF;
	    }
	    else
		ds->dda_if.if_opackets++;
	    dc->dc_flags &= ~DC_OBUSY;
#ifdef DDA_MSGQ
	    dda_mqstr ("(w wa) ");
#endif
	    wakeup (&(dc->dc_oq));	/* wake up anyone sleeping on output */
	    dda_start (ds, dc);	/* and try to output */
	    dc->dc_timer = TMO_OFF;	/* reset timer */
	}
    }
    else			/* read, process rcvd packet */
    {
#ifdef DDADEBUG
#ifdef notdef
	if (DDADBCH (19, unit))
	{
	    u_char         *p;

	    DDALOG(LOG_ERR) "dda%d:(pi) ", unit DDAELOG;
	    p = mtod (hc->hc_curr, u_char *);
	    prt_bytes (p, (cnt < 56 ? cnt : 56));
	    DDALOG(LOG_ERR) "\n" DDAELOG;
	}
#endif
#endif DDADEBUG

	if (cc == DDAIOCOK)
	{			/* Queue good packet for input */
	    hc->hc_curr->m_len += cnt;	/* update byte count */

	    ds->dda_if.if_ipackets++;
	    pi_queue_data (unit, dc->dc_line, dc->dc_lcn, cc, subcc, hc->hc_mbuf);
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	}
	else
	if (cc == DDAIOCOKP)	/* more data pending in this packet */
	{
	    hc->hc_curr->m_len += cnt;	/* update byte count */
	    dda_rrq (ds, hc);
	}
	else
	{
	    m_freem (hc->hc_mbuf);
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	    dda_rrq (ds, hc);
	}
	/* don't hang new data read.  This is done in the user read ioctl */
    }
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       PI_INIT()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This function initializes the pi_info structure.  The      */
/*	active flag is non-zero if the interface is active and got */
/*	a reset.  In this case pi_clear_single is called which     */
/*	free any resources held and send a hangup to the           */
/*	controlling process.                                       */
/*                                                                 */
/*  Call:           pi_init(unit, active)                          */
/*  Argument:       unit: unit to be initialized.                  */
/*                  active: non-zero if the interface is active    */
/*                          when the reset is issued.              */
/*  Returns:        nothing.                                       */
/*  Called by:      ddareset(), ddainit(), bufreset().             */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_init (unit, active)
int             unit;
int             active;
{
    struct pi_info *pi, *end;
    struct dda_softc *ds;

    ds = &dda_softc[unit];
    end = pi_info + ((unit + 1) * PILINES);
    for (pi = end - PILINES; pi < end; pi++)
	if (active)
	    pi_clear_single (ds, pi, 1);
	else
	    pi_init_single (pi);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                    PI_INIT_SINGLE()                         %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	Initialize a pi_info structure.  For now all this means is */
/*	zeroing out everything.	This routine is here to make future*/
/*	enhancements easier.				           */
/*                                                                 */
/*  Call:           pi_init_single (pi)                            */
/*  Argument:       pi: pointer to pi_info structure               */
/*  Returns:        nothing.                                       */
/*  Called by:      pi_init(), pi_clear_single().                  */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_init_single (pi)
struct pi_info *pi;
{
    bzero ((char *) pi, sizeof (struct pi_info));
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                 PI_CLEAR_PIQ()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This routine will free up all the mbufs that are queued on */
/*	the pi_q.  Note that this routine assumes that any pi_q    */
/*	element is zeroed when not in use.  			   */
/*                                                                 */
/*  IMPORTANT: this routine reissues reads for all the lcn         */
/*	whose data is removed from the queue.  This parallels what */
/*	is done when the data is dequeue normally by XIOREAD       */
/*                                                                 */
/*  Call:           pi_clear_piq (ds, pi)                          */
/*  Argument:       ds: pointer to softc used to call dda_rrq.     */
/*                  pi: pointer to pi_info structure.              */
/*  Returns:        nothing                                        */
/*  Called by:      pi_clear_single(), piioctl() case XIOCLRCHAN   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_clear_piq (ds, pi)
struct dda_softc *ds;
struct pi_info *pi;
{
    register int    i;

    for (i = 0; i < PIQLEN; i++)
	if (pi->pi_q[i].mb)
	{
	    m_freem (pi->pi_q[i].mb);
	    if (pi->pi_q[i].lcn)/* issue new read for the lcn */
		dda_rrq (ds, &ds->dda_cb[pi->pi_q[i].lcn].dc_rchan);
	}
    bzero ((char *) pi->pi_q, sizeof (pi->pi_q));
    pi->pi_qlen = 0;
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                  PI_CLEAR_SINGLE()                          %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	this routine will re-initialize a single pi_info structure.*/
/*	If hangup is non-zero then a hangup signal will be sent to */
/*	the controlling process.                                   */
/*                                                                 */
/*  Call:           pi_clear_single (ds, pi, hangup)               */
/*  Argument:       ds: pointer to dda_softc structure             */
/*                  pi: pointer to pi_info structure               */
/*                  hangup: non-zero if hangup is to be sent.      */
/*  Returns:        nothing.                                       */
/*  Called by:      piclose(), pi_init().                          */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/* reset a pi_info structure that is active.  This includes freeing any
 * queue mbufs and sending a hangup to the controlling process
 */
PRIVATE void
pi_clear_single (ds, pi, hangup)
struct dda_softc *ds;
struct pi_info *pi;
int             hangup;		/* non-zero if a hangup is to be sent to
				 * controlling process */
{
    pi_clear_piq (ds, pi);
    if (hangup && pi->pgrp)
    {
	gsignal (pi->pgrp, SIGHUP);
	gsignal (pi->pgrp, SIGCONT);
    }
    if (pi->msav)
	m_freem (pi->msav);
    pi_init_single (pi);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                 PI_FREE_ALL_LCNS ()                         %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	this routine will free all logical circuits associated with*/
/*	a particular channel.  This should return all mbufs that   */
/*	Are used by the channel.                                   */
/*                                                                 */
/*  Call:           pi_free_all_lcns (unit,chan)                   */
/*  Argument:       chan: channel number.                          */
/*		    unit: unit number.                             */
/*  Returns:        nothing.                                       */
/*  Called by:      piclose()                                      */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_free_all_lcns (unit, chan)
int             unit;
int             chan;
{
    register int    i, maxlcn;
    struct dda_cb  *dc;

    maxlcn = nddach[unit];
    dc = &dda_softc[unit].dda_cb[1];
    for (i = 1; i <= maxlcn; i++, dc++)
	if ((dc->dc_flags & DC_RAW) && dc->dc_line == chan)
	{
	    abort_io (unit, dc->dc_lcn);
	    dc->dc_state = LC_IDLE;
	    dc->dc_line = -1;
	    dc->dc_inaddr.s_addr = 0;	/* forget address */
	    dc->dc_key.key_addr.s_addr = 0;
	    dc->dc_wsizein = dc->dc_wsizeout = 0;
	    dc->dc_pktsizein = dc->dc_pktsizeout = 0;
	    dc->dc_flags = 0;
	}
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                   PI_QUEUE_DATA()                           %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This routine queue up supervisory and data messages.  This */
/*	messages will be dequeued when the user does a read using  */
/*	the XIOREAD ioctl.  If the user tried to do a read and no  */
/*	data was ready then the process sleeps on pi_info[chan]pi_q*/
/*      This routine wakes up any of the sleeping processes.       */
/*                                                                 */
/*  Call:           pi_queue_data( unit, chan, lcn, cc, subcc, mb) */
/*  Argument:       unit: unit number (board)                      */
/*                  chan: channel for the data to be queue on      */
/*		    lcn: the lcn that the data came in on          */
/*		    cc: the read completion status from the mailbox*/
/*		    subcc: the read completion substatus           */
/*		    mb: pointer to the mbuf (or mbuf chain) that   */
/*		        contains the data packet.                  */
/*  Returns:        0 for success, else nonzero error code         */
/*  Called by:      pi_data(), pi_supr().                          */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
pi_queue_data (unit, chan, lcn, cc, subcc, mb)
int             unit;
int             chan;
int             lcn;
int             cc;
int             subcc;
struct mbuf    *mb;
{
    int             s;
    struct pi_info *pi;
    struct pi_qmem *pq;

    if (chan == -1) {		/* invalid channel */
	DMESG (unit, 68, 
	    (DDALOG(LOG_ERR)
		"dda%d:(pi) data queue on dead channel: lcn %d\n",
		unit, lcn
	    DDAELOG));
	return;			/* don't do it, otherwise panic! */
    }

    pi = &pi_info[chan];
    s = splimp ();
    if (pi->pi_qlen == PIQLEN)	/* overflow */
    {
	DMESG(unit, 68,
	    (DDALOG(LOG_ERR)
		"dda%d:(pi) Data queue full, message dropped  chan %d lcn %d\n",
		 unit, chan, lcn
	    DDAELOG));
	m_freem (mb);
	splx (s);
	return;
    }
    else
	pi->pi_qlen++;
    if (pi->lastq == (PIQLEN - 1))
	pi->lastq = 0;
    else
	pi->lastq++;
    pq = &pi->pi_q[pi->lastq];
    pq->mb = mb;
    pq->lcn = lcn;
    pq->stat = cc;
    pq->substat = subcc;
    splx (s);
#ifdef DDA_MSGQ
    dda_mqstr ("(r wa) ");
#endif
    wakeup (pi->pi_q);		/* wakeup any sleepers */
    if (pi->signal)
	gsignal (pi->pgrp, pi->signal);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                     PI_REM_QDATA()                          %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	Remove a data item from the input queue and copy the       */
/*	information to the user pi_dblock structure that was       */
/*	passed to the XIOREAD case in piioctl.                     */
/*                                                                 */
/*  Call:           pi_rem_qdata(chan, dblk)                       */
/*  Argument:       chan: index of the channel to read.            */
/*                  dblk: pointer to user pi_dblock.               */
/*  Returns:        -1 if the input queue is empty.                */
/*                  0 if pi_dblock was filled in successfully.     */
/*		    error code if there was an error filling in    */
/*		       the pi_dblock.				   */
/*  Called by:      piioctl()                                      */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
pi_rem_qdata (chan, dblk)
int             chan;
struct pi_dblock *dblk;
{
    int             s, cnt = 0;
    struct pi_info *pi;
    struct pi_qmem *pq;
    struct mbuf    *mb, *m;
    caddr_t         usrdata;
    int             error = 0;

    pi = &pi_info[chan];
    s = splimp ();
    if (pi->pi_qlen == 0)
	return (-1);
    else
	pi->pi_qlen--;
    if (pi->firstq == (PIQLEN - 1))
	pi->firstq = 0;
    else
	pi->firstq++;
    pq = &pi->pi_q[pi->firstq];
    mb = pq->mb;
    dblk->func = pq->stat;
    dblk->subfunc = pq->substat;
    dblk->lcn = pq->lcn;
    bzero ((char *) pq, sizeof (*pq));	/* zero the structure out */
    splx (s);
    for (m = mb, usrdata = dblk->dataptr; m; m = m->m_next)
    {
	if (dblk->length < cnt + m->m_len)
	{
	    error = EINVAL;
	    goto out;
	}
	if (copyout (mtod (m, char *), usrdata, m->m_len))
	{
	    error = EFAULT;
	    goto out;
	}
	cnt += m->m_len;
	usrdata += m->m_len;
    }
out:
    dblk->length = cnt;
    m_freem (mb);
    return (error);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%               PI_FIND_CHAN_FOR_RING()                       %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This routine picks a channel to handle a ring.  It uses    */
/*	a static int (lastchan) to remember where it left off      */
/*	during the last search.  This will prevent it from always  */
/*	picking the same channel if there is more than 1 channel   */
/*	willing to accept the call.  If no channel is willing to   */
/*	accept the protocol type then it will go to a channel that */
/*	has indicated that it will accept any protocol type.       */
/* 	Note that the ring will not be given to a channel that     */
/*	has a full input queue.					   */
/*      This routine should be called a interrupt level.           */
/*                                                                 */
/*  Call:           pi_find_chan_for_ring (p)                      */
/*  Argument:       p: pointer to data containg the ring packet.   */
/*  Returns:        index of a channel willing to accept the call. */
/*                  -1 is return if no channel can accept          */
/*  Called by:      pi_supr()                                      */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
pi_find_chan_for_ring (p)
u_char          p[];		/* p is a pointer to a ring packet */

{
    register u_char *cp;
    int             i, proto, anychan = -1;
    static int      lastchan = 0;
    struct pi_info *pi;

    cp = p + 4;			/* skip over code, lcn, vcn and count in
				 * answer message */
    /* cp now points to length of called address */
    cp += *cp + 1;		/* skip over called address and length byte */
    /* cp now points to length of calling address */
    cp += *cp + 1;		/* skip over calling address and length byte */
    /* cp now points to length of protocol */
    if (*cp++ == 0)
	return (-1);
    proto = *cp;
    i = lastchan;
    do
    {
	i = (i + 1) % NPILINES;
	pi = &pi_info[i];
	if (pi->flags & PI_ACCEPT_RING)
	{
	    if (proto >= pi->protocols[0] && proto <= pi->protocols[1] && pi->pi_qlen < PIQLEN)
	    {
		lastchan = i;
		return (i);
	    }
	}
	if ((pi->flags & PI_ACCEPT_ANY) && anychan == -1 && pi->pi_qlen < PIQLEN)
	    anychan = i;
    } while (i != lastchan);
    if (anychan >= 0)
	lastchan = anychan;
    return (anychan);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%          PI_CIRCUIT_TO_HANDLE_PROTOCOL()                    %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This routine find out if there is a pi channel that is     */
/*	willing to accept a particular protocol.  This is needed   */
/*	because the PI interface has precedence over IP and X.29   */
/*	interfaces.  This routine is called before the data is     */
/*	passed to one of the interfaces.			   */
/*                                                                 */
/*  Call:           pi_circuit_to_handle_protocol(proto)           */
/*  Argument:       proto: protocol byte to check.                 */
/*  Returns:        1 if PI interface will handle the call.        */
/*		    0 otherwise.				   */
/*  Called by:      dda_decode_type().                             */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
pi_circuit_to_handle_protocol (proto)
u_char          proto;
{
    register int    i;
    struct pi_info *pi;

    for (i = 0, pi = pi_info; i < NPILINES; i++, pi++)
    {
	if (pi->flags & PI_ACCEPT_RING)
	    if (proto >= pi->protocols[0] && proto <= pi->protocols[1])
		return (1);
    }
    return (0);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      PI_WRITE()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*	This routine copies user data to an mbuf and queue the     */
/*	mbuf to go out.  The process will sleep if the output      */
/*	queue is full.   If the non-block bit is set then the      */
/*	process will not sleep and EWOULDBLOCK is returned.        */
/*      The last  byte of the built in data area for the mbuf      */
/*      is used to store the subfunction byte.  This byte is then  */
/*	put in the comregs by start_chn when it sees that this is  */
/*	a write for a RAW circuit.                                 */
/*								   */
/*  Call:           pi_write (ds, l, dblk)                         */
/*  Argument:       ds : pointer to dda_softc structure            */
/*                  l  : the channel (minor device) number         */
/*		    dblk : pointer to the pi_dblock for user data  */
/*  Returns:        0 for success, else nonzero error code         */
/*  Called by:      piioctl.                                       */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
pi_write (ds, l, dblk)
struct dda_softc *ds;
int             l;
struct pi_dblock *dblk;
{
    register int    length;
    struct dda_cb  *dc;
    struct mbuf    *m;
    char           *cp;
    struct ifqueue *oq;
    int             s, unit;

    length = dblk->length;
    unit = ds->dda_if.if_unit;
    if (length <= 0 || length > CLBYTES)
    {
	DMESG(unit, 69,
	    (DDALOG(LOG_ERR) "XIOWRITE: invalid length %d.\n", length DDAELOG));
	return (EINVAL);
    }
    m = 0;
    MGET (m, M_WAIT, MT_DATA);
    if (m == 0)
    {
	DMESG(unit, 70,
	    (DDALOG(LOG_ERR) "XIOWRITE: could not get small mbuf\n" DDAELOG));
	return (ENOBUFS);
    }
    if (length > (MLEN - 1))
    {
#if ACC_ULTRIX > 12
	struct mbuf    *p;
	MCLGET (m, p);
	if (p == 0)
#else
	MCLGET (m);
	if (m->m_len != CLBYTES)
#endif
	{
	    DMESG(unit, 71,
		(DDALOG(LOG_ERR)
		    "XIOWRITE: could not get page cluster\n"
		DDAELOG));
	    m_freem (m);
	    return (ENOBUFS);
	}
    }
    cp = mtod (m, char *);
    if (copyin (dblk->dataptr, cp, length))
    {
	m_freem (m);
	return (EFAULT);
    }
    m->m_len = length;
    if (length < (MLEN - 1))
	m->m_dat[MLEN-1] = dblk->subfunc;  /* store subfunction in last byte */
    dc = &ds->dda_cb[dblk->lcn];
    oq = &(dc->dc_oq);		/* point to output queue */
    s = splimp ();
    while (IF_QFULL (oq))	/* if q full */
    {
	if (dblk->flags & DB_NONBLOCK)
	{
	    m_freem (m);
	    splx (s);
	    return (EWOULDBLOCK);
	}
	else
	{
	    /*
	     * note that we save the mbuf here.  It may happen that the
	     * process dies in its sleep and we need a way to recover the
	     * allocated mbuf 
	     */
	    pi_info[l].msav = m;
#ifdef DDA_MSGQ
	    dda_mqstr ("(w sl) ");
#endif
	    sleep (&(dc->dc_oq), TTIPRI);
#ifdef DDA_MSGQ
	    dda_mqstr ("(w wo) ");
#endif
	}
    }
#ifdef DDA_MSGQ
    dda_mqstr ("(w go) ");
#endif
    IF_ENQUEUE (oq, m);		/* otherwise queue it */
    pi_info[l].msav = (struct mbuf *) 0;
    dda_start (ds, dc);		/* and try to output */
    dc->dc_timer = TMO_OFF;	/* reset timer */
    splx (s);
    return (0);
}

/*

Revision History:

13-Jul-89 PST	Modified LOCAL_VOIDs and statics to PRIVATE.

18-Jul-89 PST	Moved dc_key.ttyline out of union, creating dc_line.

26-Jul-89 PST	Replaced kernel printfs with DDALOG calls.

16-Aug-89 PST	Fixed DMESG macros.

29-Oct-89 PST	Fixed off-by-one errors where user could allocate but not
		use last LCN.  (Fix from brad@invector)

*/
t pi_info *pi;

    for (i = 0, pi = pi_info; i < NPILINES; i++, pi++)
    {
	if (pi->flags & PI_ACCEPT_RING)
	    if (proto >= pi->protocols[0] && proto <= pi->protocdriver/if_pivar.h   444    540     24       12411  4535335134   7371 /*************************************************************************/
/*                                                                       */
/*                                                                       */
/*       ________________________________________________________        */
/*      /                                                        \       */
/*     |          AAA          CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*     |         AAAAA        CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     |        AAAAAAA       CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |       AAAA AAAA      CCCC              CCCC              |      */
/*     |      AAAA   AAAA     CCCC              CCCC              |      */
/*     |     AAAA     AAAA    CCCC              CCCC              |      */
/*     |    AAAA       AAAA   CCCC              CCCC              |      */
/*     |   AAAA  AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |  AAAA    AAAAAAAAAAA CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     | AAAA      AAAAAAAAA   CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*      \________________________________________________________/       */
/*                                                                       */
/*  	Copyright (c) 1988 by Advanced Computer Communications           */
/*  	720 Santa Barbara Street, Santa Barbara, California  93101       */
/*  	(805) 963-9431                                                   */
/*                                                                       */
/*                                                                       */
/*  File:		if_pivar.h                                       */
/*                                                                       */
/*  Author:		Brad Engstrom.                                   */
/*                                                                       */
/*  Project:		UNIX DDA-X.25 Network Programmers                */
/*                      Interface for ACP 5250 and ACP 6250              */
/*                                                                       */
/*  Function:		This file contains definitions of the PI         */
/*			data structures and ioctl indicators.            */
/*                                                                       */
/*  Revision History:                                                    */
/*                                                                       */
/*  06-Sep-1988: V1.0 - Brad Engstrom					 */
/*		 First generated	                                 */
/*									 */
/*************************************************************************/

/*
 * Ioctl's have the command encoded in the lower word,
 * and the size of any in or out parameters in the upper
 * word.  The high 2 bits of the upper word are used
 * to encode the in/out status of the parameter; for now
 * we restrict parameters to at most 128 bytes.
 */

/* this structure is used when writing or reading data data */
struct pi_dblock
{
    caddr_t         dataptr;	/* pointer to user data to be written */
    u_int           length;	/* length of data pointed to by dataptr */
    u_short         lcn;	/* logical channel to send data out on */
    u_char	    func;       /* read status , write function value */
    u_char          subfunc;	/* read substatus, write subfunction value */
    u_short         flags;	/* special flags */
};

/* pi_dblock.flags:  The follwing values can be used to set bits */
#define	DB_NONBLOCK	0x01	/* non-blocking read or write */

typedef struct proto_range
{
    u_char          lower;	/* lower bound on protocol range (inclusive) */
    u_char          upper;	/* upper bound on protocol range (inclusive) */
}               proto_range;

/* data write */
#define	XIOWRITE	_IOWR(t, 1, struct pi_dblock)

/* data read */
#define	XIOREAD		_IOWR(t, 2, struct pi_dblock)

/* see if data is ready to be read on a particular channel.  
 * The field will return the number of pending data bytes.
 */
#define	XIORPEND	_IOR(t, 3, int)

/* allow rings on this minor device.  The proto_range structure specifies
 * the lower and upper bounds on the protocol byte of incomming calls
 */
#define	XIOACCRING	_IOW(t, 4, proto_range)

/* specify that a protocol not requested by anyone else will be accepted on
 * this channel.
 */
#define	XIOANYPROTO	_IO(t, 5)

/* reserve an lcn for use by this channel (minor device).  The number of the
 * lcn is returned the the character passed
 */
#define	XIOGETLCN	_IOR(t, 6, u_char)

/* free an lcn specified by the u_char parameter.  This should be called for all
 * lcn's obtained with XIOGETLCN before the channel is closed.
 */
#define	XIOFREELCN	_IOW(t, 11, u_char)

/* clear any data associated with a channel.  The lcn is passed.  Note that
 * this does not clear the circut.  It only flushes queued data stored in the
 * driver
 */
#define	XIOCLRCHAN	_IO(t, 7)

/* disallow incomming calls on this channel */
#define	XIONORING	_IO(t, 9)

/* specify a signal to be sent to the process when there  is data ready to
 * be read on a particular channel.  The u_int specifies the signal to be
 * sent.
 */
#define	XIORSIG		_IOW(t, 10, u_int)

/* abort all output for the given lcn. */
#define	XIOABORT	_IOW(t, 12, u_char)

/*

Revision history:

06-Jun-1988 Brad	Initial implementation

*/
                                             */
/*  Revision History:                                                    */
/*                                                                       */
/*  06-Sep-1988: V1.0 - Brad Engstrom					 */
/driver/if_x29.c   444    540     24      177517  4535335133   6727 /*
 * 
 *     X.29 option for dda driver for UNIX and Ultrix
 *      ________________________________________________________
 *     /                                                        \
 *    |          AAA          CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |
 *    |         AAAAA        CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |
 *    |        AAAAAAA       CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |
 *    |       AAAA AAAA      CCCC              CCCC              |
 *    |      AAAA   AAAA     CCCC              CCCC              |
 *    |     AAAA     AAAA    CCCC              CCCC              |
 *    |    AAAA       AAAA   CCCC              CCCC              |
 *    |   AAAA  AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |
 *    |  AAAA    AAAAAAAAAAA CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |
 *    | AAAA      AAAAAAAAA   CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |
 *     \________________________________________________________/
 * 
 *      Copyright (c) 1987 by Advanced Computer Communications
 *      720 Santa Barbara Street, Santa Barbara, California  93101
 *      (805) 963-9431
 * 
 * File:
 *      if_x29.c
 * 
 * Author:
 * 
 * Project:
 *      Development of PAD on 6250 software.
 * 
 * Function:
 *      To enable network connections on ACP_XX to communicate with UNIX.
 * 
 * Components:
 *      - files if_x29.c
 * 
 * Configuration Entry:
 *
 *      device dda0 at uba? csr 0166740 vector ddainta ddaintb
 *
 * Usage Notes:
 *
 *      - make devices in /dev and edit /etc/ttys for those x29
 *        devices which you want in your configuration
 * 
 * System Notes:
 * 
 *       Refer to the installation instructions, readme.txt, which
 *       are included on the driver distribution medium.
 *
 * Revision History at end of file
 */

/*
 *	For efficiency, it is a good idea to modify XXBOARDS when using
 *	less than 4 boards with the X29 option.  If using more than 32
 *	lines per board, you should modify XXBOARDS, XXLPERBRD, LOG2_XXBOARDS
 *	and LOG2_XXLPERBRD.
 *
 *	Minor numbers are laid out as follows (by default):
 *		(MSB) PBBLLLLL (LSB)
 *	Where P is a flag to determine if the line is outbound (pad) or
 *	inbound (tty).  BB is the board number (0-3), and LLLLL is the
 *	X29 line on a board (0-31).  Some customers may need more than
 *	32 lines/board.  If there are less than 2 boards,  one may shift
 *	the break-point between lines and boards:
 *
 *	up to 4 boards, 32 lines/board	(default)
 *		(MSB) PBBLLLLL (LSB)
 *			XXBOARDS  = 4,   LOG2_XXBOARDS  = 2
 *			XXLPERBRD = 32,	 LOG2_XXLPERBRD = 5
 *	up to 2 boards, 64 lines/board:
 *		(MSB) PBLLLLLL (LSB)
 *			XXBOARDS  = 2,   LOG2_XXBOARDS  = 1
 *			XXLPERBRD = 64,  LOG2_XXLPERBRD = 6
 *	only 1 board, 128 (actually, 126, as 126 = max svc):
 *		(MSB) PLLLLLLL (LSB)
 *			XXBOARDS  = 1,   LOG2_XXBOARDS  = 0
 *			XXLPERBRD = 128, LOG2_XXLPERBRD = 7
 *
 *	(obviously, these are all powers of two)
 */

#define	XXBOARDS	4	/* # boards running x29 */
#define	LOG2_XXBOARDS	2	/* # bits of board info */

#define XXLPERBRD	32	/* # lines per board */
#define	LOG2_XXLPERBRD	5	/* # bits of line info */

/*
 * If you require an 8-bit data path and have no parity misconfigurations,
 * you may change PARITY_MASKs to 0377.  This will leave parity stripping
 * to the ttdriver.  However,  the ttdriver won't strip parity when in
 * raw mode (e.g. at the Password: prompt),  so one symptom of a parity
 * misconfiguration is that users can't login (CR gets received as 0x8D).
 */

#define	INPUT_PARITY_MASK  0177	/* strip off the 8th bit */
#define	OUTPUT_PARITY_MASK 0377	/* don't strip off the 8th bit */

/*
 * macro to translate a device number to the unit (i.e. ACP_n250)
 * with which it is associated and the port on said unit
 */

#define UNIT(x) ((minor(x) >> LOG2_XXLPERBRD) & LOG2_XXBOARDS)

#define LINE(x)	  (minor(x) & 0177)	/* index into line table */
#define XXSHOW(x) (minor(x) == 255)	/* special "show" device */
#define IS_PAD(x) (minor(x) & 0200)	/* msb is the pad/tty selector */
#define MAJLINE(x) ((x) & ~0x80)	/* major plus corrected minor # */

#define NXXLINES        (XXBOARDS * XXLPERBRD)	/* number of total x29 lines */

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   LOCAL  FUNCTIONS                          %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void    xxcntl();
PRIVATE void    xxclear();
PRIVATE void    xxshow();
PRIVATE void    xxpadhandle();
PRIVATE int     xxpadparse();
PRIVATE int     xxpadcall();
PRIVATE void    xxpadmsg();
PRIVATE void	xx_qbit_msg();
PRIVATE void    xx_tp_hangup();
PRIVATE void    x29_init();
PRIVATE void    x29_dhandle();
PRIVATE int	x29_break_reply_is_required();

#if ACC_ULTRIX >= 30
static  int	ttbreakc();		/* always keep this private */
#endif

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   LOCAL  VARIABLES                          %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#define SET_PAD		2
#define READ_PAD	4
#define SET_READ_PAD	6
#define PAR_INDICATION	0
#define INVITE_CLEAR	1
#define BREAK_INDIC	3
#define PAD_ERROR	5

/* command codes */
#define XX_C_BREAK	001
#define XX_C_PAD	002
#define XX_C_CLOSE	003
#define XX_C_HOST	004

struct tty      xx_tty[NXXLINES];	/* tty structures */

#define MODE_UNUSED 0			/* !just for sanity checks only! */
#define	MODE_HOST 1			/* port in host mode (incoming) */
#define	MODE_PAD  2			/* port in pad mode (outgoing) */

char            xxmode[NXXLINES];	/* mode of port */

int             xxstart();

typedef struct {
    char            ref;
    char            val;
} x29_pad_pair;

PRIVATE x29_pad_pair x29_break_ack_params[] =
{
 8, 0				/* ref 8 -- normal output to terminal */
};

PRIVATE x29_pad_pair x29_callout_params[] =
{
 1, 0				/* ref 1 -- no recall char */
};

PRIVATE x29_pad_pair x29_callin_setparams[] =
{ /* these are the preferred paramters when calling in to Unix */
 2, 0,				/* ref 2 -- no echo */
 3, 127,			/* ref 3 -- forward data on any char */
 8, 0,				/* ref 8 -- normal data delivery to terminal */
 9, 0,				/* ref 9 -- no padding after carriage return */
 10, 0,				/* ref 10 -- no line folding */
 13, 0,				/* ref 13 -- no line feed after CR */
 15, 0				/* ref 15 -- no local edit */
};

/******************************************************************************
 *  PAD CONTROL INFORMATION AND DEFINITIONS
 ******************************************************************************/

/* definitions for the pad state field p_state */
#define PS_IDLE	0		/* not opened state */
#define PS_COM  1		/* the pad for this line is in command state */
#define PS_PAD  2		/* this line has data passing though the pad */
#define PS_WAIT	3		/* waiting state */
#define PS_XFR	4		/* data transfer state */

#define P_LINELEN	20
#define P_NOBLOCK	0

typedef struct padinfo {
    short           p_state;	/* pad state */
    char            p_line[P_LINELEN];	/* built up line */
    char            p_idx;	/* index into p_line */
    int		    p_flow;	/* index into mbuf when flow off,
				   P_NOBLOCK if not flowed off */
    struct mbuf    *p_msav;	/* place to hang mbuf when flow controlled */
    struct mbuf    *p_mchsav;	/* place to save mbuf chain '' '' '' */
} padinfo;
padinfo         xx_padinfo[NXXLINES];


/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   GLOBAL ROUTINES                           %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       XXOPEN()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Open a line.                                                   */
/*                                                                 */
/*  Call:           xxopen(dev, flag)                              */
/*  Argument:       dev:   device                                  */
/*                  flag:  indicates type of open, "nonblocking"   */
/*                         "or block if in use"                    */
/*  Returns:        0 for success, else nonzero error code         */
/*  Called by:      kernel software software,  this routine is in  */
/*                  the cdevsw table                               */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*ARGSUSED*/
xxopen(dev, flag)
dev_t           dev;
int             flag;
{
    register struct tty *tp;
    register        d;
    register        s;
    int             unit,
                    i;
#if ACC_ULTRIX > 00
    int             inuse;	/* store inuse bit while sleeping */
#endif

    unit = UNIT(dev);
    d = LINE(dev);

    if (XXSHOW(dev)) {		/* minor device 255 */
	xxshow();
	return (EPIPE);
    }

    /* PST NOTE TO SELF: change the test as follows:
     *	make this d >= NXXLINES, then check to see if unit is present,
     *  Keep that sleep() in the thingy below, so we don't get bouncing
     *  gettys eating up cpu time.
     */
    if ((d >= NXXLINES))
	return (ENXIO);

    /* wait for interface to come up */
    while (dda_softc[unit].dda_state != S_LINK_UP)
	sleep(&dda_softc[unit].dda_state, TTIPRI);

    tp = &xx_tty[d];
    if ((tp->t_state & TS_XCLUDE) && u.u_uid != 0)
	return EBUSY;

    /* make sure the port isn't already open in a conflicting manner */
    /* i.e. can't open /dev/padJ0 and /dev/ttyJ0 at the same time */
    if (tp->t_state & (TS_WOPEN | TS_ISOPEN)) {
	if ((IS_PAD(dev) && (xxmode[d] == MODE_HOST)) ||
	    ((!IS_PAD(dev)) && (xxmode[d] == MODE_PAD)))
	    return EBUSY;
    }

#ifdef	DDADEBUG
	if (DDADBCH(96, unit)) {
		DDALOG(LOG_DEBUG)
		    "dda%d:(x29) open line %d flag %o in %s mode\n",
		    unit, d, flag, (IS_PAD(dev) ? "pad" : "host")
		DDAELOG;
	}
#endif  DDADEBUG

    tp->t_oproc = xxstart;
    tp->t_state |= TS_WOPEN;

    /* if first open initialize state */
    if ((tp->t_state & TS_ISOPEN) == 0) {
	ttychars(tp);

#if ACC_ULTRIX >= 30		/* posix compliant tty driver */
	if (tp->t_cflag & CBAUD == 0) {
	    tp->t_iflag = IGNPAR | ICRNL | IXON | IXANY | IXOFF;
	    tp->t_oflag = OPOST | ONLCR;
	    tp->t_cflag = B9600 | CS8 | CREAD | HUPCL;
	    tp->t_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL;
	    tp->t_line = 0;
	}
#else				/* v7 tty driver */
	if (tp->t_ispeed == 0) {
	    tp->t_ispeed = B9600;
	    tp->t_ospeed = B9600;
	    tp->t_flags = CRMOD | ANYP;
	}
#endif
	xxparam(dev);
    }
    if (IS_PAD(dev)) {
	tp->t_state |= TS_CARR_ON;
	xxmode[d] = MODE_PAD;
	xxcntl(tp, XX_C_PAD, unit);
    } else {
	if ((tp->t_state & TS_CARR_ON) == 0) {
	    xxmode[d] = MODE_HOST;
	    xxcntl(tp, XX_C_HOST, unit);
	    tp->t_flags |= ECHO;
#if ACC_ULTRIX < 31	/* on everything other than Ultrix 3.1 */
	    /* on close tell ACP_XX to drop line */
	    tp->t_state |= TS_HUPCLS;
#endif
	}
    }
    /* if xxcntl did not get called (state had carrier off) or xxcntl's
     * search for a free lcn failed, then t_addr will be 0, so punt */
    if (tp->t_addr == 0) {
	tp->t_pgrp = 0;
	tp->t_state = 0;
	xxmode[d] = MODE_UNUSED;
	return (EBUSY);
    }
    xx_padinfo[d].p_flow = P_NOBLOCK;
    s = splimp();

#if ACC_ULTRIX > 00
    if (flag & O_NDELAY) {
	if (!IS_PAD(dev))
	    tp->t_state |= TS_ONDELAY;
    } else
#endif
	while ((tp->t_state & TS_CARR_ON) == 0) {
	    tp->t_state |= TS_WOPEN;
#if ACC_ULTRIX > 00
	    inuse = tp->t_state & TS_INUSE;
#endif
	    sleep(&tp->t_rawq, TTIPRI);

	    /* wakeup came from xxclear */
	    if ((tp->t_state & TS_WOPEN) == 0) {
		splx(s);
		return (EPIPE);
	    }
#if ACC_ULTRIX > 00
	    /* if port became "inuse" while we slept, return */
	    if ((flag & O_BLKINUSE) && (!inuse) &&
		(tp->t_state & TS_INUSE)) {
		splx(s);
		return (EALREADY);
	    }
#endif
	}

    splx(s);
    i = ((*linesw[tp->t_line].l_open) (dev, tp));
    if (tp->t_pgrp == 0)
	tp->t_pgrp = u.u_procp->p_pid;
    return (i);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       XXCLOSE()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Close a line.                                                  */
/*                                                                 */
/*  Call:           xxclose(dev, flag)                             */
/*  Argument:       dev:   device                                  */
/*                  flag:  unused                                  */
/*  Returns:        nothing                                        */
/*  Called by:      kernel software,  this routine is in the	   */
/*		    cdevsw table                                   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*ARGSUSED*/
xxclose(dev, flag)
dev_t           dev;
int             flag;
{
    register struct tty *tp;
    register        d;
    d = LINE(dev);
    tp = &xx_tty[d];

#ifdef	DDADEBUG
	if (DDADBCH(97, UNIT(dev))) {
		DDALOG(LOG_DEBUG) "dda%d:(x29) closing line %d\n", UNIT(dev), d
		DDAELOG;
	}
#endif  DDADEBUG

	/* PST NOTE TO SELF:
	 *	Add the 629 driver code for timing out the close below,
	 *	because the line could be flowed off and it would hang
	 * 	forever */

    (*linesw[tp->t_line].l_close) (tp);

#if ACC_ULTRIX >= 31
    if ((tp->t_cflag & HUPCL) || ((tp->t_state & TS_ISOPEN) == 0)) {
#else
    if ((tp->t_state & TS_HUPCLS) || ((tp->t_state & TS_ISOPEN) == 0)) {
#endif

#ifdef	DDADEBUG
	if (DDADBCH(97, UNIT(dev))) {
		DDALOG(LOG_DEBUG) "dda%d:(x29) close: tp->t_state = %x\n",
				  UNIT(dev), tp->t_state
		DDAELOG;
	}
#endif  DDADEBUG

	if (tp->t_state & TS_CARR_ON)
	    xxcntl(tp, XX_C_CLOSE, UNIT(dev));
	tp->t_state &= ~TS_CARR_ON;
	xxmode[d] = MODE_UNUSED;
    }
    ttyclose(tp);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       XXREAD()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Read from a line.                                              */
/*                                                                 */
/*  Call:           xxread(dev, uio)                               */
/*  Argument:       dev:   device                                  */
/*                  uio:   pointer to uio structure                */
/*  Returns:        0 for success, else nonzero error code         */
/*  Called by:      kernel software,  this routine is in	   */
/*                  the cdevsw table                               */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

xxread(dev, uio)
dev_t           dev;
struct uio     *uio;
{
    register struct tty *tp;
    register int    l,
                    error;

    if (dda_softc[UNIT(dev)].dda_state != S_LINK_UP)
	return (ENXIO);

    l = LINE(dev);
    tp = &xx_tty[l];
    error = (*linesw[tp->t_line].l_read)(tp, uio);

    if (xx_padinfo[l].p_flow != P_NOBLOCK) {	/* currently blocked? */
	if (tp->t_flags & (RAW | CBREAK)) {	/* using raw q? */
	    if (tp->t_rawq.c_cc < TTYHOG / 8) {	/* if rawq is low, then
						 * it's time to unblock */
		x29_dhandle(&dda_softc[UNIT(dev)],
			    (struct dda_cb *) (tp->t_addr), 1);
	    }
	/* else cooked mode, different test */
	/* canonical q empty? then it's time to unblock */
	} else if (tp->t_canq.c_cc == 0) {
	    x29_dhandle(&dda_softc[UNIT(dev)],
			(struct dda_cb *) (tp->t_addr), 1);
	}
    }
    return (error);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       XXWRITE()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Write on a line.                                               */
/*                                                                 */
/*  Call:           xxwrite(dev, uio)                              */
/*  Argument:       dev:   device                                  */
/*                  uio:   pointer to uio structure                */
/*  Returns:        0 for success, else nonzero error code         */
/*  Called by:      kernel software software,  this routine is in  */
/*                  the cdevsw table                               */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

xxwrite(dev, uio)
dev_t           dev;
struct uio     *uio;
{
    register struct tty *tp;
    if (dda_softc[UNIT(dev)].dda_state != S_LINK_UP)
	return (ENXIO);
    tp = &xx_tty[LINE(dev)];
    return (*linesw[tp->t_line].l_write)(tp, uio);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                       XXIOCTL()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Process ioctl request.                                         */
/*                                                                 */
/*  Call:           xxioctl(dev, cmd, data, flag)                  */
/*  Argument:       dev:   device                                  */
/*                  cmd:   ioctl command                           */
/*                  data:  pointer to data                         */
/*                  flag:  ignored                                 */
/*  Returns:        0 for sucess, else nonzero error code          */
/*  Called by:      kernel software software,  this routine is in  */
/*                  the cdevsw table                               */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#define TIOACCQBIT (int)(0x80800000|('t'<<8)|125)

xxioctl(dev, cmd, data, flag)
dev_t           dev;
caddr_t         data;
{
    register struct tty *tp;
    int             error;
    tp = &xx_tty[LINE(dev)];
    if (cmd == TIOACCQBIT) {
#ifdef	DDADEBUG
	if (DDADBCH(98, UNIT(dev))) {
		DDALOG(LOG_DEBUG) "dda%d:(x29) ioctl qbit msg: cmd=%x ACC=%x\n",
				  UNIT(dev), cmd, TIOACCQBIT
		DDAELOG;
	}
#endif  DDADEBUG
	xx_qbit_msg(tp, UNIT(dev), data);
	return (0);
    }
    error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag);
    if (error >= 0)
	return (error);
    error = ttioctl(tp, cmd, data, flag);
    if (error >= 0) {
	if (cmd == TIOCSETP || cmd == TIOCSETN)
	    xxparam(dev);
	return (error);
    }
    switch (cmd) {
    case TIOCREMOTE:
	if (xxmode[LINE(dev)] == 0)
	    return (EBUSY);
	xxcntl(tp, XX_C_PAD, UNIT(dev));
	break;
    case TIOCSBRK:
	xxcntl(tp, XX_C_BREAK, UNIT(dev));
	break;
    case TIOCCBRK:
    case TIOCSDTR:
    case TIOCCDTR:
	break;
    default:
	return (ENOTTY);
    }
    return (0);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                        XXPARAM()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Set parameters from open or stty.                              */
/*  This routine is being left in as a dummy in case in the future */
/*  there is a mechanism for the host to send information i.e.     */
/*  "hangup line" to the ACP _XX                                   */
/*                                                                 */
/*  Call:           xxparam(dev)                                   */
/*  Argument:       dev:   device                                  */
/*  Returns:        none                                           */
/*  Called by:      none                                           */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*ARGSUSED*/
xxparam(dev)
dev_t           dev;
{
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                        XXSTART()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Start (restart) transmission on a given line.  This is the     */
/*  start routine which is called from above by the tty driver and */
/*  from below on a transmission complete interrupt for a given    */
/*  line.                                                          */
/*                                                                 */
/*  Call:           xxstart(tp)                                    */
/*  Argument:       tp:   pointer to tty structure                 */
/*  Returns:        none                                           */
/*  Called by:      tty driver                                     */
/*                  xxreset()                                      */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

xxstart(tp)
register struct tty *tp;
{
    register struct dda_softc *ds;
    register int    nch,
                    cc,
		    k;
    register struct dda_cb *dc;
    register char  *cp,
                   *p;
    struct ifqueue *oq;
    struct mbuf    *m;
    padinfo	   *pp;
    int             unit,
                    line,
                    s,
		    j;
    extern int      ttrstrt();

    line = tp - xx_tty;
    unit = UNIT(line);
    dc = (struct dda_cb *) tp->t_addr;
    ds = &dda_softc[unit];
    pp = &xx_padinfo[line];

    s = splimp();

#ifdef	DDADEBUG
    if (DDADBCH(99, unit)) {
	DDALOG(LOG_DEBUG) "dda%d:(x29) xxstart: line %d t_state = %x\n",
			  unit, line, tp->t_state
	DDAELOG;
    }
#endif  DDADEBUG

    /* If it's currently active, or delaying, no need to do anything. */
    if ((tp->t_state & TS_CARR_ON) == 0) {
	tp->t_state &= ~(TS_TTSTOP | TS_BUSY);
	ttyflush(tp, FREAD | FWRITE);
	tp->t_state &= ~TS_ASLEEP;
	wakeup((caddr_t) &tp->t_outq);
	goto out;
    }
    if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
	goto out;

    /* wait for free */
    if (dda_softc[unit].dda_state != S_LINK_UP) {
	ttyflush(tp, FREAD | FWRITE);
        DMESG(unit, 96, (DDALOG(LOG_ERR)
			"dda%d:(x29) xxstart: unit offline\n", unit DDAELOG) );
	goto out;
    }
    /* If the writer was sleeping on output overflow, wake him when low tide
     * is reached. */
    if (tp->t_outq.c_cc <= TTLOWAT(tp)) {
	if (tp->t_state & TS_ASLEEP) {
	    tp->t_state &= ~TS_ASLEEP;
	    wakeup((caddr_t) &tp->t_outq);
	}
	if (tp->t_wsel) {
	    selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
	    tp->t_wsel = 0;
	    tp->t_state &= ~TS_WCOLL;
	}
    }
    /* restart transmission unless output queue is empty */
    if (tp->t_outq.c_cc == 0)
	goto out;

    /* if this is an outbound pad line and it's in command mode */
    if (pp->p_state == PS_COM) {
	xxpadhandle(ds, tp, pp);
	goto out;
    }

    /* Allocate an mbuf to stuff the chars into */
    m = 0;
    MGET(m, M_DONTWAIT, MT_DATA);
    if (m == 0) {
        DMESG(unit, 97, (DDALOG(LOG_ERR)
			"dda%d:(x29) xxstart: could not get mbuf\n",
			unit DDAELOG) );
	goto out;
    }
    cp = mtod(m, char *);
    cc = 0;

    /* copy at most MLEN-1 chars out -- must save one byte for subfunc */
    while ((cc < MLEN - 1) && (tp->t_outq.c_cc > 0)) {
	if (tp->t_flags & (RAW | LITOUT))
	    nch = ndqb(&tp->t_outq, 0);
	else {
	    nch = ndqb(&tp->t_outq, 0200);
	    if (nch == 0) {	/* if first item was a delay */
		(void) getc(&tp->t_outq);	/* discard the character */
		continue;
	    }
	}
	if (nch > (MLEN - 1) - cc)
	    nch = (MLEN - 1) - cc;

	/* If any characters were set up, start transmission; */
	if (nch) {
	    j = q_to_b(&tp->t_outq, cp, nch);

#if OUTPUT_PARITY_MASK != 0377
	    /* strip all characters as desired */
	    for (p = cp, k = j; k; k--, p++)
		*p &= OUTPUT_PARITY_MASK;
#endif

#ifdef	DDADEBUG
	    if (DDADBCH(100, unit) && j != nch) {
		DDALOG(LOG_DEBUG)
		    "dda%d:(x29) xxstart: asked for %d got %d chars\n",
		    unit, nch, j
		DDAELOG;
	    }
#endif  DDADEBUG

	    cc += nch;
	    cp += nch;
	} else
	    break;
    }

#ifdef	DDADEBUG
    if (DDADBCH(101, unit)) {
	DDALOG(LOG_DEBUG) "dda%d:(x29) xxstart: mbuf %x len %d\n",
			  unit, m, m->m_len
	DDAELOG;
    }
#endif

    /* if any data was stuffed into the mbuf then send it */
    if (cc) {
	m->m_dat[MLEN - 1] = 0;	/* subfunction: no Q-bit */
	m->m_len = cc;
	oq = &(dc->dc_oq);	/* point to output queue */
	if (IF_QFULL(oq)) {	/* if q full */
	    IF_DROP(oq);	/* drop the data */
	    m_freem(m);
	    ds->dda_if.if_collisions++;	/* for netstat display */
	    splx(s);
	    return (ENOBUFS);
	}
	IF_ENQUEUE(oq, m);	/* otherwise queue it */
	tp->t_state |= TS_BUSY;
	dda_start(ds, dc);	/* and try to output */
    } else
	m_freem(m);

out:
    if (dc->dc_lcn != 0)	/* something left in oq? */
	dda_start(ds, dc);	/* restart output */
    splx(s);
    return (0);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                        XXRESET()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  In response to UNIBUS reset, reset state and restart           */
/*  transmitters.                                                  */
/*                                                                 */
/*  Call:           xxreset(uban)                                  */
/*  Argument:       uban:  UNIBUS adaptor number                   */
/*  Returns:        none                                           */
/*  Called by:      kernel software in response to UNIBUS reset    */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*ARGSUSED*/
xxreset(uban)
int uban;
{
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                        XXSTOP()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Dummy stop routine.                                            */
/*                                                                 */
/*  Call:           xxstop(tp, flag)                               */
/*  Argument:       tp:    pointer to tty structure                */
/*                  flag:  indicates                               */
/*  Returns:        none                                           */
/*  Called by:      none                                           */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*ARGSUSED*/
xxstop(tp, flag)
struct tty     *tp;
int             flag;
{
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                        XXSELECT()                           %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Circumvent bug in our bastardized design which causes ttselect */
/*  to fail.							   */
/*                                                                 */
/*  Call:           xxselect(dev, rw)                              */
/*  Argument:       dev:   device                                  */
/*                  rw:    read or write indicator                 */
/*  Returns:        0 or 1                                         */
/*  Called by:      none                                           */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

xxselect(dev, rw)
dev_t           dev;
int             rw;
{
#ifdef	DDADEBUG
    int unit = UNIT(dev);
    if (DDADBCH(102, unit)) 
	DDALOG(LOG_DEBUG) "dda%d:(x29) select()\n", unit DDAELOG;
#endif  DDADEBUG

    return (ttselect(MAJLINE(dev), rw));
}

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                             %%*/
/*%%                   LOCAL  FUNCTIONS                          %%*/
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      X29_SUPR()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*       This routine processes received supervisor messages.      */
/*       Depending on the message type, the appropriate action is  */
/*       taken.                                                    */
/*                                                                 */
/*  Call:              x29_supr(ds, p)                             */
/*  Arguments:         ds:  pointer to dev control block struct    */
/*                     p:   pointer to a character array           */
/*                              containing the supervisor message  */
/*  Returns:           nothing                                     */
/*  Called by:         dda_supr()                                  */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
x29_supr(ds, p)
struct dda_softc *ds;
u_char          p[];
{
    register struct dda_cb *dc;
    register struct tty *tp;
    register int    lcn;
    int   	    maxlcn;
    int		    line;

#ifdef DDADEBUG
    if (DDADBCH(103, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d:(x29) x29_supr()\n", ds->dda_if.if_unit
	DDAELOG;
    }
#endif  DDADEBUG

    switch (p[0]) {
    case LINE_STATUS:		/* link status msg */
    case RESTART:		/* restart received */
    case RSTRT_ACK:		/* restart ack */
    case STATRESP:		/* Statistics Response from FEP */
	DMESG(ds->dda_if.if_unit, 98, (DDALOG(LOG_ERR)
		"dda%d:(x29) x29_supr: unexpected message type\n",
		ds->dda_if.if_unit DDAELOG));
	break;
    case ANSWER:		/* call answered */
	lcn = p[1] / 2;
	dc = &(ds->dda_cb[lcn]);
	if (dc->dc_state == LC_CALL_PENDING) {	/* if a call pending */
	    decode_answer(p, dc);
	    dc->dc_state = LC_DATA_IDLE;	/* set state */
	    dc->dc_flags = DC_X29;
	    line = dc->dc_line;			/* which line are we? */
#ifdef DDADEBUG
	    if (DDADBCH(114, ds->dda_if.if_unit)) {
		DDALOG(LOG_DEBUG) "dda%d:(x29) x29_supr: answer: line=%d\n",
			ds->dda_if.if_unit, line
		DDAELOG;
	    }
#endif  DDADEBUG

	    if (line == -1) {				/* fubar! */
		DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR)
		    "dda%d:(x29) x29_supr: answer: line was -1, VC 0x%x\n",
		    ds->dda_if.if_unit, p[1] DDAELOG));
	    }

	    xx_padinfo[line].p_state = PS_PAD;
	    xxstart(&xx_tty[line]);
	} else {
	    DMESG(ds->dda_if.if_unit, 108, (DDALOG(LOG_ERR)
		"dda%d:(x29) x29_supr: unexpected answer on LCN %d\n",
		ds->dda_if.if_unit, lcn DDAELOG));
	}
	if (LOG_CALLS) {
	    DDALOG(LOG_INFO) "dda%d:(x29) LCN %d: connected\n",
			     ds->dda_if.if_unit, lcn
	    DDAELOG;
	}
	break;

    case RING:			/* incoming call */
	if (decode_ring(p)) {
	    /* find a free lcn associated with a XX_HOST open */
	    dc = &ds->dda_cb[1];
	    maxlcn = nddach[ds->dda_if.if_unit];
	    for (lcn = 1; lcn <= maxlcn; lcn++) {
		if (dc->dc_state == LC_IDLE && dc->dc_flags & DC_X29W)
		    break;
		dc++;
	    }
	    if (lcn > maxlcn) {				/* if no free lcn's */
		if (LOG_BUSY) {
		    DDALOG(LOG_ERR)
			"dda%d:(x29) no free X29W lcns, call rejected, vc=0x%x\n",
			ds->dda_if.if_unit, p[1]
		    DDAELOG;
		}
		send_supr(ds, CLEARVC, p[2], 0);	/* clear call */
		break;					/* exit case */
	    }
	    
	    /* got a good lcn, now use it */

#ifdef DDADEBUG
	    if (DDADBCH(103, ds->dda_if.if_unit)) {
		DDALOG(LOG_ERR) "dda%d:(x29) supr_msg: call from 0x%0x\n",
		       ds->dda_if.if_unit, (u_long) dc->dc_inaddr.s_addr
		DDAELOG;
	    }
#endif DDADEBUG

	    dc->dc_state = LC_DATA_IDLE;	/* set state */
	    dc->dc_pktsizein = 0;
	    dc->dc_pktsizeout = 0;
	    dc->dc_wsizein = 0;
	    dc->dc_wsizeout = 0;
	    dc->dc_flags = DC_X29;
	    send_supr(ds, ANSWER, lcn * 2, p[2]);	/* send answer */
	    if (LOG_CALLS) {
		DDALOG(LOG_INFO) "dda%d:(x29) Call accepted LCN %d\n",
	  		         ds->dda_if.if_unit, dc->dc_lcn
		DDAELOG;
	    }

	    line = dc->dc_line;

#ifdef DDADEBUG
	    if (DDADBCH(114, ds->dda_if.if_unit)) {
		DDALOG(LOG_DEBUG) "dda%d:(x29) x29_supr: ring: line=%d\n",
			ds->dda_if.if_unit, line
		DDAELOG;
	    }
#endif  DDADEBUG

	    if (line == -1) {				/* fubar! */
		DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR)
		    "dda%d:(x29) x29_supr: ring: line was -1, VC 0x%x\n",
		    ds->dda_if.if_unit, p[1] DDAELOG));
		break;
	    }

	    tp = &xx_tty[line];
	    xx_padinfo[line].p_state = PS_XFR;
	    wakeup((caddr_t) &tp->t_rawq);
	    tp->t_state |= TS_CARR_ON;
#if ACC_ULTRIX > 00
	    tp->t_state &= ~TS_ONDELAY;
#endif
	    /* I would prefer to wait a bit before sending this */
	    send_x29_param_msg(ds, dc, SET_PAD,
			       x29_callin_setparams,
			       sizeof(x29_callin_setparams));
	} else {		/* bad decode */
	    send_supr(ds, CLEARVC, p[2], 0);	/* clear call */
	    DMESG(ds->dda_if.if_unit, 100, (DDALOG(LOG_ERR)
		"dda%d:(x29) Bad decode, call REJECTED VC 0x%x\n",
		ds->dda_if.if_unit, p[1] DDAELOG));
	}
	break;

    case CLEARLC:		/* clear by LCN */
	lcn = p[1] / 2;		/* get LCN */
	dc = &(ds->dda_cb[lcn]);
	if (dc->dc_state != LC_CLR_PENDING) {	/* if no clear pending */
	    send_supr(ds, CLEARLC, p[1], 0);	/* ack the clear */
	}
	if (dc->dc_state == LC_CALL_PENDING)	/* call is cleared */
	    DMESG(ds->dda_if.if_unit, 101, (DDALOG(LOG_ERR)
	    	"dda%d:(x29) Call cleared LCN %d (%x %x)\n",
	        ds->dda_if.if_unit, dc->dc_lcn, p[2], p[4] DDAELOG));

	hist_lcn_state(ds->dda_if.if_unit, dc->dc_state, LC_IDLE);
	dc->dc_state = LC_IDLE;
	dc->dc_timer = TMO_OFF;	/* stop timer */
	dc->dc_wsizein = dc->dc_wsizeout = 0;
	dc->dc_pktsizein = dc->dc_pktsizeout = 0;
	abort_io(ds->dda_if.if_unit, lcn);
	xx_tp_hangup(ds, dc);	/* will clear flags */
	break;

    case CLEARVC:		/* clear by VCN */
	send_supr(ds, CLEARVC, p[1], 0);	/* send clear ack */
	if (LOG_CALLS) {
	    DDALOG(LOG_INFO)
		"dda%d:(x29) Network cleared VC %x (%x %x)\n",
	        ds->dda_if.if_unit, p[1], p[2], p[4]
	    DDAELOG;
	}
	break;

    case RESET:		/* X25 reset */
	send_supr(ds, RESET_ACK, p[1], 0);	/* send reset ack */
	abort_io(ds->dda_if.if_unit, (int) p[1] / 2);
	DMESG(ds->dda_if.if_unit, 102, (DDALOG(LOG_ERR)
	    "dda%d:(x29) X25 RESET on LCN %d (%x %x)\n",
	    ds->dda_if.if_unit, p[1] / 2, p[2], p[4] DDAELOG));
	break;

    case INTERRUPT:		/* X25 interrupt */
#ifdef INDICATE_BREAK_ON_INTERRUPT
	lcn  = p[1] / 2;
	dc   = &(ds->dda_cb[lcn]);

	line = dc->dc_line;

	if (line == -1) {				/* fubar! */
	    DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR)
		"dda%d:(x29) x29_supr: break: line was -1, VC 0x%x\n",
		ds->dda_if.if_unit, p[1] DDAELOG));
	    break;
	}

	tp   = &xx_tty[line];

	if (tp->t_flags & RAW)
	    c = 0;
	else
#if ACC_ULTRIX >= 30
	    c = tp->c_cc[VINTR];/* else make it the interrupt */
#else
	    c = tp->t_intrc;	/* else make it the interrupt */
#endif
#if NBK > 0
	if (tp->t_line == NETLDISC) {
	    BKINPUT(c, tp);
	} else
#endif
	    (*linesw[tp->t_line].l_rint) (c, tp);
	/* send_supr (ds, INTR_ACK, p[1], 0); 	not needed -- done by FE */
#endif
	break;

    case INTR_ACK:
	/* quietly drop the acknowledgement */
	break;
    default:
	DMESG(ds->dda_if.if_unit, 104, (DDALOG(LOG_ERR)
	    "dda%d:(x29) supervisor error (%x %x %x %x)\n",
	    ds->dda_if.if_unit, p[0], p[1], p[2], p[3] DDAELOG));
    }
}

	/* hangup any attached processes */
PRIVATE void
xx_tp_hangup(ds, dc)
struct dda_softc *ds;
register struct dda_cb *dc;
{
    register struct tty *tp;
    register padinfo    *pp;
    register int         line;

    line = dc->dc_line;

    if (line == -1) {				/* fubar! */
	DMESG(ds->dda_if.if_unit, 107, (DDALOG(LOG_ERR)
	    "dda%d:(x29) xx_tp_hangup: line was -1\n",
	    ds->dda_if.if_unit DDAELOG));
	return;
    }

    tp   = &xx_tty[line];
    pp   = &xx_padinfo[line];

    if (pp->p_flow != P_NOBLOCK) {	/* currently blocked? */
	register struct hdx_chan *hc;
	hc = (struct hdx_chan *) & dc->dc_rchan;
	dda_rrq(ds, hc);	/* make sure we hang a read */
    }
    pp->p_flow = P_NOBLOCK;
    tp->t_state &= ~(TS_CARR_ON | TS_ASLEEP | TS_BUSY);
    ttyflush(tp, FREAD | FWRITE);
    gsignal(tp->t_pgrp, SIGHUP);
    gsignal(tp->t_pgrp, SIGCONT);
    tp->t_state &= ~TS_ASLEEP;
    wakeup((caddr_t) &tp->t_outq);
    xxmode[line] = MODE_UNUSED;
    tp->t_addr = (caddr_t) NULL;
    pp->p_state = PS_IDLE;
    if (pp->p_mchsav) {
	m_freem(pp->p_mchsav);
	pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL;
    }
    dc->dc_flags &= ~(DC_X29 | DC_X29W);	/* release to others */
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      X29_DATA()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine is called when a data channel I/O completes.    */
/*    If the completion was for a write, an attempt is made to     */
/*    start output on the next packet waiting for output on that   */
/*    LCN.  If the completion was for a read, the received packet  */
/*    is sent to the IP input queue (if no error) and another read */
/*    is started on the LCN.                                       */
/*                                                                 */
/*  Call:              x29_data(ds, hc, cc, cnt)                   */
/*  Argument:          ds:  device control block                   */
/*                     hc:  half duplex channel control block      */
/*                     cc:   Mailbox I/O completion status         */
/*                     cnt:  byte count                            */
/*  Returns:           nothing                                     */
/*  Called by:         ddainta()                                   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#define QBIT	0x80

PRIVATE void
x29_data(ds, hc, cc, cnt, subcc)
register struct dda_softc *ds;
register struct hdx_chan *hc;
int             cc,
                cnt,
                subcc;
{
    register struct dda_cb *dc = &(ds->dda_cb[hc->hc_chan / 2]);
    register struct tty *tp;

#ifdef DDADEBUG
    if (DDADBCH(104, ds->dda_if.if_unit)) {
 	DDALOG(LOG_DEBUG)
	    "dda%d:(x29) x29_data: chan=%x cc=%x cnt=%x subcc=%x\n",
	    ds->dda_if.if_unit, hc->hc_chan, cc, cnt, subcc
	DDAELOG;
    }
#endif DDADEBUG

    if (hc->hc_chan & 0x01) {	/* if write, fire up next output */
#ifdef DDADEBUG
	dc->dc_out_t = TMO_OFF;	/* turn off output completion timer */
#endif

	if ((hc->hc_func != DDAABT) && (hc->hc_curr = hc->hc_curr->m_next))
	    dda_wrq(ds, hc, 0);
	else {
	    /* it is abort | no more data left */
	    char            qbit_indicator;
	    qbit_indicator = hc->hc_mbuf->m_dat[MLEN - 1];
	    m_freem(hc->hc_mbuf);
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	    if (hc->hc_func == DDAABT) {
		hc->hc_func &= ~DDAABT;
		hc->hc_inv &= ~INVALID_MBUF;
	    } else
		ds->dda_if.if_opackets++;
	    dc->dc_flags &= ~DC_OBUSY;

	    if (qbit_indicator == QBIT) {	/* Q-bit packet? */
		dda_start(ds, dc);		/* restart output */
	    } else {
		tp = &xx_tty[dc->dc_line];
		tp->t_state &= ~TS_BUSY;
		xxstart(tp);	/* restart tty output */
	    }
	}

	/* it's a packet coming in from the front end to the host */
    } else {
#ifdef DDADEBUG
	dc->dc_flags &= ~DC_IPEND;
#endif
	hc = &dc->dc_rchan;

#ifdef DDADEBUG
	if (DDADBCH(105, ds->dda_if.if_unit)) {
	    u_char         *p;
	    DDALOG(LOG_DEBUG) "dda%d:(x29) ", ds->dda_if.if_unit DDAELOG;
	    p = mtod(hc->hc_curr, u_char *);
	    prt_bytes(ds->dda_if.if_unit, "received data", p, (cnt < 64 ? cnt : 64));
	}
	if (DDADBCH(106, ds->dda_if.if_unit)) {
	    DDALOG(LOG_DEBUG)
		"dda%d:(x29) x29_data: read complete mbuf=%x curr=%x\n",
		ds->dda_if.if_unit, hc->hc_mbuf, hc->hc_curr
	    DDAELOG;
	}
#endif DDADEBUG

	if (dc->dc_state != LC_DATA_IDLE) {
	    m_freem(hc->hc_mbuf);	/* toss the packet, lcn is dead */
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	} else if (cc == DDAIOCOK || (cc == DDAIOCOKP && !(subcc & QBIT))) {
	/* Queue up I/O completion OK transfers and I/O OK with more data
	 * pending transfers (as long as it's not a Qbit message).
	 * This algorythm operates differently than the IP handler due
	 * to the fact that we don't need to wait for the entire X.25
	 * packet to arrive on the host before we assemble it.  To do
	 * so should be OK,  but unfortunately it seems some brain-dead
	 * PAD's generate packets with the M-bit set if they have more
	 * data in their internal buffers.  This can cause the system
	 * to burn up mbufs waiting for us to finally receive a packet
	 * with the M-bit not set.  However, we should hold up on processing
	 * packets with both the Q-bit and the M-bit set until we receive
	 * the entire Q-bit message.  If we get 30k Q-bit packets, we will
	 * die, but that is obscenely absurd in the first place.
	 * (sigh)	-- pst 7-19-89
	 */

#ifdef DDADEBUG
	    if (DDADBCH(107, ds->dda_if.if_unit)) {
		DDALOG(LOG_DEBUG)
		    "dda%d:(x29) x29_data: chan=%x DDAIOCOK\n",
		    ds->dda_if.if_unit, hc->hc_chan
		DDAELOG;
	    }
#endif DDADEBUG
	    hc->hc_curr->m_len += cnt;	/* update byte count */

	    ds->dda_if.if_ipackets++;
	    /* HANDLE THE DATA HERE */
	    if (subcc & QBIT) {
		int             len;
		char           *mcp;
		mcp = mtod(hc->hc_curr, char *);
		len = hc->hc_curr->m_len;

#ifdef DDADEBUG
		if (DDADBCH(108, ds->dda_if.if_unit))
		    prt_bytes(ds->dda_if.if_unit,
			      "(x29) Qbit:", mcp, (len < 64 ? len : 64));
#endif DDADEBUG

		if (*mcp == BREAK_INDIC) {	/* Break indication? */
		    register struct tty *tp;
		    if (x29_break_reply_is_required(mcp, len)) {
			/* tell pad to stop discarding output */
			send_x29_param_msg(ds, dc, SET_PAD,
					   x29_break_ack_params, 2);
		    }
		    hc->hc_curr->m_len = 1;	/* change data to single byte */
		    tp = &xx_tty[dc->dc_line];
		    if (tp->t_flags & RAW)	/* if port is in raw mode, */
			*mcp = 0;	/* make the byte a null */
		    else
#if ACC_ULTRIX >= 30
			*mcp = tp->t_cc[VINTR];	/* else make it the interrupt */
#else
			*mcp = tp->t_intrc;	/* else make it the interrupt */
#endif
		    x29_dhandle(ds, dc, 0);
		    return;
		} else if (*mcp & READ_PAD) {
		    if (len == 1)	/* just a message, no params? */
			send_x29_param_msg(ds, dc, PAR_INDICATION,
					   x29_callout_params,
					   sizeof(x29_callout_params));
		    else
			send_x29_param_msg(ds, dc, PAR_INDICATION, mcp + 1, len - 1);
		    m_freem(hc->hc_mbuf);
		    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
		} else {
		    m_freem(hc->hc_mbuf);
		    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
		}
	    } else {			/* not Qbit data, process normally */
		x29_dhandle(ds, dc, 0);
		return;
	    }
	} else if (cc == DDAIOCOKP) {	/* good completion, more data pending */
	    hc->hc_curr->m_len += cnt;
	} else {			/* toss packet */
	    m_freem(hc->hc_mbuf);
	    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	}
	/* hang a new data read */
#ifdef DDADEBUG
	dc->dc_flags |= DC_IPEND;
#endif
	dda_rrq(ds, hc);
    }
}

/* this routine copies chars from the dc_rchan mbuf to the upper
 * level software.  If all the characters are read then the mbuf is
 * freed and a new read is hung on the channel.
 *
 * This routine is called from below by the int A handler and from above
 * by the device read routine.
 */

PRIVATE void
x29_dhandle(ds, dc, restart)
register struct dda_softc *ds;
register struct dda_cb *dc;
int             restart;
{
    register struct tty *tp;
    register struct hdx_chan *hc;
    register padinfo *pp;
    u_char         *cp,
                    c;
    struct mbuf    *m2,
                   *m;
    int             s,
                    line;
    register int    j;
    static int      recurse = 0;

    s = splimp();

    if (recurse) {	/* don't allow ourselves to be called recursively */
	splx(s);
	return;
    } else
	recurse = 1;

    hc = (struct hdx_chan *) &dc->dc_rchan;

    line = dc->dc_line;

    tp = &xx_tty[line];
    pp = &xx_padinfo[line];

    if (restart) {		/* trying to restart input? */
	j  = pp->p_flow;
	m  = pp->p_mchsav;
	m2 = pp->p_msav;

#ifdef DDADEBUG
	if (DDADBCH(109, ds->dda_if.if_unit)) {
	    DDALOG(LOG_DEBUG)
		"dda%d:(x29) flow restart [%d] in %x\n",
		ds->dda_if.if_unit, j, m
	    DDAELOG;
	}
#endif DDADEBUG

    } else {
	j = P_NOBLOCK;
	m2 = m = hc->hc_mbuf;	/* que mbuf chain */
    }

    if (m == 0) {
	DMESG(ds->dda_if.if_unit, 105, (DDALOG(LOG_ERR)
		"dda%d:(x29) x29_dhandle: null mbuf\n",
		ds->dda_if.if_unit DDAELOG));
	hc->hc_mbuf = hc->hc_curr = (struct mbuf *) NULL;
	dda_rrq(ds, hc);
	goto out;
    }
    while (m2) {
	cp = mtod(m2, u_char *);
	for (; j < m2->m_len; j++) {
	    c = cp[j] & INPUT_PARITY_MASK;
	    if (tp->t_rawq.c_cc + tp->t_canq.c_cc >= TTYHOG - 2)
		if (!ttbreakc(c, tp))
		    continue;	/* dump the character */
#if NBK > 0
	    if (tp->t_line == NETLDISC) {
		BKINPUT(c, tp);
	    } else
#endif
		(*linesw[tp->t_line].l_rint) (c, tp);


	    /* Block further input iff: Current input > threshold AND input
	     * is available to user program */

	    if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG / 4 &&
		((tp->t_flags & (RAW | CBREAK)) || (tp->t_canq.c_cc > 0))) {
#ifdef DDADEBUG
		if (DDADBCH(109, ds->dda_if.if_unit)) {
		    DDALOG(LOG_DEBUG)
			"dda%d:(x29) flow on [%d] in %x of %d\n",
			ds->dda_if.if_unit, j, m2, m2->m_len
		    DDAELOG;
		}
#endif DDADEBUG
		pp->p_flow = j + 1;
		pp->p_msav = m2;
		pp->p_mchsav = m;
		if (restart == 0)
		    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) NULL;
		goto out;
	    }
	}
	m2 = m2->m_next;
	j = P_NOBLOCK;
    }
    if (restart) 
	pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL;

    m_freem(m);
    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) NULL;
    pp->p_flow  = P_NOBLOCK;

#ifdef DDADEBUG
    dc->dc_flags |= DC_IPEND;
#endif

    dda_rrq(ds, hc);

out:
    recurse = 0;
    splx(s);
}

PRIVATE void
xx_qbit_msg(tp, unit, msg)
register struct tty *tp;
int             unit;
char           *msg;
{
    register struct dda_cb *dc;
    register struct dda_softc *ds;
    int             s;
    
    ds = &dda_softc[unit];
    dc = (struct dda_cb *) tp->t_addr;
    s = splimp();

#ifdef DDADEBUG
    if (DDADBCH(110, unit)) {
	DDALOG(LOG_DEBUG)
	    "dda%d:(x29) xx_qbit_msg: %d %d %d\n",
	    unit, msg[0], msg[1], msg[2]
	DDAELOG;
    }
#endif DDADEBUG

    if (msg[1] < (MLEN - 4))
	send_x29_param_msg(ds, dc, msg[0], msg + 2, msg[1]);
    splx(s);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                        XXCNTL()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Do modem control functions on a line.                          */
/*                                                                 */
/*  Call:           xxcntl(tp, c, d)                               */
/*  Argument:       tp:   pointer to tty structure                 */
/*                  c:    function code                            */
/*                  unit: for unit number                          */
/*  Returns:        none                                           */
/*  Called by:      xxopen()                                       */
/*                  xxclose()                                      */
/*                  xxread()                                       */
/*                  xxint()                                        */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
xxcntl(tp, c, unit)
register struct tty *tp;
int             c,
                unit;
{
    register struct dda_cb *dc;
    register struct dda_softc *ds;
    register padinfo *pp;
    int             s,
                    l;

    l = tp - xx_tty;
    ds = &dda_softc[unit];
    pp = &xx_padinfo[l];
    s = splimp();

#ifdef DDADEBUG
    if (DDADBCH(111, unit)) {
	DDALOG(LOG_DEBUG)
	    "dda%d:(x29) xxcntl: tp=0x%x line=%d\n", unit, tp, l
	DDAELOG;
    }
#endif DDADEBUG

    switch (c) {
    case XX_C_PAD:
	if (tp->t_addr)
	    break;
	if (dc = find_free_lcn(ds)) { /* race against locate_x25_lcn */
	    dc->dc_flags = DC_X29;
	    dc->dc_line = l;
	    pp->p_state = PS_COM;
	    tp->t_addr = (caddr_t) dc;
	    tp->t_flags &= ~ECHO;
	    pp->p_flow = P_NOBLOCK;
	    pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL;
	    pp->p_idx = 0;
	    pp->p_line[0] = '\0';
	} else
	    tp->t_addr = (caddr_t) NULL;
	break;
    case XX_C_HOST:
	if (tp->t_addr)
	    break;
	if (dc = find_free_lcn(ds)) { /* race against locate_x25_lcn */
	    dc->dc_flags = DC_X29W;
	    dc->dc_line = l;
	    pp->p_state = PS_WAIT;
	    pp->p_flow = P_NOBLOCK;
	    pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL;
	    tp->t_addr = (caddr_t) dc;
	} else
	    tp->t_addr = (caddr_t) NULL;
	break;
    case XX_C_CLOSE:
	pp->p_state = PS_IDLE;
	if (pp->p_mchsav) {
	    m_freem(pp->p_mchsav);
	    pp->p_msav = pp->p_mchsav = (struct mbuf *) NULL;
	}
	dc = (struct dda_cb *) tp->t_addr;
	if (dc == 0) 
	    break;
	if (pp->p_flow != P_NOBLOCK) {	/* currently blocked? */
	    register struct hdx_chan *hc;
	    hc = (struct hdx_chan *) &dc->dc_rchan;
	    dda_rrq(ds, hc);		/* make sure we hang a read */
	}
#ifdef	DDADEBUG
	if (DDADBCH(111, unit)) {
	    static char *st[] = { "lcn down", "lcn restart", "idle",
				  "call pending", "data idle", "clear pending"
				};
	    DDALOG(LOG_DEBUG)
		"dda%d:(x29) xxcntl: close state: %s\n", unit, st[dc->dc_state]
	    DDAELOG;
	}
#endif DDADEBUG

	if (dc->dc_state == LC_DATA_IDLE || dc->dc_state == LC_CALL_PENDING)
	    clear_lcn(ds, dc);	/* send clear & set state to clr_pending */
				/* timers will convert it to LC_IDLE later */

#ifdef	DDADEBUG
	else 
	    if (DDADBCH(111, unit)) {
		DDALOG(LOG_DEBUG)
		    "dda%d:(x29) xxcntl: warning: state not data_idle\n", unit
		DDAELOG;
	    }
#endif

	dc->dc_flags &= ~(DC_X29 | DC_X29W);	/* release to others */
	tp->t_addr = (caddr_t) NULL;
	break;
    case XX_C_BREAK:

	/* really should look at X.3 parameters to decide if an interrupt
	 * packet should be sent. instead, we take an action which assumes
	 * PAD parameter 7 has value 21 */
	dc = (struct dda_cb *) tp->t_addr;
	send_supr(ds, INTERRUPT, dc->dc_lcn * 2, 0);
	send_x29_param_msg(ds, dc, BREAK_INDIC, 0, 0);
	break;
    }
    splx(s);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                     X29_INIT()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  Software reset, clear lines.                                   */
/*                                                                 */
/*  Call:           x29_init(unit, active);                        */
/*  Argument:       unit:  ACP _XX device                          */
/*  Returns:        none                                           */
/*  Called by:      none                                           */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
x29_init(unit, active)
int             unit,
                active;
{
    register int    i;
    register padinfo *pp;

#ifdef	DDADEBUG
    if (DDADBCH(113, unit)) {
	DDALOG(LOG_DEBUG) "dda%d:(x29) x29_init() active=%d\n",
			  unit, active
	DDAELOG;
    }
#endif  DDADEBUG

    if (active)
	xxclear(unit);
    else {
	for (i = 0; i < XXLPERBRD; i++) {
	    xx_tty[unit * XXLPERBRD + i].t_state = PS_IDLE;
	    pp = &xx_padinfo[unit * XXLPERBRD + i];
	    pp->p_state = PS_IDLE;
	    pp->p_flow  = P_NOBLOCK;
	    pp->p_msav  = pp ->p_mchsav = (struct mbuf *) NULL;
	}
    }
}

PRIVATE void
xxclear(unit)
int             unit;
{
    register struct tty *tp;
    register struct dda_softc *ds;
    register struct dda_cb *dc;
    int             i,
                    state;

    ds = &dda_softc[unit];
    for (i = 0, tp = &xx_tty[unit * XXLPERBRD]; i < XXLPERBRD; i++, tp++) {
	state = tp->t_state;
#ifdef	DDADEBUG
	if (DDADBCH(112, unit) && state) {
	    DDALOG(LOG_DEBUG)
		"dda%d:(x29) xxclear: line=%d pgrp=%d state=%d\n",
		unit, i, tp->t_pgrp, state
	    DDAELOG;
	}
#endif  DDADEBUG
	if (state & TS_WOPEN) {
	    tp->t_state &= ~TS_WOPEN;
	    wakeup(&tp->t_rawq);
	}
	if (tp->t_state) {
	    dc = (struct dda_cb *) tp->t_addr;
	    if (dc) {
		xx_tp_hangup(ds, dc);
		dc->dc_line = -1;	/* break correspondence */
	    }
	}
    }
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                        XXSHOW()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*      Show status of each active unit                            */
/*                                                                 */
/*  Call:           xxshow()                                       */
/*  Argument:       none		                           */
/*  Returns:        none                                           */
/*  Called by:      none                                           */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
xxshow()
{
    register struct tty *tp;
    register padinfo    *pp;
    int                  unit,
                         i;
    static char	        *st[] = { "idle", " com", " pad", "wait", "xfer" };
	

    for (unit = 0; unit < (NDDA < XXBOARDS ? NDDA : XXBOARDS); unit++) {
	uprintf("\nACP5250/6250 X29 driver: state of unit %d -\n", unit);
	uprintf("line\tstate\tlcn\tflow\ttstate\ttflags\n");

	for (i = 0, tp = &xx_tty[unit * XXLPERBRD]; i < XXLPERBRD; i++, tp++) {
	    if (tp->t_state) {
		pp = &xx_padinfo[i]; 
		uprintf("%d:\t%s\t%d\t%d\t%x\t%x\n", i, st[pp->p_state],
		       (struct dda_cb *) (tp->t_addr) - dda_softc[unit].dda_cb,
		       pp->p_flow, tp->t_state, tp->t_flags);
	    }
	}
    }
    uprintf("remaining lines free\n");
}

/******************************************************************************
 *                           PAD CODE
 ******************************************************************************/
/* PADCHARUP - Pass a character up towards the user */
#define PADCHARUP(c,tp) (*linesw[(tp)->t_line].l_rint) ((c), (tp))

PRIVATE void
xxpadhandle(ds, tp, pi)
struct dda_softc *ds;
struct tty     *tp;		/* pointer to relevant tty structure */
padinfo        *pi;		/* pointer to relevant padinfo structure */
{
    register int    i;
    register char   c;
    register struct dda_cb *dc;
    int             nch;
    char            tbuf[CBSIZE];	/* CBSIZE is number of char in a
					 * cblock */
    nch = q_to_b(&tp->t_outq, tbuf, CBSIZE);

    /* handle characters in command state. Its OK if were slow here because
     * there is a person on the other end of the discussion */
    dc = (struct dda_cb *) tp->t_addr;
    for (i = 0; i < nch; i++) {
	if (pi->p_idx >= P_LINELEN) {
	    xxpadmsg("\r\ncommand too long\r\n@", tp);
	    pi->p_idx = 0;
	    return;
	}
	c = pi->p_line[pi->p_idx] = tbuf[i] & INPUT_PARITY_MASK;
	if (c == '\r' || c == '\n') {
	    PADCHARUP('\r', tp);
	    PADCHARUP('\n', tp);
	    pi->p_line[pi->p_idx] = '\0';
	    if (dc && dc->dc_state != LC_IDLE) {
		xxpadmsg("cannot call, line is in transition\r\n", tp);
		if (dc && dc->dc_state == LC_CALL_PENDING)
		    xxpadmsg("previous call still pending\r\n", tp);
	    } else if (xxpadparse(ds, pi, tp) == 0)
		PADCHARUP('@', tp);
	    pi->p_idx = 0;
	} else if (c == '\b' || c == '\177') {
	    if (pi->p_idx) {
		pi->p_idx--;
		xxpadmsg("\b \b", tp);
	    }
	} else {
	    pi->p_idx++;
	    PADCHARUP(c, tp);
	}
    }
}

PRIVATE int
xxpadparse(ds, pi, tp)
struct dda_softc *ds;
padinfo        *pi;
struct tty     *tp;
{
    char           *p = pi->p_line;

    if (*p == 'c' || *p == 'C') {	/* connect command */
	for (p++; *p == ' '; *p++);
	if (*p < '0' || *p > '9')
	    xxpadmsg("???\r\n", tp);
	else			/* place a call */
	    return xxpadcall(ds, p, tp);
    } else if (*p)
	xxpadmsg("invalid command\r\n", tp);
    return 0;
}

PRIVATE int
xxpadcall(ds, addr, tp)
struct dda_softc *ds;
char           *addr;
struct tty     *tp;
{
    register int    i = 0;
    struct in_addr  in;

    while (addr[i]) {
	if (addr[i] < '0' || addr[i] > '9') {
	    xxpadmsg("invalid address\r\n", tp);
	    return 0;
	}
	i++;
    }
    ddacb_called_addr[0] = i;
    bcopy(addr, ddacb_called_addr + 1, i);
    ddacb_user_data[0] = (u_char) 0;	/* no user data for now */
    in.s_addr = 0;
    return make_x25_call(ds, (struct dda_cb *) tp->t_addr, in, X25_PROTO_X29);
}

PRIVATE void
xxpadmsg(s, tp)
char           *s;
struct tty     *tp;
{
    while (*s) {
	PADCHARUP(*s, tp);
	s++;
    }
}

/*
 * This routine is used to respond to
 * READ_PARAMS and SET_READ_PARAMS requests, and also
 * to send out a SET_PARAMS request for incoming calls.
 * The outgoing pad supports NO parameters.
 */
send_x29_param_msg(ds, dc, type, msg, len)
register struct dda_cb *dc;
register struct dda_softc *ds;
x29_pad_pair   *msg;
{
    struct mbuf    *m;
    u_char         *p;
    short           i;
    register struct ifqueue *oq;
    m = 0;			/* Allocate an mbuf to stuff the chars into */
    MGET(m, M_DONTWAIT, MT_DATA);
    if (m == 0) {
	DMESG(ds->dda_if.if_unit, 106, (DDALOG(LOG_ERR)
		"dda%d:(x29) couldn't get mbuf for QBIT message\n",
		ds->dda_if.if_unit DDAELOG));
	return;
    }
    m->m_dat[MLEN - 1] = QBIT;	/* set Q-bit */
    p = mtod(m, u_char *);
    len = len / 2;
    *p++ = type;
    if (type == PAR_INDICATION) {	/* our pad supports NO parameters */
	for (i = 0; i < len; i++) {
	    *p++ = msg[i].ref | 0x80;	/* set invalid bit */
	    *p++ = 1;		/* not implemented */
	}
    } else {			/* BREAK_INDIC, SET_PAD to ack break */
	for (i = 0; i < len; i++) {
	    *p++ = msg[i].ref;
	    *p++ = msg[i].val;
	}
    }
    m->m_len = 1 + 2 * len;
    oq = &(dc->dc_oq);		/* point to output queue */
    if (IF_QFULL(oq)) {		/* if q full */
	IF_DROP(oq);		/* drop the data */
	m_freem(m);
	ds->dda_if.if_collisions++;	/* for netstat display */
    } else {
	IF_ENQUEUE(oq, m);	/* otherwise queue it */
	dda_start(ds, dc);	/* and try to output */
    }
}

PRIVATE int
x29_break_reply_is_required(mcp, len)
char           *mcp;
int             len;
{
    mcp++;			/* skip over break indication msg */
    while (len > 1) {		/* while there are parameters left, */
	if ((*mcp == 8) && (mcp[1] == 1))	/* paramter 8 set to 1? */
	    return 1;		/* yes */
	mcp += 2;
	len -= 2;
    }
    return 0;
}

/*
 * Ultrix 3.0 removed the old ttbreakc() kernel routine when moving to
 * a posix compliant driver.  Here it is again, (for our local use only!!!)
 *
 */
#if ACC_ULTRIX >= 30
static int
ttbreakc(c, tp)
register        c;
register struct tty *tp;
{
    return (c == tp->t_cc[VEOL] || c == tp->t_cc[VEOF] ||
	    c == tp->t_cc[VEOL2] || c == '\r' && (tp->t_flags & CRMOD));
}
#endif


/*
Revision History: 

09-Jun-1988: Unknown (Brad?)
	Initial implementation.
15-Feb-1989: Paul Traina
	Fixed point bug in send_x29_prm_msg
08-Mar-1989: Steve Johnson
	Fixed bug in xx_flow logic
24-May-1989: Paul Traina
	Upgraded for Ultrix 3.0
28-May-1989: Paul Traina
	Added more driver intelligence to disable pad durring call pending
31-May-1989: Paul Traina
	Added flexible mapping for # of boards per unit
04-Jun-1989: Paul Traina
	Fixed driver to dequeue Q-bit X29 packets from the mbuf chain properly.
19-Jun-1989: Paul Traina
	Fixed previous fix-- will need to go over if-elseif logic more
	carefully to make sure we're doing the right thing.  It should be
	recoded.
	Modernized entire debug code suite, changed xxshow functionality to
	use the uprintf() kernel call to display data on user's terminal for
	the xxshow hack.
12-Jul-1989: Paul Traina
	Changed format of some debug messages.  Removed LOCAL_VOID in
	favor of PRIVATE routine to aid in debugging.  Simplified some
	chunky logic.
18-Jul-1989: Paul Traina
	Flipped search order for finding a free X29W lcn at RING time.
	Moved the dc_key.ttyline field out of the union and made it dc_line.
	This fixed the Dartmouth singleuser bug.
19-Jul-1989: Paul Traina
	Changed the packet decode logic in x29_data to immediately process
	packets with more data pending (i.e. the M-bit) right away, instead
	of queuing them up.  (Note: it still queues up Q-bit packets)  This
	may fix the Dartmouth mbuf problem with blasting uploads.
27-Jul-1989: Paul Traina
	Removed 8-bit strip in x29_dhandle.
01-Aug-1989: Paul Traina
	Added additional two parameters to make_x25_call for userdata/length
	for merge with new pad software.
02-Aug-1989: Paul Traina
	Reinserted 8-bit strip on data received from the net.  (uses
	PARITY_MASK define for easy change).
	Fixed forward declaration of ttbreakc().
	Improved readability of xxshow output.
	Removed "super" pad code.
	Modified ps_state to be a real state variable.
03-Aug-1989: Paul Traina
	Reversed earlier change to xxselect which didn't pass major #.
	Modified xxshow output to not use %nd which isn't supported in BSD.
28-Aug-1989: Paul Traina
	Changed parameters of make_x25_call -- plug user data field directly.
14-Nov-1989: Paul Traina
	Added support for Ultrix 3.1 which uses HUPCL instead of HUPCLS
	because of that stupid termio interface (sigh).
16-Nov-1989: Paul Traina
	Changed parity mask to input_parity_mask, added output_parity_mask.
*/
da_if.if_unit, 106, (DDALOG(LOG_ERR)
		"dda%d:(x29) couldn't get mbuf for QBIT message\n",
		ds->dda_if.if_unit DDAELOG));
	return;
    }
    m->m_dat[MLEN - 1] = QBIT;	/* set Qdriver/if_pivar.h.tahoe   644    540     24       12662  4535573160  10505 /*************************************************************************/
/*	if_pivar.h		V1.1		26 Jun 1989		 */
/*************************************************************************/
/*                                                                       */
/*                                                                       */
/*       ________________________________________________________        */
/*      /                                                        \       */
/*     |          AAA          CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*     |         AAAAA        CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     |        AAAAAAA       CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |       AAAA AAAA      CCCC              CCCC              |      */
/*     |      AAAA   AAAA     CCCC              CCCC              |      */
/*     |     AAAA     AAAA    CCCC              CCCC              |      */
/*     |    AAAA       AAAA   CCCC              CCCC              |      */
/*     |   AAAA  AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |  AAAA    AAAAAAAAAAA CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     | AAAA      AAAAAAAAA   CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*      \________________________________________________________/       */
/*                                                                       */
/*  	Copyright (c) 1988 by Advanced Computer Communications           */
/*  	720 Santa Barbara Street, Santa Barbara, California  93101       */
/*  	(805) 963-9431                                                   */
/*                                                                       */
/*                                                                       */
/*  File:		if_pivar.h                                       */
/*                                                                       */
/*  Author:		Brad Engstrom.                                   */
/*                                                                       */
/*  Project:		UNIX DDA-X.25 Network Programmers                */
/*                      Interface for ACP 5250 and ACP 6250              */
/*                                                                       */
/*  Function:		This file contains definitions of the PI         */
/*			data structures and ioctl indicators.            */
/*                                                                       */
/*  Revision History:                                                    */
/*                                                                       */
/*  06-Sep-1988: V1.0 - Brad Engstrom					 */
/*		 First generated	                                 */
/*  26-Jun-1989: V1.1 - Paul Traina					 */
/*		 Updated ioctl defines for 4.3-tahoe & newer CPP	 */
/*									 */
/*************************************************************************/

/*
 * Ioctl's have the command encoded in the lower word,
 * and the size of any in or out parameters in the upper
 * word.  The high 2 bits of the upper word are used
 * to encode the in/out status of the parameter; for now
 * we restrict parameters to at most 128 bytes.
 */

/* this structure is used when writing or reading data data */
struct pi_dblock
{
    caddr_t         dataptr;	/* pointer to user data to be written */
    u_int           length;	/* length of data pointed to by dataptr */
    u_short         lcn;	/* logical channel to send data out on */
    u_char	    func;       /* read status , write function value */
    u_char          subfunc;	/* read substatus, write subfunction value */
    u_short         flags;	/* special flags */
};

/* pi_dblock.flags:  The follwing values can be used to set bits */
#define	DB_NONBLOCK	0x01	/* non-blocking read or write */

typedef struct proto_range
{
    u_char          lower;	/* lower bound on protocol range (inclusive) */
    u_char          upper;	/* upper bound on protocol range (inclusive) */
}               proto_range;

/* data write */
#define	XIOWRITE	_IOWR('t', 1, struct pi_dblock)

/* data read */
#define	XIOREAD		_IOWR('t', 2, struct pi_dblock)

/* see if data is ready to be read on a particular channel.  
 * The field will return the number of pending data bytes.
 */
#define	XIORPEND	_IOR('t', 3, int)

/* allow rings on this minor device.  The proto_range structure specifies
 * the lower and upper bounds on the protocol byte of incomming calls
 */
#define	XIOACCRING	_IOW('t', 4, proto_range)

/* specify that a protocol not requested by anyone else will be accepted on
 * this channel.
 */
#define	XIOANYPROTO	_IO('t', 5)

/* reserve an lcn for use by this channel (minor device).  The number of the
 * lcn is returned the the character passed
 */
#define	XIOGETLCN	_IOR('t', 6, u_char)

/* free an lcn specified by the u_char parameter.  This should be called for all
 * lcn's obtained with XIOGETLCN before the channel is closed.
 */
#define	XIOFREELCN	_IOW('t', 11, u_char)

/* clear any data associated with a channel.  The lcn is passed.  Note that
 * this does not clear the circut.  It only flushes queued data stored in the
 * driver
 */
#define	XIOCLRCHAN	_IO('t', 7)

/* disallow incomming calls on this channel */
#define	XIONORING	_IO('t', 9)

/* specify a signal to be sent to the process when there  is data ready to
 * be read on a particular channel.  The u_int specifies the signal to be
 * sent.
 */
#define	XIORSIG		_IOW('t', 10, u_int)

/* abort all output for the given lcn. */
#define	XIOABORT	_IOW('t', 12, u_char)
cations           */
/*  	720 Santa Barbara Street, Santa Barbara, California driver/if_dda_bibus.c   444    540     24      136505  4535573573  10225 /*************************************************************************/
/*                                                                       */
/*                                                                       */
/*       ________________________________________________________        */
/*      /                                                        \       */
/*     |          AAA          CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*     |         AAAAA        CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     |        AAAAAAA       CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |       AAAA AAAA      CCCC              CCCC              |      */
/*     |      AAAA   AAAA     CCCC              CCCC              |      */
/*     |     AAAA     AAAA    CCCC              CCCC              |      */
/*     |    AAAA       AAAA   CCCC              CCCC              |      */
/*     |   AAAA  AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |      */
/*     |  AAAA    AAAAAAAAAAA CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |      */
/*     | AAAA      AAAAAAAAA   CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |      */
/*      \________________________________________________________/       */
/*                                                                       */
/*  	Copyright (c) 1986 by Advanced Computer Communications           */
/*  	720 Santa Barbara Street, Santa Barbara, California  93101       */
/*  	(805) 963-9431                                                   */
/*                                                                       */
/*                                                                       */
/*  File:		if_dda_bibus.c                                   */
/*			BI bus support routines for dda			 */
/*                                                                       */
/*  Project:		DDN-X.25 Network Interface Driver for ACP 7250   */
/*                                                                       */
/*  revision history at the end of if_dda.c				 */
/*************************************************************************/

#include "../vaxif/if_uba.h"
#include "../vaxuba/ubavar.h"
#include "../vaxbi/bireg.h"
#include "../vaxif/if_ddabique.h"

#ifdef	SIMULATION
#define	KM_CLUSTER	28
#define	KM_NOWAIT	1
#define	KM_ALLOC(space, cast, size, type, flags) { \
	(space) = (cast)malloc(size); \
}
#define	KM_FREE(addr, type) { \
	(void)free((caddr_t)(addr)); \
}
#undef svtophy
#define svtophy(x)	((int)(x))
#endif

/*
 *	private functions in this module
 */

PRIVATE void	dda_shm_setup();
PRIVATE void	dda_disable();
PRIVATE void	dda_dump_biic_regs();
PRIVATE void	dda_dump_shm();
PRIVATE void	dda_unit_reset();
PRIVATE int	dda_dload();

extern struct bidata bidata[];

/* previous_unload contains this value if the request queue is *not* blocked */
#define	UL_NOT_BLOCKED	(RQSIZE+1)

typedef struct {
    byte	fe_state;
    byte	fe_soft_id;
    byte	fe_soft_vers;
    byte	fe_diag_status;
} GP_REG3_USAGE;

PRIVATE u_short	ddastd[] = { 0 };	/* standard addresses */

/* ddainfo is setup by OS */
struct uba_device *ddainfo[NDDA];	/* ptrs to device info */

struct uba_driver ddadriver =	/* device driver info */
{
 ddaprobe,			/* device probe routine */
 0,				/* slave probe routine */
 ddaattach,			/* device attach routine */
 0,				/* "dmago" routine */
 ddastd,			/* device address */
 "dda",				/* device name */
 ddainfo,			/* ptr to device info ptrs */
 "dda",				/* device name */
 0
};

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAPROBE()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/* Purpose:                                                        */
/*                                                                 */
/*  This routine is pretty much a dummy.  The real probe is done   */
/*  the operating system.					   */
/*                                                                 */
/*  Call:          ddaprobe(ui)	                                   */
/*  Argument:	   ui:   pointer to device data structure, used    */
/*                       for BI environment                        */
/*  Returns:       length of register structure for ACP device     */
/*  Called by:     network software, part of autoconfiguration on  */
/*                 the VAX, the address of this routine is one of  */
/*                 the fields of the uba_driver structure          */
/*  Calls to:      none                                            */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int savefirmrev;	/* save between probe & attach */

ddaprobe(nxv, nxp, cpup, binumber, binode, ui)
struct bi_nodespace *nxv;
char                *nxp;
struct cpusw        *cpup;
int                  binumber;
int                  binode;
struct uba_device   *ui;
{
    GP_REG3_USAGE   *gpreg3;

    /* check to see if this board was in system configuration */

    if (ui->ui_unit > NDDA)
	return 0;		/* extra board? punt */

    gpreg3 = (GP_REG3_USAGE *) &nxv->biic.biic_gpr3;

    /* check that the device is really a 7250 and save away */
    /* the firmware revision level for version dependent processing */
    /* If we just booted, the diagnostics may still be running as we */
    /* probe the device - it's still OK to read the ID and VERSION */
    /* numbers, which come valid within milliseconds after power is */
    /* applied to the board - so say the firmware gurus */

    dda_hasmaint = gpreg3->fe_soft_vers & 0x80;
    savefirmrev  = gpreg3->fe_soft_vers & 0x7f;

    switch (gpreg3->fe_soft_id) {
    case 0x68:
	dda_product = "ACP7250";
	break;
    default:
#ifdef	notdef
	if (dda_hasmaint == 0)	/* assume we know what we're doing */
	    return 0;
	savefirmrev = 255;	/* assume high version */
	dda_product = "ACP7250";
	break;
#else
	return 0;
#endif
    }

    return 1;	/* return that we have found a board and are happy */
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAATTACH()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  This routine attaches the device to the network software.  The */
/*  network interface structure is filled in.  The device will be  */
/*  initialized when the system is ready to accept packets.  The   */
/*  dda_init initialization/service flag is zeroed, DDN standard   */
/*  X.25 service is implemented by default unless otherwise        */
/*  specified by the user via the acpconfig program.               */
/*--
	Various other types of one-time initialization:
	    - allocate memory and setup queues
								 --*/
/*                                                                 */
/*  Call:           ddaattach(ui)                                  */
/*  Argument:       ui:  ptr to the uba_device data structure      */
/*  Returns:        nothing                                        */
/*  Called by:      network software, part of network system       */
/*                  configuration, identification to the network   */
/*                  software,  the address of this routine is one  */
/*                  of the fields of the uba_driver structure      */
/*  Calls to:       if_attach()                                    */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

ddaattach(ui)
struct uba_device *ui;
{
    register struct dda_softc *ds = &dda_softc[ui->ui_unit];
    SYSGEN_BLOCK   *SysGenPtr;
    static long     initmsgs[] = {0, 0, 0, 0, -1, -1, -1, -1, -2};

    ds->dda_init = DDA_STANDARD;	/* init/service flag <- default  */
    ds->dda_firmrev = savefirmrev;	/* saved firmware rev level */
    ds->dda_net_id = 0;			/* default */
    ds->dda_if.if_unit = ui->ui_unit;	/* set unit number */
    ds->dda_if.if_name = "dda";		/* set device name */
    ds->dda_if.if_mtu = DDAMTU;		/* set max msg size */
    ds->dda_if.if_init = ddainit;	/* set init routine addr */
    ds->dda_if.if_ioctl = ddaioctl;	/* set ioctl routine addr */
    ds->dda_if.if_output = ddaoutput;	/* set output routine addr */
    ds->dda_if.if_reset = ddareset;	/* set reset routine addr */
    ds->dda_if.if_watchdog = ddatimer;	/* set timer routine addr */

    bcopy(initmsgs, ddamsgs[ui->ui_unit], sizeof(initmsgs)); 

    /* allocate shared memory segment */

    KM_ALLOC(SysGenPtr, SYSGEN_BLOCK *, 1024, KM_CLUSTER, KM_NOWAIT);
    if (SysGenPtr == NULL) {
	DMESG(ui->ui_unit, 0, (DDALOG(LOG_ERR)
		 "dda%d: unable to get shared memory segment\n", ui->ui_unit
		 DDAELOG));
	return;
    }
	
    ds->dda_mapreg = (int) SysGenPtr;	/* save it in ds structure */

    /* hook the interrupt vector to scb */

#ifndef	SIMULATION
    bidev_vec(ui->ui_adpt, ui->ui_nexus, LEVEL14, ui);
#endif

    dda_shm_setup(ui->ui_unit, (struct biic_regs *) ui->ui_addr, SYSGEN_VALID);

    if_attach(&ds->dda_if);	/* attach new network device */
}

PRIVATE void
dda_shm_setup(unit, nxv, drvr_state)
int               unit;
struct biic_regs *nxv;
int		  drvr_state;
{
    register struct dda_softc *ds  = &dda_softc[unit];
    register struct uba_device *ui = ddainfo[unit];
    SYSGEN_BLOCK   *SysGenPtr;
    char           *mem;

    SysGenPtr = (SYSGEN_BLOCK *) ds->dda_mapreg;
    mem       = (char *)         ds->dda_mapreg;

    SysGenPtr->request     = (RQUEUE *) &mem[512 - 4]; /* get entries on page */
    SysGenPtr->completion  = (CQUEUE *) &mem[sizeof(SYSGEN_BLOCK) + 7 & 0xff7c];

    SysGenPtr->prequest    = svtophy(SysGenPtr->request);
    SysGenPtr->pcompletion = svtophy(SysGenPtr->completion);

    SysGenPtr->req_size   = RQSIZE;
    SysGenPtr->comp_size  = CQSIZE;
    SysGenPtr->intr_level = 0;		/* level 0 is lowest level */
    SysGenPtr->pwr_action = 0;		/* boot default code */

    SysGenPtr->previous_unload = UL_NOT_BLOCKED; /* request queue not blocked */

    SysGenPtr->request->load = 0;
    SysGenPtr->request->unload = 0;
    SysGenPtr->completion->load = 0;
    SysGenPtr->completion->unload = 0;
    bzero(SysGenPtr->completion->entry, sizeof(CENTRY) * CQSIZE);
    bzero(SysGenPtr->request->entry, sizeof(RENTRY) * RQSIZE);

#ifndef	SIMULATION
						/* set interrupt destination */
    nxv->biic_int_dst = bidata[ui->ui_adpt].biintr_dst;
#endif

    nxv->biic_err  = nxv->biic_err;		/* actually clears error reg */
    nxv->biic_gpr1 = svtophy(SysGenPtr);	/* get BI physical address */
    nxv->biic_gpr0 = drvr_state;		/* tell FE our state */
}

#ifdef DDADEBUG
PRIVATE void
dda_dump_biic_regs(nxv)
struct biic_regs *nxv;
{
    uprintf("biic register dump (nxv=0x%x 0x%x(p))\n",
	nxv, svtophy(nxv));
    uprintf("typ=0x%x ctrl=0x%x err=0x%x err_int=0x%x\n",
	nxv->biic_typ, nxv->biic_ctrl, nxv->biic_err, nxv->biic_err_int);
    uprintf("int_dst=0x%x ip_msk=0x%x ip_dst=0x%x ip_src=0x%x\n",
	nxv->biic_int_dst, nxv->biic_ip_dst, nxv->biic_ip_dst, nxv->biic_ip_src);
    uprintf("strt=0x%x end=0x%x bci_ctrl=0x%x wrt_stat=0x%x\n",
	nxv->biic_strt, nxv->biic_end, nxv->biic_bci_ctrl, nxv->biic_wrt_stat);
    uprintf("int_ctrl=0x%x\n", nxv->biic_int_ctrl);
    uprintf("gpr0=0x%x gpr1=0x%x gpr2=0x%x gpr3=0x%x\n",
	nxv->biic_gpr0, nxv->biic_gpr1, nxv->biic_gpr2, nxv->biic_gpr3);
}

PRIVATE void dda_dump_shm(shm)
SYSGEN_BLOCK *shm;
{
    uprintf("request=0x%x 0x%x(p) completion=0x%x 0x%x(p)\n",
	shm->request, shm->prequest, shm->completion, shm->pcompletion);
    uprintf("req_size=%d comp_size=%d intr_level=%d pwr_act=%d\n",
	shm->req_size, shm->comp_size, shm->intr_level, shm->pwr_action);
    uprintf("req_load=%d req_un=%d comp_load=%d comp_ul=%d prev=%d\n",
	shm->request->load, shm->request->unload,
	shm->completion->load, shm->completion->unload,
	shm->previous_unload);
    uprintf("request queue is%s blocked\n",
	(shm->previous_unload == UL_NOT_BLOCKED ? " not" : ""));
}
#endif

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDARESET()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*      Reset of interface.  Free mbufs if there is		   */
/*      queued output data.                                        */
/*                                                                 */
/*  Call:              ddareset(unit, uban)                        */
/*  Arguments:         unit:   ACP device unit number              */
/*		       uban:   Unibus adapter # (unused, but the   */
/*				kernel placed it on the stack)	   */
/*  Returns:           nothing                                     */
/*  Called by:         network software, address of routine is     */
/*                     defined in dda_if network interface struct  */
/*  Calls to:          DDALOG()                                    */
/*		       hist_link_state()			   */
/*		       hist_all_lcns()				   */
/*		       dda_shm_setup()			           */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*ARGSUSED*/
ddareset(unit, uban)
int             unit;
int             uban;
{
    DMESG(unit, 0, (DDALOG(LOG_ERR) "dda%d: reset\n", unit DDAELOG));
    dda_unit_reset(unit, 1, SYSGEN_VALID);
}

PRIVATE void
bufreset(unit)
int		unit;
{
    DMESG(unit, 0, (DDALOG(LOG_ERR) "dda%d: buffer reset\n", unit DDAELOG));
    dda_unit_reset(unit, 0, SYSGEN_VALID);
}

PRIVATE void
dda_unit_reset(unit, doreset, drvr_state)
int		unit;
int		doreset;
int		drvr_state;
{
    register struct uba_device *ui = ddainfo[unit];
    register struct dda_softc  *ds = &dda_softc[unit];
    register struct dda_cb     *dc;
    register int                lcn;
    struct biic_regs	       *nxv;

    if (unit >= NDDA || (ui == 0 || ui->ui_alive == 0))
	return;

    nxv = (struct biic_regs *) ui->ui_addr;

    ds->dda_if.if_flags &= ~IFF_UP;
    hist_link_state(unit, ds->dda_state, S_DISABLED);
    ds->dda_state = S_DISABLED;

    /* reset the board */
    if (doreset)
	nxv->biic_ctrl = BICTRL_STS | BICTRL_SST;

    DELAY(50000);			/* delay 50ms for BIIC to run tests */

    ds->dda_flags = 0;			/* clear ACP operational flag  */
					/* which will get us ready for B int */

    ds->dda_init &= ~DDA_INTCLOCK;	/* reset internal-clocking-set bit */

    nddach[unit] = NDDACH_DEFAULT;	/* reset SVC limit */

    dc = ds->dda_cb;			/* flush any queued output data */
    /* LCNLINK */
    for (lcn = 0; lcn <= NDDACH; lcn++) {	/* for all LCN's ... */
	dc->dc_inaddr.s_addr = 0;	/* clear remote internet addr */
        dc->dc_key.key_addr.s_addr = 0;
	dc->dc_wsizein = dc->dc_wsizeout = 0;
	dc->dc_pktsizein = dc->dc_pktsizeout = 0;
	dc->dc_state = LC_IDLE;	/* init LCN state */
	dc->dc_timer = TMO_OFF;	/* turn LCN timer off */
	dc->dc_flags = 0;
#ifdef DDADEBUG
	dc->dc_out_t = TMO_OFF;	/* turn FE completion timer off */
#endif
	dc++;
    }

    hist_all_lcns(unit, LC_IDLE);
#ifdef DDA_RAWOPT
    pi_init(unit, 1);
#endif
#ifdef DDA_PADOPT
    x29_init(unit, 1);
#endif
    dda_shm_setup(unit, nxv, drvr_state);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAINIT()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine initializes the interface for operation.  The   */
/*    device control blocks are initialized, UNIBUS resources are  */
/*    allocated and an initialization message is sent to the ACP.  */
/*                                                                 */
/*    Note that interrupt "b" is enabled here to avoid a possible  */
/*    race condition at power up time - it was previously done in  */
/*    the probe and reset routines.                                */
/*                                                                 */
/*  Call:             ddainit(unit)                                */
/*  Argument:         unit:  ACP device unit number                */
/*  Returns:          nothing                                      */
/*  Called by:        network software, address of this routine is */
/*                    defined in dda_if network interface struct   */
/*                    ddaioctl()                                   */
/*                    ddaintb()                                    */
/*  Calls to:         ddatimer()                                   */
/*                    splimp()                                     */
/*                    dda_rrq()                                    */
/*                    splx()                                       */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

ddainit(unit)
int             unit;
{
    register struct dda_softc *ds;
    register struct dda_cb *dc;
    register struct uba_device *ui;

    int             lcn,
                    s;

    ds = &dda_softc[unit];
    ui = (struct uba_device *) ddainfo[unit];
#ifdef DDADEBUG
    if (DDADBCH(0, unit)) {
	DDALOG(LOG_DEBUG) "dda%d: ddainit()\n", unit DDAELOG;
	DDALOG(LOG_DEBUG) "dda%d: ds->dda_if.if_addrlist=0x%x ds->dda_flags=0x%x\n",
			  unit, ds->dda_if.if_addrlist, ds->dda_flags DDAELOG;
    }
#endif DDADEBUG

    /* if we have no internet addr  if device not operational don't init yet */
    if (ds->dda_if.if_addrlist == (struct ifaddr *) 0 ||
	((ds->dda_flags & DDAF_OK) == 0))
	return;

    /* set default options */
    ds->dda_init &= ~DDA_INTCLOCK;	/* reset internal-clocking-set bit */
    nddach[unit] = NDDACH_DEFAULT;	/* reset SVC limit */

    if ((ds->dda_if.if_flags & IFF_RUNNING) == 0) {
	dc = ds->dda_cb;	/* setup ptr to first LCN cntl block */

	/* LCNLINK */
	for (lcn = 0; lcn <= NDDACH; lcn++) {	/* for all LCN's ... */
	    dc->dc_lcn = lcn;	/* record LCN */
	    dc->dc_inaddr.s_addr = 0;	/* clear remote internet addr */
	    dc->dc_key.key_addr.s_addr = 0;
	    dc->dc_wsizein = dc->dc_wsizeout = 0;
	    dc->dc_pktsizein = dc->dc_pktsizeout = 0;
	    dc->dc_state = LC_DOWN;	/* init LCN state */
	    dc->dc_timer = TMO_OFF;	/* turn LCN timer off */
#ifdef DDADEBUG
	    dc->dc_out_t = TMO_OFF;	/* turn FE completion timer off */
#endif

	    /* init LCN output queue */

	    s = splimp();
	    dc->dc_oq.ifq_head = (struct mbuf *) 0;
	    dc->dc_oq.ifq_tail = (struct mbuf *) 0;
	    splx(s);
	    dc->dc_oq.ifq_len = 0;
	    dc->dc_oq.ifq_maxlen = DDA_OQMAX;
	    dc->dc_oq.ifq_drops = 0;

	    /* init HDX channels */

	    dc->dc_rchan.hc_next = (struct hdx_chan *) 0;
	    dc->dc_rchan.hc_chan = lcn * 2;
	    dc->dc_wchan.hc_next = (struct hdx_chan *) 0;
	    dc->dc_wchan.hc_chan = (lcn * 2) + 1;

	    dc->dc_rchan.hc_mbuf = (struct mbuf *) 0;
	    dc->dc_rchan.hc_curr = (struct mbuf *) 0;
	    dc->dc_wchan.hc_mbuf = (struct mbuf *) 0;
	    dc->dc_wchan.hc_curr = (struct mbuf *) 0;

	    dc->dc_flags = 0;	/* initialize flags */

	    dc++;		/* point at next cntl blk */
	}
	hist_all_lcns(unit, LC_DOWN);

	ds->dda_sioq.sq_head = (struct hdx_chan *) 0;
	ds->dda_sioq.sq_tail = (struct hdx_chan *) 0;

	ds->dda_if.if_flags |= IFF_RUNNING;
    }
    s = splimp();
    dc = ds->dda_cb;		/* setup ptr to first LCN cntl block */
    for (lcn = 0; lcn <= nddach[unit]; lcn++) {	/* issue reads on all LCNs */
	dda_rrq(ds, &(dc->dc_rchan));
	dc++;
    }
    splx(s);

    ddatimer(unit);		/* start timers */

#ifdef DDA_RAWOPT
    pi_init(unit, 0);		/* initialize progammer interface */
#endif
#ifdef DDA_PADOPT
    x29_init(unit, 0);
#endif
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAINTA()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This is the interrupt handler for I/O interrupts (interrupt  */
/*    "a") from the ACP device.  The I/O mailboxes are scanned for */
/*    handshake events to process. Two types of interrupts are	   */
/*    processed:  I/O request acknowledge, and I/O completion.     */
/*    The interrupting HDX channel and interrupt type are	   */
/*    obtained. If interrupt is an I/O request acknowledge the	   */
/*    next I/O request is passed to the device.  If the interrupt  */
/*    is an I/O completion, check for errors, if ok process	   */
/*    according to whether supervisory or data channel.		   */
/*                                                                 */
/*  Call:              ddainta(unit)                               */
/*  Arguments:         unit:  ACP device unit number               */
/*  Returns:           nothing                                     */
/*  Called by:         network software, address of this routine   */
/*                     is defined in af_inet network interface     */
/*                     data structure                              */
/*  Calls to:          DDALOG()                                    */
/*                     start_chn()                                 */
/*                     dda_data()                                  */
/*                     dda_supr()                                  */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

ddainta(unit)
int             unit;
{
    register struct dda_softc *ds;
    register struct hdx_chan *hc;
    register struct uba_device *ui;
    CQUEUE         *complq;
    CENTRY         *centry_ptr;
    byte            flags;
    int             chan,
                    cc,
                    subcc,
                    cnt;
    SYSGEN_BLOCK   *SysGenPtr;

    ds = &dda_softc[unit];
    SysGenPtr = (SYSGEN_BLOCK *) ds->dda_mapreg;
    ui = (struct uba_device *) ddainfo[unit];

#ifdef DDADEBUG
    if (DDADBCH(5, unit)) {
	DDALOG(LOG_DEBUG) "dda%d: ddainta()\n", unit DDAELOG;
    }
#endif DDADEBUG

    /***********************************************************************/
    /* Check Request Queue if xmt was blocked				   */
    /***********************************************************************/

    /* was request queue blocked before? (previous unload = a blocked value) */
    if (SysGenPtr->previous_unload != UL_NOT_BLOCKED) {
	/* yes, so check to see if unload pointer has changed */
	if (SysGenPtr->previous_unload != SysGenPtr->request->unload) {
	    /* if the unload pointer changed, we're no longer blocked */
	    SysGenPtr->previous_unload = UL_NOT_BLOCKED;
	    start_chn(ds);			/* re-start I/O */
	}
    }

    /***********************************************************************/
    /* Check I/O Completion Queue                                          */
    /***********************************************************************/

    complq = SysGenPtr->completion;	/* point to completion q */
    centry_ptr = complq->entry + complq->unload;

    /* new entry valid? I/O Completion Mailbox */
    while ((flags = centry_ptr->flags) & CENTRY_VALID) {

#ifdef DDA_MSGQ
	dda_mqstr("(cx)");
#endif

	centry_ptr->flags = 0;	/* clear valid bit */
	/* Get logical channel information. */
	chan = centry_ptr->dpn;
	if (chan > nddach[unit]) {
	    DMESG(unit, 6,
		 (DDALOG(LOG_ERR) "dda%d: unknown completion channel, lcn=%d\n",
		  unit, chan DDAELOG));
	    goto bump_unload;
	}
	if (flags & FLAGS_DIR)
	    hc = &(ds->dda_cb[chan].dc_wchan);	/* write channel */
	else
	    hc = &(ds->dda_cb[chan].dc_rchan);	/* read channel */

	cc = centry_ptr->stat;		/* Mailbox I/O completion status */
	subcc = centry_ptr->sbstat;	/* Mailbox I/O completion substatus */
	cnt = centry_ptr->count;	/* Mailbox I/O completion byte count */

#ifdef	DDADEBUG
	if (DDADBCH(33, ds->dda_if.if_unit)) 
	    prt_bytes(ds->dda_if.if_unit, "incoming data", hc->hc_curr, cnt);
#endif

	switch (cc) {	/* check for unsuccessful I/O completion status */
	case DDAIOCABT:	/* probably VCN flush */
	    if (LOG_ABT)
		DDALOG(LOG_ERR) "dda%d: abort completed on chan %d\n",
		    unit, hc->hc_chan DDAELOG;
	    break;

	case DDAIOCERR:
	    DMESG(unit, 7,
		  (DDALOG(LOG_ERR) "dda%d: program error\n", unit DDAELOG));
	    goto daterr;

	case DDAIOCOVR:
	    DMESG(unit, 8,
		  (DDALOG(LOG_ERR) "dda%d: overrun error\n", unit DDAELOG));
	    goto daterr;

	case DDAIOCUBE:
	    DMESG(unit, 9,
	      (DDALOG(LOG_ERR) "dda%d: transfer count = 0\n", unit DDAELOG));
	    goto daterr;

	case DDAIODMAE:
	    DMESG(unit, 10,
		  (DDALOG(LOG_ERR) "dda%d: DMA completion error (%x)\n",
		   unit, subcc DDAELOG));
	    goto daterr;

	case DDAIOLCOL:
	    DMESG(unit, 11,
		(DDALOG(LOG_ERR) "dda%d: listen collision\n", unit DDAELOG));
	    goto daterr;

	case DDAIOFUNC:
	    DMESG(unit, 12,
		(DDALOG(LOG_ERR) "dda%d: invalid function\n", unit DDAELOG));
	    goto daterr;

	case DDAIODPN:
	    DMESG(unit, 13,
		  (DDALOG(LOG_ERR) "dda%d: invalid dpn\n", unit DDAELOG));
	    goto daterr;

    daterr:
	    DMESG(unit, 14,
	    (DDALOG(LOG_ERR) "lcn=%d func=%x\n", chan, hc->hc_func DDAELOG));
	    if (hc->hc_func & DDARDB)
		ds->dda_if.if_ierrors++;
	    else
		ds->dda_if.if_oerrors++;
	}

	/* was it supervisor or data traffic? */

	if (chan > DDA_SUPR) {
#ifdef DDA_PADOPT
	    if (ds->dda_cb[chan].dc_flags & DC_X29)
		x29_data(ds, hc, cc, cnt, subcc);
	    else
#endif
#ifdef DDA_RAWOPT
	    if (ds->dda_cb[chan].dc_flags & DC_RAW)
		pi_data(ds, hc, cc, cnt, subcc);
	    else
#endif
		dda_data(ds, hc, cc, cnt);
	} else
	    dda_supr(ds, hc, cc, cnt);

bump_unload:
	/* Ack the interrupt.  Fix the Mailbox Ready and Done bits:  set DON
	   bits, and clear RDY bits so mailbox may be reused. */

	complq->unload = (complq->unload + 1) % CQSIZE;
	centry_ptr = complq->entry + complq->unload;
    }
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDAINTB()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*   Service interrupt "b", system interrupt, from the ACP device. */
/*   If the ACP device is operational, interrupt is unexpected,    */
/*   disable the device.  If the interrupt indicates a powerup     */
/*   diagnostic failure, disable the device.  Otherwise, set ACP   */
/*   flag for device operational, enable interrupt a, enable DMA,  */
/*   and perform initialization tasks.                             */
/*                                                                 */
/*  Call:             ddaintb(unit)                                */
/*  Argument:         unit: DDA device unit number                 */
/*  Returns:          nothing                                      */
/*  Called by:        network software, address of this routine is */
/*                    defined in af_inet network interface struct  */
/*  Calls to:         DDALOG()                                     */
/*                    ddainit()                                    */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

ddaintb(unit)
int unit;
{
    register struct dda_softc *ds = &dda_softc[unit];
    register int    lcn;
    register struct mbuf *m;
    register struct hdx_chan *hc;
    register struct dda_cb *dc;
    struct biic_regs *nxv = (struct biic_regs *) ddainfo[unit]->ui_addr;
    int               stat_val;

    stat_val = ((GP_REG3_USAGE *) &nxv->biic_gpr3)->fe_diag_status;
   
#ifdef DDADEBUG
    if (DDADBCH(6, unit)) {
	DDALOG(LOG_DEBUG) "dda%d: ddaintb()\n", unit DDAELOG;
    }
#endif DDADEBUG

    if (ds->dda_flags & DDAF_OK) {
	DMESG(unit, 15,
	      (DDALOG(LOG_ERR) "dda%d: asynchronous restart, status = %d\n",
	       unit, stat_val DDAELOG));
	ds->dda_flags = 0;
	ds->dda_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
	hist_link_state(unit, ds->dda_state, S_DISABLED);
	ds->dda_state = S_DISABLED;
	ddareset(unit, 0);
	return;
    }

    if ((stat_val & DDASTAT_ERR) != 0) {
	DMESG(unit, 16,
	      (DDALOG(LOG_ERR) "dda%d: Diagnostic failure = %d\n",
	       unit, stat_val DDAELOG));
	dda_disable(unit);
    } else if (stat_val == DDASTAT_NMC) {
	DMESG(unit, 17,
	  (DDALOG(LOG_ERR) "dda%d: No Microcode Present!\n", unit DDAELOG));
	dda_disable(unit);
    } else if (stat_val == DDASTAT_OK) {
	SYSGEN_BLOCK *SysGenPtr = (SYSGEN_BLOCK *) ds->dda_mapreg;
	ds->dda_flags |= DDAF_OK;	/* no longer expecting a B interrupt */

	for (lcn = 0; lcn <= NDDACH; lcn++) {	/* for all LCNs */
	    dc = &dda_softc[unit].dda_cb[lcn];
	    while (dc->dc_oq.ifq_len) {		/* clear output queue */
		IF_DEQUEUE(&dc->dc_oq, m);
		if (m)
		    m_freem(m);
	    }
	    hc = &dc->dc_rchan;
	    if (hc->hc_mbuf) {
		m_freem(hc->hc_mbuf);		/* free read mbufs */
		hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	    }
	    hc = &dc->dc_wchan;
	    if (hc->hc_mbuf) {
		m_freem(hc->hc_mbuf);		/* free write mbufs */
		hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
	    }
	}

	SysGenPtr->request->load = 0;
	SysGenPtr->request->unload = 0;
	SysGenPtr->completion->load = 0;
	SysGenPtr->completion->unload = 0;
	bzero(SysGenPtr->completion->entry, sizeof(CENTRY) * CQSIZE);
	bzero(SysGenPtr->request->entry, sizeof(RENTRY) * RQSIZE);
	SysGenPtr->pwr_action = 0xff;	/* we are operational */

	/* this 10ms delay is here to give the FEP enough time to re-program
	   its interrupt vector to point to the A interrupt routine and
	   reset the MFP interrupt counter before the driver posts the
	   NDDACH reads in ddainit.  It's a kludge, but keep it for safety */

	DELAY(10000);			/* delay 10 miliseconds */
	ddainit(unit);
    }
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDA_WRQ()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    Process write requests.  Put I/O request values in           */
/*    half-duplex control channel structure:  set function code    */
/*    for write to ACP with Transfer Grant set.  If there are no   */
/*    more mbufs in chain, mark DDAEOS for end of stream.  Set     */
/*    count from data length (byte count) in mbuf, and subfunction */
/*    is zero.  If the COMREGs are busy, queue for start later.    */
/*                                                                 */
/*  Call:            dda_wrq(ds, hc, abt)                          */
/*  Argument:        ds:   pointer to device control block struct  */
/*                   hc:   pointer to half-duplex channel cntl blk */
/*		     abt:  indication of whether request is an     */
/*			   abort request.			   */
/*  Returns:         nothing                                       */
/*  Called by:         dda_start()                                 */
/*                     dda_data()                                  */
/*  Calls to:          mtod()                                      */
/*                     start_chn()                                 */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
dda_wrq(ds, hc, abt)
struct dda_softc *ds;
register struct hdx_chan *hc;
u_char          abt;
{
    register struct mbuf *m;
    register int    s;

    if (abt)
	hc->hc_func = DDAABT;
    else
	hc->hc_func = DDAWRT;

#ifdef DDADEBUG
    if (DDADBCH(15, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: dda_wrq: chan=%d func=%x\n",
	    ds->dda_if.if_unit, hc->hc_chan, hc->hc_func DDAELOG;
    }
#endif DDADEBUG

    s = splimp();

    /*
     * If ACP comm regs busy, queue start i/o for later. 
     */
    if (ds->dda_sioq.sq_head) {
	(ds->dda_sioq.sq_tail)->hc_next = hc;
	ds->dda_sioq.sq_tail = hc;
	hc->hc_next = 0;
	splx(s);
	return;
    }
    /* start i/o on channel now */

    ds->dda_sioq.sq_head = hc;
    ds->dda_sioq.sq_tail = hc;
    hc->hc_next = 0;
    splx(s);
    (void) start_chn(ds);
}

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      DDA_RRQ()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    Process read requests.  Quit if attempt to get an mbuf is    */
/*    unsuccessful.  Put I/O request values in half-duplex control */
/*    channel structure:  set function code for read from ACP with */
/*    Transfer Grant set, set count from data length (byte count)  */
/*    in mbuf, and subfunction is zero.                            */
/*                                                                 */
/*  Call:            dda_rrq(ds, hc)                               */
/*  Argument:        ds:   pointer to device control block struct  */
/*                   hc:   pointer to half-duplex control chan     */
/*  Returns:         nothing                                       */
/*  Called by:       ddainit()                                     */
/*                   dda_data()                                    */
/*                   dda_supr()                                    */
/*  Calls to:        MGET()                                        */
/*                   MCLGET()                                      */
/*                   DDALOG()                                      */
/*                   start_chn()                                   */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE void
dda_rrq(ds, hc)
struct dda_softc *ds;
register struct hdx_chan *hc;
{
    register struct mbuf *m;
    register int    s;
#if ACC_ULTRIX > 12
    struct mbuf    *p;
#endif

#ifdef DDADEBUG
    if (DDADBCH(16, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: dda_rrq()\n", ds->dda_if.if_unit DDAELOG;
    }
#endif DDADEBUG

    MGET(m, M_DONTWAIT, MT_DATA);
    if (m == 0) {
	DMESG(ds->dda_if.if_unit, 25, (DDALOG(LOG_ERR) 
	    "dda%d:  couldn't get buffer for read\n",
	    ds->dda_if.if_unit DDAELOG));
	return;
    }

    s = splimp();

    /* hc_mbuf set to zero during initialization */

    /* if hc->hc_mbuf is zero, then this is the first mbuf in the chain,
       so be conservative and only queue up a small mbuf */

    if (hc->hc_mbuf == 0) {
	m->m_len = MLEN;	/* set the size to a small mbuf */
	hc->hc_mbuf = m;
	hc->hc_curr = m;

    /* if it's not the first mbuf in the chain, we may be running a FTP
       or something that deserves higher performance, so queue a cluster */

    } else {
#if	ACC_ULTRIX > 12
	MCLGET(m, p);		/* associate a page cluster with this mbuf */
#else
	MCLGET(m);
#endif
	hc->hc_curr->m_next = m;
	hc->hc_curr = m;
	m->m_next = 0;
    }
    splx(s);

    hc->hc_func = DDARDB + DDASTR;
    hc->hc_sbfc = 0;

    s = splimp();

    /*
     * If ACP comm regs busy, queue start i/o for later. 
     */
    if (ds->dda_sioq.sq_head) {
	(ds->dda_sioq.sq_tail)->hc_next = hc;
	ds->dda_sioq.sq_tail = hc;
	hc->hc_next = 0;
	splx(s);
	return;
    }
    /* start i/o on channel now */

    ds->dda_sioq.sq_head = hc;
    ds->dda_sioq.sq_tail = hc;
    hc->hc_next = 0;
    splx(s);
    (void) start_chn(ds);
}





/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      START_CHN()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine copies ACP I/O requests into the ACP            */
/*    Communications Registers (COMREGs) and notifies the ACP.     */
/*    If the channel number is odd, indicating write, then the     */
/*    direction flag is set to indicate a transfer from the host   */
/*    to the front end.                                            */
/*                                                                 */
/*  Call:              start_chn(ds)                               */
/*  Argument:          ds:  pointer to device control block struct */
/*  Returns:           nothing                                     */
/*  Called by:         ddainta()                                   */
/*                     dda_rrq()                                   */
/*                     dda_wrq()                                   */
/*  Calls to:          none                                        */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

PRIVATE int
start_chn(ds)
struct dda_softc *ds;
{
    register struct hdx_chan *hc;
    register int    s;
    struct uba_device *ui;
    struct biic_regs *nxv;
    RQUEUE         *requestq;
    short           next_load;
    SYSGEN_BLOCK   *SysGenPtr;

    SysGenPtr = (SYSGEN_BLOCK *) (ds->dda_mapreg);
    ui = (struct uba_device *) (ddainfo[ds->dda_if.if_unit]);
    nxv = (struct biic_regs *) ui->ui_addr;

#ifdef DDADEBUG
    if (DDADBCH(17, ds->dda_if.if_unit)) {
	DDALOG(LOG_DEBUG) "dda%d: start_chn()\n", ds->dda_if.if_unit DDAELOG;
    }
#endif DDADEBUG

    s = splimp();

    while (hc = (struct hdx_chan *) (ds->dda_sioq.sq_head)) {
	requestq  = SysGenPtr->request;
	next_load = (requestq->load + 1) % RQSIZE;

	if (next_load == requestq->unload) {	/* any room left in q? */
	    /* no, say we're blocked */
	    SysGenPtr->previous_unload = requestq->unload;
	    break;
	}
/*
 *  Check sioq for invalid writes
 */

	/* if write channel, but not supervisor, and it's marked invalid,
	   then either drop or abort it */
	if ((hc->hc_chan & 0x01) && (hc->hc_chan != 1) &&
	    (hc->hc_inv & INVALID_MBUF)) {
	    if (ds->dda_cb[hc->hc_chan >> 1].dc_flags & DC_OBUSY) {
#ifdef DDADEBUG
		if (DDADBCH(28, ds->dda_if.if_unit)) {
		    DDALOG(LOG_DEBUG) "dda%d: start_chn: aborting chan %d\n",
			ds->dda_if.if_unit, hc->hc_chan DDAELOG;
		}
#endif DDADEBUG
		goto send;	/* send an abort if output not complete */
	    } else {		/* otherwise free it */
		if (hc->hc_mbuf) {	/* free pending request */
		    m_freem(hc->hc_mbuf);
		    hc->hc_mbuf = hc->hc_curr = (struct mbuf *) 0;
		}
/*
 * Restart Output
 */
		hc->hc_inv &= ~INVALID_MBUF;
		ds->dda_sioq.sq_head = ds->dda_sioq.sq_head->hc_next;
		/* The following call may cause a re-entrant call to
		   start_chn, but only if the sioq is currently empty. In
		   that case, the re-entrant call is ok */
		dda_start(ds, &ds->dda_cb[hc->hc_chan >> 1]);
		continue;
	    }
	}
send:
	{			/* Read or Write request is valid */
	    RENTRY         *rentry_ptr;
	    int             index;

	    /* Set up queue element */
	    rentry_ptr = requestq->entry + requestq->load;

	    /* Fill the slots starting at hc->hc_curr */
	    rentry_ptr->count  = 0;
	    index              = 0;

#ifdef DDADEBUG
    if (DDADBCH(33, ds->dda_if.if_unit)) {
	struct mbuf *m;
	int    i;
	DDALOG(LOG_DEBUG)
	    "hc->hc_mbuf=0x%x hc->hc_curr=0x%x\n", hc->hc_mbuf, hc->hc_curr
	DDAELOG;
	for (i = 0, m = hc->hc_mbuf; m && m != hc->hc_curr; i++, m = m->m_next)
	    DDALOG(LOG_DEBUG)
		"previous(%d): 0x%x\n", i, m
	    DDAELOG;
	for (i = 0, m = hc->hc_curr; m; i++, m = m->m_next) 
	    DDALOG(LOG_DEBUG)
	        "chain(%d): 0x%x\n", i, m
	    DDAELOG;
    }
#endif DDADEBUG

	    /* if it's a write, map as many mbufs on as we can */
	    if (hc->hc_chan & 1) {
		register struct mbuf *last_mapped;
		while (hc->hc_curr) {
		    register int   len, seglen, pages;
		    register char *addr;

		    addr  = mtod(hc->hc_curr, char *);
		    len   = hc->hc_curr->m_len;
		    pages = (len - 1 >> PGSHIFT) + 1;

		    if (len == 0) 
			printf("start_chn: len was zero?!?\n");

		    /* If we have enough slots left in this entry to map this
		       mbuf,  then do so */

		    if (NUM_BI_ADDR - index > pages) {
			while ((seglen = MIN(NBPG, len)) > 0) {
			    rentry_ptr->BI_address[index] = svtophy(addr);
			    rentry_ptr->BI_count[index]   = seglen;
			    rentry_ptr->count            += seglen;
			    len  -= seglen;
			    addr += seglen;
			    index++;
			}
			/* we've mapped this mbuf onto the queue, deal with next */
			last_mapped = hc->hc_curr;
			hc->hc_curr = hc->hc_curr->m_next;

		    } else {
			/* if this buffer can't ever fit, there is a major
			   problem (someone changed the size of a large mbuf
			   so it can't fit in NUM_BI_ADDR memory pages).  This
			   should never happen. */
		       if (index == 0)
			    panic("dda driver: mbuf too large to map\n");
		       break;
		    }
		}

		/* if more mbufs to map, we're not at the end of stream */
		if (hc->hc_curr)
		    hc->hc_func |= DDASTR;
		else
		    hc->hc_func |= DDAEOS;

		/* higher up routines expect hc->hc_curr to be the last
		   mapped mbuf */
		hc->hc_curr = last_mapped;

	    } else {	/* otherwise it's a read, so process the one mbuf */

		register int   len, seglen, pages;
		register char *addr;

		addr  = mtod(hc->hc_curr, char *);
		len   = hc->hc_curr->m_len;
		pages = (len - 1 >> PGSHIFT) + 1;

		/* if it's a read, zero out the mbuf's m_len field because
		   the mbuf currently contains no valid data */

		hc->hc_curr->m_len = 0;

		if (len == 0) 
		    printf("start_chn: len was zero?!?\n");

		/* If we have enough slots left in this entry to map this
		   mbuf,  then do so */

		if (NUM_BI_ADDR >= pages) 
		    while ((seglen = MIN(NBPG, len)) > 0) {
			rentry_ptr->BI_address[index] = svtophy(addr);
			rentry_ptr->BI_count[index]   = seglen;
			rentry_ptr->count            += seglen;
			len  -= seglen;
			addr += seglen;
			index++;
		    }

		/* if this buffer can't ever fit, there is a major problem
		   (someone changed the size of a large mbuf so it can't
		   fit in NUM_BI_ADDR memory pages).  This should never
		   happen. */
		else 
		   panic("dda driver: mbuf too large to map\n");
	    }

	    rentry_ptr->opcode = CMD_MPCPRQS;
	    rentry_ptr->dpn    = hc->hc_chan >> 1;
	    rentry_ptr->func   = hc->hc_func;
	    rentry_ptr->sbfunc = hc->hc_sbfc;
		
#ifdef DDADEBUG
	    if (DDADBCH(34, ds->dda_if.if_unit)) {
		int i;
		DDALOG(LOG_DEBUG)
		    "dpn=0x%x func=0x%x sbfunc=0x%x count=0x%x index=%d\n",
		    rentry_ptr->dpn, rentry_ptr->func, rentry_ptr->sbfunc,
		    rentry_ptr->count, index
		DDAELOG;
		for (i = 0; i < NUM_BI_ADDR; i++)
		    DDALOG(LOG_DEBUG)
			"addr[%d]=0x%x count[%d]=0x%x\n",
			rentry_ptr->BI_address[i], rentry_ptr->BI_count[i]
		    DDAELOG;
	    }
#endif DDADEBUG

#ifdef DDADEBUG
	    if (hc->hc_chan & 1) {	/* write */
		struct dda_cb  *dc;
		dc = &ds->dda_cb[hc->hc_chan >> 1];
		if (DDADBCH(17, ds->dda_if.if_unit))
		    DDALOG(LOG_DEBUG)
			"dda%d: start_chn: WRITE on lcn %d func %x\n",
			ds->dda_if.if_unit, hc->hc_chan >> 1,
			hc->hc_func DDAELOG;

		if (dc->dc_lcn)			/* don't start timer on lcn 0 */
		    dc->dc_out_t = TMO_RESTART;	/* Wait 90 sec for completion */
	    }
	    if (DDADBCH(30, ds->dda_if.if_unit))
		DDALOG(LOG_DEBUG) "dda%d: interrupting FE\n",
				  ds->dda_if.if_unit
		DDAELOG;
#endif DDADEBUG

	    /* TELL 7000 about new entry in request queue */
	    requestq->load = next_load;		/* boom! */
	    nxv->biic_gpr0 = SYSGEN_VALID;	/* interrupt FE */

#ifdef SIMULATION
	    MFP_COUNTER_SIM();	/* simulate hardware action */
#endif

	}
next_sioq_element:
	ds->dda_sioq.sq_head = ds->dda_sioq.sq_head->hc_next;
    }
    splx(s);
    return;
}

/*
 * disable interrupts and forget about unit
 */

PRIVATE void
dda_disable(unit)
int             unit;
{
    struct uba_device *ui = ddainfo[unit];
    struct biic_regs *nxv;

    nxv = (struct biic_regs *) ui->ui_addr;

    nxv->biic_int_dst = 0;
}

PRIVATE int
dda_dload(unit, dl)
int		unit;
struct dda_dnload *dl;
{
    register struct uba_device *ui = ddainfo[unit];
    register int i;
    static unsigned char *dlbuf[RQSIZE];

    if (unit >= NDDA)
	return EFAULT;

    switch (dl->lcommand) {
	case DN_LCMD_SETUP:
	    /* allocate buffers, reset board/setup shared memory */
	    for (i = 0; i < RQSIZE; i++) {
		KM_ALLOC(dlbuf[i], unsigned char *, 512, KM_CLUSTER, KM_NOWAIT);
		if (dlbuf[i] == NULL)
		    return ENOMEM;
	    }
 
	    dda_unit_reset(unit, 1, SYSGEN_DLOAD);
	    DMESG(unit, 0,
	      (DDALOG(LOG_ERR) "dda%d: download mode\n", unit DDAELOG));
	    return 0;

        case DN_LCMD_CLEANUP:
	    /* deallocate buffers and restore shm to normal */
	    for (i = 0; i < RQSIZE; i++) 
		if (dlbuf[i])
		    KM_FREE(dlbuf[i], KM_CLUSTER);

	    dda_shm_setup(unit, (struct biic_regs *) ui->ui_addr, SYSGEN_VALID);
	    DMESG(unit, 0,
	      (DDALOG(LOG_ERR) "dda%d: operational mode\n", unit DDAELOG));

	    /* now we will expect a B interrupt from the unit which will
	       cause ddainit to be called and set up the driver */
	    return 0;

	case DN_LCMD_FEOP:
        {
		/* perform a front end operation */
   		struct dda_softc *ds = &dda_softc[unit];
    		struct biic_regs *nxv = (struct biic_regs *) ui->ui_addr;
	        register RQUEUE         *requestq;
		register RENTRY         *rentry_ptr;
		register unsigned	 checksum;
		register unsigned char	*buffer;
		short	                 next_load;

		requestq  = ((SYSGEN_BLOCK *) ds->dda_mapreg)->request;
		next_load = (requestq->load + 1) % RQSIZE;

		if (next_load == requestq->unload) { /* any room left in q? */
		    /* no, say we're blocked */
		    return ENOMEM;
		}

		rentry_ptr = requestq->entry + requestq->load;

		switch (dl->type) {
		    case DN_TYPE_DATA:
		    case DN_TYPE_ID:
		    case DN_TYPE_VER:
		    case DN_TYPE_COPY:
#ifdef DDADEBUG
			if (DDADBCH(29, unit))
			    DDALOG(LOG_DEBUG)
				"dda%d: xfr type=%d buf=%d len=%d dest=0x%x\n",
				unit, dl->type, requestq->load,
				dl->len, dl->dest
			    DDAELOG;
#endif DDADEBUG
			buffer   = dlbuf[requestq->load];	

			if (copyin(dl->data, buffer, dl->len)) 
			    return EFAULT;
			/* Set up queue element */
			rentry_ptr->opcode        = dl->type;
			rentry_ptr->count         = dl->len;
			rentry_ptr->BI_address[0] = svtophy(buffer);
			rentry_ptr->BI_address[1] = dl->dest;

			for (checksum = 0, i = dl->len; i--; ) 		
			    checksum += *buffer++;

			rentry_ptr->BI_address[2] = checksum;
			rentry_ptr->BI_address[2] = 0;
			break;

		    case DN_TYPE_XFR:
#ifdef DDADEBUG
			if (DDADBCH(29, unit))
			    DDALOG(LOG_DEBUG)
				"dda%d: execute buf=%d addr=0x%x\n",
				unit, requestq->load, dl->dest
			    DDAELOG;
#endif DDADEBUG

			rentry_ptr->opcode	  = dl->type;
			rentry_ptr->BI_address[1] = dl->dest;
			break;
		}			

		/* TELL 7000 about new entry in request queue */
		requestq->load = next_load;	/* boom! */
		nxv->biic_gpr0 = SYSGEN_DLOAD;	/* interrupt FE */
#ifdef SIMULATION
		MFP_COUNTER_SIM();	/* simulate hardware action */
#endif

		return 0;
	}
	default:
	    return EFAULT;
    }
}

/*

Revision history:

30-Nov-1989	Paul Traina
	First cut for beta release.

*/
c->hc_func DDAELOG;

		if (dc->dc_lcn)			/* don't start timer on lcn 0 */
		    dc->dc_out_t = TMO_RESTART;	/* Wait 90 sec for completion */
	    }
	    if (DDADBCH(30, ds->dda_if.if_unitdriver/README   644    540     24         760  4535574376   6264 
Some of the files in this directory:

if_dda.c		-- the main driver module
if_dda_uqbus.c		-- included module for ACP5250/ACP6250 boards
if_dda_bibus.c		-- included module for ACP7250 only
if_ddareg.h		-- dda register descriptions
if_ddavar.h		-- shared data between acpconfig and the driver

if_pi.c			-- programmer's interface option
if_pivar.h		-- include file for PI for pre-4.3+tahoe systems
if_pivar.h.tahoe	-- include file for PI for 4.3+tahoe and 4.4bsd
if_x29.c		-- X29 interface option
bout new entry i                                                */
/*                                                                       */
/*  File:		if_dda_bibus.c                                   */
/*			BI bus support routines for dda			 */
/*                                                                       */
/*  Project:		DDN-X.25 Network Interface Driver for ACP 7250   */
/*                                                                       */
/*  revision history at the end of if_dda.c				 */
/*************************************************************************/

#include "../vaxif/if_uba.h"
#include "../vaxuba/ubavar.h"
#include "../vaxbi/bireg.h"
#include "../vaxif/if_ddabique.h"

#ifdef	SIMULATION
#define	KM_CLUSTER	28
#define	KM_NOWAIT	1
#define	KM_ALLOC(space, cast, size, type, flags) { \
	(space) = (cast)malloc(size); \
}
#define	KM_FREE(addr, type) { \
	(void)free((caddr_t)(addr)); \
}
#undef svtophy
#define svtophy(x)	((int)(x))
#endif

/*
 *	private functions in this module
 */

PRIVATE void