4.4BSD/usr/src/sys/vax/if/if_acp.c

Compare this file to the similar file:
Show the results in this format:

/*-
 *	@(#)if_acp.c	7.3 (Berkeley) 12/16/90
 */

/*************************************************************************/
/*                                                                       */
/*                                                                       */
/*       ________________________________________________________        */
/*      /                                                        \       */
/*     |          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) 1985 by Advanced Computer Communications           */
/*  	720 Santa Barbara Street, Santa Barbara, California  93101       */
/*  	(805) 963-9431                                                   */
/*                                                                       */
/*                                                                       */
/*  File:		if_acp.c                                         */
/*                                                                       */
/*  Author:		Arthur Berggreen                                 */
/*                                                                       */
/*  Project:		ACP6100 (UPB with HDLC firmware)                 */
/*                                                                       */
/*  Function:		4.2BSD UNIX Network Interface Driver for ACP6100 */
/*                                                                       */
/*  Components:		if_acp.c, if_acpreg.h, if_acpvar.h               */
/*                                                                       */
/*  Revision History:                                                    */
/*                                                                       */
/*    16-AUG-1985  Clare Russ:  add fileheader and comments              */
/*    24-SEP-1985  Clare Russ:  modify for socket ioctl user interface   */
/*    06-NOV-1985  Clare Russ:  modify for socket ioctl under TWG        */
/*    11-NOV-1985  Clare Russ:  Add a call to acpreset() in acpioctl()   */
/*         before processing socket ioctl to clear COMREGs.  In the      */
/*         acpinit() routine, avoid redundant allocation of UMRs by      */
/*         doing so only if the front end is not RUNNING.                */
/*    14-NOV-1985  Clare Russ:  Trace if_ubainit failure:  happens with  */
/*         TWG, not 4.2BSD.                                              */
/*    21-NOV-1985  Clare Russ:  Modify for compliance with the new       */
/*         Control Interface (CIF) and Access Path Allocation Protocol   */
/*         (APAP).  The CIF requires that Control Interface Messages     */
/*         (CIMs) are exchanged between the host and front end in        */
/*         command/response pairs.  The APAP requires that the control   */
/*         and data paths be established (via exchange of CIMs between   */
/*         the host and the front end) prior to use.                     */
/*    26-NOV-1985  Clare Russ:  Add ability to bring down line in        */
/*         response to 'acpconfig' command.                              */
/*    27-NOV-1985  Clare Russ:  Add ability to specify DTE or DCE mode   */
/*         in response to 'acpconfig' command.                           */
/*    02-DEC-1985  Clare Russ:  Add ability to set baud rate (external   */
/*         clock) or set internal clock.                                 */
/*    14-JAN-1986  Clare Russ:  Add acpinit call to acpioctl under       */
/*         SIOCSIFADDR processing                                        */
/*    21-JAN-1986  Clare Russ:  Flush pending I/O in acpreset, free the  */
/*         mbufs                                                         */
/*    30-MAY-1986  Clare Russ:  Update MPCP host request subfunction     */
/*         values, fix baud rate values in baud_rate[], change default   */
/*         clock source from internal to external (in ssp_msg[])         */
/*    24-JUL-1986  Clare Russ:  In supr_msg() print out RSF field when   */
/*         path allocation or deallocation fails                         */
/*    23-FEB-1987  Jeff Berkowitz: port to 4.3BSD by adding #ifdefs for  */
/*	   new interface address formats, trapping 0 length mbufs, etc.  */
/*    08-JAN-1988  Brad Engstrom:  port to ULTRIX 2.0 by using the       */
/*	   UBAUVII (ultrix 2.0) and MVAX (microvax) defines to handle    */
/*         special cases.  These cases are:                              */
/*         1) not declaring br, cvec as value-result in the probe routine*/
/*         2) using 0x17 as the ipl for a microvax                       */
/*         3) in all other cases the ULTRIX drivers behaves like a 4.3   */
/*            driver.                                                    */
/*                                                                       */
/*  Usage Notes:                                                         */
/*                                                                       */
/*    device acp0 at uba0 csr 016700 flags 0 vector acpinta acpintb      */
/*                                                                       */
/*         The 'flags' value is nonzero in the configuration file        */
/*         for TWG, and may be left as zero in the configuration         */
/*         file for UNIX 4.2 BSD.                                        */
/*                                                                       */
/*  Application Notes:	                                                 */
/*                                                                       */
/*    Refer to the Installation Instructions and the UNIX Programmer's   */
/*    Manual page which are on the driver distribution medium.           */
/*                                                                       */
/*                                                                       */
/*************************************************************************/


/* #define ACPDEBUG 1	/* define for debug printf statements */

#ifdef ACPDEBUG
int acp_debug = 0;	/* acp_debug is 1-8 for increasing verbosity */
#endif ACPDEBUG

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

/* The number of ACP 6100s in the system is defined in the configuration */
/* file in /sys/conf.  When 'config' is run, the file acp.h is created   */
/* with the definition of NACP, the number of ACP 6100s in the system.   */

#include "acp.h"
#if NACP > 0
#include "../include/pte.h"

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

#include "net/if.h"
#include "net/netisr.h"
#include "net/route.h"
#include "netinet/in.h"
#include "netinet/in_systm.h"
#ifndef FOURTWO
# include "netinet/in_var.h"
#endif
#include "netinet/ip.h"
#include "netinet/ip_var.h"

#include "../include/cpu.h"
#include "../include/mtpr.h"
#include "../if/if_acpreg.h"
#include "../if/if_acpvar.h"
#include "../if/if_uba.h"
#include "../uba/ubareg.h"
#include "../uba/ubavar.h"

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

int acpprobe();
int acpattach();
int acpreset();
int acpinit();
int acpoutput();
int acptimer();		/* currently no timer routine exists   */
int acpioctl();
int acpinta();
int acpintb();
int acpstart();

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

static void acp_alloc();	/* allocate control and data paths       */
static void acp_init();		/* send Set System Parameters Message    */
static void acp_iorq();
static void start_chn();
static void acp_data();
static void acp_response();	/* send CIM response to the front end    */
static void acp_supr();
static void supr_msg();
static void send_supr();

#ifdef ACPDEBUG
static void prt_addr();
static void prt_bytes();
#endif ACPDEBUG

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                                                                   %%*/
/*%%                         LOCAL VARIABLES                           %%*/
/*%%                                                                   %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

struct	uba_device *acpinfo[NACP];	/* ptrs to device info           */
u_short	acpstd[] = { 0767000, 0 };	/* standard UNIBUS CSR addresses */
struct	uba_driver acpdriver =		/* device driver info            */
  {
    acpprobe,				/* device probe routine */
    0,					/* slave probe routine */
    acpattach,				/* device attach routine */
    0,					/* "dmago" routine */
    acpstd,				/* device address */
    "acp",				/* device name */
    acpinfo				/* ptr to device info ptrs */
  };

/* The alloc_msg array contains the Command Interface Message (CIM)    */
/* for path allocation.  There are 12 bytes of header followed by 6    */
/* bytes of command information                                        */

static u_char alloc_msg[] =
  {
    0x00,				/* reserved, must be zero      */
    FAC_ALLOC,				/* front end ALLOC facility    */
    0x00,				/* reserved, must be zero      */
    CMD_ALLOC,				/* allocate path command       */
    0x0f, 0x0a, 0x0c, 0x0e,		/* Command ID (CID)            */
    0x00, 0x00, 0x00, 0x00,		/* Response/Status Field (RSF) */
    0x00, ACP_SUPR,			/* Data Path Number (DPN)      */
    0x00, FAC_HDLC,			/* front end HDLC facility     */
    0x00, TYPE_CNTL			/* type of path:  control      */
  };


/* The dealloc_msg array contains the Command Interface Message (CIM)  */
/* for path deallocation.  There are 12 bytes of header followed by 2  */
/* bytes of command information                                        */

static u_char dealloc_msg[] =
  {
    0x00,				/* reserved, must be zero      */
    FAC_ALLOC,				/* front end ALLOC facility    */
    0x00,				/* reserved, must be zero      */
    CMD_DEALLOC,			/* allocate path command       */
    0x0c, 0x0a, 0x0f, 0x0e,		/* Command ID (CID)            */
    0x00, 0x00, 0x00, 0x00,		/* Response/Status Field (RSF) */
    0x00, ACP_SUPR,			/* Data Path Number (DPN)      */
  };


/* Table of baud rate values and the associated parameter for the Set  */
/* System Parameters message, ssp_msg.  The second byte is nonzero for */
/* valid baud rate divisors.                                           */

struct	baud	{
	char	b_value;
	u_char	parameter1;	/* first byte of baud rate setting  */
	u_char	parameter2;	/* second byte of baud rate setting */
}	baud_rate[] =	{
	{ 1,	0x00, 	0x02 },
	{ 2,	0x00, 	0x03 },
	{ 3,	0x00, 	0x04 },
	{ 4,	0x00, 	0x08 },
	{ 5,	0x00, 	0x10 },
	{ 6,	0x00, 	0x28 },
	{ 7,	0x00, 	0x3e },
	{ 8,	0x00, 	0x47 },
	{ 9,	0x00, 	0x85 },
	{ 10,	0x00, 	0xd0 },
	{ 11,	0x01, 	0xa1 },
	{ 12,	0x03, 	0x41 },
	{ 13,	0x06,	0x83 },
	{ 14,	0x0d, 	0x05 },
	{ 0,	0,	0 },
};

/* The ssp_msg array contains the Command Interface Message (CIM) for  */
/* Setting HDLC System Paramters.  There are 12 bytes of header        */
/* followed by the line number and parameter modification commands     */
/* (PMCs).  The driver sends this CIM to the front end when kicked by  */
/* the acpconfig program (via socket ioctl).  In future versions, the  */
/* CIM won't be here in the driver, it will be passed to the driver.   */

u_char ssp_msg[] =
  {
    0x00,				/* reserved, must be zero      */
    FAC_HDLC,				/* front end HDLC facility     */
    0x00,				/* reserved, must be zero      */
    CMD_SSP,				/* set HDCL system parameters  */
    0x0b, 0x0e, 0x0e, 0x0f,		/* Command ID (CID)            */
    0x00, 0x00, 0x00, 0x00,		/* Response/Status Field (RSF) */
    0x00, 0x00,				/* HDLC Line Number (0)        */
    LINK_DISABLE,			/* link disable                */
    LINK_LOOPBACK,			/* loopback mode               */
    LOOP_EXTERNAL,			/*   external loopback         */
    DCE_OR_DTE,				/* specify DTE or DCE mode     */
    DTE_MODE,				/*   DTE mode                  */
    BAUD_CNTL,				/* baud rate divisor           */
    0x00,				/*                             */
    0x03,				/*  3 = 1.333 Mb/sec           */
    IDLE_POLL,				/* idle poll selection         */
    0x01,				/*  1 = on                     */ 
    CLOCK_CNTL,				/* xmit clock selection        */
    0x00,				/*  0 = external source        */
    LINK_ENABLE				/* link enable                 */
  };

/* The response_msg array contains the Command Interface Message (CIM) */
/* response to be sent back to the front end in response to a CIM      */
/* command for Frame Level Status from the front end.   The front end  */
/* sends the Frame Level Status CIM command to the host when the frame */
/* level status changes from up to down or vice versa.  In keeping     */
/* with the philosophy with CIMs, they are always exchanged in command */
/* response pairs.                                                     */

static u_char response_msg[] =
  {
    0x00,				/* reserved, must be zero      */
    FAC_HDLC,				/* front end HDLC facility     */
    0x00,				/* reserved, must be zero      */
    RSP_FLUP,				/* Frame Level Status          */
    0x00, 0x00, 0x00, 0x00,		/* Command ID (CID)            */
    0x00, 0x00, 0x00, 0x00,		/* RSF is 0 for success        */
    0x00, 0x00				/* HDLC Line Number  (0)       */
  };


/***********************************************************************\
*									*
*	Information for each device unit is maintained in an array	*
*	of structures named acp_softc[].  The array is indexed by	*
*	unit number.  Each entry includes the network interface		*
*	structure (acp_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 communication with the ACP is maintained, 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 */
    u_char		hc_chan;	/* HDX channel number */
    u_char		hc_adx;		/* address bits 17-16 */
    u_short		hc_addr;	/* address bits 15-00 */
    u_short		hc_cnt;		/* byte count */
    u_char		hc_func;	/* I/O function */
    u_char		hc_sbfc;	/* I/O subfunction */
  };

struct acp_cb		/* Logical Channel control block */
  {
    u_char		dc_lcn;		/* LCN number */
    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 */
    struct ifuba	dc_ifuba;	/* UNIBUS resources */
    u_short		dc_flags;	/* misc flags */
  };

struct acp_softc	/* device control structure */
  {
    struct ifnet	acp_if;		/* network-visible interface   */
    struct acp_cb	acp_cb[NACPCH+1]; /* Logical Channel cntl blks */
    struct sioq		acp_sioq;	/* start I/O queue             */
    u_short		acp_vector;	/* UNIBUS interrupt vector     */
    u_short		acp_flags;	/* ACP operational flag        */
    u_char		acp_path;	/* path allocation flag        */
    u_short		acp_maxout;	/* maximum IP message sent     */
    u_short 		acp_maxin;	/* maximum IP message rcvd     */
#ifndef FOURTWO
    struct in_addr	acp_ipaddr;	/* local IP address */
#endif
  } acp_softc[NACP];

/* The acp_path flag indicates whether or not a path has been allocated */
/* and also whether or not to call acp_init to send an ssp_msg to the   */
/* front end:  acp_path = 1    indicates supervisory path is allocated  */
/*             acp_path = 2    indicates data path is allocated         */
/*             acp_path = 0x10 indicates acp_init should be called      */
/*                             to send CIM ssp_msg to the front end     */

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                                                             %%*/ 
/*%%                   GLOBAL ROUTINES                           %%*/ 
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                      ACPPROBE()                             %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */ 
/* 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:          acpprobe(reg)                                   */
/*  Argument:      reg:  caddr_t address in virtual memory of the  */
/*                        control-status register                  */
/*  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:      nothing                                         */ 
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static int savevec;			/* static variable for vector */

acpprobe(reg, ui)
caddr_t reg;
struct uba_device *ui;			/* TWG VAX/VMS ONLY! */
  {
#ifndef UBAUVII                         /* not for Ultrix 2.0 */
    register int br, cvec;		/* r11, r10 value-result */
#endif UBAUVII
    register struct acpregs *addr = (struct acpregs *)reg;

#ifdef lint
    br = 0; cvec = br; br = cvec;	/* these variables are value-result */
#endif

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

					/* return a vector pair */
					/*   aligned on QUADWORD boundary */

					/* cvec is the interrupt vector   */
					/*   address on the UNIBUS        */

					/* br is the IPL of the device    */
					/*   when it interrupts           */

#ifdef MVAX
    br = 0x17;				/* return bus level for a uVAX */
#else
    br = 0x15;				/* return bus level */
#endif MVAX

    addr->req_flags = 0;		/* clear handshake flags */
    addr->cmp_flags = 0;
    addr->xfr_flags = 0;
    addr->sys_stat = 0;
    addr->sys_vect = cvec >> 2;		/* pass vector to ACP */
    addr->csr = CSR_RESET;		/* reset the board */
    addr->csr |= CSR_IENB;		/* enable status intr */

    return (sizeof(struct acpregs));
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                      ACPATTACH()                            %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */ 
/*  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.        */
/*                                                                 */ 
/*  Call:           acpattach(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 sturcture      */ 
/*  Calls to:       if_attach()                                    */ 
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

acpattach(ui)
struct uba_device *ui;
  {
    register struct acp_softc *ds = &acp_softc[ui->ui_unit];

    ds->acp_vector = savevec;		/* save vector from probe() */
    ds->acp_if.if_unit = ui->ui_unit;	/* set unit number */
    ds->acp_if.if_name = "acp";		/* set device name */
    ds->acp_if.if_mtu = ACPMTU;		/* set max msg size */
    ds->acp_if.if_init = acpinit;	/* set init routine addr */
    ds->acp_if.if_ioctl = acpioctl;	/* set ioctl routine addr */
    ds->acp_if.if_output = acpoutput;	/* set output routine addr */
    ds->acp_if.if_start = acpstart;	/* set start routine addr */
    ds->acp_if.if_reset = acpreset;	/* set reset routine addr */
    if_attach(&ds->acp_if);		/* attach new network device */
					/*  add to list of "active"  */
					/*  interfaces, the argument */
					/*  passed locates the ifnet */
					/*  data structure           */
  }
  

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                      ACPRESET()                             %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */ 
/*  Purpose:                                                       */ 
/*                                                                 */
/*      Reset of interface after UNIBUS reset.  If interface is on */
/*      specified uba, reset its state.                            */
/*                                                                 */
/*  Call:              acpreset(unit, uban)                        */ 
/*  Arguments:         unit:   ACP device unit number              */
/*                     uban:   UNIBUS adapter number               */
/*  Returns:           nothing                                     */ 
/*  Called by:         network software, address of routine is     */
/*                     defined in acp_if network interface struct  */
/*  Calls to:          printf()                                    */ 
/*                     IF_DEQUEUE()                                */ 
/*                     m_freem()                                   */ 
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

acpreset(unit, uban)
int unit, uban;
  {
    register struct uba_device *ui;	/* per "device" structure        */
    register struct acpregs *addr;	/* ACP device register struct    */
    register struct acp_cb *dc;
    register struct mbuf *m;
    register int lcn;

    if (unit >= NACP || (ui = acpinfo[unit]) == 0 || ui->ui_alive == 0 ||
      ui->ui_ubanum != uban)
	return;

    printf("acp%d\n", unit);

    addr = (struct acpregs *)ui->ui_addr;  /* address of device in I/O space  */

    addr->req_flags = 0;		/* clear handshake flags, mailbox     */
					/*  flags for I/O requests            */
    addr->cmp_flags = 0;		/* mailbox flags for I/O completion   */
    addr->xfr_flags = 0;		/* mailbox flags for I/O transfer     */
					/*  requests                          */
    addr->sys_stat = 0;			/* mailbox flags for system status    */
    addr->sys_vect = acp_softc[unit].acp_vector >> 2;  /* pass base interrupt */
					/*  vector to ACP                     */
    addr->csr = CSR_RESET;		/* reset the board                    */
    addr->csr |= CSR_IENB;		/* enable status intr                 */
    acp_softc[unit].acp_flags = 0;	/* clear ACP operational flag         */
    acp_softc[unit].acp_path = 0;	/* clear path allocation flag         */

    dc = acp_softc[unit].acp_cb;	/* flush any queued output data */
    for(lcn = 0; lcn <= NACPCH; lcn++)	/* for all LCN's ... */
    {
        while(dc->dc_oq.ifq_len)
            {
                IF_DEQUEUE(&dc->dc_oq, m);
                m_freem(m);
            }
        dc++;
    }

  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                      ACPINIT()                              %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */ 
/*  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.  */
/*                                                                 */
/*  Call:             acpinit(unit)                                */ 
/*  Argument:         unit:  ACP device unit number                */
/*  Returns:          nothing                                      */ 
/*  Called by:        network software, address of this routine is */
/*                    defined in acp_if network interface struct   */
/*                    acpioctl()                                   */ 
/*                    acpintb()                                    */ 
/*  Calls to:         in_netof() return the network number from    */ 
/*                               internet address                  */ 
/*                    if_ubainit()                                 */ 
/*                    btoc()                                       */ 
/*                    splimp()                                     */ 
/*                    acp_ioreq()                                  */ 
/*                    acp_alloc()                                  */ 
/*                    acp_init()                                   */ 
/*                    splx()                                       */ 
/*                    if_rtinit()                                  */ 
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

acpinit(unit)
int unit;
  {
    register struct acp_softc *ds = &acp_softc[unit];
    register struct acp_cb *dc;
    register struct uba_device *ui = acpinfo[unit];
#ifdef FOURTWO
    struct sockaddr_in *sin;
#else
    struct ifaddr *ifa = ds->acp_if.if_addrlist;
#endif
    int lcn, s;

#ifdef ACPDEBUG
if (acp_debug > 0)
  {
      printf("acp%d: acpinit()\n", unit);
  }
#endif ACPDEBUG

#ifdef FOURTWO
    sin = (struct sockaddr_in *)&ds->acp_if.if_addr;
    if (in_netof(sin->sin_addr) == 0)
#else
ifa = ds->acp_if.ifaddrlist;
#ifdef AF_LINK
for (; ifa; ifa = ifa->ifa_next)
    if (ifa->ifa_addr->sa_family != AF_LINK)
	break;
#endif
if (    ifa == 0)    /* if we have no internet addr */
#endif
	return;
    if ((ds->acp_flags & ACPF_OK) == 0)	/* or if ACP not operational */
	return;				/*   don't init */


    dc = ds->acp_cb;			/* setup ptr to first LCN cntl block */

    for(lcn=ACP_ALLOC;lcn<=NACPCH;lcn++)	/* for all LCN's ... */
      {
    	dc->dc_lcn = lcn;		/* record LCN */

		/* init LCN output queue */

    	dc->dc_oq.ifq_head = (struct mbuf *)0;
    	dc->dc_oq.ifq_tail = (struct mbuf *)0;
    	dc->dc_oq.ifq_len = 0;
    	dc->dc_oq.ifq_maxlen = ACP_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;

    		/* init UNIBUS resources, allocate UNIBUS map registers */

    	if ((ds->acp_if.if_flags & IFF_RUNNING) == 0)
          {
    	    if (if_ubainit(&dc->dc_ifuba, ui->ui_ubanum,
    	      0, (int)btoc(ACPMTU)) == 0)
    	      {
    	        printf("acp%d: failed getting UBA resources for lcn %d\n",
    		  unit, lcn);
    	        ds->acp_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
    	        return;
    	      }
	  }
    	dc->dc_flags = 0;		/* initialize flags */

	dc++;				/* point at next cntl blk */
      }

    ds->acp_sioq.sq_head = (struct hdx_chan *)0;
    ds->acp_sioq.sq_tail = (struct hdx_chan *)0;
    ds->acp_if.if_flags |= IFF_RUNNING;

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

    dc = ds->acp_cb;			/* setup ptr to first LCN cntl block */

    for(lcn=ACP_ALLOC;lcn<=NACPCH;lcn++)	/* issue reads on all LCNs */
      {
    	acp_iorq(ds, dc, ACPMTU, ACPRED+ACPSTR);
	dc++;
      }

	/* if not already established, allocate control and data paths  */

    if ((ds->acp_path & ACP_SUPR) == 0)
        acp_alloc(ds, TYPE_CNTL);	/* allocate control path  */
    if ((ds->acp_path & ACP_DATA) == 0)
        acp_alloc(ds, TYPE_DATA);	/* allocate data path     */

    if ((ds->acp_path & INIT_OK) == INIT_OK)
      {
        acp_init(ds);			/* init the ACP, if ioctl to do so */
    	ds->acp_path &= ~INIT_OK;	/* turn off flag for acpinit() */
      }

    splx(s);				/* enable interrupts        */

#ifdef FOURTWO
    if_rtinit(&ds->acp_if, RTF_UP);	/* initialize the routing table entry */
					/*  according to the network, args    */
					/*  are the addr of the ifnet struct  */
					/*  and RTF_UP means the route is     */
					/*  useable                           */
#endif
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      ACPOUTPUT()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*   This routine is called by the network software when it has an */
/*   IP datagram to send out this interface.  The datagtram is     */
/*   queued for output on that LCN.                                */
/*                                                                 */
/*  Call:            acpoutput(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 acp_if network interface struct  */
/*  Calls to:      printf()                                        */
/*                 mfreem()                                        */
/*                 splimp()                                        */
/*                 IF_QFULL()                                      */
/*                 IF_DROP()                                       */
/*                 splx()                                          */
/*                 IF_ENQUEUE()                                    */
/*                 m_freem()                                       */
/*                 acp_start()                                     */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

acpoutput(ifp, m0, dst)
struct ifnet *ifp;		/* network interface          */
struct mbuf *m0;		/* buffer                     */
struct sockaddr_in *dst;	/* socket destination address */
  {
    register struct mbuf *m = m0;
    register struct acp_softc *ds = &acp_softc[ifp->if_unit];
    register struct acp_cb *dc;
    register struct ifqueue *oq;
    struct mbuf *prev;
    int s;

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

    switch (dst->sin_family)
      {

#ifdef INET
    case AF_INET:		/* address format of protocol family */
				/*  this is the internet:  TCP, UDP, */
				/*  ICMP, IP, etc.                   */
	break;
#endif INET

    default:
	printf("acp%d: can't handle af%d\n", ifp->if_unit,
	    dst->sin_family);
	m_freem(m0);
	return (EAFNOSUPPORT);
      }


#ifdef ACPDEBUG
if (acp_debug > 6)
  {
      printf("acpoutput(): dst = ");
      prt_addr(dst->sin_addr);
      printf("\n");
  }
#endif ACPDEBUG

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

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

    dc = &(ds->acp_cb[ACP_DATA]);	/*   data channel          */
    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->acp_if.if_collisions++;
	splx(s);
	return (ENOBUFS);
      }
    IF_ENQUEUE(oq, m);			/*   otherwise queue it */
    acp_start(ds, dc);			/*   and try to output  */
    splx(s);				/*   enable interrupts  */
    return (0);				/*   successful return  */
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      ACPIOCTL()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*   This routine processes device dependent ioctl's.  Supported   */
/*   ioctls set the host's internet address for this network       */
/*   interface, or send CIMs (Command Interface Messages) to the   */
/*   ACP (ie to bring up the line).  The logic for setting the     */
/*   interface address must remain compatible with both ifconfig   */
/*   and acpconfig programs.                                       */
/*                                                                 */
/*  Call:            acpioctl(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()                                   */
/*                    acpinit()                                    */
/*                    acpreset()                                   */
/*                    printf()                                     */
/*                    splx()                                       */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

acpioctl(ifp, cmd, data)
register struct ifnet *ifp;	/* network interface data structure      */
int cmd;			/* type of ioctl request                 */
caddr_t data;			/* address of data for ioctl request     */
  {
    register struct uba_device *ui = acpinfo[ifp->if_unit];
    struct ifreq *ifr = (struct ifreq *)data;	/* ifreq is the interface */
				/* request struct used for socket ioctls  */
    struct acp_softc *ds = &acp_softc[ifp->if_unit];
    int s = splimp(), error = 0;	/* disable interrupts    */
#ifdef FOURTWO
    struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
#else
    struct ifaddr *ifa = ds->acp_if.if_addrlist;
#endif

#ifdef ACPDEBUG
if (acp_debug > 2)
  {
      printf("acp%d:  acpioctl()\n", ifp->if_unit);
  }
#endif ACPDEBUG

    switch (cmd)
      {
    case SIOCSIFADDR:			/* set ifnet address    */
#ifdef FOURTWO
	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);	/* RTF_UP means route useable */
	else
	    acpinit(ifp->if_unit);
#else
        if (ifa->ifa_addr.sa_family != AF_INET)
                return(EINVAL);
        if ((ifp->if_flags & IFF_RUNNING) == 0)
                acpinit(ifp->if_unit);
        ds->acp_ipaddr = IA_SIN(ifa)->sin_addr;
#endif
	break;

    case SIOCACPCONFIG:
	/* if not trying to bring down link (case '0') then trying to bring */
	/* it up, or reconfigure it -- don't do cmd unless internet address */
	/* has already been set                                             */

    	if (*(ifr->ifr_data) != '0' )
	{
#ifdef FOURTWO
            sin = (struct sockaddr_in *)&ds->acp_if.if_addr;  
            if (in_netof(sin->sin_addr) == 0)
#else
	    if (ds->acp_if.if_addrlist == 0)
#endif
	    {
	        printf("acp%d:  no internet address is set,", ifp->if_unit);
	        printf(" acpconfig command ignored\n");
	        goto exit;
	    }
  	}
  	acpreset(ifp->if_unit, ui->ui_ubanum); /* reset device */
    	ds->acp_path |= INIT_OK;	/* set flag for acpinit() */

	/* if command is set the baud rate, then set clocking for  */
	/* internal generation, and look up the value for the baud */
	/* rate divisor in the baud_rate table, put this value in  */
	/* the Set System Parameters message, ssp_msg              */

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

  	    ssp_msg[CLOCK_OFFSET] = INTERNAL_CLOCK;
		
	    for (p = baud_rate; p->b_value; p++)
		{
	        if ((*(ifr->ifr_data) - p->b_value) == 0)
		    break;
		}
	    ssp_msg[BAUD_OFFSET] = p->parameter1;
	    ssp_msg[BAUD_OFFSET + 1] = p->parameter2;
	    if (p->b_value == 0)	
	    { 	
		printf("acp%d: invalid value for baud rate\n", ifp->if_unit);
		goto exit;
	    } 	
	}
	else
	{
    	    switch (*(ifr->ifr_data))
            {
    	        case '0':
  		    ssp_msg[DOWN_OFFSET] = LINK_DISABLE;
        	    break;
    	        case '1':
  		    ssp_msg[LOOP_OFFSET] = LOOP_NONE;
  		    ssp_msg[DTE_OFFSET] = DTE_MODE;
  		    ssp_msg[DOWN_OFFSET] = LINK_ENABLE;
		    break;
    	        case '2':
  		    ssp_msg[LOOP_OFFSET] = LOOP_NONE;
  		    ssp_msg[DTE_OFFSET] = DCE_MODE;
  		    ssp_msg[DOWN_OFFSET] = LINK_ENABLE;
		    break;
    	        case '3': 
  		    ssp_msg[LOOP_OFFSET] = LOOP_EXTERNAL;
  		    ssp_msg[DOWN_OFFSET] = LINK_ENABLE;
        	    break;
    	        case '4':
  		    ssp_msg[LOOP_OFFSET] = LOOP_INTERNAL;
  		    ssp_msg[DOWN_OFFSET] = LINK_ENABLE;
        	    break;
    	        case 'b':
  		    ssp_msg[CLOCK_OFFSET] = EXTERNAL_CLOCK;
        	    break;
                default:
		    error = EINVAL;
		    goto exit;
              }
	 }
  	 acpinit(ifp->if_unit);		/* send ssp_msg to frontend */
         break;

    default:
	error = EINVAL;
    }

exit:
    splx(s);				/* enable interrupts   */
    return (error);
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      ACPINTA()                              %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*  This is the interrupt handler for I/O interrupts from the ACP  */
/*  The I/O mailboxes are scanned for handshake events to process. */
/*  The events are Transfer Request, I/O Request done and I/O      */
/*  Completion ready.  Note that the Transfer Request is not yet   */
/*  supported; an error message is printed if one is received.     */
/*                                                                 */
/*  Call:           acpinta(unit)                                  */
/*  Argument:       unit:  ACP device unit number                  */
/*  Returns:        nothing                                        */
/*  Called by:      network software, address of this routine is   */
/*                  defined in af_inet network interface struct    */
/*  Calls to:       printf()                                       */
/*                  start_chn()                                    */
/*                  acp_data()                                     */
/*                  acp_supr()                                     */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

acpinta(unit)
int unit;
  {
    register struct acpregs *addr = (struct acpregs *)acpinfo[unit]->ui_addr;
    register struct acp_softc *ds = &acp_softc[unit];
    register struct hdx_chan *hc;
    int chan, cc, cnt;

#ifdef ACPDEBUG
if (acp_debug > 7)
  {
      printf("acp%d: acpinta()\n", unit);
  }
#endif ACPDEBUG

    /* Figure out what kind of interrupt it was */

    if (addr->xfr_flags & FLAGS_RDY)	/* Transfer Request Mailbox */
      {
	printf("acp%d: UNEXPECTED TRANSFER REQUEST!\n", unit);
	addr->xfr_cnt = 0;
	addr->xfr_flags = (addr->xfr_flags & ~FLAGS_RDY) | FLAGS_DON;
	addr->csr |= CSR_INTRA;
      }

    if (addr->req_flags & FLAGS_DON)	/* I/O Request Mailbox  */
      {
    	/* try to start any queued i/o request */

    	if (ds->acp_sioq.sq_head = ds->acp_sioq.sq_head->hc_next)
    	  {
    	    start_chn(ds);
    	  }
	else
	  {
	    addr->req_flags &= ~FLAGS_DON;
	  }
      }

    if (addr->cmp_flags & FLAGS_RDY)	/* I/O Completion  Mailbox */
      {

	/*
	 * Get logical channel info.
	 */
	if ((chan = addr->cmp_chan) > NACPCH)
          {
	    printf("acp%d: unknown channel, chan=%d\n", unit, chan);
	    return;
	  }

	if (addr->cmp_flags & FLAGS_DIR)
    	    hc = &(ds->acp_cb[chan].dc_wchan);
	else
    	    hc = &(ds->acp_cb[chan].dc_rchan);
	cc = addr->cmp_stat;	/* Mailbox I/O completion status      */
	cnt = addr->cmp_cnt;	/* Mailbox I/O completion byte count  */

    	switch (cc)	/* check for unsuccessful I/O completion status */
    	  {
    	case ACPIOCABT:
    	    printf("acp%d: I/O abort ", unit);
    	    goto daterr;

    	case ACPIOCERR:
    	    printf("acp%d: program error ", unit);
    	    goto daterr;

    	case ACPIOCOVR:
    	    printf("acp%d: overrun error ", unit);
    	    goto daterr;

    	case ACPIOCUBE:
    	    printf("acp%d: NXM timeout or UB parity error ", unit);

    	daterr:
    	    printf("chan=%d func=%x\n", chan, hc->hc_func);
    	    if (addr->cmp_flags & FLAGS_DIR)
    		ds->acp_if.if_oerrors++;
    	    else
    		ds->acp_if.if_ierrors++;
    	  }

    	/* was it supervisor or data traffic? */

    	if (chan > ACP_SUPR)
    	    acp_data(ds, hc, cc, cnt);
    	else
    	    acp_supr(ds, hc, cc, cnt, chan);  /* chan = ACP_ALLOC or ACP_SUPR */

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

      }
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                      ACPINTB()                              %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */ 
/*  Purpose:                                                       */ 
/*                                                                 */
/*   This is the interrupt handler for system interrupts from the  */
/*   ACP.                                                          */
/*                                                                 */
/*  Call:             acpintb(unit)                                */
/*  Argument:         unit: ACP device unit number                 */
/*  Returns:          nothing                                      */ 
/*  Called by:        network software, address of this routine is */
/*                    defined in af_inet network interface struct  */
/*  Calls to:         printf()                                     */ 
/*                    acpinit()                                    */ 
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

acpintb(unit)
int unit;
  {
    register struct acpregs *addr = (struct acpregs *)acpinfo[unit]->ui_addr;
    register struct acp_softc *ds = &acp_softc[unit];

#ifdef ACPDEBUG
if (acp_debug > 1)
  {
      printf("acp%d: acpintb()\n", unit);
  }
#endif ACPDEBUG

    if (ds->acp_flags & ACPF_OK)
      {
	printf("acp%d: Unexpected System interrupt, status = %d\n",
	    unit, addr->sys_stat);
	addr->csr = 0;
	printf("acp%d: DISABLED!\n", unit);
	ds->acp_if.if_flags &= ~(IFF_RUNNING | IFF_UP);
      }
    else
      {
	if (addr->sys_stat != ACPSTAT_OK)
	  {
	    printf("acp%d: PWRUP Diagnostic failure = %d\n",
		unit, addr->sys_stat);
	    addr->csr = 0;
	  }
	else
	  {
	    ds->acp_flags |= ACPF_OK;
	    addr->csr |= (CSR_IENA | CSR_DMAEN);
	    acpinit(unit);
	  }
      }
  }

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                                                             %%*/ 
/*%%                   LOCAL  ROUTINES                           %%*/ 
/*%%                                                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                      ACP_ALLOC()                            %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */ 
/*  Purpose:                                                       */ 
/*                                                                 */
/*   This routine allocates control or data paths.  Commands are   */
/*   sent (over DPN 0) from the host to the ALLOC facility on the  */
/*   front end to allocate the paths.  The ALLOC facility returns  */    
/*   a response to the allocation command which indicates success  */    
/*   or failure.  Note that DPN 0 is used only for the ALLOC       */    
/*   commands, and is not a control path as it was been in the     */    
/*   past.  The paths are symbolically defined as ACP_ALLOC,       */    
/*   ACP_SUPR, and ACP_DATA for allocation messages, control       */    
/*   messages, and data respectively.  The CID field indicates     */
/*   the data path number of the allocation command.  The CID      */
/*   is set here so that the response will have the same CID, and  */
/*   will therefore indicate to which path the response            */
/*   corresponds. (The CID is set in the command and must be       */
/*   returned, untouched, in the response.)                        */
/*                                                                 */
/*  Call:          acp_alloc(ds, type)                             */ 
/*  Argument:      ds:  pointer to ACP device control structure    */
/*                 type:  specifies if path is for control or data */
/*  Returns:       nothing                                         */ 
/*  Called by:     acpinit()                                       */
/*  Calls to:          MGET()                                      */ 
/*                     printf()                                    */ 
/*                     mtod()                                      */ 
/*                     bcopy()                                     */ 
/*                     sizeof()                                    */ 
/*                     IF_ENQUEUE()                                */ 
/*                     acp_start()                                 */ 
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void acp_alloc(ds, type)
struct acp_softc *ds;
int type;
  {
    struct mbuf *m;
    register u_char *bp;

#ifdef ACPDEBUG
if (acp_debug > 4)
  {
      printf("acp%d: acp_alloc()\n", ds->acp_if.if_unit);
  }
#endif ACPDEBUG

    MGET(m, M_DONTWAIT, MT_DATA);	/* try to get init buffer */
    if (m == 0)
      {
    	printf("acp%d: couldn't get init buffer\n", ds->acp_if.if_unit);
    	return;
      }

	/* modify the path allocation message to get a control path */
	/* or a data path                                           */

    if (type == TYPE_CNTL)
      {
        alloc_msg[CID_OFFSET] = ACP_SUPR;	/* set CID for response */
        alloc_msg[DPN_OFFSET] = ACP_SUPR;	/* path number          */
        alloc_msg[TYPE_OFFSET] = TYPE_CNTL;	/* path type = control  */
      }
    else
      {
        alloc_msg[CID_OFFSET] = ACP_DATA;	/* set CID for response  */
        alloc_msg[DPN_OFFSET] = ACP_DATA;	/* path number           */
        alloc_msg[TYPE_OFFSET] = TYPE_DATA;	/* path type = data      */
      }

    bp = mtod(m, u_char *);		/* point to data section of mbuf */

    bcopy(alloc_msg, bp, sizeof(alloc_msg));  /* set sys params msg in mbuf */

#ifdef ACPDEBUG
if (acp_debug > 5)
  {
printf("acp_alloc():  ");
prt_bytes(bp, sizeof(alloc_msg));   /* print 12-byte header + data */
printf("\n");
  }
#endif ACPDEBUG


    m->m_len = sizeof(alloc_msg);		/* set msg length */

    IF_ENQUEUE(&(ds->acp_cb[ACP_ALLOC].dc_oq), m);	/* output queue   */

    acp_start(ds, &(ds->acp_cb[ACP_ALLOC]));	/* start ouput of data  */
  }


/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                      ACP_INIT()                             %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */ 
/*  Purpose:                                                       */ 
/*                                                                 */
/*   This routine builds and sends an initialization message to    */
/*   the ACP.  A canned Set System Parameters Message is sent to   */
/*   start HDLC.                                                   */
/*                                                                 */
/*  Call:          acp_init(ds)                                    */ 
/*  Argument:      ds:  pointer to ACP device control structure    */
/*  Returns:                nothing                                */ 
/*  Called by:         acpinit()                                   */
/*  Calls to:          MGET()                                      */ 
/*                     printf()                                    */ 
/*                     mtod()                                      */ 
/*                     bcopy()                                     */ 
/*                     sizeof()                                    */ 
/*                     IF_ENQUEUE()                                */ 
/*                     acp_start()                                 */ 
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void acp_init(ds)
struct acp_softc *ds;
  {
    struct mbuf *m;
    register u_char *bp;

#ifdef ACPDEBUG
if (acp_debug > 5)
  {
      printf("acp%d: acp_init()\n", ds->acp_if.if_unit);
  }
#endif ACPDEBUG

    MGET(m, M_DONTWAIT, MT_DATA);	/* try to get init buffer */
    if (m == 0)
      {
    	printf("acp%d: couldn't get init buffer\n", ds->acp_if.if_unit);
    	return;
      }

    bp = mtod(m, u_char *);		/* point to data section of mbuf */

    bcopy(ssp_msg, bp, sizeof(ssp_msg));  /* put msg into mbuf */

#ifdef ACPDEBUG
if (acp_debug > 4)
  {
printf("acp_init():  ssp msg\n");
prt_bytes(bp, sizeof(ssp_msg));		/* print 12-byte header + data */
printf("\n");
  }
#endif ACPDEBUG

    m->m_len = sizeof(ssp_msg);		/* set msg length */

    IF_ENQUEUE(&(ds->acp_cb[ACP_SUPR].dc_oq), m);	/* output queue   */

    acp_start(ds, &(ds->acp_cb[ACP_SUPR]));	/* start ouput of data  */
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      ACP_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 an I/O request queued to the ACP.                 */
/*                                                                 */
/*  Call:              acpstart(ds, dc)                            */
/*  Arguments:         ds:  pointer to device control structure    */
/*                     dc:  pointer to the Logical Channel control */
/*                            block structure                      */
/*  Returns:           nothing                                     */
/*  Called by:         acpoutput()                                 */
/*                     acp_init()                                  */
/*                     acp_data()                                  */
/*                     acp_supr()                                  */
/*  Calls to:          IF_DEQUEUE()                                */
/*                     if_wubaput()                                */
/*                     acp_ioreqs()                                */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void acp_start(ds, dc)
register struct acp_softc *ds;
register struct acp_cb *dc;
  {
    register struct mbuf *m;
    int len;

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

#ifdef ACPDEBUG
if (acp_debug > 7)
  {
      printf("acp: acp_start()\n");
  }
#endif ACPDEBUG

    if ((dc->dc_flags & DC_OBUSY) || (dc->dc_oq.ifq_len == 0))
    	return;

    IF_DEQUEUE(&dc->dc_oq, m);	/* remove data from LCN output queue */

    len = if_wubaput(&dc->dc_ifuba, m);	/* copy data to mapped mem */

    if (len > ds->acp_maxout)
      {

#ifdef ACPDEBUG
if (acp_debug > 7)
  {
    printf("acp: %d byte msg sent.\n", len);
  }
#endif ACPDEBUG

	ds->acp_maxout = len;
      }

    dc->dc_flags |= DC_OBUSY;

    acp_iorq(ds, dc, len, ACPWRT+ACPEOS);	/* build I/O request, enqueue */
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      ACP_IORQ()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine builds ACP I/O requests and queues them for     */
/*    delivery to the ACP. If the ACP I/O request comm regs are    */
/*    not busy, the I/O request is passed to the ACP.              */
/*                                                                 */
/*  Call:            acp_iorq(ds, dc, len, func)                   */
/*  Argument:        ds:   pointer to device control block struct  */
/*                   dc:   pointer to the Logical Channel control  */
/*                                block structure                  */
/*                   len:  byte count                              */
/*                   func: the function:  read or write            */
/*  Returns:         nothing                                       */
/*  Called by:         acpinit()                                   */
/*                     acp_start()                                 */
/*                     acp_data()                                  */
/*                     acp_supr()                                  */
/*  Calls to:          start_chn()                                 */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void acp_iorq(ds, dc, len, func)
struct acp_softc *ds;
struct acp_cb *dc;
int len, func;
  {
    register struct hdx_chan *hc;
    register int info;


#ifdef ACPDEBUG
if (acp_debug > 6)
      printf("acp: acp_iorq()\n");
#endif ACPDEBUG

    /* get appropriate UNIBUS mapping info */

    if ((func & FCN_MASK) == ACPRED)	/* read or write? */
      {
    	hc = &dc->dc_rchan;		/* read */
    	info = dc->dc_ifuba.ifu_r.ifrw_info;
      }
    else
      {
    	hc = &dc->dc_wchan;		/* write */
    	info = dc->dc_ifuba.ifu_w.ifrw_info;
      }

    /* set channel info */

    hc->hc_adx = (u_char)((info & 0x30000) >> 12);	/* address bits 17-16 */
    hc->hc_addr = (unsigned short)(info & 0xffff);	/* address bits 15-00 */
    hc->hc_cnt = len;					/* byte count         */
    hc->hc_func = (u_char)func;				/* I/O function       */

    if (dc->dc_lcn > ACP_SUPR)
        hc->hc_sbfc = SBFCN_DATA;		/* I/O subfunction for data */ 
    else
        hc->hc_sbfc = SBFCN_SUPR;		/* I/O subfunction for cntrl */

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

    /* start i/o on channel now */

    ds->acp_sioq.sq_head = hc;
    ds->acp_sioq.sq_tail = hc;
    hc->hc_next = 0;
    start_chn(ds);
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      START_CHN()                            %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*    This routine copies ACP I/O requests into the ACP comm regs  */
/*    and notifies the ACP.                                        */
/*                                                                 */
/*  Call:              start_chn(ds)                               */
/*  Argument:          ds:  pointer to device control block struct */
/*  Returns:           nothing                                     */
/*  Called by:         acpinta()                                   */
/*                     acp_iorq()                                  */
/*  Calls to:          none                                        */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void start_chn(ds)
struct acp_softc *ds;
  {
    register struct hdx_chan *hc = ds->acp_sioq.sq_head;
    register struct acpregs *addr =
    	(struct acpregs *)acpinfo[ds->acp_if.if_unit]->ui_addr;

    /*
     * Set up comm regs.
     */

#ifdef ACPDEBUG
if (acp_debug > 7)
  {
      printf("acp: start_chn()\n");
  }
#endif ACPDEBUG

    addr->req_chan = hc->hc_chan >> 1;
    addr->req_adx = hc->hc_adx;
    addr->req_addr = hc->hc_addr;
    addr->req_cnt = hc->hc_cnt;
    addr->req_fcn = hc->hc_func;
    addr->req_sbf = hc->hc_sbfc;
    if (hc->hc_chan & 1)
	addr->req_flags = FLAGS_RDY | FLAGS_DIR;
    else 
	addr->req_flags = FLAGS_RDY;

    addr->csr |= CSR_INTRA;
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      ACP_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:              acp_data(ds, hc, cc, rcnt)                  */
/*  Arguments:         ds:  pointer to device control block struct */
/*                     hc:  pointer to half duplex channel control */
/*                               block                             */
/*                     cc:  Mailbox I/O completion status          */
/*                     rcnt: byte count                            */
/*  Returns:           nothing                                     */
/*  Called by:         acpinta()                                   */
/*  Calls to:          m_freem()                                   */
/*                     acp_start()                                 */
/*                     if_rubaget()                                */
/*                     IF_QFULL()                                  */
/*                     IF_DROP()                                   */
/*                     IF_ENQUEUE()                                */
/*                     schednetisr()                               */
/*                     acp_ioreq()                                 */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void acp_data(ds, hc, cc, rcnt)
register struct acp_softc *ds;
register struct hdx_chan *hc;
int cc, rcnt;
{
    register struct acp_cb *dc = &(ds->acp_cb[hc->hc_chan/2]);
    register struct ifqueue *inq = &ipintrq;
    register struct mbuf *m;

    if (hc->hc_chan & 0x01)		/* was it read or write? */	
      {					/*   write, fire up next output */
    	ds->acp_if.if_opackets++;
	if (dc->dc_ifuba.ifu_xtofree)
	  {
	    m_freem(dc->dc_ifuba.ifu_xtofree);
	    dc->dc_ifuba.ifu_xtofree = 0;
	  }
    	dc->dc_flags &= ~DC_OBUSY;
    	acp_start(ds, dc);
      }
    else				/*   read, process rcvd packet */
      {

#ifdef ACPDEBUG
if (acp_debug > 6)
  {
printf("acp: data read completed, cc = %d, cnt = %d\n", cc, rcnt);
prt_bytes((u_char *)(dc->dc_ifuba.ifu_r.ifrw_addr), (rcnt < 20 ? rcnt:20));
  }
#endif ACPDEBUG

    	if (cc == ACPIOCOK)
    	  {				 /* Queue good packet for input */
    	    ds->acp_if.if_ipackets++;
	    if (rcnt > ds->acp_maxin)
	      {

#ifdef ACPDEBUG
if (acp_debug > 4)
  {
    printf("acp: %d byte msg received.\n", rcnt);
  }
#endif ACPDEBUG

		ds->acp_maxin = rcnt;
	      }
				/* call if_rubaget() to pull read data  */
				/*  off the interface, the args are the */
				/*  ifuba struct, the length of data,   */
				/*  and the 0 indicates that no trailer */
				/*  protocol is used                    */
    	    m = if_rubaget(&(dc->dc_ifuba), rcnt, 0, &(ds->acp_if));
    	    if (m)
    	      {
    		if (IF_QFULL(inq))
    		  {
    		    IF_DROP(inq);
    		    m_freem(m);
    		  }
    		else
    		  {
    		    IF_ENQUEUE(inq, m);
    		    schednetisr(NETISR_IP);
    		  }
    	      }
    	  }

    	/* hang a new data read */

    	acp_iorq(ds, dc, ACPMTU, ACPRED+ACPSTR);

      }
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                      ACP_RESPONSE()                         %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */ 
/*  Purpose:                                                       */ 
/*                                                                 */
/*   This routine sends a Control Interface Message (CIM) response */
/*   to the front end to indicate that a CIM command from the      */
/*   front end was successfully received.  Presently there are two */    
/*   types of CIM responses sent to the front end:  frame level    */    
/*   up, and frame level down.  Future applications may send a     */    
/*   CIM response to DCP CIM commands.  The basic philosophy with  */    
/*   CIMs is that there is always a paired command/response which  */    
/*   is exchanged between the host and the front end.              */    
/*   Currently, the front end does not process the responses from  */    
/*   the host, they are merely discarded.  The one thing left to   */    
/*   do in the case that the front end does ever look at these     */    
/*   responses is to use the same CID (Command ID field, bytes 5   */
/*   to 8 of the CIM header) that was present in the command.      */    
/*                                                                 */
/*  Call:          acp_response(ds, response)                      */ 
/*  Argument:      ds:  pointer to ACP device control structure    */
/*                 response:  response for CIM command field       */
/*                   the response value = command value + 1        */
/*  Returns:       nothing                                         */ 
/*  Called by:     supr_msg()                                      */
/*  Calls to:          MGET()                                      */ 
/*                     printf()                                    */ 
/*                     mtod()                                      */ 
/*                     bcopy()                                     */ 
/*                     sizeof()                                    */ 
/*                     IF_ENQUEUE()                                */ 
/*                     acp_start()                                 */ 
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void acp_response(ds, response)
struct acp_softc *ds;
u_char response;
  {
    struct mbuf *m;
    u_char *bp;

    MGET(m, M_DONTWAIT, MT_DATA);	/* try to get init buffer */
    if (m == 0)
      {
    	printf("acp%d: couldn't get init buffer\n", ds->acp_if.if_unit);
    	return;
      }
			/* put response in CIM cmd field */

    response_msg[CMD_OFFSET] = response;	

    bp = mtod(m, u_char *);		/* point to data section of mbuf */

    bcopy(response_msg, bp, sizeof(response_msg));  /* put msg in mbuf */

#ifdef ACPDEBUG
if (acp_debug > 6)
  {
printf("acp_response():  ");
prt_bytes(bp, sizeof(response_msg));   /* print messge  */
printf("\n");
  }
#endif ACPDEBUG

    m->m_len = sizeof(response_msg);		/* set msg length */

    IF_ENQUEUE(&(ds->acp_cb[ACP_SUPR].dc_oq), m);	/* output queue   */

    acp_start(ds, &(ds->acp_cb[ACP_SUPR]));	/* start ouput of data  */
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      ACP_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:              acp_supr(ds, hc, cc, rcnt, channel)         */
/*  Argument:          ds:   pointer to dev control block struct   */
/*                     hc:   pointer to the Half Duplex cntrl      */
/*                                 block structure                 */
/*                     cc:   Mailbox I/O completion status         */
/*                     rcnt:  byte count, length of data           */
/*                     channel:  indicates ACP_ALLOC or ACP_SUPR   */
/*  Returns:           nothing                                     */
/*  Called by:         acpinta()                                   */
/*  Calls to:          m_freem()                                   */
/*                     acp_start()                                 */
/*                     supr_msg()                                  */
/*                     acp_ioreq()                                 */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void acp_supr(ds, hc, cc, rcnt, chan)
register struct acp_softc *ds;
register struct hdx_chan *hc;
int cc, rcnt, chan;
  {
    register struct acp_cb *dc = &(ds->acp_cb[chan]);
    register u_char *p;

    /* was it read or write? */

    if (hc->hc_chan & 0x01)
      {
	if (dc->dc_ifuba.ifu_xtofree)
	  {
	    m_freem(dc->dc_ifuba.ifu_xtofree);
	    dc->dc_ifuba.ifu_xtofree = 0;
	  }
    	dc->dc_flags &= ~DC_OBUSY;
    	acp_start(ds, dc);
      }
    else
      {

#ifdef ACPDEBUG
if (acp_debug > 3)
  {
printf("acp: acp_supr(), read completed, cc = %d, cnt = %d\n", cc, rcnt);
prt_bytes((u_char *)(dc->dc_ifuba.ifu_r.ifrw_addr), rcnt);
printf("\n");
  }
#endif ACPDEBUG

    	if (cc == ACPIOCOK)
    	  {
    	    p = (u_char *)(dc->dc_ifuba.ifu_r.ifrw_addr);

    	    /* process supervisor message */
    	    supr_msg(ds, p);
    	  }
    	/* hang a new supr read */
    	acp_iorq(ds, dc, ACPMTU, ACPRED+ACPSTR);
      }
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                      SUPR_MSG()                             %%*/
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                 */
/*  Purpose:                                                       */
/*                                                                 */
/*       This routine processes received supervisor messages.      */
/*       Depending on the message type, the appropriate action is  */
/*       taken.  Note that the RSF field is checked for responses. */
/*       The RSF field is 4 bytes long, but ony that least         */
/*       significant byte is used.                                 */
/*                                                                 */
/*  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:         acp_supr()                                  */
/*  Calls to:          printf()                                    */
/*                     IF_DEQUEUE()                                */
/*                     m_freem()                                   */
/*                     acp_response()                              */
/*                                                                 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void supr_msg(ds, p)
struct acp_softc *ds;
u_char p[];
  {
    register struct acp_cb *dc;
    register int lcn;
    register struct mbuf *m;

    switch (p[3])
      {
    case RSP_ALLOC:			/*  alloc response   */
        if (p[RSF_OFFSET])	/* check if RSF is 0 for success */
          {
            printf("acp%d:  attempt to allocate path failed ");
            printf("rsf field = %x\n", p[RSF_OFFSET]);
            return;
          }
        else
          {
            if (p[CID_OFFSET] >=  ACP_SUPR  &&  p[CID_OFFSET] <= ACP_DATA )
                ds->acp_path |= p[CID_OFFSET];
            else
              {
                printf("acp%d:  path allocation ",ds->acp_if.if_unit);
                printf("response contains invalid DPN = %d\n", p[CID_OFFSET]);
              }
          }
        break;

    case RSP_DEALLOC:			/*  dealloc response */
        if (p[RSF_OFFSET])	/* check if RSF is 0 for success */
          {
            printf("acp%d:  attempt to deallocate path failed ");
            printf("rsf field = %x\n", p[RSF_OFFSET]);
            return;
          }
        break;

    case RSP_SSP:			/*  set sys parm rsp */
        if (p[RSF_OFFSET])	/* check if RSF is 0 for success */
          {
            printf("acp%d:  attempt to set HDLC system parameters failed\n");
            return;
          }
        break;

    case CMD_FLUP:			/*   frame level up  */

	/* check that the data path was successfully allocated, we */
	/* know that the control path was successfully allocated   */
	/* otherwise the FLUP command would not have been issued   */

        if ((ds->acp_path & ACP_DATA) == 0)
          { 
            printf("acp%d:  data path was not successfully allocated\n",
			ds->acp_if.if_unit);
          } 
        ds->acp_if.if_flags |= IFF_UP;
        printf("acp%d:  frame level up\n", ds->acp_if.if_unit);
        acp_response(ds, RSP_FLUP);	/* send response to front end */
        break;

    case CMD_FLDWN:			/* frame level down  */
        ds->acp_if.if_flags &= ~IFF_UP;
        dc = ds->acp_cb;
        for(lcn=ACP_ALLOC;lcn<=NACPCH;lcn++)	/* for all LCN's */
          {
            while (dc->dc_oq.ifq_len) /* drop pending data */
              {
 	        IF_DEQUEUE(&dc->dc_oq, m);
    	        m_freem(m);
              }
	    dc++;
    	  }
        printf("acp%d:  frame level down\n", ds->acp_if.if_unit);
        acp_response(ds, RSP_FLDWN);	/* send response to front end */
    	break;

    default:
    	printf("acp%d: supervisor error, code=%x\n",
	   ds->acp_if.if_unit, p[3]);
      }
  }


#ifdef ACPDEBUG

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 
/*%%                            PRT_ADDR()                             %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */ 
/*  Purpose:                                                             */ 
/*                                                                       */
/*	This routine is used to print internet addresses in the		 */
/*	standard A.B.C.D format.  Note that this routine is for          */
/*	debugging purposes (ifdef ACPDEBUG).                             */
/*									 */
/*  Call:  		prt_addr(addr)                                   */ 
/*  Argument:      	addr:  internet address structure                */
/*  Returns:  		nothing                                          */ 
/*  Called by:  	acpoutput()                                      */
/*  Calls to:   	printf()                                         */ 
/*									 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void prt_addr(addr)
struct in_addr addr;
  {
#ifndef FOURTWO
        union {
                struct in_addr ip;
                struct {                /* (assume Class A network number) */
                        u_char s_net;
                        u_char s_host;
                        u_char s_lh;
                        u_char s_impno;
                } imp;
        } imp_addr;
        imp_addr.ip = addr;
    printf("%d.%d.%d.%d", imp_addr.imp.s_net, imp_addr.imp.s_host,
                imp_addr.imp.s_lh, imp_addr.imp.s_impno);
#else
    printf("%d.%d.%d.%d", addr.s_net, addr.s_host, addr.s_lh, addr.s_impno);
#endif
  }

/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%%                            PRT_BYTES()                            %%*/ 
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*                                                                       */ 
/*  Purpose:                                                             */ 
/*                                                                       */
/*	This routine is used to print a string of bytes in hex.		 */
/*	Note that this routine is for debugging purposes (ifdef          */
/*	ACPDEBUG).                                                       */
/*									 */
/*  Call:  		prt_bytes(bp, cnt)                               */ 
/*  Argument:      	bp:  pointer to the string                       */
/*                 	cnt: number of bytes                             */
/*  Returns:  		nothing                                          */ 
/*  Called by:  	acp_data()                                       */
/*              	acp_supr()                                       */
/*              	supr_msg()                                       */
/*  Calls to:   	printf()                                         */ 
/*									 */
/*##%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

static void prt_bytes(bp, cnt)
u_char *bp;
int cnt;
  {
    while(cnt--)
      {
	printf(" %x", *bp++ & 0xff);
      }
  }

#endif ACPDEBUG

#endif NACP