BSD 4_3 release
[unix-history] / usr / src / sys / vaxif / if_hy.c
/*
* @(#)if_hy.c 7.1 (Berkeley) 6/5/86
*/
/*
* 4.2 BSD Unix Kernel - Vax Network Interface Support
*
* $Header: if_hy.c,v 10.1 84/07/22 21:02:56 steveg Exp $
* $Locker: $
*
* Modifications from Berkeley 4.2 BSD
* Copyright (c) 1983, Tektronix Inc.
* All Rights Reserved
*
* $Log: if_hy.c,v $
* Revision 10.1 84/07/22 21:02:56 steveg
* define PI13 (moved from if_hyreg.h, somehow got dropped in the process)
* rework hywatch to check for power fails first
*
* Revision 10.0 84/06/30 19:54:27 steveg
* Big Build
*
* Revision 3.17 84/06/20 19:20:28 steveg
* increment hy_ntime in hywatch
* print out state name, csr, last command, and hy_flags when watchdog timer
* expires
*
* Revision 3.16 84/06/20 19:09:34 steveg
* turn on continuous logging by default
*
* Revision 3.15 84/05/30 22:19:09 steveg
* changes to reflect new layout ot statistics data
*
* Revision 3.14 84/05/30 19:25:15 steveg
* move driver states to if_hy.h so log printing programs can use them
*
* Revision 3.13 84/05/30 17:13:26 steveg
* make it compile
*
* Revision 3.12 84/05/30 13:46:16 steveg
* rework logging
*
* Revision 3.11 84/05/18 19:35:02 steveg
* clear IFF_RUNNING and IFF_UP on unibus reset to force resource allocation
* by the init routine
*
* Revision 3.10 84/05/04 12:14:44 steveg
* more rework to make it actually work under 4.2
*
* Revision 3.9 84/05/01 23:34:52 steveg
* fix typo so it compiles (unit -> ui->ui_unit)
*
* Revision 3.8 84/05/01 23:18:30 steveg
* changes after talking with rickk
* - check power off more closely
* - support remote loopback through A710 adapters
* - IMPLINK -> HYLINK
* - return EHOSTUNREACH on hyroute failure
* - bump if_collisions on abnormal interrupts that aren't input or output
*
*
*/
#include "hy.h"
#if NHY > 0
/*
* Network Systems Copropration Hyperchanel interface
*/
#include "machine/pte.h"
#include "param.h"
#include "systm.h"
#include "mbuf.h"
#include "buf.h"
#include "protosw.h"
#include "socket.h"
#include "vmmac.h"
#include "errno.h"
#include "time.h"
#include "kernel.h"
#include "ioctl.h"
#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"
#ifdef INET
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#endif
#include "../vax/cpu.h"
#include "../vax/mtpr.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
/*
* configuration specific paramters
* - change as appropriate for particular installaions
*/
#define HYROUTE
#define HYELOG
#define HYLOG
#define HYMTU 1100
#define PI13
#ifdef DEBUG
#define HYLOG
#endif
#include "if_hy.h"
#include "if_hyreg.h"
#include "if_uba.h"
int hyprobe(), hyattach(), hyinit(), hyioctl();
int hyoutput(), hyreset(), hywatch();
struct uba_device *hyinfo[NHY];
u_short hystd[] = { 0772410, 0 };
struct uba_driver hydriver =
{ hyprobe, 0, hyattach, 0, hystd, "hy", hyinfo };
/*
* Hyperchannel software status per interface.
*
* Each interface is referenced by a network interface structure,
* hy_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
* We also have, for each interface, a UBA interface structure, which
* contains information about the UNIBUS resources held by the interface:
* map registers, buffered data paths, etc. Information is cached in this
* structure for use by the if_uba.c routines in running the interface
* efficiently.
*/
struct hy_softc {
struct ifnet hy_if; /* network-visible interface */
struct ifuba hy_ifuba; /* UNIBUS resources */
short hy_flags; /* flags */
short hy_state; /* driver state */
u_short hy_host; /* local host number */
struct in_addr hy_addr; /* internet address */
int hy_olen; /* packet length on output */
int hy_lastwcr; /* last command's word count */
short hy_savedstate; /* saved for reissue after status */
short hy_savedcmd; /* saved command for reissue */
int hy_savedcount; /* saved byte count for reissue */
int hy_savedaddr; /* saved unibus address for reissue */
int hy_ntime; /* number of timeouts since last cmd */
int hy_retry; /* retry counter */
struct hy_stat hy_stat; /* statistics */
struct hy_status hy_status; /* status */
} hy_softc[NHY];
#ifdef HYELOG
u_long hy_elog[HYE_SIZE];
#endif
#ifdef HYLOG
struct hy_log hy_log;
#endif
#ifdef HYROUTE
struct hy_route hy_route[NHY];
#endif
#ifdef DEBUG
#define printL printf
#define printD if (hy_debug_flag) printf
int hy_debug_flag = 0;
/*
* hy_nodebug bit 0x01 set hy_debug_flag on hycancel
* hy_nodebug bit 0x02 set hy_debug_flag on command reissue
* hy_nodebug bit 0x04 set hy_debug_flag on abnormal interrupt
*/
int hy_nodebug = 0x0;
#endif
#define SCANINTERVAL 10 /* seconds */
#define MAXINTERVAL 20 /* seconds (max action) */
/*
* Cause a device interrupt. This code uses a buffer starting at
* location zero on the unibus (which is already mapped by the
* autoconfigure code in the kernel).
*/
hyprobe(reg)
caddr_t reg;
{
register int br, cvec; /* r11, r10 value-result */
register struct hydevice *addr = (struct hydevice *) reg;
#ifdef lint
br = 0; cvec = br; br = cvec;
hyint(0);
#endif
/*
* request adapter status to a buffer starting at unibus location 0
*/
addr->hyd_bar = 0;
addr->hyd_wcr = -((sizeof(struct hy_status) + 1) >> 1);
addr->hyd_dbuf = HYF_STATUS;
#ifdef PI13
addr->hyd_csr |= S_GO | S_IE | S_IATTN;
#else
addr->hyd_csr |= S_GO | S_IE;
#endif
DELAY(10000);
#ifdef PI13
addr->hyd_csr |= S_CLRINT; /* clear any stacked interrupts */
#endif
addr->hyd_csr &= ~(S_IE | S_CLRINT); /* disable further interrupts */
return(sizeof(struct hydevice));
}
/*
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets.
*/
hyattach(ui)
struct uba_device *ui;
{
register struct hy_softc *is = &hy_softc[ui->ui_unit];
register struct ifnet *ifp = &is->hy_if;
ifp->if_unit = ui->ui_unit;
ifp->if_name = "hy";
ifp->if_mtu = HYMTU;
is->hy_state = STARTUP; /* don't allow state transitions yet */
ifp->if_init = hyinit;
ifp->if_ioctl = hyioctl;
ifp->if_output = hyoutput;
ifp->if_reset = hyreset;
ifp->if_watchdog = hywatch;
ifp->if_timer = SCANINTERVAL;
is->hy_ifuba.ifu_flags = UBA_CANTWAIT;
#ifdef notdef
is->hy_ifuba.ifu_flags |= UBA_NEEDBDP;
#endif
if_attach(ifp);
}
/*
* Reset of interface after UNIBUS reset.
* If interface is on specified uba, reset its state.
*/
hyreset(unit, uban)
int unit, uban;
{
register struct uba_device *ui;
register struct hy_softc *is;
if (unit >= NHY || (ui = hyinfo[unit]) == 0 || ui->ui_alive == 0 ||
ui->ui_ubanum != uban)
return;
printf(" hy%d", unit);
is = &hy_softc[unit]; /* force unibus resource allocation */
is->hy_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
hyinit(unit);
}
/*
* Initialization of interface; clear recorded pending
* operations, and reinitialize UNIBUS usage.
*/
hyinit(unit)
int unit;
{
register struct hy_softc *is = &hy_softc[unit];
register struct uba_device *ui = hyinfo[unit];
register struct mbuf *m;
int s;
if (is->hy_if.if_addrlist == 0) /* address still unknown */
return;
if (is->hy_if.if_flags & IFF_RUNNING) /* just reset the device */
goto justreset;
if (if_ubainit(&is->hy_ifuba, ui->ui_ubanum,
sizeof (struct hym_hdr), (int)btoc(HYMTU)) == 0) {
#ifdef DEBUG
if (hy_nodebug & 4)
hy_debug_flag = 1;
#endif
printf("hy%d: can't initialize\n", unit);
is->hy_if.if_flags &= ~IFF_UP;
return;
}
is->hy_if.if_flags |= IFF_RUNNING;
justreset:
/*
* remove any left over outgoing messages, reset the hardware and
* start the state machine
*/
s = splimp();
#ifdef HYLOG
hylog(HYL_RESET, 0, (char *)0);
#endif
is->hy_state = IDLE;
is->hy_flags = RQ_STATUS | RQ_STATISTICS | RQ_MARKUP;
is->hy_retry = 0;
for(;;) {
IF_DEQUEUE(&is->hy_if.if_snd, m);
if (m != NULL)
m_freem(m);
else
break;
}
hycancel(ui); /* also bumps the state machine */
splx(s);
}
/*
* Issue a command to the adapter
*/
hystart(ui, cmd, count, ubaddr)
struct uba_device *ui;
int cmd, count, ubaddr;
{
register struct hy_softc *is = &hy_softc[ui->ui_unit];
register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
#ifdef DEBUG
printD("hy%d: hystart cmd = 0x%x count=%d ubaddr=0x%x\n",
ui->ui_unit, cmd, count, ubaddr);
printD("hy%d: - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
ui->ui_unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar,
addr->hyd_wcr);
#endif
if (((is->hy_flags & RQ_REISSUE) == 0) &&
(cmd != HYF_STATUS) && (cmd != HYF_END_OP) && (cmd != HYF_RSTATS)) {
is->hy_savedstate = is->hy_state;
is->hy_savedcmd = cmd;
is->hy_savedcount = count;
is->hy_savedaddr = ubaddr;
}
#ifdef PI13
if (addr->hyd_csr & S_POWEROFF) {
printf("hy%d: \"Soft\" Adapter Power Failure (hystart)\n", ui->ui_unit);
addr->hyd_csr |= S_POWEROFF;
DELAY(100);
if (addr->hyd_csr & S_POWEROFF) {
printf( "hy%d: \"Hard\" Adapter Power Failure, Network Shutdown (hystart)\n", ui->ui_unit);
if_down(&is->hy_if);
is->hy_if.if_flags &= ~IFF_UP;
is->hy_state = STARTUP;
} else {
printf("hy%d: Adapter Power Restored (hystart)\n", ui->ui_unit);
}
return;
}
#endif
addr->hyd_bar = ubaddr & 0xffff;
addr->hyd_wcr = is->hy_lastwcr = -((count+1) >> 1);
addr->hyd_dbuf = cmd;
#ifdef PI13
addr->hyd_csr = ((ubaddr >> XBASHIFT) & S_XBA) | S_GO | S_IE | S_IATTN;
#else
addr->hyd_csr = ((ubaddr >> XBASHIFT) & S_XBA) | S_GO | S_IE;
#endif
#ifdef DEBUG
printD("hy%d: exit hystart - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
ui->ui_unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar,
addr->hyd_wcr);
#endif
#ifdef HYLOG
{
struct {
u_char hcmd;
u_char hstate;
short hcount;
} hcl;
hcl.hcmd = cmd;
hcl.hstate = is->hy_state;
hcl.hcount = count;
hylog(HYL_CMD, sizeof(hcl), (char *)&hcl);
}
#endif
is->hy_ntime = 0;
}
int hyint_active = 0; /* set during hy interrupt */
/*
* Hyperchannel interface interrupt.
*
* An interrupt can occur for many reasons. Examine the status of
* the hyperchannel status bits to determine what to do next.
*
* If input error just drop packet.
* Otherwise purge input buffered data path and examine
* packet to determine type. Othewise decapsulate
* packet based on type and pass to type specific higher-level
* input routine.
*/
hyint(unit)
int unit;
{
register struct hy_softc *is = &hy_softc[unit];
register struct uba_device *ui = hyinfo[unit];
register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
if (hyint_active)
panic("RECURSIVE HYPERCHANNEL INTERRUPT");
hyint_active++;
#ifdef DEBUG
printD("hy%d: hyint enter - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar, addr->hyd_wcr);
#endif
#ifdef HYLOG
logit:
{
struct {
u_char hstate;
u_char hflags;
short hcsr;
short hwcr;
} hil;
hil.hstate = is->hy_state;
hil.hflags = is->hy_flags;
hil.hcsr = addr->hyd_csr;
hil.hwcr = addr->hyd_wcr;
hylog(HYL_INT, sizeof(hil), (char *)&hil);
}
#endif
if (HYS_ERROR(addr) && ((addr->hyd_csr & S_ATTN) == 0)) {
/*
* Error bit set, some sort of error in the interface.
*
* The adapter sets attn on command completion so that's not
* a real error even though the interface considers it one.
*/
#ifdef DEBUG
if (hy_nodebug & 4)
hy_debug_flag = 1;
#endif
printf("csr = 0x%b\nbar = 0x%x\nwcr = 0x%x\n",
addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar,
addr->hyd_wcr);
if (addr->hyd_csr & S_NEX) {
printf("hy%d: NEX - Non Existant Memory\n", unit);
#ifdef PI13
addr->hyd_csr |= S_NEX; /* as per PI13 manual */
#else
addr->hyd_csr &= ~S_NEX;
#endif
hycancel(ui);
#ifdef PI13
} else if (addr->hyd_csr & S_POWEROFF) {
printf("hy%d: \"Soft\" Adapter Power Failure (hyint)\n", unit);
addr->hyd_csr |= S_POWEROFF;
DELAY(100);
if (addr->hyd_csr & S_POWEROFF) {
printf( "hy%d: \"Hard\" Adapter Power Failure, Network Shutdown (hyint)\n", unit);
if_down(&is->hy_if);
is->hy_if.if_flags &= ~IFF_UP;
is->hy_state = STARTUP;
} else {
printf("hy%d: Adapter Power Restored (hyint)\n", unit);
}
#endif
} else {
printf("hy%d: BAR overflow\n", unit);
hycancel(ui);
}
} else if (HYS_NORMAL(addr)) {
/*
* Normal interrupt, bump state machine unless in state
* waiting and no data present (assumed to be word count
* zero interrupt or other hardware botch).
*/
if (is->hy_state != WAITING || HYS_RECVDATA(addr))
hyact(ui);
} else if (HYS_ABNORMAL(addr)) {
/*
* Abnormal termination.
* bump error counts, retry the last function
* 'MAXRETRY' times before kicking the bucket.
*
* Don't reissue the cmd if in certain states, abnormal
* on a reissued cmd or max retry exceeded.
*/
#ifdef HYLOG
if (hy_log.hyl_enable != hy_log.hyl_onerr) {
hy_log.hyl_enable = hy_log.hyl_onerr;
goto logit;
}
#endif
#ifdef DEBUG
if (hy_nodebug & 4)
hy_debug_flag = 1;
printD("hy%d: abnormal interrupt, driver state \"%s\" (%d)\n",
unit, hy_state_names[is->hy_state], is->hy_state);
printD("\tflags 0x%x olen %d lastwcr %d retry %d\n",
is->hy_flags, is->hy_olen, is->hy_lastwcr, is->hy_retry);
printD("\tsaved: state %d count %d cmd 0x%x ptr 0x%x\n",
is->hy_savedstate, is->hy_savedcount,
is->hy_savedaddr, is->hy_savedcmd);
#endif
#ifdef PI13
addr->hyd_csr &= ~S_C; /* clear the damned PI-13 */
#endif
if (is->hy_state == XMITSENT || is->hy_state == XMITDATASENT)
is->hy_if.if_oerrors++;
else if (is->hy_state == RECVSENT || is->hy_state == RECVDATASENT)
is->hy_if.if_ierrors++;
else
is->hy_if.if_collisions++; /* other errors */
if (is->hy_state == XMITDATASENT ||
is->hy_state == RECVSENT ||
is->hy_state == RECVDATASENT ||
(is->hy_flags & RQ_REISSUE) != 0 || is->hy_retry > MAXRETRY)
hycancel(ui);
else {
#ifdef DEBUG
if (hy_nodebug & 2)
hy_debug_flag = 1;
#endif
is->hy_retry++;
is->hy_flags |= RQ_ENDOP | RQ_STATUS | RQ_REISSUE;
is->hy_state = IDLE;
hyact(ui);
}
} else {
/*
* Interrupt is neither normal, abnormal, or interface error.
* Ignore it. It's either stacked or a word count 0.
*/
#ifdef HYLOG
if (hy_log.hyl_enable != hy_log.hyl_onerr) {
hy_log.hyl_enable = hy_log.hyl_onerr;
goto logit;
}
#endif
#ifdef DEBUG
printD("hy%d: possible stacked interrupt ignored\n", unit);
#endif
}
#ifdef DEBUG
printD("hy%d: hyint exit\n\n", unit);
#endif
hyint_active = 0;
}
int hyoutprint = 0;
/*
* Encapsulate a packet of type family for the local net.
*/
hyoutput(ifp, m0, dst)
struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
{
register struct hym_hdr *hym;
register struct mbuf *m;
register char *mp;
int dlen; /* packet size, incl hardware header, but not sw header */
int error = 0;
int s;
/*
* Calculate packet length for later deciding whether it will fit
* in a message proper or we also need associated data.
*/
dlen = 0;
for (m = m0; m; m = m->m_next)
dlen += m->m_len;
m = m0;
if (dst->sa_family == AF_HYLINK) { /* don't add header */
dlen -= HYM_SWLEN;
goto headerexists;
}
/*
* Add the software and hardware hyperchannel headers.
* If there's not enough space in the first mbuf, allocate another.
* If that should fail, drop this sucker.
* No extra space for headers is allocated.
*/
mp = mtod(m, char *); /* save pointer to real message */
if (m->m_off > MMAXOFF ||
MMINOFF + sizeof(struct hym_hdr) > m->m_off) {
m = m_get(M_DONTWAIT, MT_HEADER);
if (m == 0) {
m = m0;
error = ENOBUFS;
goto drop;
}
m->m_next = m0;
m->m_off = MMINOFF;
m->m_len = sizeof(struct hym_hdr);
} else {
m->m_off -= sizeof(struct hym_hdr);
m->m_len += sizeof(struct hym_hdr);
}
dlen += sizeof(struct hym_hdr) - HYM_SWLEN;
hym = mtod(m, struct hym_hdr *);
bzero((caddr_t)hym, sizeof(struct hym_hdr));
switch(dst->sa_family) {
#ifdef INET
case AF_INET: {
int i;
/*
* if loopback address, swizzle ip header so when
* it comes back it looks like it was addressed to us
*/
i = hyroute(ifp, (u_long)in_lnaof(((struct sockaddr_in *)dst)->sin_addr), hym);
if (i < 0)
goto notfound;
if (i > 0) {
struct in_addr temp;
temp.s_addr = ((struct ip *)mp)->ip_dst.s_addr;
((struct ip *)mp)->ip_dst.s_addr = ((struct ip *)mp)->ip_src.s_addr;
((struct ip *)mp)->ip_src.s_addr = temp.s_addr;
}
/*
* If entire packet won't fit in message proper, just
* send hyperchannel hardware header and ip header in
* message proper.
*
* This insures that the associated data is at least a
* TCP/UDP header in length and thus prevents potential
* problems with very short word counts.
*/
if (dlen > MPSIZE)
hym->hym_mplen = sizeof(struct hy_hdr) + (((struct ip *)mp)->ip_hl << 2);
hym->hym_type = HYLINK_IP;
break;
}
#endif
default:
printf("hy%d: can't handle af%d\n", ifp->if_unit,
dst->sa_family);
error = EAFNOSUPPORT;
goto drop;
}
headerexists:
/*
* insure message proper is below the maximum
*/
if (hym->hym_mplen > MPSIZE || (dlen > MPSIZE && hym->hym_mplen == 0))
hym->hym_mplen = MPSIZE;
hym->hym_from = htons(hy_softc[ifp->if_unit].hy_host);
if (hym->hym_mplen)
hym->hym_ctl |= H_ASSOC;
else
hym->hym_ctl &= ~H_ASSOC;
if (hyoutprint) printf("hy%d: output mplen=%x ctl=%x access=%x to=%x from=%x param=%x type=%x\n",
ifp->if_unit, hym->hym_mplen, hym->hym_ctl,
hym->hym_access, hym->hym_to, hym->hym_from,
hym->hym_param, hym->hym_type);
#ifdef DEBUG
printD("hy%d: output mplen=%x ctl=%x access=%x to=%x from=%x param=%x type=%x\n",
ifp->if_unit, hym->hym_mplen, hym->hym_ctl,
hym->hym_access, hym->hym_to, hym->hym_from,
hym->hym_param, hym->hym_type);
#endif
s = splimp();
if (IF_QFULL(&ifp->if_snd)) {
IF_DROP(&ifp->if_snd);
error = ENOBUFS;
splx(s);
goto drop;
}
IF_ENQUEUE(&ifp->if_snd, m);
if (hy_softc[ifp->if_unit].hy_state == WAITING)
hyact(hyinfo[ifp->if_unit]);
splx(s);
return (0);
notfound:
error = EHOSTUNREACH;
drop:
m_freem(m);
return (error);
}
int
hyroute(ifp, dest, hym)
register struct ifnet *ifp;
u_long dest;
register struct hym_hdr *hym;
{
#ifdef HYROUTE
register struct hy_route *rt = &hy_route[ifp->if_unit];
register struct hyr_hash *rhash;
register int i;
#endif
hym->hym_param = 0;
#ifdef HYROUTE
if (rt->hyr_lasttime != 0) {
i = HYRHASH(dest);
rhash = &rt->hyr_hash[i];
i = 0;
while (rhash->hyr_key != dest) {
if (rhash->hyr_flags == 0 || i > HYRSIZE)
return(-1);
rhash++; i++;
if (rhash >= &rt->hyr_hash[HYRSIZE])
rhash = &rt->hyr_hash[0];
}
if (rhash->hyr_flags & HYR_GATE) {
i = rhash->hyr_nextgate;
if (i >= rhash->hyr_egate)
rhash->hyr_nextgate = rhash->hyr_pgate;
else
rhash->hyr_nextgate++;
rhash = &rt->hyr_hash[rt->hyr_gateway[i]];
if ((rhash->hyr_flags & HYR_DIR) == 0)
return(-1);
} else if (rhash->hyr_flags & HYR_LOOP) {
hym->hym_param = H_LOOPBK; /* adapter loopback */
} else if (rhash->hyr_flags & HYR_RLOOP) {
hym->hym_param = H_RLOOPBK; /* A710 remote loopback */
}
hym->hym_ctl = rhash->hyr_ctl;
hym->hym_access = rhash->hyr_access;
hym->hym_to = rhash->hyr_dst;
} else {
#endif
hym->hym_ctl = H_XTRUNKS | H_RTRUNKS;
hym->hym_access = 0;
hym->hym_to = htons((u_short)dest);
if (dest & 0x010000)
hym->hym_param = H_LOOPBK; /* adapter loopback */
else if (dest & 0x020000)
hym->hym_param = H_RLOOPBK; /* A710 remote loopback */
#ifdef HYROUTE
}
#endif
if (hym->hym_param == 0)
return(0);
else
return(1);
}
hyact(ui)
register struct uba_device *ui;
{
register struct hy_softc *is = &hy_softc[ui->ui_unit];
register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
actloop:
#ifdef DEBUG
printD("hy%d: hyact, enter state \"%s\"\n", ui->ui_unit,
hy_state_names[is->hy_state]);
#endif
switch (is->hy_state) {
case STARTUP:
goto endintr;
case IDLE: {
register rq = is->hy_flags;
if (rq & RQ_STATUS) {
is->hy_flags &= ~RQ_STATUS;
is->hy_state = STATSENT;
hystart(ui, HYF_STATUS, sizeof (is->hy_status),
is->hy_ifuba.ifu_r.ifrw_info);
} else if (rq & RQ_ENDOP) {
is->hy_flags &= ~RQ_ENDOP;
is->hy_state = ENDOPSENT;
hystart(ui, HYF_END_OP, 0, 0);
} else if (rq & RQ_STATISTICS) {
is->hy_flags &= ~RQ_STATISTICS;
is->hy_state = RSTATSENT;
hystart(ui, HYF_RSTATS, sizeof (is->hy_stat),
is->hy_ifuba.ifu_r.ifrw_info);
} else if (HYS_RECVDATA(addr)) {
is->hy_state = RECVSENT;
is->hy_retry = 0;
hystart(ui, HYF_INPUTMSG, MPSIZE, is->hy_ifuba.ifu_r.ifrw_info + HYM_SWLEN);
} else if (rq & RQ_REISSUE) {
is->hy_flags &= ~RQ_REISSUE;
is->hy_state = is->hy_savedstate;
#ifdef DEBUG
printD("hy%d: reissue cmd=0x%x count=%d",
ui->ui_unit, is->hy_savedcmd, is->hy_savedcount);
printD(" ubaddr=0x%x retry=%d\n",
is->hy_savedaddr, is->hy_retry);
#endif
hystart(ui, is->hy_savedcmd, is->hy_savedcount,
is->hy_savedaddr);
} else {
register struct mbuf *m;
IF_DEQUEUE(&is->hy_if.if_snd, m);
if (m != NULL) {
register struct hym_hdr *hym;
register int mplen;
register int cmd;
is->hy_state = XMITSENT;
is->hy_retry = 0;
hym = mtod(m, struct hym_hdr *);
#ifdef HYLOG
hylog(HYL_XMIT, sizeof(struct hym_hdr),
(char *)hym);
#endif
mplen = hym->hym_mplen;
if (hym->hym_to_adapter == hym->hym_from_adapter)
cmd = HYF_XMITLOCMSG;
else
cmd = HYF_XMITMSG;
#ifdef DEBUG
printD("hy%d: hym_hdr = ", ui->ui_unit);
if (hy_debug_flag)
hyprintdata((char *)hym,
sizeof (struct hym_hdr));
#endif
is->hy_olen = if_wubaput(&is->hy_ifuba, m) - HYM_SWLEN;
if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP)
UBAPURGE(is->hy_ifuba.ifu_uba,
is->hy_ifuba.ifu_w.ifrw_bdp);
#ifdef DEBUG
printD(
"hy%d: sending packet (mplen = %d, hy_olen = %d) data = ",
ui->ui_unit, mplen, is->hy_olen);
if (hy_debug_flag)
hyprintdata(
is->hy_ifuba.ifu_w.ifrw_addr,
is->hy_olen + HYM_SWLEN);
#endif
if (mplen == 0) {
is->hy_flags &= ~RQ_XASSOC;
mplen = is->hy_olen;
} else {
is->hy_flags |= RQ_XASSOC;
}
hystart(ui, cmd, mplen, is->hy_ifuba.ifu_w.ifrw_info + HYM_SWLEN);
} else if (rq & RQ_MARKDOWN) {
is->hy_flags &= ~(RQ_MARKUP | RQ_MARKDOWN);
is->hy_state = MARKPORT;
is->hy_retry = 0;
/*
* Port number is taken from status data
*/
hystart(ui,
(int)(HYF_MARKP0|(PORTNUM(&is->hy_status)<<2)),
0, 0);
} else if (rq & RQ_MARKUP) {
register struct ifnet *ifp = &is->hy_if;
is->hy_flags &= ~RQ_MARKUP;
is->hy_retry = 0;
/*
* Fill in the host number
* from the status buffer
*/
printf(
"hy%d: unit number 0x%x port %d type %x microcode level 0x%x\n",
ui->ui_unit,
is->hy_stat.hyc_uaddr,
PORTNUM(&is->hy_status),
(is->hy_stat.hyc_atype[0]<<8) |
is->hy_stat.hyc_atype[1],
is->hy_stat.hyc_atype[2]);
is->hy_host =
(is->hy_stat.hyc_uaddr << 8) |
PORTNUM(&is->hy_status);
ifp->if_flags |= IFF_UP;
#ifdef HYLOG
hylog(HYL_UP, 0, (char *)0);
#endif
} else {
is->hy_state = WAITING;
is->hy_retry = 0;
hystart(ui, HYF_WAITFORMSG, 0, 0);
}
}
break;
}
case STATSENT:
bcopy(is->hy_ifuba.ifu_r.ifrw_addr, (caddr_t)&is->hy_status,
sizeof (struct hy_status));
#ifdef DEBUG
printD("hy%d: status - %x %x %x %x %x %x %x %x\n",
ui->ui_unit, is->hy_status.hys_gen_status,
is->hy_status.hys_last_fcn,
is->hy_status.hys_resp_trunk,
is->hy_status.hys_status_trunk,
is->hy_status.hys_recd_resp,
is->hy_status.hys_error,
is->hy_status.hys_caddr,
is->hy_status.hys_pad);
#endif
is->hy_state = IDLE;
#ifdef HYLOG
hylog(HYL_STATUS, sizeof (struct hy_status),
(char *)&is->hy_status);
#endif
#ifdef HYELOG
{
register int i;
i = is->hy_status.hys_error;
if (i > HYE_MAX)
i = HYE_MAX;
switch (is->hy_status.hys_last_fcn) {
case HYF_XMITLOCMSG:
i += HYE_MAX+1; /* fall through */
case HYF_XMITLSTDATA:
i += HYE_MAX+1; /* fall through */
case HYF_XMITMSG:
i += HYE_MAX+1;
}
hy_elog[i]++;
}
#endif
break;
case RSTATSENT: {
register struct hy_stat *p =
(struct hy_stat *)is->hy_ifuba.ifu_r.ifrw_addr;
bcopy((caddr_t)p, (caddr_t)&is->hy_stat, sizeof(struct hy_stat));
#ifdef DEBUG
printD("hy%d: statistics - df0 %d df1 %d df2 %d df3 %d\n",
ui->ui_unit,
(is->hy_stat.hyc_df0[0]<<16) | (is->hy_stat.hyc_df0[1]<<8) | is->hy_stat.hyc_df0[2],
(is->hy_stat.hyc_df1[0]<<16) | (is->hy_stat.hyc_df1[1]<<8) | is->hy_stat.hyc_df1[2],
(is->hy_stat.hyc_df2[0]<<16) | (is->hy_stat.hyc_df2[1]<<8) | is->hy_stat.hyc_df2[2],
(is->hy_stat.hyc_df3[0]<<16) | (is->hy_stat.hyc_df3[1]<<8) | is->hy_stat.hyc_df3[2]);
printD(" ret0 %d ret1 %d ret2 %d ret3 %d\n",
(is->hy_stat.hyc_ret0[0]<<16) | (is->hy_stat.hyc_ret0[1]<<8) | is->hy_stat.hyc_ret0[2],
(is->hy_stat.hyc_ret1[0]<<16) | (is->hy_stat.hyc_ret1[1]<<8) | is->hy_stat.hyc_ret1[2],
(is->hy_stat.hyc_ret2[0]<<16) | (is->hy_stat.hyc_ret2[1]<<8) | is->hy_stat.hyc_ret2[2],
(is->hy_stat.hyc_ret3[0]<<16) | (is->hy_stat.hyc_ret3[1]<<8) | is->hy_stat.hyc_ret3[2]);
printD(" cancel %d abort %d atype %x %x %x uaddr %x\n",
(is->hy_stat.hyc_cancel[0]<<8) | is->hy_stat.hyc_cancel[1],
(is->hy_stat.hyc_abort[0]<<8) | is->hy_stat.hyc_abort[1],
is->hy_stat.hyc_atype[0], is->hy_stat.hyc_atype[1],
is->hy_stat.hyc_atype[2], is->hy_stat.hyc_uaddr);
#endif
is->hy_state = IDLE;
#ifdef HYLOG
hylog(HYL_STATISTICS, sizeof (struct hy_stat),
(char *)&is->hy_stat);
#endif
break;
}
case CLEARSENT:
is->hy_state = IDLE;
break;
case ENDOPSENT:
is->hy_state = IDLE;
break;
case RECVSENT: {
register struct hym_hdr *hym;
register unsigned len;
if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP)
UBAPURGE(is->hy_ifuba.ifu_uba,
is->hy_ifuba.ifu_r.ifrw_bdp);
hym = (struct hym_hdr *) (is->hy_ifuba.ifu_r.ifrw_addr);
len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1;
if (len > MPSIZE) {
printf("hy%d: RECVD MP > MPSIZE (%d)\n",
ui->ui_unit, len);
is->hy_state = IDLE;
#ifdef DEBUG
hy_debug_flag = 1;
printD("hy%d: csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
ui->ui_unit, addr->hyd_csr, HY_CSR_BITS,
addr->hyd_bar, addr->hyd_wcr);
#endif
}
hym->hym_mplen = len;
#ifdef DEBUG
printD("hy%d: recvd mp, len = %d, data = ", ui->ui_unit, len);
if (hy_debug_flag)
hyprintdata((char *)hym, len + HYM_SWLEN);
#endif
if (hym->hym_ctl & H_ASSOC) {
is->hy_state = RECVDATASENT;
is->hy_retry = 0;
hystart(ui, HYF_INPUTDATA,
(int)(HYMTU + sizeof (struct hy_hdr) - len),
(int)(HYM_SWLEN + is->hy_ifuba.ifu_r.ifrw_info + len));
} else {
hyrecvdata(ui, hym, (int)len + HYM_SWLEN);
is->hy_state = IDLE;
}
break;
}
case RECVDATASENT: {
register struct hym_hdr *hym;
register unsigned len;
if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP)
UBAPURGE(is->hy_ifuba.ifu_uba,
is->hy_ifuba.ifu_r.ifrw_bdp);
hym = (struct hym_hdr *) (is->hy_ifuba.ifu_r.ifrw_addr);
len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1;
#ifdef DEBUG
printD("hy%d: recvd assoc data, len = %d, data = ",
ui->ui_unit, len);
if (hy_debug_flag)
hyprintdata((char *)hym + hym->hym_mplen, len);
#endif
hyrecvdata(ui, hym, (int)(len + hym->hym_mplen + HYM_SWLEN));
is->hy_state = IDLE;
break;
}
case XMITSENT:
if (is->hy_flags & RQ_XASSOC) {
register int len;
is->hy_flags &= ~RQ_XASSOC;
is->hy_state = XMITDATASENT;
is->hy_retry = 0;
len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1;
if (len > is->hy_olen) {
printf(
"hy%d: xmit error - len > hy_olen [%d > %d]\n",
ui->ui_unit, len, is->hy_olen);
#ifdef DEBUG
hy_debug_flag = 1;
#endif
}
hystart(ui, HYF_XMITLSTDATA, is->hy_olen - len,
is->hy_ifuba.ifu_w.ifrw_info + HYM_SWLEN + len);
break;
}
/* fall through to ... */
case XMITDATASENT:
hyxmitdata(ui);
is->hy_state = IDLE;
break;
case WAITING: /* wait for message complete or output requested */
if (HYS_RECVDATA(addr))
is->hy_state = IDLE;
else {
is->hy_state = CLEARSENT;
is->hy_retry = 0;
hystart(ui, HYF_CLRWFMSG, 0, 0);
}
break;
case MARKPORT:
is->hy_state = STARTUP;
if_down(&is->hy_if);
is->hy_if.if_flags &= ~IFF_UP;
goto endintr;
default:
printf("hy%d: DRIVER BUG - INVALID STATE %d\n",
ui->ui_unit, is->hy_state);
panic("HYPERCHANNEL IN INVALID STATE");
/*NOTREACHED*/
}
if (is->hy_state == IDLE)
goto actloop;
endintr:
;
#ifdef DEBUG
printD("hy%d: hyact, exit at \"%s\"\n", ui->ui_unit,
hy_state_names[is->hy_state]);
#endif
}
struct sockproto hypproto = { PF_HYLINK };
struct sockaddr_in hypdst = { AF_HYLINK };
struct sockaddr_in hypsrc = { AF_HYLINK };
/*
* Called from device interrupt when receiving data.
* Examine packet to determine type. Decapsulate packet
* based on type and pass to type specific higher-level
* input routine.
*/
hyrecvdata(ui, hym, len)
struct uba_device *ui;
register struct hym_hdr *hym;
int len;
{
register struct hy_softc *is = &hy_softc[ui->ui_unit];
struct mbuf *m;
register struct ifqueue *inq;
is->hy_if.if_ipackets++;
#ifdef DEBUG
printD("hy%d: recieved packet, len = %d\n", ui->ui_unit, len);
#endif
#ifdef HYLOG
{
struct {
short hlen;
struct hym_hdr hhdr;
} hh;
hh.hlen = len;
hh.hhdr = *hym;
hylog(HYL_RECV, sizeof(hh), (char *)&hh);
}
#endif
if (len > HYMTU + MPSIZE || len == 0)
return; /* sanity */
/*
* Pull packet off interface.
*/
m = if_rubaget(&is->hy_ifuba, len, 0, &is->hy_if);
if (m == NULL)
return;
/*
* if normal or adapter loopback response packet believe hym_type,
* otherwise, use the raw input queue cause it's a response from an
* adapter command.
*/
if (hym->hym_param != 0 && (u_short)hym->hym_param != 0x80ff)
goto rawlinkin;
switch (hym->hym_type) {
#ifdef INET
case HYLINK_IP:
schednetisr(NETISR_IP);
inq = &ipintrq;
break;
#endif
default:
rawlinkin:
{
struct mbuf *m0;
MGET(m0, M_DONTWAIT, MT_DATA);
if (m0 == 0) {
m_freem(m);
return;
}
m0->m_off = MMINOFF;
m0->m_len = sizeof(struct hym_hdr);
m0->m_next = m;
bcopy((caddr_t)hym, mtod(m0, caddr_t), sizeof(struct hym_hdr));
m = m0;
hypproto.sp_protocol = 0;
hypdst.sin_addr = is->hy_addr;
hypsrc.sin_addr = is->hy_addr;
raw_input(m, &hypproto, (struct sockaddr *)&hypsrc,
(struct sockaddr *)&hypdst);
return;
}
}
if (IF_QFULL(inq)) {
IF_DROP(inq);
m_freem(m);
} else
IF_ENQUEUE(inq, m);
}
/*
* Transmit done, release resources, bump counters.
*/
hyxmitdata(ui)
struct uba_device *ui;
{
register struct hy_softc *is = &hy_softc[ui->ui_unit];
is->hy_if.if_opackets++;
if (is->hy_ifuba.ifu_xtofree) {
m_freem(is->hy_ifuba.ifu_xtofree);
is->hy_ifuba.ifu_xtofree = 0;
}
}
hycancel(ui)
register struct uba_device *ui;
{
register struct hy_softc *is = &hy_softc[ui->ui_unit];
if (is->hy_ifuba.ifu_xtofree) {
m_freem(is->hy_ifuba.ifu_xtofree);
is->hy_ifuba.ifu_xtofree = 0;
}
#ifdef HYLOG
hylog(HYL_CANCEL, 0, (char *)0);
#endif
#ifdef DEBUG
if (hy_nodebug & 1)
hy_debug_flag = 1;
#endif
#ifdef DEBUG
printD("hy%d: cancel from state \"%s\" cmd=0x%x count=%d ptr=0x%x\n",
ui->ui_unit, hy_state_names[is->hy_state], is->hy_savedcmd,
is->hy_savedcount, is->hy_savedaddr);
printD("\tflags 0x%x olen %d lastwcr %d retry %d\n",
is->hy_flags, is->hy_olen, is->hy_lastwcr, is->hy_retry);
printD("\tsaved: state %d count %d ptr 0x%x cmd 0x%x\n",
is->hy_savedstate, is->hy_savedcount, is->hy_savedaddr,
is->hy_savedcmd);
#endif
is->hy_state = IDLE;
is->hy_flags |= (RQ_ENDOP | RQ_STATUS);
hyact(ui);
}
#ifdef DEBUG
hyprintdata(cp, len)
register char *cp;
register int len;
{
register int count = 16;
register char *fmt;
static char regfmt[] = "\n\t %x";
fmt = &regfmt[2];
while (--len >= 0) {
printL(fmt, *cp++ & 0xff);
fmt = &regfmt[2];
if (--count <= 0) {
fmt = &regfmt[0];
count = 16;
}
}
printL("\n");
}
#endif
hywatch(unit)
int unit;
{
register struct hy_softc *is = &hy_softc[unit];
register struct uba_device *ui = hyinfo[unit];
register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
int s;
s = splimp();
#ifdef PI13
if ((addr->hyd_csr & S_POWEROFF) != 0) {
addr->hyd_csr |= S_POWEROFF;
DELAY(100);
if ((addr->hyd_csr & S_POWEROFF) == 0) {
printf("hy%d: Adapter Power Restored (hywatch)\n", unit);
is->hy_state = IDLE;
is->hy_flags |=
(RQ_MARKUP | RQ_STATISTICS | RQ_ENDOP | RQ_STATUS);
hyact(ui);
}
}
#endif
if (++is->hy_ntime >= 2 && is->hy_state != WAITING &&
is->hy_state != STARTUP && is->hy_state != IDLE) {
#ifdef HYLOG
printf("hy%d: watchdog timer expired in state \"%s\"\n", unit,
hy_state_names[is->hy_state]);
#else
printf("hy%d: watchdog timer expired in state %d\n", unit,
is->hy_state);
#endif
printf("hy%d: last command 0x%x, flags 0x%x, csr 0x%b\n", unit,
is->hy_savedcmd, is->hy_flags, addr->hyd_csr, HY_CSR_BITS);
hycancel(ui);
}
splx(s);
is->hy_if.if_timer = SCANINTERVAL;
}
#ifdef HYLOG
hylog(code, len, ptr)
int code, len;
char *ptr;
{
register unsigned char *p;
int s;
s = splimp();
if (hy_log.hyl_self != &hy_log) {
hy_log.hyl_eptr = &hy_log.hyl_buf[HYL_SIZE];
hy_log.hyl_ptr = &hy_log.hyl_buf[0];
hy_log.hyl_self = &hy_log;
hy_log.hyl_enable = HYL_CONTINUOUS;
hy_log.hyl_onerr = HYL_CONTINUOUS;
hy_log.hyl_count = 0;
hy_log.hyl_icount = 16;
hy_log.hyl_filter = 0xffff; /* enable all */
}
if (hy_log.hyl_enable == HYL_DISABLED || ((1 << code) & hy_log.hyl_filter) == 0)
goto out;
p = hy_log.hyl_ptr;
if (p + len + 3 >= hy_log.hyl_eptr) {
bzero((caddr_t)p, (unsigned)(hy_log.hyl_eptr - p));
p = &hy_log.hyl_buf[0];
if (hy_log.hyl_enable != HYL_CONTINUOUS) {
hy_log.hyl_enable = HYL_DISABLED;
goto out;
}
}
*p++ = code;
*p++ = len;
bcopy((caddr_t)ptr, (caddr_t)p, (unsigned)len);
if (hy_log.hyl_count != 0 && --hy_log.hyl_count == 0) {
*p++ = '\0';
hy_log.hyl_enable = HYL_DISABLED;
hy_log.hyl_count = hy_log.hyl_icount;
}
p += len;
if (hy_log.hyl_wait != 0) { /* wakeup HYGETLOG if wanted */
if (hy_log.hyl_wait <= p - hy_log.hyl_ptr) {
wakeup((caddr_t)&hy_log);
hy_log.hyl_wait = 0;
} else
hy_log.hyl_wait -= p - hy_log.hyl_ptr;
}
hy_log.hyl_ptr = p;
out:
splx(s);
}
#endif
/*ARGSUSED*/
hyioctl(ifp, cmd, data)
register struct ifnet *ifp;
int cmd;
caddr_t data;
{
struct ifaddr *ifa = (struct ifaddr *) data;
struct hyrsetget *sg = (struct hyrsetget *)data;
#if defined(HYLOG) || defined(HYELOG)
struct hylsetget *sgl = (struct hylsetget *)data;
#endif
struct hy_route *r = (struct hy_route *)&hy_route[ifp->if_unit];
int s = splimp(), error = 0;
#ifdef HYLOG
struct hy_softc *is = &hy_softc[ifp->if_unit];
struct {
u_char hstate;
u_char hflags;
u_short iflags;
int hcmd;
int herror;
u_long haddr;
u_long hmisc;
} hil;
hil.hmisc = -1;
hil.hstate = is->hy_state;
hil.hflags = is->hy_flags;
hil.hcmd = cmd;
#endif
switch(cmd) {
case SIOCSIFADDR:
if (ifa->ifa_addr.sa_family != AF_INET)
return(EINVAL);
if ((ifp->if_flags & IFF_RUNNING) == 0)
hyinit(ifp->if_unit);
hy_softc[ifp->if_unit].hy_addr = IA_SIN(ifa)->sin_addr;
#ifdef HYLOG
hil.haddr = is->hy_addr.s_addr;
#endif
break;
case HYSETROUTE:
if (!suser()) {
error = EPERM;
goto out;
}
if (sg->hyrsg_len != sizeof(struct hy_route)) {
error = EINVAL;
goto out;
}
if (copyin((caddr_t)(sg->hyrsg_ptr), (caddr_t)r, sg->hyrsg_len)) {
r->hyr_lasttime = 0; /* disable further routing if trouble */
error = EFAULT;
goto out;
}
r->hyr_lasttime = time.tv_sec;
#ifdef HYLOG
hil.hmisc = r->hyr_lasttime;
#endif
break;
case HYGETROUTE:
if (sg->hyrsg_len < sizeof(struct hy_route)) {
error = EINVAL;
goto out;
}
if (copyout((caddr_t)r, (caddr_t) (sg->hyrsg_ptr), sizeof(struct hy_route))) {
error = EFAULT;
goto out;
}
break;
#ifdef HYELOG
case HYGETELOG:
if (sgl->hylsg_len < sizeof(hy_elog)) {
error = EINVAL;
goto out;
}
if (copyout((caddr_t)hy_elog, sgl->hylsg_ptr, sizeof(hy_elog))) {
error = EFAULT;
goto out;
}
if (sgl->hylsg_cmd) {
if (!suser()) {
error = EPERM;
goto out;
}
bzero((caddr_t)hy_elog, sizeof(hy_elog));
}
break;
#endif
#ifdef HYLOG
case HYSETLOG:
if (!suser()) {
error = EPERM;
goto out;
}
hy_log.hyl_enable = HYL_DISABLED;
hylog(HYL_NOP, 0, (char *)0); /* force log init */
hy_log.hyl_enable = sgl->hylsg_cmd & 0x0f;
hy_log.hyl_onerr = (sgl->hylsg_cmd >> 4) & 0x0f;
hy_log.hyl_filter = (sgl->hylsg_cmd >> 8) & 0xffffff;
hy_log.hyl_count = hy_log.hyl_icount = sgl->hylsg_len;
wakeup((caddr_t)&hy_log); /* wakeup sleeping HYGETLOG */
break;
case HYGETLOG:
if (sgl->hylsg_len < sizeof(hy_log)) {
error = EINVAL;
goto out;
}
if (sgl->hylsg_cmd != 0) {
if (hy_log.hyl_wait) {
error = EBUSY;
goto out;
}
hy_log.hyl_wait = sgl->hylsg_cmd;
sleep((caddr_t)&hy_log, PZERO - 1);
}
if (copyout((caddr_t)&hy_log, sgl->hylsg_ptr, sizeof(hy_log))) {
error = EFAULT;
goto out;
}
break;
#endif
default:
error = EINVAL;
break;
}
out:
#ifdef HYLOG
hil.herror = error;
hil.iflags = ifp->if_flags;
hil.haddr = is->hy_addr.s_addr;
hylog(HYL_IOCTL, sizeof(hil), (char *)&hil);
#endif
splx(s);
return (error);
}
#endif