07b064e02e
Some of the stuff (e.g., rarpd, bootpd, dhcpd etc., libsa) still will only support Ethernet. Tcpdump itself should be ok, but libpcap needs lot of work. For the detailed change history, look at the commit log entries for the is-newarp branch.
1003 lines
24 KiB
C
1003 lines
24 KiB
C
/* $NetBSD: if_hp.c,v 1.24 1997/03/15 18:11:44 is Exp $ */
|
|
|
|
/* XXX THIS DRIVER IS BROKEN. IT WILL NOT EVEN COMPILE. */
|
|
|
|
/*-
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* 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 <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/ioctl.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/syslog.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_ether.h>
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in_var.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/if_inarp.h>
|
|
#endif
|
|
|
|
#ifdef NS
|
|
#include <netns/ns.h>
|
|
#include <netns/ns_if.h>
|
|
#endif
|
|
|
|
#include "bpfilter.h"
|
|
#if NBPFILTER > 0
|
|
#include <sys/select.h>
|
|
#include <net/bpf.h>
|
|
#include <net/bpfdesc.h>
|
|
#endif
|
|
|
|
#include <machine/cpu.h>
|
|
#include <machine/pio.h>
|
|
|
|
#include <i386/isa/isa_device.h> /* XXX BROKEN */
|
|
#include <dev/isa/if_nereg.h>
|
|
#include <i386/isa/icu.h> /* XXX BROKEN */
|
|
|
|
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 ethercom ns_ec; /* Ethernet common part */
|
|
#define ns_if ns_ac.ac_if /* network-visible interface */
|
|
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
|
|
u_int8_t ns_addrp[ETHER_ADDR_LEN]; /* hardware Ethernet address */
|
|
|
|
}
|
|
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_addrp[i] = inb(hpc - 0x10 + i);
|
|
ns->hp_type = inb(hpc - 0x10 + 7);
|
|
|
|
if (ns->ns_addrp[0] != 0x08 ||
|
|
ns->ns_addrp[1] != 0x00 ||
|
|
ns->ns_addrp[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_addrp));
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS;
|
|
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_addrp[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, i, total;
|
|
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;
|
|
i = 0;
|
|
total = len = m->m_pkthdr.len;
|
|
|
|
#ifdef HP_DEBUG
|
|
printf("hpstart: len=%d\n", len);
|
|
#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;
|
|
u_short etype;
|
|
|
|
/*
|
|
* 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;
|
|
etype = ntohs((u_short) eh->ether_type);
|
|
#define hpdataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off))))
|
|
if (etype >= ETHERTYPE_TRAIL &&
|
|
etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) {
|
|
off = (etype - ETHERTYPE_TRAIL) * 512;
|
|
if (off >= ETHERMTU)
|
|
return; /* sanity */
|
|
eh->ether_type = *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_addrp,
|
|
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 = ⊤
|
|
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;
|
|
u_long 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_addrp);
|
|
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_addrp, sizeof(ns->ns_addrp));
|
|
}
|
|
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_addrp, (caddr_t) & ifr->ifr_data,
|
|
sizeof(ns->ns_addrp));
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
error = EINVAL;
|
|
}
|
|
splx(s);
|
|
return (error);
|
|
}
|
|
#endif
|