diff options
Diffstat (limited to 'i386/i386at/if_ns8390.c')
-rw-r--r-- | i386/i386at/if_ns8390.c | 2578 |
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 00000000..15c94400 --- /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 |