1003 lines
24 KiB
C
1003 lines
24 KiB
C
/* $NetBSD: if_hp.c,v 1.21 1995/12/24 02:31:31 mycroft 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/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 <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 arpcom ns_ac; /* Ethernet common part */
|
|
#define ns_if ns_ac.ac_if /* network-visible interface */
|
|
#define ns_addrp 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_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
|