BSD 4_4 release
[unix-history] / usr / src / sys / vax / if / if_acp.c
/*-
* @(#)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. */
/* */
/* */
/*************************************************************************/
\f
/* #define ACPDEBUG 1 /* define for debug printf statements */
#ifdef ACPDEBUG
int acp_debug = 0; /* acp_debug is 1-8 for increasing verbosity */
#endif ACPDEBUG
\f
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% %%*/
/*%% 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"
\f
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% %%*/
/*%% 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
\f
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% %%*/
/*%% 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) */
};
\f
/* 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 },
};
\f
/* 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) */
};
\f
/***********************************************************************\
* *
* 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 */
\f
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% %%*/
/*%% 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));
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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 */
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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++;
}
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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 */
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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);
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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" */
}
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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);
}
}
}
\f
/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% %%*/
/*%% 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 */
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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 */
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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 */
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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);
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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;
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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);
}
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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 */
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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);
}
}
\f
/*@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
/*%% 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]);
}
}
\f
#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