Source to arch/i386/isa/if_hp.c


Enter a symbol's name here to quickly find it.

/*-
 * Copyright (c) 1990, 1991 William F. Jolitz.
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	if_hp.c,v 1.9 1993/07/13 21:53:06 mycroft Exp
 */

/*
 * HP LAN Ethernet driver
 *
 * Parts inspired from Tim Tucker's if_wd driver for the wd8003,
 * insight on the ne2000 gained from Robert Clements PC/FTP driver.
 *
 * receive bottom end totally rewritten by Curt Mayer, Dec 1992.
 * no longer loses back to back packets.
 * note to driver writers: RTFM!
 *
 * hooks for packet filter added by Charles Hannum, 29DEC1992.
 *
 * Mostly rewritten for HP-labelled EISA controllers by Charles Hannum,
 * 18JAN1993.
 */

#include "hp.h"
#if NHP > 0

#include "param.h"
#include "systm.h"
#include "mbuf.h"
#include "buf.h"
#include "protosw.h"
#include "socket.h"
#include "ioctl.h"
#include "errno.h"
#include "syslog.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"
#include "netinet/if_ether.h"
#endif

#ifdef NS
#include "netns/ns.h"
#include "netns/ns_if.h"
#endif

#include "bpfilter.h"
#if NBPFILTER > 0
#include "net/bpf.h"
#include "net/bpfdesc.h"
#endif

#include "machine/cpufunc.h"
#include "i386/isa/isa_device.h"
#include "i386/isa/if_nereg.h"
#include "i386/isa/icu.h"

int hpprobe (), hpattach (), hpintr ();
int hpstart (), hpinit (), ether_output (), hpioctl ();

struct isa_driver hpdriver =
{
  hpprobe, hpattach, "hp",
};

struct mbuf *hpget ();

#define ETHER_MIN_LEN 64
#define ETHER_MAX_LEN 1536

/*
 * Ethernet software status per interface.
 *
 * Each interface is referenced by a network interface structure,
 * ns_if, which the routing code uses to locate the interface.
 * This structure contains the output queue for the interface, its address, ...
 */
struct hp_softc
  {
    struct arpcom ns_ac;	/* Ethernet common part */
#define	ns_if	ns_ac.ac_if	/* network-visible interface */
#define	ns_addr	ns_ac.ac_enaddr	/* hardware Ethernet address */
    int ns_flags;
#define	DSF_LOCK	1	/* block re-entering enstart */
    int ns_oactive;
    int ns_mask;
    struct prhdr ns_ph;		/* hardware header of incoming packet*/
    u_char ns_pb[2048];
    u_char ns_txstart;		/* transmitter buffer start */
    u_char ns_rxstart;		/* receiver buffer start */
    u_char ns_rxend;		/* receiver buffer end */
    u_char hp_type;		/* HP board type */
    u_char hp_irq;		/* interrupt vector */
    short ns_port;		/* i/o port base */
    short ns_mode;		/* word/byte mode */
    short ns_rcr;
#if NBPFILTER > 0
    caddr_t ns_bpf;
#endif
  }

hp_softc[NHP];
#define	ENBUFSIZE	(sizeof(struct ether_header) + ETHERMTU + 2 + ETHER_MIN_LEN)

#define	PAT(n)	(0xa55a + 37*(n))

u_short boarddata[16];

#define hp_option (-8)
#define hp_data (-4)
#define HP_RUN (0x01)
#define HP_DATA (0x10)

hpprobe (dvp)
     struct isa_device *dvp;
{
  int val, i, s, sum, pat;
  register struct hp_softc *ns = &hp_softc[0];
  register hpc;

#ifdef lint
  hpintr (0);
#endif

  hpc = (ns->ns_port = dvp->id_iobase + 0x10);
  s = splnet ();

  ns->hp_irq = ffs(dvp->id_irq) - 1;

  /* Extract board address */
  for (i = 0; i < 6; i++)
    ns->ns_addr[i] = inb (hpc - 0x10 + i);
  ns->hp_type = inb (hpc - 0x10 + 7);

  if (ns->ns_addr[0] != 0x08 ||
      ns->ns_addr[1] != 0x00 ||
      ns->ns_addr[2] != 0x09) {
    splx (s);
    return 0;
  }

  /* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
  /* On this board, WTS means 32-bit transfers, which is still
     experimental.  - mycroft, 18JAN93 */
#ifdef HP_32BIT
  ns->ns_mode = DSDC_WTS | DSDC_BMS | DSDC_FT1;
#else
  ns->ns_mode = DSDC_BMS | DSDC_FT1;
#endif
  ns->ns_txstart =  0 * 1024 / DS_PGSIZE;
  ns->ns_rxend   = 32 * 1024 / DS_PGSIZE;

  ns->ns_rxstart = ns->ns_txstart + (PKTSZ / DS_PGSIZE);

  outb (hpc + hp_option, HP_RUN);

#if 0
  outb (hpc + ds0_isr, 0xff);
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG0 | DSCM_STOP);
  DELAY (1000);

  /* Check cmd reg and fail if not right */
  if ((i = inb (hpc + ds_cmd)) != (DSCM_NODMA | DSCM_PG0 | DSCM_STOP)) {
    splx (s);
    return (0);
  }
#endif

  outb (hpc + hp_option, 0);

  splx (s);
  return (32);
}

/*
 * Fetch from onboard ROM/RAM
 */
hpfetch (ns, up, ad, len)
     struct hp_softc *ns;
     caddr_t up;
{
  u_char cmd;
  register hpc = ns->ns_port;
  int counter = 100000;

  outb (hpc + hp_option, inb (hpc + hp_option) | HP_DATA);

  cmd = inb (hpc + ds_cmd);
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG0 | DSCM_START);

  /* Setup remote dma */
  outb (hpc + ds0_isr, DSIS_RDC);

  if (ns->ns_mode & DSDC_WTS)
    len = (len + 3) & ~3;
  else
    len = (len + 1) & ~1;

  outb (hpc + ds0_rbcr0, len);
  outb (hpc + ds0_rbcr1, len >> 8);
  outb (hpc + ds0_rsar0, ad);
  outb (hpc + ds0_rsar1, ad >> 8);

#ifdef HP_DEBUG
  printf ("hpfetch: len=%d ioaddr=0x%03x addr=0x%04x option=0x%02x %d-bit\n",
	  len, hpc+hp_data, ad, inb (hpc + hp_option),
	  ns->ns_mode & DSDC_WTS ? 32 : 16);
  printf ("hpfetch: cmd=0x%02x isr=0x%02x ",
	  inb (hpc + ds_cmd), inb (hpc + ds0_isr));
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG2 | DSCM_START);
  printf ("imr=0x%02x rcr=0x%02x tcr=0x%02x dcr=0x%02x\n",
	  inb (hpc + ds0_imr), inb (hpc + ds0_rcr), inb (hpc + ds0_tcr),
	  inb (hpc + ds0_dcr));
#endif

  /* Execute & extract from card */
  outb (hpc + ds_cmd, DSCM_RREAD | DSCM_PG0 | DSCM_START);

#ifdef HP_32BIT
  if (ns->ns_mode & DSDC_WTS)
    len = (caddr_t) insd (hpc + hp_data, up, len >> 2) - up;
  else
#endif
    len = (caddr_t) insw (hpc + hp_data, up, len >> 1) - up;

#ifdef HP_DEBUG
  printf ("hpfetch: done len=%d\n", len);
#endif

  /* Wait till done, then shutdown feature */
  while ((inb (hpc + ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
    ;
  outb (hpc + ds0_isr, DSIS_RDC);
  outb (hpc + ds_cmd, cmd);

  outb (hpc + hp_option, inb (hpc + hp_option) & ~HP_DATA);
}

/*
 * Put to onboard RAM
 */
hpput (ns, up, ad, len)
     struct hp_softc *ns;
     caddr_t up;
{
  u_char cmd;
  register hpc = ns->ns_port;
  int counter = 100000;

  outb (hpc + hp_option, inb (hpc + hp_option) | HP_DATA);

  cmd = inb (hpc + ds_cmd);
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG0 | DSCM_START);

  /* Setup for remote dma */
  outb (hpc + ds0_isr, DSIS_RDC);

  if (ns->ns_mode & DSDC_WTS)
    len = (len + 3) & ~3;
  else
    len = (len + 1) & ~1;

#ifdef HP_DEBUG
  printf ("hpput: len=%d ioaddr=0x%03x addr=0x%04x option=0x%02x %d-bit\n",
	  len, hpc+hp_data, ad, inb (hpc + hp_option),
	  ns->ns_mode & DSDC_WTS ? 32 : 16);
  printf ("hpput: cmd=0x%02x isr=0x%02x ",
	  inb (hpc + ds_cmd), inb (hpc + ds0_isr));
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG2 | DSCM_START);
  printf ("imr=0x%02x rcr=0x%02x tcr=0x%02x dcr=0x%02x\n",
	  inb (hpc + ds0_imr), inb (hpc + ds0_rcr), inb (hpc + ds0_tcr),
	  inb (hpc + ds0_dcr));
  {
    unsigned char *p = (unsigned char *) up;
    int n = len;
    printf ("hpput:");
    while (n--)
      printf (" %02x", *(p++));
    printf ("\n");
  }
#endif

  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG0 | DSCM_START);
  outb (hpc + ds0_rbcr0, 0xff);
  outb (hpc + ds_cmd, DSCM_RREAD | DSCM_PG0 | DSCM_START);

  outb (hpc + ds0_rbcr0, len);
  outb (hpc + ds0_rbcr1, len >> 8);
  outb (hpc + ds0_rsar0, ad);
  outb (hpc + ds0_rsar1, ad >> 8);

  /* Execute & stuff to card */
  outb (hpc + ds_cmd, DSCM_RWRITE | DSCM_PG0 | DSCM_START);

#ifdef HP_32BIT
  if (ns->ns_mode & DSDC_WTS)
    len = (caddr_t) outsd (hpc + hp_data, up, len >> 2) - up;
  else
#endif
    len = (caddr_t) outsw (hpc + hp_data, up, len >> 1) - up;

#ifdef HP_DEBUG
  printf ("hpput: done len=%d\n", len);
#endif

  /* Wait till done, then shutdown feature */
  while ((inb (hpc + ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
    ;
  outb (hpc + ds0_isr, DSIS_RDC);
  outb (hpc + ds_cmd, cmd);

  outb (hpc + hp_option, inb (hpc + hp_option) & ~HP_DATA);
}

/*
 * Reset of interface.
 */
hpreset (unit, uban)
     int unit, uban;
{
  register struct hp_softc *ns = &hp_softc[unit];
  register hpc = ns->ns_port;
  if (unit >= NHP)
    return;
  printf ("hp%d: reset\n", unit);
  outb (hpc + hp_option, 0);
  ns->ns_flags &= ~DSF_LOCK;
  hpinit (unit);
}

static char *
hp_id (type)
     u_char type;
{
  static struct
    {
      u_char type;
      char *name;
    }
  boards[] =
  { { 0x00, "hp27240" } ,
    { 0x10, "hp24240" } ,
    { 0x01, "hp27245" } ,
    { 0x02, "hp27250" } ,
    { 0x81, "hp27247" } ,
    { 0x91, "hp27247r1" } };
  int n = sizeof (boards) / sizeof (boards[0]);

  while (n)
    if (boards[--n].type == type)
      return boards[n].name;

  return "UNKNOWN";
}

/*
 * Interface exists: make available by filling in network interface
 * record.  System will initialize the interface when it is ready
 * to accept packets.  We get the ethernet address here.
 */
hpattach (dvp)
     struct isa_device *dvp;
{
  int unit = dvp->id_unit;
  register struct hp_softc *ns = &hp_softc[unit];
  register struct ifnet *ifp = &ns->ns_if;

  ifp->if_unit = unit;
  ifp->if_name = hpdriver.name;
  ifp->if_mtu = ETHERMTU;
  printf ("hp%d: %s %d-bit ethernet address %s\n", unit,
	  hp_id (ns->hp_type), ns->ns_mode & DSDC_WTS ? 32 : 16,
	  ether_sprintf (ns->ns_addr));
  ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
  ifp->if_init = hpinit;
  ifp->if_output = ether_output;
  ifp->if_start = hpstart;
  ifp->if_ioctl = hpioctl;
  ifp->if_reset = hpreset;
  ifp->if_watchdog = 0;
  if_attach (ifp);

#if NBPFILTER > 0
  bpfattach (&ns->ns_bpf, ifp, DLT_EN10MB,
	     sizeof (struct ether_header));
#endif
}

/*
 * Initialization of interface; set up initialization block
 * and transmit/receive descriptor rings.
 */
hpinit (unit)
     int unit;
{
  register struct hp_softc *ns = &hp_softc[unit];
  struct ifnet *ifp = &ns->ns_if;
  int s;
  int i;
  char *cp;
  register hpc = ns->ns_port;

  if (ifp->if_addrlist == (struct ifaddr *) 0)
    return;
  if (ifp->if_flags & IFF_RUNNING)
    return;

  s = splnet ();

#ifdef HP_DEBUG
  printf ("hpinit: hp%d at 0x%x irq %d\n", unit, hpc, (int) ns->hp_irq);
  printf ("hpinit: promiscuous mode %s\n",
	  ns->ns_if.if_flags & IFF_PROMISC ? "on" : "off");
#endif

  ns->ns_rcr = (ns->ns_if.if_flags & IFF_BROADCAST ? DSRC_AB  : 0) |
  	       (ns->ns_if.if_flags & IFF_PROMISC   ? DSRC_PRO : 0);
#ifdef HP_LOG_ERRORS
  ns->ns_rcr |= DSRC_SEP;
#endif

  /* set irq and turn on board */
  outb (hpc + hp_option, HP_RUN | (ns->hp_irq << 1));

  /* init regs */
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG0 | DSCM_STOP);
  outb (hpc + ds0_dcr, 0);
  outb (hpc + ds0_rbcr0, 0);
  outb (hpc + ds0_rbcr1, 0);
  outb (hpc + ds0_rcr, DSRC_MON);
  outb (hpc + ds0_tpsr, ns->ns_txstart);
  outb (hpc + ds0_imr, 0);
  outb (hpc + ds0_tcr, DSTC_LB0);
  outb (hpc + ds0_pstart, ns->ns_rxstart);
  outb (hpc + ds0_bnry, ns->ns_rxend-1);
  outb (hpc + ds0_pstop, ns->ns_rxend);
  outb (hpc + ds0_isr, 0xff);
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG1 | DSCM_STOP);
  outb (hpc + ds1_curr, ns->ns_rxstart);

  /* set physical address on ethernet */
  for (i = 0; i < 6; i++)
    outb (hpc + ds1_par0 + i, ns->ns_addr[i]);

  /* clr logical address hash filter for now */
  for (i = 0; i < 8; i++)
    outb (hpc + ds1_mar0 + i, 0xff);

  /* fire it up */
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG0 | DSCM_START);
  outb (hpc + ds0_dcr, ns->ns_mode);
  outb (hpc + ds0_rcr, ns->ns_rcr);
  outb (hpc + ds0_tcr, 0);
  outb (hpc + ds0_imr, 0xff);

  ns->ns_if.if_flags |= IFF_RUNNING;
  ns->ns_flags &= ~DSF_LOCK;
  ns->ns_oactive = 0;
  ns->ns_mask = ~0;
  hpstart (ifp);

#ifdef HP_DEBUG
  printf ("hpinit: done\n", unit, hpc);
#endif

  splx (s);
}

/*
 * Setup output on interface.
 * Get another datagram to send off of the interface queue,
 * and map it to the interface before starting the output.
 * called only at splnet or interrupt level.
 */
hpstart (ifp)
     struct ifnet *ifp;
{
  register struct hp_softc *ns = &hp_softc[ifp->if_unit];
  struct mbuf *m0, *m;
  int buffer;
  int len = 0, i, total, t;
  register hpc = ns->ns_port;

  /*
	 * The DS8390 has only one transmit buffer, if it is busy we
	 * must wait until the transmit interrupt completes.
	 */
  if (ns->ns_flags & DSF_LOCK)
    return;

  if (inb (hpc + ds_cmd) & DSCM_TRANS)
    return;

  if ((ns->ns_if.if_flags & IFF_RUNNING) == 0)
    return;

  IF_DEQUEUE (&ns->ns_if.if_snd, m);

  if (m == 0)
    return;

  /*
	 * Copy the mbuf chain into the transmit buffer
	 */

  ns->ns_flags |= DSF_LOCK;	/* prevent entering hpstart */
  buffer = ns->ns_txstart * DS_PGSIZE;
  len = i = 0;
  t = 0;
  for (m0 = m; m != 0; m = m->m_next)
    t += m->m_len;

  m = m0;
  total = t;

#ifdef HP_DEBUG
  printf ("hpstart: len=%d\n", total);
#endif

#if NBPFILTER > 0
  if (ns->ns_bpf)
    bpf_mtap (ns->ns_bpf, m);
#endif

  for (m0 = m; m != 0;)
    {
      if (m->m_len & 1 && t > m->m_len)
	{
	  m->m_len -= 1;
	  hpput (ns, mtod (m, caddr_t), buffer, m->m_len);
	  t -= m->m_len;
	  buffer += m->m_len;
	  m->m_data += m->m_len;
	  m->m_len = 1;
	  m = m_pullup (m, 2);
	}
      else
	{
	  hpput (ns, mtod (m, caddr_t), buffer, m->m_len);
	  t -= m->m_len;
	  buffer += m->m_len;
	  MFREE (m, m0);
	  m = m0;
	}
    }

  /*
	 * Init transmit length registers, and set transmit start flag.
	 */

  len = total;
  if (len < ETHER_MIN_LEN)
    len = ETHER_MIN_LEN;
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG0 | DSCM_START);
  outb (hpc + ds0_tbcr0, len & 0xff);
  outb (hpc + ds0_tbcr1, (len >> 8) & 0xff);
  outb (hpc + ds0_tpsr, ns->ns_txstart);
  outb (hpc + ds_cmd, DSCM_TRANS | DSCM_NODMA | DSCM_PG0 | DSCM_START);

#ifdef HP_DEBUG
  printf ("hpstart: done\n", hpc);
#endif
}

/*
 * Controller interrupt.
 */
hpintr (unit)
{
  register struct hp_softc *ns = &hp_softc[unit];
  u_char cmd, isr;
  register hpc = ns->ns_port;
  u_char err;

  /* Save cmd, clear interrupt */
  cmd = inb (hpc + ds_cmd);
loop:
  isr = inb (hpc + ds0_isr);
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG0 | DSCM_START);
  outb (hpc + ds0_isr, isr);

  /* Receiver error */
  if (isr & DSIS_RXE)
    {
      /* need to read these registers to clear status */
      err = inb (hpc + ds0_rsr);
      (void) inb (hpc + 0xD);
      (void) inb (hpc + 0xE);
      (void) inb (hpc + 0xF);
      ns->ns_if.if_ierrors++;
#ifdef HP_LOG_ERRORS
      isr |= DSIS_RX;
#endif
    }

  /* We received something */
  if (isr & DSIS_RX)
    {
      u_char bnry;
      u_char curr;
      u_short addr;
      int len;
      int i;
      unsigned char c;

      while (1)
	{
	  outb (hpc + ds_cmd, DSCM_START | DSCM_NODMA | DSCM_PG0);
	  bnry = inb (hpc + ds0_bnry);
	  outb (hpc + ds_cmd, DSCM_START | DSCM_NODMA | DSCM_PG1);
	  curr = inb (hpc + ds1_curr);

#ifdef HP_DEBUG
	  printf ("hpintr: receive isr=0x%02x bnry=0x%02x curr=0x%02x\n",
		  isr, bnry, curr);
#endif

	  if (++bnry >= ns->ns_rxend)
	    bnry = ns->ns_rxstart;

	  /* if ring empty, done! */
	  if (bnry == curr)
	    break;

	  addr = bnry * DS_PGSIZE;

	  outb (hpc + hp_option, inb (hpc + hp_option) | HP_DATA);

#if 0
	  /* send packet with auto packet release */
	  outb (hpc + ds_cmd, DSCM_START | DSCM_NODMA | DSCM_PG0);
	  outb (hpc + ds0_rbcr1, 0x0f);
	  outb (hpc + ds0_dcr, ns->ns_mode | DSDC_AR);
	  outb (hpc + ds_cmd, DSCM_SENDP | DSCM_PG0 | DSCM_START);
#endif

	  /* get length */
	  hpfetch (ns, (caddr_t) &ns->ns_ph, addr, sizeof ns->ns_ph);
	  addr += sizeof ns->ns_ph;

#ifdef HP_DEBUG
	  printf ("hpintr: sendp packet hdr: %x %x %x %x\n",
		  ns->ns_ph.pr_status,
		  ns->ns_ph.pr_nxtpg,
		  ns->ns_ph.pr_sz0,
		  ns->ns_ph.pr_sz1);
#endif

#ifdef HP_LOG_ERRORS
	  if (ns->ns_ph.pr_status & (DSRS_CRC|DSRS_FO|DSRS_DFR))
	    {
	      /* Get packet header */
	      if (len > 14)
		len = 14;
	      hpfetch (ns, (caddr_t) (ns->ns_pb), addr, len);

	      /* move boundary up */
	      bnry = ns->ns_ph.pr_nxtpg;
	      if (--bnry < ns->ns_rxstart)
	        bnry = ns->ns_rxend-1;
	      outb (hpc + ds_cmd, DSCM_START | DSCM_NODMA | DSCM_PG0);
	      outb (hpc + ds0_bnry, bnry);

	      printf ("hp%d: receive error status=0x%02x\n", unit,
		      ns->ns_ph.pr_status);
	      printf ("hp%d: packet header:", unit);
	      {
		int n;
		for (n = 0; n < len; n++)
		  printf (" %02x", ns->ns_pb[n]);
	      }
	      printf ("\n");

	      continue;
	    }
#endif

	  ns->ns_if.if_ipackets++;
	  len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1 << 8);
	  if (len < ETHER_MIN_LEN || len > ETHER_MAX_LEN)
	    {
	      printf ("hpintr: bnry %x curr %x\n", bnry, curr);
	      printf ("hpintr: packet hdr: %x %x %x %x\n",
		      ns->ns_ph.pr_status,
		      ns->ns_ph.pr_nxtpg,
		      ns->ns_ph.pr_sz0,
		      ns->ns_ph.pr_sz1);
	      printf ("isr = 0x%x reg_isr=0x%x\n",
		      isr, inb (hpc + ds0_isr));
	      outb (hpc + ds_cmd, DSCM_START | DSCM_NODMA | DSCM_PG0);
	      bnry = inb (hpc + ds0_bnry);
	      outb (hpc + ds_cmd, DSCM_START | DSCM_NODMA | DSCM_PG1);
	      curr = inb (hpc + ds1_curr);
	      printf ("hpintr: new bnry %x curr %x\n", bnry, curr);
	      printf ("hpintr: bad len %d\n-hanging-\n",
		      len);
	      while (1);
	    }

	  /* read packet */
	  hpfetch (ns, (caddr_t) (ns->ns_pb), addr, len);

	  /* move boundary up */
	  bnry = ns->ns_ph.pr_nxtpg;
	  if (--bnry < ns->ns_rxstart)
	    bnry = ns->ns_rxend-1;
	  outb (hpc + ds_cmd, DSCM_START | DSCM_NODMA | DSCM_PG0);
	  outb (hpc + ds0_bnry, bnry);

#ifdef HP_DEBUG
	  printf ("hpintr: receive done bnry=0x%02x\n", bnry);
#endif

	  outb (hpc + hp_option, inb (hpc + hp_option) & ~HP_DATA);

	  /* adjust for ether header and checksum */
	  len -= sizeof (struct ether_header) + sizeof (long);

	  /* process packet */
	  hpread (ns, (caddr_t) (ns->ns_pb), len);
	}
    }

  /* Transmit error */
  if (isr & DSIS_TXE)
    {
      ns->ns_flags &= ~DSF_LOCK;
      /* Need to read these registers to clear status */
      ns->ns_if.if_collisions += inb (hpc + ds0_tbcr0);
      ns->ns_if.if_oerrors++;
    }

  /* Packet Transmitted */
  if (isr & DSIS_TX)
    {
      ns->ns_flags &= ~DSF_LOCK;
      ++ns->ns_if.if_opackets;
      ns->ns_if.if_collisions += inb (hpc + ds0_tbcr0);
    }

  /* Receiver ovverun? */
  if (isr & DSIS_ROVRN)
    {
      log (LOG_ERR, "hp%d: error: isr %x\n", ns - hp_softc, isr
	   /*, DSIS_BITS*/ );
      outb (hpc + ds0_rbcr0, 0);
      outb (hpc + ds0_rbcr1, 0);
      outb (hpc + ds0_tcr, DSTC_LB0);
      outb (hpc + ds0_rcr, DSRC_MON);
      outb (hpc + ds_cmd, DSCM_START | DSCM_NODMA);
      outb (hpc + ds0_rcr, ns->ns_rcr);
      outb (hpc + ds0_tcr, 0);
    }

  /* Any more to send? */
  outb (hpc + ds_cmd, DSCM_NODMA | DSCM_PG0 | DSCM_START);
  hpstart (&ns->ns_if);
  outb (hpc + ds_cmd, cmd);
  outb (hpc + ds0_imr, 0xff);

  /* Still more to do? */
  isr = inb (hpc + ds0_isr);
  if (isr)
    goto loop;
}

/*
 * Pass a packet to the higher levels.
 * We deal with the trailer protocol here.
 */
hpread (ns, buf, len)
     register struct hp_softc *ns;
     char *buf;
     int len;
{
  register struct ether_header *eh;
  struct mbuf *m;
  int off, resid;
  register struct ifqueue *inq;

  /*
	 * Deal with trailer protocol: if type is trailer type
	 * get true type from first 16-bit word past data.
	 * Remember that type was trailer by setting off.
	 */
  eh = (struct ether_header *) buf;
  eh->ether_type = ntohs ((u_short) eh->ether_type);
#define	hpdataaddr(eh, off, type)	((type)(((caddr_t)((eh)+1)+(off))))
  if (eh->ether_type >= ETHERTYPE_TRAIL &&
      eh->ether_type < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER)
    {
      off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
      if (off >= ETHERMTU)
	return;			/* sanity */
      eh->ether_type = ntohs (*hpdataaddr (eh, off, u_short *));
      resid = ntohs (*(hpdataaddr (eh, off + 2, u_short *)));
      if (off + resid > len)
	return;			/* sanity */
      len = off + resid;
    }
  else
    off = 0;

  if (len == 0)
    return;

#if NBPFILTER > 0
  if (ns->ns_bpf)
    bpf_tap (ns->ns_bpf, buf, len + sizeof (struct ether_header));
#endif

  if ((ns->ns_if.if_flags & IFF_PROMISC)
      && bcmp (eh->ether_dhost, ns->ns_addr,
	       sizeof (eh->ether_dhost)) != 0
      && bcmp (eh->ether_dhost, etherbroadcastaddr,
	       sizeof (eh->ether_dhost)) != 0)
    return;

  /*
	 * Pull packet off interface.  Off is nonzero if packet
	 * has trailing header; hpget will then force this header
	 * information to be at the front, but we still have to drop
	 * the type and length which are at the front of any trailer data.
	 */
  m = hpget (buf, len, off, &ns->ns_if);
  if (m == 0)
    return;

  ether_input (&ns->ns_if, eh, m);
}

/*
 * Supporting routines
 */

/*
 * Pull read data off a interface.
 * Len is length of data, with local net header stripped.
 * Off is non-zero if a trailer protocol was used, and
 * gives the offset of the trailer information.
 * We copy the trailer information and then all the normal
 * data into mbufs.  When full cluster sized units are present
 * we copy into clusters.
 */
struct mbuf *
hpget (buf, totlen, off0, ifp)
     caddr_t buf;
     int totlen, off0;
     struct ifnet *ifp;
{
  struct mbuf *top, **mp, *m, *p;
  int off = off0, len;
  register caddr_t cp = buf;
  char *epkt;

  buf += sizeof (struct ether_header);
  cp = buf;
  epkt = cp + totlen;


  if (off)
    {
      cp += off + 2 * sizeof (u_short);
      totlen -= 2 * sizeof (u_short);
    }

  MGETHDR (m, M_DONTWAIT, MT_DATA);
  if (m == 0)
    return (0);
  m->m_pkthdr.rcvif = ifp;
  m->m_pkthdr.len = totlen;
  m->m_len = MHLEN;

  top = 0;
  mp = &top;
  while (totlen > 0)
    {
      if (top)
	{
	  MGET (m, M_DONTWAIT, MT_DATA);
	  if (m == 0)
	    {
	      m_freem (top);
	      return (0);
	    }
	  m->m_len = MLEN;
	}
      len = min (totlen, epkt - cp);
      if (len >= MINCLSIZE)
	{
	  MCLGET (m, M_DONTWAIT);
	  if (m->m_flags & M_EXT)
	    m->m_len = len = min (len, MCLBYTES);
	  else
	    len = m->m_len;
	}
      else
	{
	  /*
			 * Place initial small packet/header at end of mbuf.
			 */
	  if (len < m->m_len)
	    {
	      if (top == 0 && len + max_linkhdr <= m->m_len)
		m->m_data += max_linkhdr;
	      m->m_len = len;
	    }
	  else
	    len = m->m_len;
	}
      bcopy (cp, mtod (m, caddr_t), (unsigned) len);
      cp += len;
      *mp = m;
      mp = &m->m_next;
      totlen -= len;
      if (cp == epkt)
	cp = buf;
    }
  return (top);
}

/*
 * Process an ioctl request.
 */
hpioctl (ifp, cmd, data)
     register struct ifnet *ifp;
     int cmd;
     caddr_t data;
{
  register struct ifaddr *ifa = (struct ifaddr *) data;
  struct hp_softc *ns = &hp_softc[ifp->if_unit];
  struct ifreq *ifr = (struct ifreq *) data;
  int s = splnet (), error = 0;


  switch (cmd)
    {

    case SIOCSIFADDR:
      ifp->if_flags |= IFF_UP;

      switch (ifa->ifa_addr->sa_family)
	{
#ifdef INET
	case AF_INET:
	  hpinit (ifp->if_unit);/* before arpwhohas */
	  ((struct arpcom *) ifp)->ac_ipaddr =
	    IA_SIN (ifa)->sin_addr;
	  arpwhohas ((struct arpcom *) ifp, &IA_SIN (ifa)->sin_addr);
	  break;
#endif
#ifdef NS
	case AF_NS:
	  {
	    register struct ns_addr *ina = &(IA_SNS (ifa)->sns_addr);

	    if (ns_nullhost (*ina))
	      ina->x_host = *(union ns_host *) (ns->ns_addr);
	    else
	      {
		/*
				 * The manual says we can't change the address
				 * while the receiver is armed,
				 * so reset everything
				 */
		ifp->if_flags &= ~IFF_RUNNING;
		bcopy ((caddr_t) ina->x_host.c_host,
		       (caddr_t) ns->ns_addr, sizeof (ns->ns_addr));
	      }
	    hpinit (ifp->if_unit);	/* does hp_setaddr() */
	    break;
	  }
#endif
	default:
	  hpinit (ifp->if_unit);
	  break;
	}
      break;

    case SIOCSIFFLAGS:
#ifdef HP_DEBUG
      printf ("hp: setting flags, up: %s, running: %s\n",
	      ifp->if_flags & IFF_UP ? "yes" : "no",
	      ifp->if_flags & IFF_RUNNING ? "yes" : "no");
#endif
      if ((ifp->if_flags & IFF_UP) == 0 &&
	  ifp->if_flags & IFF_RUNNING)
	{
	  ifp->if_flags &= ~IFF_RUNNING;
	  outb (ns->ns_port + ds_cmd, DSCM_STOP | DSCM_NODMA);
	}
      else if (ifp->if_flags & IFF_UP &&
	       (ifp->if_flags & IFF_RUNNING) == 0)
	hpinit (ifp->if_unit);
      break;

#ifdef notdef
    case SIOCGHWADDR:
      bcopy ((caddr_t) ns->ns_addr, (caddr_t) & ifr->ifr_data,
	     sizeof (ns->ns_addr));
      break;
#endif

    default:
      error = EINVAL;
    }
  splx (s);
  return (error);
}

#endif