summaryrefslogtreecommitdiff
path: root/i386/i386at/if_ns8390.c
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /i386/i386at/if_ns8390.c
Initial source
Diffstat (limited to 'i386/i386at/if_ns8390.c')
-rw-r--r--i386/i386at/if_ns8390.c2578
1 files changed, 2578 insertions, 0 deletions
diff --git a/i386/i386at/if_ns8390.c b/i386/i386at/if_ns8390.c
new file mode 100644
index 0000000..15c9440
--- /dev/null
+++ b/i386/i386at/if_ns8390.c
@@ -0,0 +1,2578 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* NOTE:
+ * There are three outstanding bug/features in this implementation.
+ * They may even be hardware misfeatures. The conditions are registered
+ * by counters maintained by the software.
+ * 1: over_write is a condition that means that the board wants to store
+ * packets, but there is no room. So new packets are lost. What seems to
+ * be happening is that we get an over_write condition, but there are no
+ * or just a few packets in the board's ram. Also it seems that we get
+ * several over_writes in a row.
+ * 2: Since there is only one transmit buffer, we need a lock to indicate
+ * whether it is in use. We clear this lock when we get a transmit interrupt.
+ * Sometimes we go to transmit and although there is no transmit in progress,
+ * the lock is set. (In this case, we just ignore the lock.) It would look
+ * like we can miss transmit interrupts?
+ * 3: We tried to clean up the unnecessary switches to bank 0.
+ * Unfortunately, when you do an ifconfig "down", the system tend to lock up
+ * a few seconds later (this was when DSF_RUNNING) was not being set before.
+ * But even with DSF_RUNNING, on an EISA bus machine we ALWAYS lock up after
+ * a few seconds.
+ */
+
+/*
+ * Western Digital 8003E Mach Ethernet driver (for intel 80386)
+ * Copyright (c) 1990 by Open Software Foundation (OSF).
+ */
+
+/*
+ Copyright 1990 by Open Software Foundation,
+Cambridge, MA.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appears in all copies and
+that both the copyright notice and this permission notice appear in
+supporting documentation, and that the name of OSF or Open Software
+Foundation not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+ OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+<INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define IF_CNTRS MACH
+
+#include <ns8390.h>
+#if NNS8390 > 0
+
+#include <mach_ttd.h>
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#include "vm_param.h"
+#include <i386/ipl.h>
+#include <chips/busses.h>
+#include <i386at/ds8390.h>
+#include <i386at/if_wd8003.h>
+#include <i386at/if_3c503.h>
+
+#if MACH_TTD
+#include <ttd/ttd_stub.h>
+#endif /* MACH_TTD */
+
+
+#define SPLNET spl6
+
+int wd_debug = 0;
+
+int ns8390probe();
+void ns8390attach();
+int ns8390intr();
+int ns8390init();
+int ns8390output();
+int ns8390ioctl();
+int ns8390reset();
+int ns8390rcv();
+int ns8390watch();
+int ns8390get_CURR();
+int ns8390over_write();
+
+struct bus_device *ns8390info[NNS8390]; /* ???? */
+
+static vm_offset_t ns8390_std[NNS8390] = { 0 };
+static struct bus_device *ns8390_info[NNS8390];
+struct bus_driver ns8390driver =
+ {ns8390probe, 0, ns8390attach, 0, ns8390_std, "ns8390", ns8390_info, 0, 0, 0};
+
+int watchdog_id;
+
+char *wd8003_card = "wd";
+char *elii_card = "el";
+/* 2e0, 2a0, 280, 250, 350, 330, 310, 300*/
+int elii_irq[8] = {5, 2, 2, 5, 5, 0x711, 0x711, 5};
+int elii_bnc[8] = {1, 0, 1, 1, 0, 0x711, 0x711, 0};
+/*int elii_bnc[8] = {0, 1, 1, 1, 1, 1, 0, 1}; */
+
+typedef struct {
+#ifdef MACH_KERNEL
+ struct ifnet ds_if; /* generic interface header */
+ u_char ds_addr[6]; /* Ethernet hardware address */
+#else MACH_KERNEL
+ struct arpcom ns8390_ac;
+#define ds_if ns8390_ac.ac_if
+#define ds_addr ns8390_ac.ac_enaddr
+#endif MACH_KERNEL
+ int flags;
+ int timer;
+ int interrupt;
+ char *nic;
+ u_char address[ETHER_ADDR_SIZE];
+ short mode;
+ int tbusy;
+ char *sram; /* beginning of the shared memory RAM buffer */
+ int read_nxtpkt_ptr;/* pointer to next packet available */
+ int pstart; /* page start hold */
+ int pstop; /* page stop hold */
+ int tpsr; /* transmit page start hold */
+ int fifo_depth; /* NIC fifo threshold */
+ char *card;
+ int board_id;
+}
+ns8390_softc_t;
+
+ns8390_softc_t ns8390_softc[NNS8390];
+
+struct ns8390_cntrs {
+u_int ovw,
+ jabber,
+ crc,
+ frame,
+ miss,
+ fifo,
+ rcv;
+u_int xmt,
+ xmti,
+ busy,
+ heart;
+} ns8390_cntrs[NNS8390];
+
+#if MACH_TTD
+boolean_t ttd_poll_loop;
+
+int ns8390poll_receive();
+int ns8390transmit_ttd();
+#endif /* MACH_TTD */
+
+#ifdef IF_CNTRS
+int ns_narp = 1, ns_arp = 0;
+int ns_ein[32], ns_eout[32];
+int ns_lin[128/8], ns_lout[128/8];
+static
+log_2(no)
+unsigned long no;
+{
+ return ({ unsigned long _temp__;
+ asm("bsr %1, %0; jne 0f; xorl %0, %0; 0:" :
+ "=r" (_temp__) : "a" (no));
+ _temp__;});
+}
+#endif IF_CNTRS
+
+/* Interrupts mask bits */
+int imr_hold = DSIM_PRXE|DSIM_PTXE|DSIM_RXEE|DSIM_TXEE|DSIM_OVWE|DSIM_CNTE;
+
+/*
+ * ns8390probe:
+ *
+ * This function "probes" or checks for the wd8003 board on the bus to see
+ * if it is there. As far as I can tell, the best break between this
+ * routine and the attach code is to simply determine whether the board
+ * is configured in properly. Currently my approach to this is to test the
+ * base I/O special offset for the Western Digital unique byte sequence
+ * identifier. If the bytes match we assume board is there.
+ * The config code expects to see a successful return from the probe
+ * routine before attach will be called.
+ *
+ * input : address device is mapped to, and unit # being checked
+ * output : a '1' is returned if the board exists, and a 0 otherwise
+ *
+ */
+
+ns8390probe(port, dev)
+struct bus_device *dev;
+{
+ caddr_t hdwbase = (caddr_t)dev->address;
+ int unit = dev->unit;
+ ns8390_softc_t *sp = &ns8390_softc[unit];
+ int tmp;
+ int vendor_id;
+
+ if ((unit < 0) || (unit > NNS8390)) {
+ printf("ns8390 ethernet unit %d out of range\n", unit);
+ return(0);
+ }
+ if (((u_char) inb(hdwbase+IFWD_LAR_0) == (u_char) WD_NODE_ADDR_0) &&
+ ((u_char) inb(hdwbase+IFWD_LAR_1) == (u_char) WD_NODE_ADDR_1) &&
+ ((u_char) inb(hdwbase+IFWD_LAR_2) == (u_char) WD_NODE_ADDR_2)) {
+ ns8390info[unit] = dev;
+ sp->card = wd8003_card;
+ dev->name = wd8003_card;
+ sp->nic = hdwbase + OFF_8390;
+ /* enable mem access to board */
+ sp->board_id = wd80xxget_board_id(dev);
+
+ *(sp->address) = inb(hdwbase+IFWD_LAR_0);
+ *(sp->address + 1) = inb(hdwbase+IFWD_LAR_1);
+ *(sp->address + 2) = inb(hdwbase+IFWD_LAR_2);
+ *(sp->address + 3) = inb(hdwbase+IFWD_LAR_3);
+ *(sp->address + 4) = inb(hdwbase+IFWD_LAR_4);
+ *(sp->address + 5) = inb(hdwbase+IFWD_LAR_5);
+ return (1);
+ } /* checks the address of the board to verify that it is a WD */
+
+ /* try to avoid any NE2000 pretending to be an el II */
+ if (inb(hdwbase + 0x408) == 0xff)
+ return 0;
+
+ /* check vendor id */
+ tmp = inb(hdwbase + CTLR);
+
+ outb(hdwbase + CTLR, CTLR_RST|CTLR_THIN); /* Reset it... */
+ outb(hdwbase + CTLR, CTLR_THIN);
+ /*
+ * Map the station addr PROM into the lower I/O ports. We now
+ * check for both the old and new 3Com prefix
+ */
+ outb(hdwbase + CTLR, CTLR_STA_ADDR|CTLR_THIN);
+ vendor_id = inb(hdwbase)*0x10000 + inb(hdwbase + 1)*0x100 +
+ inb(hdwbase + 2);
+ /* Restore the register we frobbed. */
+ outb(hdwbase + CTLR, tmp);
+ if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID))
+ return 0;
+
+ if ((tmp = inb(hdwbase+BCFR))) {
+ switch(tmp) {
+ case (1<<7): sp->board_id = 7; break; /*irq5 xvcr*/
+#ifdef not_currently_possible
+ case (1<<6): sp->board_id = 6; break;
+ case (1<<5): sp->board_id = 5; break;
+#endif not_currently_possible
+ case (1<<4): sp->board_id = 4; break;
+ case (1<<3): sp->board_id = 3; break;
+ case (1<<2): sp->board_id = 2; break; /*irq2 bnc*/
+ case (1<<1): sp->board_id = 1; break; /*irq2 xvcr*/
+ case (1<<0): sp->board_id = 0; break; /*irq5 bnc*/
+ default: return 0;
+ }
+ switch (inb(hdwbase+PCFR)) {
+ case (1<<7): dev->phys_address = 0xDC000; break;
+ case (1<<6): dev->phys_address = 0xD8000; break;
+#ifdef not_currently_possible
+ case (1<<5): dev->phys_address = 0xCC000; break;
+ case (1<<4): dev->phys_address = 0xC8000; break;
+#endif not_currently_possible
+ default:
+ printf("EtherLink II with NO memory configured\n");
+ return 0;
+ }
+ ns8390info[unit] = dev;
+ dev->sysdep1 = elii_irq[sp->board_id];
+ if (dev->sysdep1 == 2)
+ dev->sysdep1 = 9;
+ sp->card = elii_card;
+ dev->name = elii_card;
+ sp->nic = hdwbase;
+ return 1;
+ }
+
+ return(0);
+}
+
+/*
+ * ns8390attach:
+ *
+ * This function attaches a ns8390 board to the "system". The rest of
+ * runtime structures are initialized here (this routine is called after
+ * a successful probe of the board). Once the ethernet address is read
+ * and stored, the board's ifnet structure is attached and readied.
+ *
+ * input : bus_device structure setup in autoconfig
+ * output : board structs and ifnet is setup
+ *
+ */
+
+void ns8390attach(dev)
+struct bus_device *dev;
+{
+ ns8390_softc_t *sp;
+ struct ifnet *ifp;
+ u_char unit;
+ int temp;
+
+ take_dev_irq(dev);
+ unit = (u_char)dev->unit;
+ sp = &ns8390_softc[unit];
+ printf(", port = %x, spl = %d, pic = %d. ",
+ dev->address, dev->sysdep, dev->sysdep1);
+
+ if (sp->card == elii_card) {
+ if (elii_bnc[sp->board_id])
+ printf("cheapernet ");
+ else
+ printf("ethernet ");
+ } else
+ printf("ethernet ");
+
+ (volatile char *)sp->sram =
+ (volatile char *) phystokv(dev->phys_address);
+ dev->address = (vm_offset_t) phystokv(dev->address);
+ sp->timer = -1;
+ sp->flags = 0;
+ sp->mode = 0;
+
+ if (!ns8390hwrst(unit)) {
+ printf("%s%d: attach(): reset failed.\n",
+ sp->card, unit);
+ return;
+ }
+ /* N.B. sp->address is not determined till
+ * hwrst time. */
+ *(sp->ds_addr) = *(sp->address);
+ *(sp->ds_addr + 1) = *(sp->address + 1);
+ *(sp->ds_addr + 2) = *(sp->address + 2);
+ *(sp->ds_addr + 3) = *(sp->address + 3);
+ *(sp->ds_addr + 4) = *(sp->address + 4);
+ *(sp->ds_addr + 5) = *(sp->address + 5);
+
+ printf("id [%x:%x:%x:%x:%x:%x]",
+ sp->address[0],sp->address[1],sp->address[2],
+ sp->address[3],sp->address[4],sp->address[5]);
+ ifp = &(sp->ds_if);
+ ifp->if_unit = unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST;
+#ifdef MACH_KERNEL
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = 6;
+ ifp->if_address = (char *)&sp->address[0];
+ if_init_queues(ifp);
+#else MACH_KERNEL
+ ifp->if_name = sp->card;
+ ifp->if_init = ns8390init;
+ ifp->if_output = ns8390output;
+ ifp->if_ioctl = ns8390ioctl;
+ ifp->if_reset = ns8390reset;
+ ifp->if_next = NULL;
+ if_attach(ifp);
+#ifdef notdef
+ watchdog_id = timeout(ns8390watch, &(ifp->if_unit), 20*HZ);
+#endif
+#endif MACH_KERNEL
+
+#ifdef MACH_KERNEL
+#if MACH_TTD
+ if (!ttd_get_packet) {
+ ttd_device_unit = unit;
+ ttd_get_packet = ns8390poll_receive;
+ ttd_send_packet = ns8390transmit_ttd;
+ ttd_host_ether_id.array[0] = *(sp->address);
+ ttd_host_ether_id.array[1] = *(sp->address + 1);
+ ttd_host_ether_id.array[2] = *(sp->address + 2);
+ ttd_host_ether_id.array[3] = *(sp->address + 3);
+ ttd_host_ether_id.array[4] = *(sp->address + 4);
+ ttd_host_ether_id.array[5] = *(sp->address + 5);
+ }
+#endif /* MACH_TTD */
+#endif /* MACH_KERNEL */
+}
+
+/*
+ * ns8390watch():
+ *
+ */
+
+int
+ns8390watch(b_ptr)
+caddr_t b_ptr;
+{
+ int x,
+ y,
+ opri,
+ unit;
+ int temp_cr;
+ caddr_t nic;
+
+ unit = *b_ptr;
+#ifdef MACH_KERNEL
+ timeout(ns8390watch,b_ptr,20*HZ);
+#else MACH_KERNEL
+ watchdog_id = timeout(ns8390watch,b_ptr,20*HZ);
+#endif MACH_KERNEL
+ nic = ns8390_softc[unit].nic;
+ temp_cr = inb(nic+ds_cmd);
+ outb(nic + ds_cmd, (temp_cr & 0x3f) | DSCM_PG0);
+ printf("<<< ISR=%x CURR=%x rdnxt=%x BNDY=%x>>> ",
+ inb(nic + ds0_isr),
+ ns8390get_CURR(unit), ns8390_softc[unit].read_nxtpkt_ptr,
+ inb(nic+ds0_bndy));
+ outb(nic+ds_cmd,temp_cr);
+}
+
+#ifdef MACH_KERNEL
+int ns8390start(); /* forward */
+
+/*ARGSUSED*/
+wd8003open(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit = minor(dev);
+
+ if (ns8390_softc[unit].card != wd8003_card)
+ return (ENXIO);
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+
+ ns8390_softc[unit].ds_if.if_flags |= IFF_UP;
+ ns8390init(unit);
+ return(0);
+}
+
+eliiopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit = minor(dev);
+
+ if (ns8390_softc[unit].card != elii_card)
+ return (ENXIO);
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+
+ ns8390_softc[unit].ds_if.if_flags |= IFF_UP;
+ ns8390init(unit);
+ return(0);
+}
+
+ns8390output(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+ return (net_write(&ns8390_softc[unit].ds_if, ns8390start, ior));
+}
+
+ns8390setinput(dev, receive_port, priority, filter, filter_count)
+ dev_t dev;
+ mach_port_t receive_port;
+ int priority;
+ filter_t filter[];
+ unsigned int filter_count;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+
+ return (net_set_filter(&ns8390_softc[unit].ds_if,
+ receive_port, priority,
+ filter, filter_count));
+}
+
+#else MACH_KERNEL
+/*
+ * ns8390output:
+ *
+ * This routine is called by the "if" layer to output a packet to
+ * the network. This code resolves the local ethernet address, and
+ * puts it into the mbuf if there is room. If not, then a new mbuf
+ * is allocated with the header information and precedes the data
+ * to be transmitted. The routine ns8390xmt() which actually
+ * transmits the data expects the ethernet header to precede the
+ * data in the mbuf.
+ *
+ * input: ifnet structure pointer, an mbuf with data, and address
+ * to be resolved
+ * output: mbuf is updated to hold enet address, or a new mbuf
+ * with the address is added
+ *
+ */
+
+ns8390output(ifp, m0, dst)
+struct ifnet *ifp;
+struct mbuf *m0;
+struct sockaddr *dst;
+{
+ register ns8390_softc_t *is = &ns8390_softc[ifp->if_unit];
+ u_char edst[6];
+ struct in_addr idst;
+ register struct mbuf *m = m0;
+ register struct ether_header *eh;
+ register int off;
+ int usetrailers;
+ int type, error;
+ spl_t opri;
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ printf("%s%d output(): Turning off board %d\n",
+ is->card, ifp->if_unit);
+ ns8390intoff(ifp->if_unit);
+ error = ENETDOWN;
+ goto bad;
+ }
+ switch (dst->sa_family) {
+#ifdef INET
+ case AF_INET:
+ idst = ((struct sockaddr_in *)dst)->sin_addr;
+ if (!arpresolve(&is->ns8390_ac, m, &idst, edst, &usetrailers)){
+ return (0); /* if not yet resolved */
+ }
+ off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
+ if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
+ m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
+ type = ETHERTYPE_TRAIL + (off>>9);
+ m->m_off -= 2 * sizeof (u_short);
+ m->m_len += 2 * sizeof (u_short);
+ *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
+ *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
+ goto gottrailertype;
+ }
+ type = ETHERTYPE_IP;
+ off = 0;
+ goto gottype;
+#endif
+#ifdef NS
+ case AF_NS:
+ type = ETHERTYPE_NS;
+ bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
+ (caddr_t)edst,
+ sizeof (edst));
+ off = 0;
+ goto gottype;
+#endif
+ case AF_UNSPEC:
+ eh = (struct ether_header *)dst->sa_data;
+ bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
+ type = eh->ether_type;
+ goto gottype;
+ default:
+ printf("%s%d output(): can't handle af%d\n",
+ is->card, ifp->if_unit,
+ dst->sa_family);
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+gottrailertype:
+ /*
+ * Packet to be sent as trailer: move first packet
+ * (control information) to end of chain.
+ */
+ while (m->m_next)
+ m = m->m_next;
+ m->m_next = m0;
+ m = m0->m_next;
+ m0->m_next = 0;
+ m0 = m;
+gottype:
+ /*
+ * Add local net header. If no space in first mbuf,
+ * allocate another.
+ */
+ if (m->m_off > MMAXOFF ||
+ MMINOFF + sizeof (struct ether_header) > m->m_off) {
+ m = m_get(M_DONTWAIT, MT_HEADER);
+ if (m == 0) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ m->m_next = m0;
+ m->m_off = MMINOFF;
+ m->m_len = sizeof (struct ether_header);
+ } else {
+ m->m_off -= sizeof (struct ether_header);
+ m->m_len += sizeof (struct ether_header);
+ }
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = htons((u_short)type);
+ bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
+ bcopy((caddr_t)is->address,
+ (caddr_t)eh->ether_shost,
+ sizeof(edst));
+ /*
+ * Queue message on interface, and start output if interface
+ * not yet active.
+ */
+ opri = SPLNET();
+ if (IF_QFULL(&ifp->if_snd)) {
+ IF_DROP(&ifp->if_snd);
+ splx(opri);
+ m_freem(m);
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(&ifp->if_snd, m);
+ /*
+ * Some action needs to be added here for checking whether the
+ * board is already transmitting. If it is, we don't want to
+ * start it up (ie call ns8390start()). We will attempt to send
+ * packets that are queued up after an interrupt occurs. Some
+ * flag checking action has to happen here and/or in the start
+ * routine. This note is here to remind me that some thought
+ * is needed and there is a potential problem here.
+ *
+ */
+ ns8390start(ifp->if_unit);
+ splx(opri);
+ return (0);
+bad:
+ m_freem(m0);
+ return (error);
+}
+#endif MACH_KERNEL
+
+/*
+ * ns8390reset:
+ *
+ * This routine is in part an entry point for the "if" code. Since most
+ * of the actual initialization has already (we hope already) been done
+ * by calling ns8390attach().
+ *
+ * input : unit number or board number to reset
+ * output : board is reset
+ *
+ */
+
+int
+ns8390reset(unit)
+int unit;
+{
+
+ ns8390_softc[unit].ds_if.if_flags &= ~IFF_RUNNING;
+ return(ns8390init(unit));
+}
+
+/*
+ * ns8390init:
+ *
+ * Another routine that interfaces the "if" layer to this driver.
+ * Simply resets the structures that are used by "upper layers".
+ * As well as calling ns8390hwrst that does reset the ns8390 board.
+ *
+ * input : board number
+ * output : structures (if structs) and board are reset
+ *
+ */
+
+int
+ns8390init(unit)
+int unit;
+{
+ struct ifnet *ifp;
+ int stat;
+ spl_t oldpri;
+
+ ifp = &(ns8390_softc[unit].ds_if);
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ if (ifp->if_addrlist == (struct ifaddr *)0) {
+ return;
+ }
+#endif MACH_KERNEL
+ oldpri = SPLNET();
+ if ((stat = ns8390hwrst(unit)) == TRUE) {
+ ns8390_softc[unit].ds_if.if_flags |= IFF_RUNNING;
+ ns8390_softc[unit].flags |= DSF_RUNNING;
+ ns8390_softc[unit].tbusy = 0;
+ ns8390start(unit);
+ } else
+ printf("%s%d init(): trouble resetting board %d\n",
+ ns8390_softc[unit].card, unit);
+ ns8390_softc[unit].timer = 5;
+ splx(oldpri);
+ return(stat);
+}
+
+/*
+ * ns8390start:
+ *
+ * This is yet another interface routine that simply tries to output a
+ * in an mbuf after a reset.
+ *
+ * input : board number
+ * output : stuff sent to board if any there
+ *
+ */
+
+ns8390start(unit)
+int unit;
+{
+ register ns8390_softc_t *is = &ns8390_softc[unit];
+ struct ifnet *ifp;
+#ifdef MACH_KERNEL
+ io_req_t m;
+#else MACH_KERNEL
+ struct mbuf *m;
+#endif MACH_KERNEL
+
+ if (is->tbusy) {
+ caddr_t nic = ns8390_softc[unit].nic;
+ if (!(inb(nic+ds_cmd) & DSCM_TRANS)) {
+ is->tbusy = 0;
+ ns8390_cntrs[unit].busy++;
+ } else
+ return;
+ }
+
+ ifp = &(ns8390_softc[unit].ds_if);
+
+ IF_DEQUEUE(&ifp->if_snd, m);
+#ifdef MACH_KERNEL
+ if (m != 0)
+#else MACH_KERNEL
+ if (m != (struct mbuf *)0)
+#endif MACH_KERNEL
+ {
+ is->tbusy++;
+ ns8390_cntrs[unit].xmt++;
+ ns8390xmt(unit, m);
+ }
+}
+
+#ifdef MACH_KERNEL
+/*ARGSUSED*/
+ns8390getstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status; /* pointer to OUT array */
+ unsigned int *count; /* out */
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+
+ return (net_getstat(&ns8390_softc[unit].ds_if,
+ flavor,
+ status,
+ count));
+}
+ns8390setstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned int count;
+{
+ register int unit = minor(dev);
+ register ns8390_softc_t *sp;
+
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+
+ sp = &ns8390_softc[unit];
+
+ switch (flavor) {
+ case NET_STATUS:
+ {
+ /*
+ * All we can change are flags, and not many of those.
+ */
+ register struct net_status *ns = (struct net_status *)status;
+ int mode = 0;
+
+ if (count < NET_STATUS_COUNT)
+ return (D_INVALID_SIZE);
+
+ if (ns->flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ns->flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+
+ /*
+ * Force a complete reset if the receive mode changes
+ * so that these take effect immediately.
+ */
+ if (sp->mode != mode) {
+ sp->mode = mode;
+ if (sp->flags & DSF_RUNNING) {
+ sp->flags &= ~(DSF_LOCK | DSF_RUNNING);
+ ns8390init(unit);
+ }
+ }
+ break;
+ }
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+#else MACH_KERNEL
+/*
+ * ns8390ioctl:
+ *
+ * This routine processes an ioctl request from the "if" layer
+ * above.
+ *
+ * input : pointer the appropriate "if" struct, command, and data
+ * output : based on command appropriate action is taken on the
+ * ns8390 board(s) or related structures
+ * return : error is returned containing exit conditions
+ *
+ */
+
+int
+ns8390ioctl(ifp, cmd, data)
+struct ifnet *ifp;
+int cmd;
+caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ register ns8390_softc_t *is;
+ int error;
+ spl_t opri;
+ short mode = 0;
+
+ is = &ns8390_softc[ifp->if_unit];
+ opri = SPLNET();
+ error = 0;
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ ns8390init(ifp->if_unit);
+ switch (ifa->ifa_addr.sa_family) {
+#ifdef INET
+ case AF_INET:
+ ((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 *)(ds->ds_addr);
+ else
+????
+ ns8390seteh(ina->x_host.c_host,
+ ns8390_softc[ifp->if_unit].base);
+ break;
+ }
+#endif
+ }
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ifp->if_flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+ /*
+ * force a complete reset if the receive multicast/
+ * promiscuous mode changes so that these take
+ * effect immediately.
+ *
+ */
+ if (is->mode != mode) {
+ is->mode = mode;
+ if (is->flags & DSF_RUNNING) {
+ is->flags &=
+ ~(DSF_LOCK|DSF_RUNNING);
+ ns8390init(ifp->if_unit);
+ }
+ }
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ is->flags & DSF_RUNNING) {
+ printf("%s%d ioctl(): turning off board %d\n",
+ is->card, ifp->if_unit);
+ is->flags &= ~(DSF_LOCK | DSF_RUNNING);
+ is->timer = -1;
+ ns8390intoff(ifp->if_unit);
+ ns8390over_write(ifp->if_unit);
+ } else
+ if (ifp->if_flags & IFF_UP &&
+ (is->flags & DSF_RUNNING) == 0)
+ ns8390init(ifp->if_unit);
+ break;
+#ifdef IF_CNTRS
+ case SIOCCIFCNTRS:
+ if (!suser()) {
+ error = EPERM;
+ break;
+ }
+ bzero((caddr_t)ns_ein, sizeof (ns_ein));
+ bzero((caddr_t)ns_eout, sizeof (ns_eout));
+ bzero((caddr_t)ns_lin, sizeof (ns_lin));
+ bzero((caddr_t)ns_lout, sizeof (ns_lout));
+ bzero((caddr_t)&ns_arp, sizeof (int));
+ bzero((caddr_t)&ns8390_cntrs, sizeof (ns8390_cntrs));
+ break;
+#endif IF_CNTRS
+ default:
+ error = EINVAL;
+ }
+ splx(opri);
+ return (error);
+}
+#endif MACH_KERNEL
+
+/*
+ * ns8390hwrst:
+ *
+ * This routine resets the ns8390 board that corresponds to the
+ * board number passed in.
+ *
+ * input : board number to do a hardware reset
+ * output : board is reset
+ *
+ */
+
+int
+ns8390hwrst(unit)
+int unit;
+{
+ caddr_t nic = ns8390_softc[unit].nic;
+ int count;
+ u_char stat;
+ spl_t spl = SPLNET();
+
+ if (ns8390_softc[unit].card == wd8003_card &&
+ config_wd8003(unit) == FALSE) {
+ printf("%s%d hwrst(): config_wd8003 failed.\n",
+ ns8390_softc[unit].card, unit);
+ splx(spl);
+ return(FALSE);
+ }
+ if (ns8390_softc[unit].card == elii_card &&
+ config_3c503(unit) == FALSE) {
+ printf("%s%d hwrst(): config_3c503 failed.\n",
+ ns8390_softc[unit].card, unit);
+ splx(spl);
+ return(FALSE);
+ }
+ if (config_nic(unit) == FALSE) {
+ printf("%s%d hwrst(): config_nic failed.\n",
+ ns8390_softc[unit].card, unit);
+ splx(spl);
+ return(FALSE);
+ }
+ splx(spl);
+ return(TRUE);
+}
+
+/*
+ * ns8390intr:
+ *
+ * This function is the interrupt handler for the ns8390 ethernet
+ * board. This routine will be called whenever either a packet
+ * is received, or a packet has successfully been transfered and
+ * the unit is ready to transmit another packet.
+ *
+ * input : board number that interrupted
+ * output : either a packet is received, or a packet is transfered
+ *
+ */
+int
+ns8390intr(unit)
+{
+ int opri, i;
+ int isr_status;
+ int temp_cr;
+ caddr_t nic = ns8390_softc[unit].nic;
+
+ temp_cr = inb(nic+ds_cmd);
+ outb(nic+ds_cmd, (temp_cr & 0x3f) | DSCM_PG0);
+ outb(nic+ds0_imr, 0); /* stop board interrupts */
+ outb(nic+ds_cmd, temp_cr);
+ while (isr_status = inb(nic+ds0_isr)) {
+ outb(nic+ds0_isr, isr_status); /* clear interrupt status */
+
+ if ((isr_status & (DSIS_ROVRN|DSIS_RXE)) == DSIS_RXE) {
+ int rsr = inb(nic+ds0_rsr);
+ if (rsr & DSRS_DFR) ns8390_cntrs[unit].jabber++;
+ if (rsr & ~(DSRS_DFR|DSRS_PHY|DSRS_FAE|DSRS_CRC|DSIS_RX))
+ printf("%s%d intr(): isr = %x, RSR = %x\n",
+ ns8390_softc[unit].card, unit,
+ isr_status, rsr);
+ } else if (isr_status & DSIS_ROVRN) {
+ ns8390_cntrs[unit].ovw++;
+ ns8390over_write(unit);
+ }
+ if (isr_status & DSIS_RX) { /* DFR & PRX is possible */
+ ns8390rcv(unit);
+
+#if MACH_TTD
+ if (kttd_active)
+ ttd_poll_loop = FALSE;
+#endif /* MACH_TTD */
+ }
+
+ if (isr_status & DSIS_TXE) {
+ int tsr = inb(nic+ds0_tsr);
+ tsr &= ~0x2; /* unadvertised special */
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ {
+ if (tsr == (DSTS_CDH|DSTS_ABT))
+ ns8390_cntrs[unit].heart++;
+ else
+ printf("%s%d intr(): isr = %x, TSR = %x\n",
+ ns8390_softc[unit].card, unit,
+ isr_status, tsr);
+ ns8390_softc[unit].tbusy = 0;
+ ns8390start(unit);
+ }
+ } else if (isr_status & DSIS_TX) {
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ {
+ ns8390_cntrs[unit].xmti++;
+ ns8390_softc[unit].tbusy = 0;
+ ns8390start(unit);
+ }
+ }
+
+ if (isr_status & DSIS_CTRS) {
+ int c0 = inb(nic+ds0_cntr0);
+ int c1 = inb(nic+ds0_cntr1);
+ int c2 = inb(nic+ds0_cntr2);
+ ns8390_cntrs[unit].frame += c0;
+ ns8390_cntrs[unit].crc += c1;
+ ns8390_cntrs[unit].miss += c2;
+#ifdef COUNTERS
+ printf("%s%d intr(): isr = %x, FRAME %x, CRC %x, MISS %x\n",
+ ns8390_softc[unit].card, unit,
+ isr_status, c0, c1, c2);
+ printf("%s%d intr(): TOTAL , FRAME %x, CRC %x, MISS %x\n",
+ ns8390_softc[unit].card, unit,
+ ns8390_cntrs[unit].frame,
+ ns8390_cntrs[unit].crc,
+ ns8390_cntrs[unit].miss);
+#endif COUNTERS
+ outb(nic+ds0_isr, isr_status); /* clear interrupt status again */
+ }
+ }
+ temp_cr=inb(nic+ds_cmd);
+ outb(nic+ds_cmd, (temp_cr & 0x3f) | DSCM_PG0);
+ outb(nic+ds0_imr, imr_hold);
+ outb(nic+ds_cmd, temp_cr);
+ return(0);
+}
+
+/*
+ * Called if on board buffer has been completely filled by ns8390intr. It stops
+ * the board, reads in all the buffers that are currently in the buffer, and
+ * then restart board.
+ */
+ns8390over_write(unit)
+int unit;
+{
+ caddr_t nic = ns8390_softc[unit].nic;
+ int no;
+ int count = 0;
+
+ outb(nic+ds_cmd, DSCM_NODMA|DSCM_STOP|DSCM_PG0); /* clear the receive buffer */
+ outb(nic+ds0_rbcr0, 0);
+ outb(nic+ds0_rbcr1, 0);
+ while ((!(inb (nic + ds0_isr) & DSIS_RESET)) && (count < 10000))
+ count++;
+ if (count == 10000) {
+ printf("%s%d: over_write(): would not reset.\n",
+ ns8390_softc[unit].card, unit);
+ }
+ no = ns8390rcv(unit);
+#ifdef OVWBUG
+ printf("%s%d over_write(): ns8390 OVW ... %d.\n",
+ ns8390_softc[unit].card, unit, no);
+#endif OVWBUG
+ outb(nic+ds0_tcr, DSTC_LB0); /* External loopback mode */
+ outb(nic+ds_cmd, DSCM_NODMA|DSCM_START|DSCM_PG0);
+ outb(nic+ds0_tcr, 0);
+ return;
+}
+
+/*
+ * ns8390rcv:
+ *
+ * This routine is called by the interrupt handler to initiate a
+ * packet transfer from the board to the "if" layer above this
+ * driver. This routine checks if a buffer has been successfully
+ * received by the ns8390. If so, it does the actual transfer of the
+ * board data (including the ethernet header) into a packet (consisting
+ * of an mbuf chain) and enqueues it to a higher level.
+ * Then check again whether there are any packets in the receive ring,
+ * if so, read the next packet, until there are no more.
+ *
+ * input : number of the board to check
+ * output : if a packet is available, it is "sent up"
+ */
+ns8390rcv(unit)
+int unit;
+{
+ register ns8390_softc_t *is = &ns8390_softc[unit];
+ register struct ifnet *ifp = &is->ds_if;
+ caddr_t nic = is->nic;
+ int packets = 0;
+ struct ether_header eh;
+ u_short mlen, len, bytes_in_mbuf, bytes;
+ u_short remaining;
+ int temp_cr;
+ u_char *mb_p;
+ int board_id = is->board_id;
+ vm_offset_t hdwbase = ns8390info[unit]->address;
+ spl_t s;
+
+ /* calculation of pkt size */
+ int nic_overcount; /* NIC says 1 or 2 more than we need */
+ int pkt_size; /* calculated size of received data */
+ int wrap_size; /* size of data before wrapping it */
+ int header_nxtpkt_ptr; /* NIC's next pkt ptr in rcv header */
+ int low_byte_count; /* low byte count of read from rcv header */
+ int high_byte_count; /* calculated high byte count */
+
+
+ volatile char *sram_nxtpkt_ptr; /* mem location of next packet */
+ volatile char *sram_getdata_ptr; /* next location to be read */
+#ifdef MACH_KERNEL
+ ipc_kmsg_t new_kmsg;
+ struct ether_header *ehp;
+ struct packet_header *pkt;
+#else MACH_KERNEL
+ struct mbuf *m, *tm; /* initial allocation of mem; temp */
+#endif MACH_KERNEL
+
+
+#if MACH_TTD
+ if (((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) &&
+ !kttd_active) {
+#else
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+#endif /* MACH_TTD */
+ temp_cr = inb(nic+ds_cmd); /* get current CR value */
+ outb(nic+ds_cmd,((temp_cr & 0x3F)|DSCM_PG0|DSCM_STOP));
+ outb(nic+ds0_imr, 0); /* Interrupt Mask Register */
+ outb(nic+ds_cmd, temp_cr);
+ return -1;
+ }
+
+ while(is->read_nxtpkt_ptr != ns8390get_CURR(unit)) {
+
+ /* while there is a packet to read from the buffer */
+
+ if ((is->read_nxtpkt_ptr < is->pstart) ||
+ (is->read_nxtpkt_ptr >= is->pstop)) {
+ ns8390hwrst(unit);
+ return -1;
+ } /* if next packet pointer is out of receive ring bounds */
+
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ {
+ packets++;
+ ns8390_cntrs[unit].rcv++;
+ }
+
+ sram_nxtpkt_ptr = (char *) (is->sram + (is->read_nxtpkt_ptr << 8));
+
+ /* get packet size and location of next packet */
+ header_nxtpkt_ptr = *(sram_nxtpkt_ptr + 1);
+ header_nxtpkt_ptr &= 0xFF;
+ low_byte_count = *(sram_nxtpkt_ptr + 2);
+ low_byte_count &= 0xFF;
+
+ if ((low_byte_count + NIC_HEADER_SIZE) > NIC_PAGE_SIZE)
+ nic_overcount = 2;
+ else
+ nic_overcount = 1;
+ if (header_nxtpkt_ptr > is->read_nxtpkt_ptr) {
+ wrap_size = 0;
+ high_byte_count = header_nxtpkt_ptr - is->read_nxtpkt_ptr -
+ nic_overcount;
+ } else {
+ wrap_size = (int) (is->pstop - is->read_nxtpkt_ptr - nic_overcount);
+ high_byte_count = is->pstop - is->read_nxtpkt_ptr +
+ header_nxtpkt_ptr - is->pstart - nic_overcount;
+ }
+ pkt_size = (high_byte_count << 8) | (low_byte_count & 0xFF);
+ /* does not seem to include NIC_HEADER_SIZE */
+ if (!pkt_size) {
+ printf("%s%d rcv(): zero length.\n",
+ ns8390_softc[unit].card, unit);
+ goto next_pkt;
+ }
+ len = pkt_size;
+
+ sram_getdata_ptr = sram_nxtpkt_ptr + NIC_HEADER_SIZE;
+ if (board_id & IFWD_SLOT_16BIT) {
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { s = splhi(); }
+
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr,
+ &eh,
+ sizeof(struct ether_header));
+ dis_16bit_access (hdwbase, board_id);
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { splx(s); }
+
+ } else {
+ bcopy16 (sram_getdata_ptr,
+ &eh,
+ sizeof(struct ether_header));
+ }
+ sram_getdata_ptr += sizeof(struct ether_header);
+ len -= (sizeof(struct ether_header) + 4); /* crc size */
+#ifdef MACH_KERNEL
+#if MACH_TTD
+ if (kttd_active) {
+ new_kmsg = (ipc_kmsg_t)ttd_request_msg;
+ }else
+#endif /* MACH_TTD */
+ {
+ new_kmsg = net_kmsg_get();
+ if (new_kmsg == IKM_NULL) {
+ /*
+ * Drop the packet.
+ */
+ is->ds_if.if_rcvdrops++;
+ /*
+ * not only do we want to return, we need to drop
+ * the packet on the floor to clear the interrupt.
+ */
+ ns8390lost_frame(unit);
+ return;/* packets;*/
+ }
+ }
+
+#if DEBUG_TTD
+ dump_ether_header("ns8390wire",&eh);
+#endif /* DEBUG_TTD */
+
+ ehp = (struct ether_header *) (&net_kmsg(new_kmsg)->header[0]);
+ pkt = (struct packet_header *) (&net_kmsg(new_kmsg)->packet[0]);
+
+#if DEBUG_TTD
+ printf("!ehp = 0x%x, pkt = 0x%x!",ehp, pkt);
+#endif /* DEBUG_TTD */
+
+ *ehp = eh;
+ if (len >
+ (wrap_size = (is->sram + (is->pstop << 8) - sram_getdata_ptr))) {
+ /* if needs to wrap */
+ if (board_id & IFWD_SLOT_16BIT) {
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { s = splhi(); }
+
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr, (char *) (pkt + 1),
+ wrap_size);
+ dis_16bit_access (hdwbase, board_id);
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { splx(s); }
+ } else {
+ bcopy (sram_getdata_ptr, (char *) (pkt + 1),
+ wrap_size);
+ }
+ sram_getdata_ptr = (volatile char *)
+ (is->sram + (is->pstart << 8));
+ } else { /* normal getting data from buffer */
+ wrap_size = 0;
+ }
+ if (board_id & IFWD_SLOT_16BIT) {
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { s = splhi(); }
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr,
+ (char *) (pkt + 1) + wrap_size,
+ len - wrap_size);
+ dis_16bit_access (hdwbase, board_id);
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { splx(s); }
+ } else {
+ bcopy (sram_getdata_ptr,
+ (char *) (pkt + 1) + wrap_size,
+ len - wrap_size);
+ }
+
+ pkt->type = ehp->ether_type;
+ pkt->length = len + sizeof(struct packet_header);
+
+#if MACH_TTD
+ /*
+ * Don't want to call net_packet if we are polling
+ * for a packet.
+ */
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ {
+ /*
+ * Hand the packet to the network module.
+ */
+ net_packet(ifp, new_kmsg, pkt->length,
+ ethernet_priority(new_kmsg));
+ }
+
+#else MACH_KERNEL
+#define NEW
+#ifdef NEW
+ m = (struct mbuf *) 0;
+ eh.ether_type = ntohs(eh.ether_type);
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == (struct mbuf *) 0) {
+ printf("%s%d rcv(): Lost frame\n",
+ ns8390_softc[unit].card, unit);
+ ns8390lost_frame(unit); /* update NIC pointers and registers */
+ return packets;
+ }
+ m->m_next = (struct mbuf *) 0;
+ tm = m;
+ m->m_len = MLEN;
+ if (len > 2 * MLEN - sizeof (struct ifnet **)) {
+ MCLGET(m);
+ }
+ *(mtod(tm, struct ifnet **)) = ifp;
+ mlen = sizeof (struct ifnet **);
+ bytes_in_mbuf = m->m_len - sizeof(struct ifnet **);
+ mb_p = mtod(tm, u_char *) + sizeof (struct ifnet **);
+ bytes = min(bytes_in_mbuf, len);
+ remaining = (int) (is->sram + (is->pstop << 8) -
+ sram_getdata_ptr);
+ bytes = min(bytes, remaining);
+ do {
+ if (board_id & IFWD_SLOT_16BIT) {
+ s = splhi();
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr, mb_p, bytes);
+ dis_16bit_access (hdwbase, board_id);
+ splx(s);
+ } else {
+ bcopy16 (sram_getdata_ptr, mb_p, bytes);
+ }
+
+ mlen += bytes;
+
+ if (!(bytes_in_mbuf -= bytes)) {
+ MGET(tm->m_next, M_DONTWAIT, MT_DATA);
+ tm = tm->m_next;
+ if (tm == (struct mbuf *)0) {
+ printf("%s%d rcv(): No mbufs, lost frame\n",
+ ns8390_softc[unit].card, unit);
+ m_freem(m); /* free the mbuf chain */
+ ns8390lost_frame(unit); /* update NIC pointers and registers */
+ return;
+ }
+ mlen = 0;
+ tm->m_len = MLEN;
+ bytes_in_mbuf = MLEN;
+ mb_p = mtod(tm, u_char *);
+ } else
+ mb_p += bytes;
+
+ if (!(len -= bytes)) {
+ tm->m_len = mlen;
+ break;
+ } else if (bytes == remaining) {
+ sram_getdata_ptr = (volatile char *) (is->sram +
+ (is->pstart << 8));
+ bytes = len;
+ remaining = ETHERMTU;
+ } else {
+ sram_getdata_ptr += bytes;
+ remaining -= bytes;
+ }
+
+ bytes = min(bytes_in_mbuf, len);
+ bytes = min(bytes, remaining);
+ } while(1);
+#else NEW
+ m = (struct mbuf *) 0;
+ eh.ether_type = ntohs(eh.ether_type);
+
+ while ( len ) {
+ if (m == (struct mbuf *) 0) {
+ m = m_get(M_DONTWAIT, MT_DATA);
+ if (m == (struct mbuf *) 0) {
+ printf("%s%d rcv(): Lost frame\n",
+ ns8390_softc[unit].card, unit);
+ ns8390lost_frame(unit); /* update NIC pointers and registers */
+ return packets;
+ }
+ tm = m;
+ tm->m_off = MMINOFF;
+
+
+ /*
+ * first mbuf in the packet must contain a pointer to the
+ * ifnet structure. other mbufs that follow and make up
+ * the packet do not need this pointer in the mbuf.
+ *
+ */
+
+ *(mtod(tm, struct ifnet **)) = ifp;
+ tm->m_len = sizeof(struct ifnet **);
+
+ /* end of first buffer of packet */
+ } else {
+ tm->m_next = m_get(M_DONTWAIT, MT_DATA);
+ tm = tm->m_next;
+ if (tm == (struct mbuf *) 0) {
+ printf("%s%d rcv(): No mbufs, lost frame\n",
+ ns8390_softc[unit].card, unit);
+ m_freem(m); /* free the mbuf chain */
+ ns8390lost_frame(unit); /* update NIC pointers and registers */
+ return packets;
+ }
+ tm->m_off = MMINOFF;
+ tm->m_len = 0;
+ }
+
+ tlen = MIN( MLEN - tm->m_len, len);
+ /* size of mbuf so you know how much you can copy from board */
+ tm->m_next = (struct mbuf *) 0;
+ if (sram_getdata_ptr + tlen >=
+ (volatile char *) (is->sram + (is->pstop << 8))) {
+ /* if needs to wrap */
+ wrap_size = (int) (is->sram + (is->pstop << 8) -
+ sram_getdata_ptr);
+ if (board_id & IFWD_SLOT_16BIT) {
+ s = splhi();
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr,
+ mtod(tm, char*) + tm->m_len,
+ wrap_size);
+ dis_16bit_access (hdwbase, board_id);
+ splx(s);
+ } else {
+ bcopy16 (sram_getdata_ptr,
+ mtod(tm, char*) + tm->m_len,
+ wrap_size);
+ }
+ tm->m_len += wrap_size;
+ len -= wrap_size;
+
+ sram_getdata_ptr = (volatile char *) (is->sram +
+ (is->pstart << 8));
+ } else { /* normal getting data from buffer */
+ if (board_id & IFWD_SLOT_16BIT) {
+ s = splhi();
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr,
+ mtod(tm, char*) + tm->m_len,
+ tlen);
+ dis_16bit_access (hdwbase, board_id);
+ splx(s);
+ } else {
+ bcopy16 (sram_getdata_ptr,
+ mtod(tm, char*) + tm->m_len,
+ tlen);
+ }
+ sram_getdata_ptr += tlen;
+ tm->m_len += tlen;
+ len -= tlen;
+
+ }
+ }
+
+#endif NEW
+ if (!ns8390send_packet_up(m, &eh, is))
+ m_freem(m);
+#ifdef IF_CNTRS
+ ns_ein[log_2(pkt_size)]++;
+ if (pkt_size < 128) ns_lin[(pkt_size)>>3]++;
+
+ if (eh.ether_type == ETHERTYPE_ARP) {
+ ns_arp++;
+ if (ns_narp) {
+ ns_ein[log_2(pkt_size)]--;
+ if (pkt_size < 128) ns_lin[(pkt_size)>>3]--;
+ }
+ }
+#endif IF_CNTRS
+#endif MACH_KERNEL
+
+next_pkt:
+ is->read_nxtpkt_ptr = *(sram_nxtpkt_ptr + 1);
+ is->read_nxtpkt_ptr &= 0xFF;
+
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ {
+ temp_cr = inb(nic+ds_cmd);
+ outb(nic+ds_cmd, (temp_cr & 0x3f) | DSCM_PG0);
+ }
+
+ if (is->read_nxtpkt_ptr == ns8390get_CURR(unit))
+ if (is->read_nxtpkt_ptr == is->pstart)
+ outb(nic+ds0_bndy, is->pstop - 1);
+ else
+ outb(nic+ds0_bndy, is->read_nxtpkt_ptr - 1);
+ else
+ outb(nic+ds0_bndy, is->read_nxtpkt_ptr);
+
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { outb(nic+ds_cmd, temp_cr); }
+
+#if MACH_TTD
+ /*
+ * Hand the packet back to the TTD server, if active.
+ */
+ if (kttd_active && pkt_size)
+ return 1;
+#endif /* MACH_TTD */
+
+
+ }
+ return packets;
+
+}
+
+#ifdef MACH_KERNEL
+#if MACH_TTD
+/*
+ * Polling routines for the TTD debugger.
+ */
+int ns8390poll_receive(unit)
+ int unit;
+{
+ int s;
+ int orig_cr;
+ int orig_imr;
+ int isr_status;
+ int pkts;
+
+ ttd_poll_loop = TRUE;
+
+
+ /*
+ * Should already in at splhigh. Is this necessary? XXX
+ */
+ s = splhigh();
+
+#if 0
+ if (kttd_debug)
+ printf("ns8390poll_receive: beginning polling loop\n");
+#endif /* DEBUG_TTD */
+
+ /*
+ * Loop until packet arrives.
+ */
+ while(ttd_poll_loop) {
+
+ /*
+ * Call intr routine
+ */
+
+ ns8390intr(unit);
+ }
+
+#if 0
+ if (kttd_debug)
+ printf("ns8390poll_receive: got packet exiting loop\n");
+#endif /* DEBUG_TTD */
+
+ splx(s);
+}
+
+int ns8390transmit_ttd(unit, packet, len)
+ int unit;
+ char * packet;
+ int len;
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ caddr_t nic = is->nic;
+ u_short count = 0; /* amount of data already copied */
+ volatile char *sram_write_pkt;
+ int board_id = is->board_id;
+ caddr_t hdwbase = ns8390info[unit]->address;
+ int s;
+ int orig_cr;
+ int orig_imr;
+ int isr_status;
+ boolean_t loop = TRUE;
+
+#if 0
+ dump_ipudpbootp("Beg of xmit",packet);
+#endif
+
+ s = splhigh();
+
+ /* begining of physical address of transmition buffer */
+
+ sram_write_pkt = is->sram + is->tpsr * 0x100;
+
+ count = len;
+ if (board_id & IFWD_SLOT_16BIT) {
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (packet, sram_write_pkt, count);
+ dis_16bit_access (hdwbase, board_id);
+ } else {
+ bcopy (packet, sram_write_pkt, count);
+ }
+
+ while (count < ETHERMIN+sizeof(struct ether_header)) {
+ *(sram_write_pkt + count) = 0;
+ count++;
+ }
+ outb(nic+ds_cmd, DSCM_NODMA|DSCM_START|DSCM_PG0); /* select page 0 */
+ outb(nic+ds0_tpsr, is->tpsr); /* xmt page start at 0 of RAM */
+ outb(nic+ds0_tbcr1, count >> 8); /* upper byte of count */
+ outb(nic+ds0_tbcr0, count & 0xFF); /* lower byte of count */
+ outb(nic+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START); /* start transmission */
+
+ ns8390intr(unit);
+
+ splx(s);
+}
+#endif /* MACH_TTD */
+#endif /* MACH_KERNEL */
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+/*
+ * Send a packet composed of an mbuf chain to the higher levels
+ *
+ */
+ns8390send_packet_up(m, eh, is)
+struct mbuf *m;
+struct ether_header *eh;
+ns8390_softc_t *is;
+{
+ register struct ifqueue *inq;
+ spl_t opri;
+
+ switch (eh->ether_type) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ schednetisr(NETISR_IP);
+ inq = &ipintrq;
+ break;
+ case ETHERTYPE_ARP:
+ arpinput(&is->ns8390_ac, m);
+ return(TRUE);
+#endif
+#ifdef NS
+ case ETHERTYPE_NS:
+ schednetisr(NETISR_NS);
+ inq = &nsintrq;
+ break;
+#endif
+ default:
+ return(FALSE);
+ }
+ opri = SPLNET();
+ if (IF_QFULL(inq)) {
+ IF_DROP(inq);
+ splx(opri);
+ return(FALSE);
+ }
+ IF_ENQUEUE(inq, m);
+ splx(opri);
+ return(TRUE);
+}
+#endif MACH_KERNEL
+
+/*
+ * ns8390lost_frame:
+ * this routine called by ns8390read after memory for mbufs could not be
+ * allocated. It sets the boundary pointers and registers to the next
+ * packet location.
+ */
+
+ns8390lost_frame(unit)
+int unit;
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ caddr_t nic = is->nic;
+ volatile char *sram_nxtpkt_ptr;
+ int temp_cr;
+
+
+
+ sram_nxtpkt_ptr = (volatile char *) (is->sram +
+ (is->read_nxtpkt_ptr << 8));
+
+ is->read_nxtpkt_ptr = *(sram_nxtpkt_ptr + 1);
+ is->read_nxtpkt_ptr &= 0xFF;
+
+ temp_cr = inb(nic+ds_cmd);
+ outb(nic+ds_cmd, (temp_cr & 0x3f) | DSCM_PG0);
+
+ /* update boundary register */
+ if (is->read_nxtpkt_ptr == ns8390get_CURR(unit))
+ if (is->read_nxtpkt_ptr == is->pstart)
+ outb(nic+ds0_bndy, is->pstop - 1);
+ else
+ outb(nic+ds0_bndy, is->read_nxtpkt_ptr - 1);
+ else
+ outb(nic+ds0_bndy, is->read_nxtpkt_ptr);
+
+ outb(nic+ds_cmd, temp_cr);
+
+ return;
+}
+
+/*
+ * ns8390get_CURR():
+ *
+ * Returns the value of the register CURR, which points to the next
+ * available space for NIC to receive from network unto receive ring.
+ *
+ */
+
+int
+ns8390get_CURR(unit)
+int unit;
+{
+ caddr_t nic = ns8390_softc[unit].nic;
+ int temp_cr;
+ int ret_val;
+ spl_t s;
+
+ s = SPLNET();
+
+ temp_cr = inb(nic+ds_cmd); /* get current CR value */
+ outb(nic+ds_cmd, ((temp_cr & 0x3F) | DSCM_PG1)); /* select page 1 registers */
+ ret_val = inb(nic+ds1_curr); /* read CURR value */
+ outb(nic+ds_cmd, temp_cr);
+ splx(s);
+ return (ret_val & 0xFF);
+}
+
+/*
+ * ns8390xmt:
+ *
+ * This routine fills in the appropriate registers and memory
+ * locations on the ns8390 board and starts the board off on
+ * the transmit.
+ *
+ * input : board number of interest, and a pointer to the mbuf
+ * output : board memory and registers are set for xfer and attention
+ *
+ */
+
+ns8390xmt(unit, m)
+int unit;
+#ifdef MACH_KERNEL
+io_req_t m;
+#else MACH_KERNEL
+struct mbuf *m;
+#endif MACH_KERNEL
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ caddr_t nic = is->nic;
+ struct ether_header *eh;
+ int i;
+ int opri;
+ u_short count = 0; /* amount of data already copied */
+ volatile char *sram_write_pkt;
+ int board_id = is->board_id;
+ vm_offset_t hdwbase = ns8390info[unit]->address;
+ spl_t s;
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ register struct mbuf *tm_p;
+#endif MACH_KERNEL
+ /* begining of physical address of transmition buffer */
+
+ sram_write_pkt = is->sram + is->tpsr * 0x100;
+
+#ifdef MACH_KERNEL
+ count = m->io_count;
+ if (board_id & IFWD_SLOT_16BIT) {
+ s = splhi();
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (m->io_data, sram_write_pkt, count);
+ dis_16bit_access (hdwbase, board_id);
+ splx(s);
+ } else {
+ bcopy (m->io_data, sram_write_pkt, count);
+ }
+#else MACH_KERNEL
+ for(tm_p = m; tm_p != (struct mbuf *)0; tm_p = tm_p->m_next) {
+ if (count + tm_p->m_len > ETHERMTU + sizeof (struct ether_header))
+ break;
+ if (tm_p->m_len == 0)
+ continue;
+ if (board_id & IFWD_SLOT_16BIT) {
+ s = splhi();
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (mtod(tm_p, caddr_t),
+ sram_write_pkt + count,
+ tm_p->m_len);
+ dis_16bit_access (hdwbase, board_id);
+ splx(s);
+ } else {
+ bcopy16 (mtod(tm_p, caddr_t),
+ sram_write_pkt + count,
+ tm_p->m_len);
+ }
+ count += tm_p->m_len;
+ }
+#ifdef IF_CNTRS
+ ns_eout[log_2(count+4/*crc*/)]++;
+ if (count < 128) ns_lout[(count+4/*crc*/)>>3]++;
+#endif IF_CNTRS
+#endif MACH_KERNEL
+ while (count < ETHERMIN+sizeof(struct ether_header)) {
+ *(sram_write_pkt + count) = 0;
+ count++;
+ }
+
+ /* select page 0 */
+ outb(nic+ds_cmd, DSCM_NODMA|DSCM_START|DSCM_PG0);
+ outb(nic+ds0_tpsr, is->tpsr); /* xmt page start at 0 of RAM */
+ outb(nic+ds0_tbcr1, count >> 8); /* upper byte of count */
+ outb(nic+ds0_tbcr0, count & 0xFF); /* lower byte of count */
+ /* start transmission */
+ outb(nic+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START);
+
+#ifdef MACH_KERNEL
+ iodone(m);
+ m=0;
+#else MACH_KERNEL
+ /* If this is a broadcast packet, loop it back to rcv. */
+ eh = mtod( m, struct ether_header *);
+ for (i=0; ((i < 6) && (eh->ether_dhost[i] == 0xff)); i++) ;
+ if (i == 6) {
+ if (!ns8390send_packet_up(m, eh, is))
+ m_freem(m);
+ } else
+ m_freem(m);
+#endif MACH_KERNEL
+ return;
+}
+
+config_nic(unit)
+int unit;
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ caddr_t nic = is->nic;
+ int i;
+ int temp;
+ int count = 0;
+ spl_t s;
+
+ /* soft reset and page 0 */
+ outb (nic+ds_cmd, DSCM_PG0|DSCM_NODMA|DSCM_STOP);
+
+ while ((!(inb (nic + ds0_isr) & DSIS_RESET)) && (count < 10000))
+ count++;
+ if (count == 10000) {
+ printf("%s%d: config_nic(): would not reset.\n",
+ ns8390_softc[unit].card, unit);
+ }
+
+ /* fifo depth | not loopback */
+ temp = ((is->fifo_depth & 0x0c) << 3) | DSDC_BMS;
+
+ /* word xfer select (16 bit cards ) */
+ if (is->board_id & IFWD_SLOT_16BIT)
+ temp |= DSDC_WTS;
+
+ outb (nic+ds0_dcr, temp);
+ outb (nic+ds0_tcr, 0);
+ outb (nic+ds0_rcr, DSRC_MON); /* receive configuration register */
+ /* recieve ring starts 2k into RAM */
+ outb (nic+ds0_pstart, is->pstart);
+ /* stop at last RAM buffer rcv location */
+ outb (nic+ds0_pstop, is->pstop);
+
+ /* boundary pointer for page 0 */
+ outb (nic+ds0_bndy, is->pstart);
+ s = SPLNET();
+
+ /* maintain rst | sel page 1 */
+ outb (nic+ds_cmd, DSCM_PG1|DSCM_NODMA|DSCM_STOP);
+
+ /* internal next packet pointer */
+ is->read_nxtpkt_ptr = is->pstart + 1;
+
+ outb (nic+ds1_curr, is->read_nxtpkt_ptr); /* Current page register */
+ for(i=0; i<ETHER_ADDR_SIZE; i++)
+ outb (nic+ds1_par0+i, is->address[i]);
+ for(i=0; i<8; i++)
+ outb (nic+ds1_mar0+i, 0);
+
+ outb (nic+ds_cmd, DSCM_PG0|DSCM_STOP|DSCM_NODMA);
+ splx(s);
+ outb (nic+ds0_isr, 0xff); /* clear all interrupt status bits */
+ outb (nic+ds0_imr, imr_hold); /* Enable interrupts */
+ outb (nic+ds0_rbcr0, 0); /* clear remote byte count */
+ outb (nic+ds0_rbcr1, 0);
+
+ /* start NIC | select page 0 */
+ outb (nic+ds_cmd, DSCM_PG0|DSCM_START|DSCM_NODMA);
+
+ outb (nic+ds0_rcr, DSRC_AB); /* receive configuration register */
+
+ return TRUE;
+}
+
+/*
+ * config_ns8390:
+ *
+ * This routine does a standard config of a wd8003 family board, with
+ * the proper modifications to different boards within this family.
+ *
+ */
+config_wd8003(unit)
+int unit;
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ vm_offset_t hdwbase = ns8390info[unit]->address;
+ int i;
+ int RAMsize;
+ volatile char *RAMbase;
+ int addr_temp;
+
+ is->tpsr = 0; /* transmit page start hold */
+ is->pstart = 0x06; /* receive page start hold */
+ is->read_nxtpkt_ptr = is->pstart + 1; /* internal next packet pointer */
+ is->fifo_depth = 0x08; /* NIC fifo threshold */
+ switch (is->board_id & IFWD_RAM_SIZE_MASK) {
+ case IFWD_RAM_SIZE_8K:
+ RAMsize = 0x2000; break;
+ case IFWD_RAM_SIZE_16K:
+ RAMsize = 0x4000; break;
+ case IFWD_RAM_SIZE_32K:
+ RAMsize = 0x8000; break;
+ case IFWD_RAM_SIZE_64K:
+ RAMsize = 0x10000; break;
+ default:
+ RAMsize = 0x2000; break;
+ }
+ is->pstop = (((int)RAMsize >> 8) & 0x0ff); /* rcv page stop hold */
+ RAMbase = (volatile char *)ns8390info[unit]->phys_address;
+ addr_temp = ((int)(RAMbase) >> 13) & 0x3f; /* convert to be written to MSR */
+ outb(hdwbase+IFWD_MSR, addr_temp | IFWD_MENB); /* initialize MSR */
+ /* enable 16 bit access from lan controller */
+ if (is->board_id & IFWD_SLOT_16BIT) {
+ if (is->board_id & IFWD_INTERFACE_CHIP) {
+ outb(hdwbase+IFWD_REG_5,
+ (inb(hdwbase + IFWD_REG_5) & IFWD_REG5_MEM_MASK) |
+ IFWD_LAN16ENB);
+ } else {
+ outb(hdwbase+IFWD_REG_5, (IFWD_LAN16ENB | IFWD_LA19));
+ }
+ }
+ /*
+ outb(hdwbase+LAAR, LAN16ENB | LA19| MEM16ENB | SOFTINT);
+ */
+
+ return TRUE;
+}
+
+/*
+ * config_ns8390:
+ *
+ * This routine does a standard config of a 3 com etherlink II board.
+ *
+ */
+int
+config_3c503(unit)
+int unit;
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ struct bus_device *dev = ns8390info[unit];
+ vm_offset_t hdwbase = dev->address;
+ int RAMsize = dev->am;
+ int i;
+
+ is->tpsr = 0x20; /* transmit page start hold */
+ is->sram = (char *)phystokv(dev->phys_address) - is->tpsr * 0x100;
+ /* When NIC says page 20, this means go to
+ the beginning of the sram range */
+ is->pstart = 0x26; /* receive page start hold */
+ is->read_nxtpkt_ptr = is->pstart + 1; /* internal next packet pointer */
+ is->fifo_depth = 0x08; /* NIC fifo threshold */
+ is->pstop = is->tpsr + ((RAMsize >> 8) & 0x0ff); /* rcv page stop hold */
+
+ outb(hdwbase+CTLR, CTLR_RST|CTLR_THIN);
+ outb(hdwbase+CTLR, CTLR_THIN);
+ outb(hdwbase+CTLR, CTLR_STA_ADDR|CTLR_THIN);
+ for (i = 0; i < 6; i++)
+ is->address[i] = inb(hdwbase+i);
+ outb(hdwbase+CTLR, elii_bnc[is->board_id]?CTLR_THIN:CTLR_THICK);
+ outb(hdwbase+PSTR, is->pstart);
+ outb(hdwbase+PSPR, is->pstop);
+ outb(hdwbase+IDCFR, IDCFR_IRQ2 << (elii_irq[is->board_id] - 2));
+ outb(hdwbase+GACFR, GACFR_TCM|GACFR_8K);
+ /* BCFR & PCRFR ro */
+ /* STREG ro & dma */
+ outb(hdwbase+DQTR, 0);
+ outb(hdwbase+DAMSB, 0);
+ outb(hdwbase+DALSB, 0);
+ outb(hdwbase+VPTR2, 0);
+ outb(hdwbase+VPTR1, 0);
+ outb(hdwbase+VPTR0, 0);
+ outb(hdwbase+RFMSB, 0);
+ outb(hdwbase+RFLSB, 0);
+ return TRUE;
+}
+
+/*
+ * ns8390intoff:
+ *
+ * This function turns interrupts off for the ns8390 board indicated.
+ *
+ */
+void
+ns8390intoff(unit)
+int unit;
+{
+ caddr_t nic = ns8390_softc[unit].nic;
+ int temp_cr = inb(nic+ds_cmd); /* get current CR value */
+
+ outb(nic+ds_cmd,((temp_cr & 0x3F)|DSCM_PG0|DSCM_STOP));
+ outb(nic+ds0_imr, 0); /* Interrupt Mask Register */
+ outb(nic+ds_cmd, temp_cr|DSCM_STOP);
+
+}
+
+
+/*
+ * wd80xxget_board_id:
+ *
+ * determine which board is being used.
+ * Currently supports:
+ * wd8003E (tested)
+ * wd8003EBT
+ * wd8003EP (tested)
+ * wd8013EP (tested)
+ *
+ */
+wd80xxget_board_id(dev)
+struct bus_device *dev;
+{
+ vm_offset_t hdwbase = dev->address;
+ long unit = dev->unit;
+ long board_id = 0;
+ int reg_temp;
+ int rev_num; /* revision number */
+ int ram_flag;
+ int intr_temp;
+ int i;
+ boolean_t register_aliasing;
+
+ rev_num = (inb(hdwbase + IFWD_BOARD_ID) & IFWD_BOARD_REV_MASK) >> 1;
+ printf("%s%d: ", ns8390_softc[unit].card, unit);
+
+ if (rev_num == 0) {
+ printf("rev 0x00\n");
+ /* It must be 8000 board */
+ return 0;
+ }
+
+ /* Check if register aliasing is true, that is reading from register
+ offsets 0-7 will return the contents of register offsets 8-f */
+
+ register_aliasing = TRUE;
+ for (i = 1; i < 5; i++) {
+ if (inb(hdwbase + IFWD_REG_0 + i) !=
+ inb(hdwbase + IFWD_LAR_0 + i))
+ register_aliasing = FALSE;
+ }
+ if (inb(hdwbase + IFWD_REG_7) != inb(hdwbase + IFWD_CHKSUM))
+ register_aliasing = FALSE;
+
+
+ if (register_aliasing == FALSE) {
+ /* Check if board has interface chip */
+
+ reg_temp = inb(hdwbase + IFWD_REG_7); /* save old */
+ outb(hdwbase + IFWD_REG_7, 0x35); /* write value */
+ inb(hdwbase + IFWD_REG_0); /* dummy read */
+ if ((inb(hdwbase + IFWD_REG_7) & 0xff) == 0x35) {
+ outb(hdwbase + IFWD_REG_7, 0x3a);/* Try another value*/
+ inb(hdwbase + IFWD_REG_0); /* dummy read */
+ if ((inb(hdwbase + IFWD_REG_7) & 0xff) == 0x3a) {
+ board_id |= IFWD_INTERFACE_CHIP;
+ outb(hdwbase + IFWD_REG_7, reg_temp);
+ /* restore old value */
+ }
+ }
+
+ /* Check if board is 16 bit by testing if bit zero in
+ register 1 is unchangeable by software. If so then
+ card has 16 bit capability */
+ reg_temp = inb(hdwbase + IFWD_REG_1);
+ outb(hdwbase + IFWD_REG_1, reg_temp ^ IFWD_16BIT);
+ inb(hdwbase + IFWD_REG_0); /* dummy read */
+ if ((inb(hdwbase + IFWD_REG_1) & IFWD_16BIT) ==
+ (reg_temp & IFWD_16BIT)) { /* Is bit unchanged */
+ board_id |= IFWD_BOARD_16BIT; /* Yes == 16 bit */
+ reg_temp &= 0xfe; /* For 16 bit board
+ always reset bit 0 */
+ }
+ outb(hdwbase + IFWD_REG_1, reg_temp); /* write value back */
+
+ /* Test if 16 bit card is in 16 bit slot by reading bit zero in
+ register 1. */
+ if (board_id & IFWD_BOARD_16BIT) {
+ if (inb(hdwbase + IFWD_REG_1) & IFWD_16BIT) {
+ board_id |= IFWD_SLOT_16BIT;
+ }
+ }
+ }
+
+ /* Get media type */
+
+ if (inb(hdwbase + IFWD_BOARD_ID) & IFWD_MEDIA_TYPE) {
+ board_id |= IFWD_ETHERNET_MEDIA;
+ } else if (rev_num == 1) {
+ board_id |= IFWD_STARLAN_MEDIA;
+ } else {
+ board_id |= IFWD_TWISTED_PAIR_MEDIA;
+ }
+
+ if (rev_num == 2) {
+ if (inb(hdwbase + IFWD_BOARD_ID) & IFWD_SOFT_CONFIG) {
+ if ((board_id & IFWD_STATIC_ID_MASK) == WD8003EB ||
+ (board_id & IFWD_STATIC_ID_MASK) == WD8003W) {
+ board_id |= IFWD_ALTERNATE_IRQ_BIT;
+ }
+ }
+ /* Check for memory size */
+
+ ram_flag = inb(hdwbase + IFWD_BOARD_ID) & IFWD_MEMSIZE;
+
+ switch (board_id & IFWD_STATIC_ID_MASK) {
+ case WD8003E: /* same as WD8003EBT */
+ case WD8003S: /* same as WD8003SH */
+ case WD8003WT:
+ case WD8003W:
+ case WD8003EB: /* same as WD8003EP */
+ if (ram_flag)
+ board_id |= IFWD_RAM_SIZE_32K;
+ else
+ board_id |= IFWD_RAM_SIZE_8K;
+ break;
+ case WD8003ETA:
+ case WD8003STA:
+ case WD8003EA:
+ case WD8003SHA:
+ case WD8003WA:
+ board_id |= IFWD_RAM_SIZE_16K;
+ break;
+ case WD8013EBT:
+ if (board_id & IFWD_SLOT_16BIT) {
+ if (ram_flag)
+ board_id |= IFWD_RAM_SIZE_64K;
+ else
+ board_id |= IFWD_RAM_SIZE_16K;
+ } else {
+ if (ram_flag)
+ board_id |= IFWD_RAM_SIZE_32K;
+ else
+ board_id |= IFWD_RAM_SIZE_8K;
+ }
+ break;
+ default:
+ board_id |= IFWD_RAM_SIZE_UNKNOWN;
+ break;
+ }
+ } else if (rev_num >= 3) {
+ board_id &= (long) ~IFWD_MEDIA_MASK; /* remove media info */
+ board_id |= IFWD_INTERFACE_584_CHIP;
+ board_id |= wd80xxget_eeprom_info(hdwbase, board_id);
+ } else {
+ /* Check for memory size */
+ if (board_id & IFWD_BOARD_16BIT) {
+ if (board_id & IFWD_SLOT_16BIT)
+ board_id |= IFWD_RAM_SIZE_16K;
+ else
+ board_id |= IFWD_RAM_SIZE_8K;
+ } else if (board_id & IFWD_MICROCHANNEL)
+ board_id |= IFWD_RAM_SIZE_16K;
+ else if (board_id & IFWD_INTERFACE_CHIP) {
+ if (inb(hdwbase + IFWD_REG_1) & IFWD_MEMSIZE)
+ board_id |= IFWD_RAM_SIZE_32K;
+ else
+ board_id |= IFWD_RAM_SIZE_8K;
+ } else
+ board_id |= IFWD_RAM_SIZE_UNKNOWN;
+
+ /* No support for 690 chip yet. It should be checked here */
+ }
+
+ switch (board_id & IFWD_STATIC_ID_MASK) {
+ case WD8003E: printf("WD8003E or WD8003EBT"); break;
+ case WD8003S: printf("WD8003S or WD8003SH"); break;
+ case WD8003WT: printf("WD8003WT"); break;
+ case WD8003W: printf("WD8003W"); break;
+ case WD8003EB:
+ if (board_id & IFWD_INTERFACE_584_CHIP)
+ printf("WD8003EP");
+ else
+ printf("WD8003EB");
+ break;
+ case WD8003EW: printf("WD8003EW"); break;
+ case WD8003ETA: printf("WD8003ETA"); break;
+ case WD8003STA: printf("WD8003STA"); break;
+ case WD8003EA: printf("WD8003EA"); break;
+ case WD8003SHA: printf("WD8003SHA"); break;
+ case WD8003WA: printf("WD8003WA"); break;
+ case WD8013EBT: printf("WD8013EBT"); break;
+ case WD8013EB:
+ if (board_id & IFWD_INTERFACE_584_CHIP)
+ printf("WD8013EP");
+ else
+ printf("WD8013EB");
+ break;
+ case WD8013W: printf("WD8013W"); break;
+ case WD8013EW: printf("WD8013EW"); break;
+ default: printf("unknown"); break;
+ }
+ printf(" rev 0x%02x", rev_num);
+ switch(board_id & IFWD_RAM_SIZE_RES_7) {
+ case IFWD_RAM_SIZE_UNKNOWN:
+ break;
+ case IFWD_RAM_SIZE_8K:
+ printf(" 8 kB ram");
+ break;
+ case IFWD_RAM_SIZE_16K:
+ printf(" 16 kB ram");
+ break;
+ case IFWD_RAM_SIZE_32K:
+ printf(" 32 kB ram");
+ break;
+ case IFWD_RAM_SIZE_64K:
+ printf(" 64 kB ram");
+ break;
+ default:
+ printf("wd: Internal error ram size value invalid %d\n",
+ (board_id & IFWD_RAM_SIZE_RES_7)>>16);
+ }
+
+ if (board_id & IFWD_BOARD_16BIT) {
+ if (board_id & IFWD_SLOT_16BIT) {
+ printf(", in 16 bit slot");
+ } else {
+ printf(", 16 bit board in 8 bit slot");
+ }
+ }
+ if (board_id & IFWD_INTERFACE_CHIP) {
+ if (board_id & IFWD_INTERFACE_584_CHIP) {
+ printf(", 584 chip");
+ } else {
+ printf(", 583 chip");
+ }
+ }
+ if ((board_id & IFWD_INTERFACE_CHIP) == IFWD_INTERFACE_CHIP) {
+ /* program the WD83C583 EEPROM registers */
+ int irr_temp, icr_temp;
+
+ icr_temp = inb(hdwbase + IFWD_ICR);
+ irr_temp = inb(hdwbase + IFWD_IRR);
+
+ irr_temp &= ~(IFWD_IR0 | IFWD_IR1);
+ irr_temp |= IFWD_IEN;
+
+ icr_temp &= IFWD_WTS;
+
+ if (!(board_id & IFWD_INTERFACE_584_CHIP)) {
+ icr_temp |= IFWD_DMAE | IFWD_IOPE;
+ if (ram_flag)
+ icr_temp |= IFWD_MSZ;
+ }
+
+ if (board_id & IFWD_INTERFACE_584_CHIP) {
+ switch(ns8390info[unit]->sysdep1) {
+ case 10:
+ icr_temp |= IFWD_DMAE;
+ break;
+ case 2:
+ case 9: /* Same as 2 */
+ break;
+ case 11:
+ icr_temp |= IFWD_DMAE;
+ /*FALLTHROUGH*/
+ case 3:
+ irr_temp |= IFWD_IR0;
+ break;
+ case 15:
+ icr_temp |= IFWD_DMAE;
+ /*FALLTHROUGH*/
+ case 5:
+ irr_temp |= IFWD_IR1;
+ break;
+ case 4:
+ icr_temp |= IFWD_DMAE;
+ /*FALLTHROUGH*/
+ case 7:
+ irr_temp |= IFWD_IR0 | IFWD_IR1;
+ break;
+ default:
+ printf("%s%d: wd80xx_get_board_id(): Could not set Interrupt Request Register according to pic(%d).\n",
+ ns8390_softc[unit].card, unit,
+ ns8390info[unit]->sysdep1);
+ break;
+ }
+ } else {
+ switch(ns8390info[unit]->sysdep1) {
+ /* attempt to set interrupt according to assigned pic */
+ case 2:
+ case 9: /* Same as 2 */
+ break;
+ case 3:
+ irr_temp |= IFWD_IR0;
+ break;
+ case 4:
+ irr_temp |= IFWD_IR1;
+ break;
+ case 5:
+ irr_temp |= IFWD_IR1 | IFWD_AINT;
+ break;
+ case 7:
+ irr_temp |= IFWD_IR0 | IFWD_IR1;
+ break;
+ default:
+ printf("%s%d: wd80xx_get_board_id(): Could not set Interrupt Request Register according to pic(%d).\n",
+ ns8390_softc[unit].card, unit,
+ ns8390info[unit]->sysdep1);
+ }
+ }
+ outb(hdwbase + IFWD_IRR, irr_temp);
+ outb(hdwbase + IFWD_ICR, icr_temp);
+ }
+ printf("\n");
+ return (board_id);
+}
+
+wd80xxget_eeprom_info(hdwbase, board_id)
+ caddr_t hdwbase;
+ long board_id;
+{
+ unsigned long new_bits = 0;
+ int reg_temp;
+
+ outb(hdwbase + IFWD_REG_1,
+ ((inb(hdwbase + IFWD_REG_1) & IFWD_ICR_MASK) | IFWD_OTHER_BIT));
+ outb(hdwbase + IFWD_REG_3,
+ ((inb(hdwbase + IFWD_REG_3) & IFWD_EAR_MASK) | IFWD_ENGR_PAGE));
+ outb(hdwbase + IFWD_REG_1,
+ ((inb(hdwbase + IFWD_REG_1) & IFWD_ICR_MASK) |
+ (IFWD_RLA | IFWD_OTHER_BIT)));
+ while (inb(hdwbase + IFWD_REG_1) & IFWD_RECALL_DONE_MASK)
+ ;
+
+ reg_temp = inb(hdwbase + IFWD_EEPROM_1);
+ switch (reg_temp & IFWD_EEPROM_BUS_TYPE_MASK) {
+ case IFWD_EEPROM_BUS_TYPE_AT:
+ if (wd_debug & 1) printf("wd: AT bus, ");
+ break;
+ case IFWD_EEPROM_BUS_TYPE_MCA:
+ if (wd_debug & 1) printf("wd: MICROCHANNEL, ");
+ new_bits |= IFWD_MICROCHANNEL;
+ break;
+ default:
+ break;
+ }
+ switch (reg_temp & IFWD_EEPROM_BUS_SIZE_MASK) {
+ case IFWD_EEPROM_BUS_SIZE_8BIT:
+ if (wd_debug & 1) printf("8 bit bus size, ");
+ break;
+ case IFWD_EEPROM_BUS_SIZE_16BIT:
+ if (wd_debug & 1) printf("16 bit bus size ");
+ new_bits |= IFWD_BOARD_16BIT;
+ if (inb(hdwbase + IFWD_REG_1) & IFWD_16BIT) {
+ new_bits |= IFWD_SLOT_16BIT;
+ if (wd_debug & 1)
+ printf("in 16 bit slot, ");
+ } else {
+ if (wd_debug & 1)
+ printf("in 8 bit slot (why?), ");
+ }
+ break;
+ default:
+ if (wd_debug & 1) printf("bus size other than 8 or 16 bit, ");
+ break;
+ }
+ reg_temp = inb(hdwbase + IFWD_EEPROM_0);
+ switch (reg_temp & IFWD_EEPROM_MEDIA_MASK) {
+ case IFWD_STARLAN_TYPE:
+ if (wd_debug & 1) printf("Starlan media, ");
+ new_bits |= IFWD_STARLAN_MEDIA;
+ break;
+ case IFWD_TP_TYPE:
+ if (wd_debug & 1) printf("Twisted pair media, ");
+ new_bits |= IFWD_TWISTED_PAIR_MEDIA;
+ break;
+ case IFWD_EW_TYPE:
+ if (wd_debug & 1) printf("Ethernet and twisted pair media, ");
+ new_bits |= IFWD_EW_MEDIA;
+ break;
+ case IFWD_ETHERNET_TYPE: /*FALLTHROUGH*/
+ default:
+ if (wd_debug & 1) printf("ethernet media, ");
+ new_bits |= IFWD_ETHERNET_MEDIA;
+ break;
+ }
+ switch (reg_temp & IFWD_EEPROM_IRQ_MASK) {
+ case IFWD_ALTERNATE_IRQ_1:
+ if (wd_debug & 1) printf("Alternate irq 1\n");
+ new_bits |= IFWD_ALTERNATE_IRQ_BIT;
+ break;
+ default:
+ if (wd_debug & 1) printf("\n");
+ break;
+ }
+ switch (reg_temp & IFWD_EEPROM_RAM_SIZE_MASK) {
+ case IFWD_EEPROM_RAM_SIZE_8K:
+ new_bits |= IFWD_RAM_SIZE_8K;
+ break;
+ case IFWD_EEPROM_RAM_SIZE_16K:
+ if ((new_bits & IFWD_BOARD_16BIT) && (new_bits & IFWD_SLOT_16BIT))
+ new_bits |= IFWD_RAM_SIZE_16K;
+ else
+ new_bits |= IFWD_RAM_SIZE_8K;
+ break;
+ case IFWD_EEPROM_RAM_SIZE_32K:
+ new_bits |= IFWD_RAM_SIZE_32K;
+ break;
+ case IFWD_EEPROM_RAM_SIZE_64K:
+ if ((new_bits & IFWD_BOARD_16BIT) && (new_bits & IFWD_SLOT_16BIT))
+ new_bits |= IFWD_RAM_SIZE_64K;
+ else
+ new_bits |= IFWD_RAM_SIZE_32K;
+ break;
+ default:
+ new_bits |= IFWD_RAM_SIZE_UNKNOWN;
+ break;
+ }
+ outb(hdwbase + IFWD_REG_1,
+ ((inb(hdwbase + IFWD_REG_1) & IFWD_ICR_MASK) | IFWD_OTHER_BIT));
+ outb(hdwbase + IFWD_REG_3,
+ ((inb(hdwbase + IFWD_REG_3) & IFWD_EAR_MASK) | IFWD_EA6));
+ outb(hdwbase + IFWD_REG_1,
+ ((inb(hdwbase + IFWD_REG_1) & IFWD_ICR_MASK) | IFWD_RLA));
+ return (new_bits);
+}
+
+wdpr(unit)
+{
+ caddr_t nic = ns8390_softc[unit].nic;
+ spl_t s;
+ int temp_cr;
+
+ s = SPLNET();
+ temp_cr = inb(nic+ds_cmd); /* get current CR value */
+
+ printf("CR %x, BNDRY %x, TSR %x, NCR %x, FIFO %x, ISR %x, RSR %x\n",
+ inb(nic+0x0), inb(nic+0x3), inb(nic+0x4), inb(nic+0x5),
+ inb(nic+0x6), inb(nic+0x7), inb(nic+0xc));
+ printf("CLD %x:%x, CRD %x:%x, FR %x, CRC %x, Miss %x\n",
+ inb(nic+0x1), inb(nic+0x2),
+ inb(nic+0x8), inb(nic+0x9),
+ inb(nic+0xd), inb(nic+0xe), inb(nic+0xf));
+
+
+ outb(nic, (temp_cr&0x3f)|DSCM_PG1); /* page 1 CR value */
+ printf("PHYS %x:%x:%x:%x:%x CUR %x\n",
+ inb(nic+0x1), inb(nic+0x2), inb(nic+0x3),
+ inb(nic+0x4), inb(nic+0x5), inb(nic+0x6),
+ inb(nic+0x7));
+ printf("MAR %x:%x:%x:%x:%x:%x:%x:%x\n",
+ inb(nic+0x8), inb(nic+0x9), inb(nic+0xa), inb(nic+0xb),
+ inb(nic+0xc), inb(nic+0xd), inb(nic+0xe), inb(nic+0xf));
+ outb(nic, temp_cr); /* restore current CR value */
+ splx(s);
+}
+
+
+/*
+ This sets bit 7 (0 justified) of register offset 0x05. It will enable
+ the host to access shared RAM 16 bits at a time. It will also maintain
+ the LAN16BIT bit high in addition, this routine maintains address bit 19
+ (previous cards assumed this bit high...we must do it manually)
+
+ note 1: this is a write only register
+ note 2: this routine should be called only after interrupts are disabled
+ and they should remain disabled until after the routine 'dis_16bit_access'
+ is called
+*/
+
+en_16bit_access (hdwbase, board_id)
+ caddr_t hdwbase;
+ long board_id;
+{
+ if (board_id & IFWD_INTERFACE_CHIP)
+ outb(hdwbase+IFWD_REG_5,
+ (inb(hdwbase+IFWD_REG_5) & IFWD_REG5_MEM_MASK)
+ | IFWD_MEM16ENB | IFWD_LAN16ENB);
+ else
+ outb(hdwbase+IFWD_REG_5, (IFWD_MEM16ENB | IFWD_LAN16ENB |
+ IFWD_LA19));
+}
+
+/*
+ This resets bit 7 (0 justified) of register offset 0x05. It will disable
+ the host from accessing shared RAM 16 bits at a time. It will maintain the
+ LAN16BIT bit high in addition, this routine maintains address bit 19
+ (previous cards assumed this bit high...we must do it manually)
+
+ note: this is a write only register
+*/
+
+dis_16bit_access (hdwbase, board_id)
+ caddr_t hdwbase;
+ long board_id;
+{
+ if (board_id & IFWD_INTERFACE_CHIP)
+ outb(hdwbase+IFWD_REG_5,
+ ((inb(hdwbase+IFWD_REG_5) & IFWD_REG5_MEM_MASK) |
+ IFWD_LAN16ENB));
+ else
+ outb(hdwbase+IFWD_REG_5, (IFWD_LAN16ENB | IFWD_LA19));
+}
+
+#endif