aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorPaul Richards <paul@FreeBSD.org>1994-10-02 21:14:38 +0000
committerPaul Richards <paul@FreeBSD.org>1994-10-02 21:14:38 +0000
commita57c5abbd5ce0b2c33327d3dad742cf321b85522 (patch)
treea76a6eab36ac35544ab6c9491e614f02f33c4f03 /sys
parentce1e23b99c259970124eed83239c2fd81350a15b (diff)
Notes
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/lnc/if_lnc.c1659
-rw-r--r--sys/dev/lnc/if_lncvar.h178
-rw-r--r--sys/i386/isa/if_lnc.c1659
-rw-r--r--sys/i386/isa/if_lnc.h178
-rw-r--r--sys/i386/isa/npx.c.orig555
5 files changed, 4229 insertions, 0 deletions
diff --git a/sys/dev/lnc/if_lnc.c b/sys/dev/lnc/if_lnc.c
new file mode 100644
index 000000000000..89cec80d0680
--- /dev/null
+++ b/sys/dev/lnc/if_lnc.c
@@ -0,0 +1,1659 @@
+/*
+ * Copyright (c) 1994, Paul Richards.
+ *
+ * All rights reserved.
+ *
+ * This software may be used, modified, copied, distributed, and
+ * sold, in both source and binary form provided that the above
+ * copyright and these terms are retained, verbatim, as the first
+ * lines of this file. Under no circumstances is the author
+ * responsible for the proper functioning of this software, nor does
+ * the author assume any responsibility for damages incurred with
+ * its use.
+ */
+
+/*
+#define DIAGNOSTIC
+#define DEBUG
+ *
+ * TODO ----
+ *
+ * This driver will need bounce buffer support when dma'ing to mbufs above the
+ * 16Mb mark.
+ *
+ * Check all the XXX comments -- some of them are just things I've left
+ * unfinished rather than "difficult" problems that were hacked around.
+ *
+ * Check log settings.
+ *
+ * Check how all the arpcom flags get set and used.
+ *
+ * Re-inline and re-static all routines after debugging.
+ *
+ * Remember to assign iobase in SHMEM probe routines.
+ *
+ * Replace all occurences of LANCE-controller-card etc in prints by the name
+ * strings of the appropriate type -- nifty window dressing
+ *
+ * Add DEPCA support -- mostly done.
+ *
+ */
+
+#include "lnc.h"
+#if NLNC > 0
+
+#include "bpfilter.h"
+
+/* Some defines that should really be in generic locations */
+#define FCS_LEN 4
+#define ETHER_ADDR_LEN 6
+#define ETHER_HDR_LEN 14
+#define MULTICAST_ADDR_LEN 8
+#define ETHER_MIN_LEN 64
+
+#include "param.h"
+#include "systm.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "syslog.h"
+#include "net/if.h"
+#include "net/if_dl.h"
+#include "net/if_types.h"
+#ifdef INET
+#include "netinet/in.h"
+#include "netinet/in_systm.h"
+#include "netinet/in_var.h"
+#include "netinet/ip.h"
+#include "netinet/if_ether.h"
+#endif
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#include "i386/isa/isa_device.h"
+#include "i386/isa/if_lnc.h"
+
+struct lnc_softc {
+ struct arpcom arpcom; /* see ../../netinet/if_ether.h */
+ struct nic_info nic; /* NIC specific info */
+ int nrdre;
+ struct host_ring_entry *recv_ring; /* start of alloc'd mem */
+ int recv_next;
+ int ntdre;
+ struct host_ring_entry *trans_ring;
+ int trans_next;
+ struct init_block *init_block; /* Initialisation block */
+ int pending_transmits; /* No. of transmit descriptors in use */
+ int next_to_send;
+ struct mbuf *mbufs;
+ int mbuf_count;
+ int initialised;
+ int rap;
+ int rdp;
+#ifdef DEBUG
+ int lnc_debug;
+#endif
+#if NBPFILTER > 0
+ caddr_t bpf; /* XXX bpf magic cookie - move to arpcom */
+#endif
+ LNCSTATS_STRUCT
+} lnc_softc[NLNC];
+
+/* Function prototypes */
+void lnc_init(int);
+void lnc_start(struct ifnet *);
+int lnc_ioctl(struct ifnet *, int, caddr_t);
+void lnc_watchdog(int);
+int lnc_probe(struct isa_device *);
+int lnc_attach(struct isa_device *);
+void lnc_dump_state(int);
+
+struct isa_driver lncdriver = {lnc_probe, lnc_attach, "lnc"};
+
+inline void
+write_csr(int unit, u_short port, u_short val)
+{
+ outw(lnc_softc[unit].rap, port);
+ outw(lnc_softc[unit].rdp, val);
+}
+
+inline u_short
+read_csr(int unit, u_short port)
+{
+ outw(lnc_softc[unit].rap, port);
+ return (inw(lnc_softc[unit].rdp));
+}
+
+
+void
+lnc_setladrf(struct ifnet *ifp, struct lnc_softc *sc)
+{
+
+}
+
+void
+lnc_stop(int unit)
+{
+ write_csr(unit, CSR0, STOP);
+}
+
+void
+lnc_reset(int unit)
+{
+ int s;
+
+ lnc_init(unit);
+}
+
+void
+lnc_free_mbufs(struct lnc_softc *sc)
+{
+ int i;
+
+ /*
+ * We rely on other routines to keep the buff.mbuf field valid. If
+ * it's not NULL then we assume it points to an allocated mbuf.
+ */
+
+ for (i = 0; i < NDESC(sc->nrdre); i++)
+ if ((sc->recv_ring + i)->buff.mbuf)
+ m_free((sc->recv_ring + i)->buff.mbuf);
+
+ for (i = 0; i < NDESC(sc->ntdre); i++)
+ if ((sc->trans_ring + i)->buff.mbuf)
+ m_free((sc->trans_ring + i)->buff.mbuf);
+
+ if (sc->mbuf_count)
+ m_freem(sc->mbufs);
+}
+
+inline int
+alloc_mbuf_cluster(struct lnc_softc *sc, struct host_ring_entry *desc)
+{
+ register struct mds *md = desc->md;
+ struct mbuf *m=0;
+ int addr;
+
+ /* Try and get cluster off local cache */
+ if (sc->mbuf_count) {
+ sc->mbuf_count--;
+ m = sc->mbufs;
+ sc->mbufs = m->m_next;
+ /* XXX m->m_data = m->m_ext.ext_buf;*/
+ } else {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (!m)
+ return(1);
+ MCLGET(m, M_DONTWAIT);
+ if (!m->m_ext.ext_buf) {
+ m_free(m);
+ return(1);
+ }
+ }
+
+ desc->buff.mbuf = m;
+ addr = kvtop(m->m_data);
+ md->md0 = addr;
+ md->md1= ((addr >> 16) & 0xff) | OWN;
+ md->md2 = -(short)(MCLBYTES - sizeof(struct pkthdr));
+ md->md3 = 0;
+ return(0);
+}
+
+inline struct mbuf *
+chain_mbufs(struct lnc_softc *sc, int start_of_packet, int pkt_len)
+{
+ struct mbuf *head, *m;
+ struct host_ring_entry *desc;
+
+ /*
+ * Turn head into a pkthdr mbuf --
+ * assumes a pkthdr type mbuf was
+ * allocated to the descriptor
+ * originally.
+ */
+
+ desc = sc->recv_ring + start_of_packet;
+
+ head = desc->buff.mbuf;
+ head->m_flags |= M_PKTHDR;
+
+ m = head;
+ do {
+ m = desc->buff.mbuf;
+ m->m_len = min(-(desc->md->md2), pkt_len);
+ pkt_len -= m->m_len;
+ if (alloc_mbuf_cluster(sc, desc))
+ return((struct mbuf *)NULL);
+ INC_MD_PTR(start_of_packet, sc->nrdre)
+ desc = sc->recv_ring + start_of_packet;
+ m->m_next = desc->buff.mbuf;
+ } while (start_of_packet != sc->recv_next);
+
+ m->m_next = 0;
+ return(head);
+}
+
+inline struct mbuf *
+mbuf_packet(struct lnc_softc *sc, int start_of_packet, int pkt_len)
+{
+
+ struct host_ring_entry *start;
+ struct mbuf *head,*m,*m_prev;
+ char *data,*mbuf_data;
+ short blen;
+ int amount;
+
+ /* Get a pkthdr mbuf for the start of packet */
+ MGETHDR(head, M_DONTWAIT, MT_DATA);
+ if (!head) {
+ LNCSTATS(drop_packet)
+ return(0);
+ }
+
+ m = head;
+ m->m_len = 0;
+ start = sc->recv_ring + start_of_packet;
+ blen = -(start->md->md2);
+ data = start->buff.data;
+ mbuf_data = m->m_data;
+
+ while (start_of_packet != sc->recv_next) {
+ /*
+ * If the data left fits in a single buffer then set
+ * blen to the size of the data left.
+ */
+ if (pkt_len < blen)
+ blen = pkt_len;
+
+ /*
+ * amount is least of data in current ring buffer and
+ * amount of space left in current mbuf.
+ */
+ amount = min(blen, M_TRAILINGSPACE(m));
+ if (amount == 0) {
+ /* mbuf must be empty */
+ m_prev = m;
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (!m) {
+ m_freem(head);
+ return(0);
+ }
+ if (pkt_len >= MINCLSIZE)
+ MCLGET(m, M_DONTWAIT);
+ m->m_len = 0;
+ m_prev->m_next = m;
+ amount = min(blen, M_TRAILINGSPACE(m));
+ mbuf_data = m->m_data;
+ }
+ bcopy(data, mbuf_data, amount);
+ blen -= amount;
+ pkt_len -= amount;
+ m->m_len += amount;
+ data += amount;
+ mbuf_data += amount;
+
+ if (blen == 0) {
+ start->md->md1 &= HADR;
+ start->md->md1 |= OWN;
+ INC_MD_PTR(start_of_packet, sc->nrdre)
+ start = sc->recv_ring + start_of_packet;
+ data = start->buff.data;
+ blen = -(start->md->md2);
+ }
+ }
+ return(head);
+}
+
+
+inline void
+lnc_rint(int unit)
+{
+ register struct lnc_softc *sc = &lnc_softc[unit];
+ struct host_ring_entry *next, *start;
+ int start_of_packet;
+ struct mbuf *head;
+ struct ether_header *eh;
+ int lookahead;
+ int flags;
+ int pkt_len;
+
+ /*
+ * The LANCE will issue a RINT interrupt when the ownership of the
+ * last buffer of a receive packet has been relinquished by the LANCE.
+ * Therefore, it can be assumed that a complete packet can be found
+ * before hitting buffers that are still owned by the LANCE, if not
+ * then there is a bug in the driver that is causing the descriptors
+ * to get out of sync.
+ */
+
+#ifdef DIAGNOSTIC
+ if ((sc->recv_ring + sc->recv_next)->md->md1 & OWN) {
+ log(LOG_ERR, "lnc%d: Receive interrupt with buffer still owned by controller -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ if (!((sc->recv_ring + sc->recv_next)->md->md1 & STP)) {
+ log(LOG_ERR, "lnc%d: Receive interrupt but not start of packet -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+#endif
+
+ lookahead = 0;
+ next = sc->recv_ring + sc->recv_next;
+ while ((flags = next->md->md1) & STP) {
+
+ /* Make a note of the start of the packet */
+ start_of_packet = sc->recv_next;
+
+ /*
+ * Find the end of the packet. Even if not data chaining,
+ * jabber packets can overrun into a second descriptor.
+ * If there is no error, then the ENP flag is set in the last
+ * descriptor of the packet. If there is an error then the ERR
+ * flag will be set in the descriptor where the error occured.
+ * Therefore, to find the last buffer of a packet we search for
+ * either ERR or ENP.
+ */
+
+ if (!(flags & (ENP | MDERR))) {
+ do {
+ INC_MD_PTR(sc->recv_next, sc->nrdre)
+ next = sc->recv_ring + sc->recv_next;
+ flags = next->md->md1;
+ } while (!(flags & (STP | OWN | ENP | MDERR)));
+
+ if (flags & STP) {
+ log(LOG_ERR, "lnc%d: Start of packet found before end of previous in receive ring -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ if (flags & OWN) {
+ if (lookahead) {
+ /*
+ * Looked ahead into a packet still
+ * being received
+ */
+ sc->recv_next = start_of_packet;
+ break;
+ } else {
+ log(LOG_ERR, "lnc%d: End of received packet not found-- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ }
+ }
+
+ pkt_len = next->md->md3 & MCNT - FCS_LEN;
+
+ /* Move pointer onto start of next packet */
+ INC_MD_PTR(sc->recv_next, sc->nrdre)
+ next = sc->recv_ring + sc->recv_next;
+
+ if (flags & MDERR) {
+ if (flags & RBUFF) {
+ LNCSTATS(rbuff)
+ log(LOG_ERR, "lnc%d: Receive buffer error\n", unit);
+ }
+ if (flags & OFLO) {
+ /* OFLO only valid if ENP is not set */
+ if (!(flags & ENP)) {
+ LNCSTATS(oflo)
+ log(LOG_ERR, "lnc%d: Receive overflow error \n", unit);
+ }
+ } else {
+ /*
+ * FRAM and CRC are valid only if OFLO is not
+ * set and ENP is
+ */
+ if (flags & (FRAM | ENP)) {
+ LNCSTATS(fram)
+ log(LOG_ERR, "lnc%d: Framming error\n", unit);
+ /*
+ * FRAM is only set if there's a CRC
+ * error so avoid multiple messages
+ */
+ } else if (flags & (CRC | ENP)) {
+ LNCSTATS(crc)
+ log(LOG_ERR, "lnc%d: Receive CRC error\n", unit);
+ }
+ }
+
+ /* Drop packet */
+ LNCSTATS(rerr)
+ sc->arpcom.ac_if.if_ierrors++;
+ while (start_of_packet != sc->recv_next) {
+ start = sc->recv_ring + start_of_packet;
+ start->md->md1 &= HADR;
+ start->md->md1 |= OWN;
+ INC_MD_PTR(start_of_packet, sc->nrdre)
+ }
+ } else { /* Valid packet */
+
+ sc->arpcom.ac_if.if_ipackets++;
+
+
+ if (sc->nic.mem_mode == DMA_MBUF)
+ head = chain_mbufs(sc,start_of_packet,pkt_len);
+ else
+ head = mbuf_packet(sc,start_of_packet,pkt_len);
+
+ if (head) {
+ /*
+ * First mbuf in packet holds the
+ * ethernet and packet headers
+ */
+ head->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ head->m_pkthdr.len = pkt_len - sizeof(*eh);
+ eh = (struct ether_header *) head->m_data;
+ eh->ether_type = ntohs(eh->ether_type);
+ head->m_data += sizeof *eh;
+ head->m_len -= sizeof *eh;
+
+#if NBPFILTER > 0
+ if (sc->bpf)
+ bpf_mtap(sc->bpf, head);
+
+ /* Check this packet is really for us */
+ /* XXX -- this doesn't look right */
+
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0) &&
+ (bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0))
+ m_freem(head);
+ else
+#endif
+ ether_input(&sc->arpcom.ac_if, eh, head);
+
+ } else {
+ log(LOG_ERR,"lnc%d: Packet dropped, no mbufs\n",unit);
+ LNCSTATS(drop_packet)
+ }
+ }
+
+ lookahead++;
+ }
+
+ /*
+ * At this point all completely received packets have been processed
+ * so clear RINT since any packets that have arrived while we were in
+ * here have been dealt with.
+ */
+
+ outw(sc->rdp, RINT | INEA);
+}
+
+inline void
+lnc_tint(int unit)
+{
+ register struct lnc_softc *sc = &lnc_softc[unit];
+ struct host_ring_entry *next, *start;
+ int start_of_packet;
+ int lookahead;
+
+ /*
+ * If the driver is reset in this routine then we return immediately to
+ * the interrupt driver routine. Any interrupts that have occured
+ * since the reset will be dealt with there. sc->trans_next
+ * should point to the start of the first packet that was awaiting
+ * transmission after the last transmit interrupt was dealt with. The
+ * LANCE should have relinquished ownership of that descriptor before
+ * the interrupt. Therefore, sc->trans_next should point to a
+ * descriptor with STP set and OWN cleared. If not then the driver's
+ * pointers are out of sync with the LANCE, which signifies a bug in
+ * the driver. Therefore, the following two checks are really
+ * diagnostic, since if the driver is working correctly they should
+ * never happen.
+ */
+
+#ifdef DIAGNOSTIC
+ if ((sc->trans_ring + sc->trans_next)->md->md1 & OWN) {
+ log(LOG_ERR, "lnc%d: Transmit interrupt with buffer still owned by controller -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+#endif
+
+
+ /*
+ * The LANCE will write the status information for the packet it just
+ * tried to transmit in one of two places. If the packet was
+ * transmitted successfully then the status will be written into the
+ * last descriptor of the packet. If the transmit failed then the
+ * status will be written into the descriptor that was being accessed
+ * when the error occured and all subsequent descriptors in that
+ * packet will have been relinquished by the LANCE.
+ *
+ * At this point we know that sc->trans_next points to the start
+ * of a packet that the LANCE has just finished trying to transmit.
+ * We now search for a buffer with either ENP or ERR set.
+ */
+
+ lookahead = 0;
+
+ do {
+ start_of_packet = sc->trans_next;
+ next = sc->trans_ring + sc->trans_next;
+
+#ifdef DIAGNOSTIC
+ if (!(next->md->md1 & STP)) {
+ log(LOG_ERR, "lnc%d: Transmit interrupt but not start of packet -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+#endif
+
+ /*
+ * Find end of packet.
+ */
+
+ if (!(next->md->md1 & (ENP | MDERR))) {
+ do {
+ INC_MD_PTR(sc->trans_next, sc->ntdre)
+ next = sc->trans_ring + sc->trans_next;
+ } while (!(next->md->md1 & (STP | OWN | ENP | MDERR)));
+
+ if (next->md->md1 & STP) {
+ log(LOG_ERR, "lnc%d: Start of packet found before end of previous in transmit ring -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ if (next->md->md1 & OWN) {
+ if (lookahead) {
+ /*
+ * Looked ahead into a packet still
+ * being transmitted
+ */
+ sc->trans_next = start_of_packet;
+ break;
+ } else {
+ log(LOG_ERR, "lnc%d: End of transmitted packet not found -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ }
+ }
+ /*
+ * Check for ERR first since other flags are irrelevant if an
+ * error occurred.
+ */
+ if (next->md->md1 & MDERR) {
+
+ LNCSTATS(terr)
+ sc->arpcom.ac_if.if_oerrors++;
+
+ if (next->md->md3 & LCOL) {
+ LNCSTATS(lcol)
+ log(LOG_ERR, "lnc%d: Transmit late collision -- Net error?\n", unit);
+ sc->arpcom.ac_if.if_collisions++;
+ /*
+ * Clear TBUFF since it's not valid when LCOL
+ * set
+ */
+ next->md->md3 &= ~TBUFF;
+ }
+ if (next->md->md3 & LCAR) {
+ LNCSTATS(lcar)
+ log(LOG_ERR, "lnc%d: Loss of carrier during transmit -- Net error?\n", unit);
+ }
+ if (next->md->md3 & RTRY) {
+ LNCSTATS(rtry)
+ log(LOG_ERR, "lnc%d: Transmit of packet failed after 16 attempts -- TDR = %d\n", unit, ((sc->trans_ring + sc->trans_next)->md->md3 & TDR));
+ sc->arpcom.ac_if.if_collisions += 16;
+ /*
+ * Clear TBUFF since it's not valid when RTRY
+ * set
+ */
+ next->md->md3 &= ~TBUFF;
+ }
+ /*
+ * TBUFF is only valid if neither LCOL nor RTRY are set.
+ * We need to check UFLO after LCOL and RTRY so that we
+ * know whether or not TBUFF is valid. If either are
+ * set then TBUFF will have been cleared above. A
+ * UFLO error will turn off the transmitter so we
+ * have to reset.
+ *
+ */
+
+ if (next->md->md3 & UFLO) {
+ LNCSTATS(uflo)
+ /*
+ * If an UFLO has occured it's possibly due
+ * to a TBUFF error
+ */
+ if (next->md->md3 & TBUFF) {
+ LNCSTATS(tbuff)
+ log(LOG_ERR, "lnc%d: Transmit buffer error -- Resetting\n", unit);
+ } else
+ log(LOG_ERR, "lnc%d: Transmit underflow error -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ do {
+ INC_MD_PTR(sc->trans_next, sc->ntdre)
+ next = sc->trans_ring + sc->trans_next;
+ } while (!(next->md->md1 & STP) && (sc->trans_next != sc->next_to_send));
+
+ } else {
+ /*
+ * Since we check for ERR first then if we get here
+ * the packet was transmitted correctly. There may
+ * still have been non-fatal errors though.
+ * Don't bother checking for DEF, waste of time.
+ */
+
+ sc->arpcom.ac_if.if_opackets++;
+
+ if (next->md->md1 & MORE) {
+ LNCSTATS(more)
+ sc->arpcom.ac_if.if_collisions += 2;
+ }
+
+ /*
+ * ONE is invalid if LCOL is set. If LCOL was set then
+ * ERR would have also been set and we would have
+ * returned from lnc_tint above. Therefore we can
+ * assume if we arrive here that ONE is valid.
+ *
+ */
+
+ if (next->md->md1 & ONE) {
+ LNCSTATS(one)
+ sc->arpcom.ac_if.if_collisions++;
+ }
+ INC_MD_PTR(sc->trans_next, sc->ntdre)
+ next = sc->trans_ring + sc->trans_next;
+ }
+
+ /*
+ * Clear descriptors and free any mbufs.
+ */
+
+ do {
+ start = sc->trans_ring + start_of_packet;
+ start->md->md1 &= HADR;
+ if (sc->nic.mem_mode == DMA_MBUF) {
+ /* Cache clusters on a local queue */
+ if ((start->buff.mbuf->m_flags & M_EXT) && (sc->mbuf_count < MBUF_CACHE_LIMIT)) {
+ if (sc->mbuf_count) {
+ start->buff.mbuf->m_next = sc->mbufs;
+ sc->mbufs = start->buff.mbuf;
+ } else
+ sc->mbufs = start->buff.mbuf;
+ sc->mbuf_count++;
+ start->buff.mbuf = 0;
+ } else {
+ struct mbuf *junk;
+ MFREE(start->buff.mbuf, junk);
+ start->buff.mbuf = 0;
+ }
+ }
+ sc->pending_transmits--;
+ INC_MD_PTR(start_of_packet, sc->ntdre)
+ }while (start_of_packet != sc->trans_next);
+
+ /*
+ * There's now at least one free descriptor
+ * in the ring so indicate that we can accept
+ * more packets again.
+ */
+
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ lookahead++;
+
+ } while (sc->pending_transmits && !(next->md->md1 & OWN));
+
+ /*
+ * Clear TINT since we've dealt with all
+ * the completed transmissions.
+ */
+
+ outw(sc->rdp, TINT | INEA);
+
+ /* XXX only while doing if_is comparisons */
+ if (!(sc->arpcom.ac_if.if_flags & IFF_OACTIVE))
+ lnc_start(&sc->arpcom.ac_if);
+
+}
+
+int
+lnc_probe(struct isa_device * isa_dev)
+{
+int nports;
+int vsw;
+
+#ifdef DEBUG
+{
+ int i;
+ log(LOG_DEBUG, "Dumping io space for lnc%d starting at %x\n", isa_dev->id_unit, isa_dev->id_iobase);
+ for (i = 0; i < 32; i++)
+ log(LOG_DEBUG, " %x ", inb(isa_dev->id_iobase + i));
+}
+#endif
+
+#ifdef DIAGNOSTIC
+ /* First check the Vendor Specific Word */
+ vsw = inw(isa_dev->id_iobase + PCNET_VSW);
+ printf("Vendor Specific Word = %x\n", vsw);
+#endif
+
+ if (nports = bicc_probe(isa_dev))
+ return (nports);
+ if (nports = ne2100_probe(isa_dev))
+ return (nports);
+ if (nports = depca_probe(isa_dev))
+ return (nports);
+
+ return (0);
+}
+
+int
+ne2100_probe(struct isa_device * isa_dev)
+{
+ struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit];
+ int i;
+
+ sc->rap = isa_dev->id_iobase + PCNET_RAP;
+ sc->rdp = isa_dev->id_iobase + PCNET_RDP;
+
+ if (sc->nic.ic = lance_probe(isa_dev->id_unit)) {
+ sc->nic.ident = NE2100;
+ sc->nic.mem_mode = DMA_FIXED;
+
+ /* Extract MAC address from PROM */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ sc->arpcom.ac_enaddr[i] = inb(isa_dev->id_iobase + i);
+
+ return (NE2100_IOSIZE);
+
+ } else {
+ return (0);
+ }
+}
+
+int
+bicc_probe(struct isa_device * isa_dev)
+{
+struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit];
+int i;
+
+ /*
+ * There isn't any way to determine if a NIC is a BICC. Basically, if
+ * the lance probe succeeds using the i/o addresses of the BICC then
+ * we assume it's a BICC.
+ *
+ */
+
+ sc->rap = isa_dev->id_iobase + BICC_RAP;
+ sc->rdp = isa_dev->id_iobase + BICC_RDP;
+
+ /* I think all these cards us the Am7990 */
+
+ if (sc->nic.ic = lance_probe(isa_dev->id_unit)) {
+ sc->nic.ident = BICC;
+ sc->nic.mem_mode = DMA_FIXED;
+
+ /* XXX - For now just use the defines */
+ sc->nrdre = NRDRE;
+ sc->ntdre = NTDRE;
+
+ /* Extract MAC address from PROM */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ sc->arpcom.ac_enaddr[i] = inb(isa_dev->id_iobase + (i * 2));
+ return (BICC_IOSIZE);
+ } else {
+ return (0);
+ }
+}
+
+/*
+ * I don't have data sheets for the dec cards but it looks like the mac
+ * address is contained in a 32 byte ring. Each time you read from the port
+ * you get the next byte in the ring. The mac address is stored after a
+ * signature so keep searching for the signature first.
+ */
+int
+dec_macaddr_extract(u_char ring[], struct lnc_softc * sc)
+{
+ const unsigned char signature[] = {0xff, 0x00, 0x55, 0xaa, 0xff, 0x00, 0x55, 0xaa};
+
+ int i, j, rindex;
+
+ for (i = 0; i < sizeof ring; i++) {
+ for (j = 0, rindex = i; j < sizeof signature; j++) {
+ if (ring[rindex] != signature[j])
+ break;
+ if (++rindex > sizeof ring)
+ rindex = 0;
+ }
+ if (j == sizeof signature) {
+ for (j = 0, rindex = i; j < ETHER_ADDR_LEN; j++) {
+ sc->arpcom.ac_enaddr[j] = ring[rindex];
+ if (++rindex > sizeof ring)
+ rindex = 0;
+ }
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int
+depca_probe(struct isa_device * isa_dev)
+{
+ int i;
+ struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit];
+ unsigned char maddr_ring[DEPCA_ADDR_ROM_SIZE];
+
+ sc->rap = isa_dev->id_iobase + DEPCA_RAP;
+ sc->rdp = isa_dev->id_iobase + DEPCA_RDP;
+
+ if (sc->nic.ic = lance_probe(isa_dev->id_unit)) {
+ sc->nic.ident = DEPCA;
+ sc->nic.mem_mode = SHMEM;
+
+ /* Extract MAC address from PROM */
+ for (i = 0; i < DEPCA_ADDR_ROM_SIZE; i++)
+ maddr_ring[i] = inb(isa_dev->id_iobase + DEPCA_ADP);
+ if (dec_macaddr_extract(maddr_ring, sc))
+ return (DEPCA_IOSIZE);
+ }
+ return (0);
+}
+
+int
+lance_probe(int unit)
+{
+ write_csr(unit, CSR0, STOP);
+
+ if ((inw(lnc_softc[unit].rdp) & STOP) && !(read_csr(unit, CSR3))) {
+ /*
+ * Check to see if it's a C-LANCE. For the LANCE the INEA bit
+ * cannot be set while the STOP bit is. This restriction is
+ * removed for the C-LANCE.
+ */
+ write_csr(unit, CSR0, INEA);
+ if (read_csr(unit, CSR0) & INEA)
+ return (C_LANCE);
+ else
+ return (LANCE);
+ } else
+ return (UNKNOWN);
+}
+
+int
+pcnet_probe(int unit)
+{
+ u_long chip_id;
+ int type;
+
+ /*
+ * The PCnet family don't reset the RAP register on reset so we'll
+ * have to write during the probe :-) It does have an ID register
+ * though so the probe is just a matter of reading it.
+ */
+
+ if (type = lance_probe(unit)) {
+
+ chip_id = read_csr(unit, CSR88);
+ chip_id <<= 16;
+ chip_id |= read_csr(unit, CSR89);
+
+ if (chip_id & AMD_MASK) {
+ chip_id >>= 12;
+ switch (chip_id & PART_MASK) {
+ case Am79C960:
+ return (PCnet_ISA);
+ case Am79C961:
+ return (PCnet_ISAplus);
+ case Am79C965:
+ return (PCnet_32);
+ case Am79C970:
+ return (PCnet_PCI);
+ default:
+ break;
+ }
+ }
+ }
+ return (type);
+}
+
+int
+lnc_attach(struct isa_device * isa_dev)
+{
+ struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit];
+ int lnc_mem_size;
+
+ /*
+ * Allocate memory for use by the controller.
+ *
+ * XXX -- the Am7990 and Am79C960 only have 24 address lines and so can
+ * only access the lower 16Mb of physical memory. For the moment we
+ * assume that malloc will allocate memory within the lower 16Mb
+ * range. This is not a very valid assumption but there's nothing
+ * that can be done about it yet. For shared memory NICs this isn't
+ * relevant.
+ *
+ */
+
+ lnc_mem_size = ((NDESC(sc->nrdre) + NDESC(sc->ntdre)) *
+ sizeof(struct host_ring_entry));
+
+ if (sc->nic.mem_mode != SHMEM)
+ lnc_mem_size += sizeof(struct init_block) + (sizeof(struct mds) *
+ (NDESC(sc->nrdre) + NDESC(sc->ntdre))) +
+ MEM_SLEW;
+
+ /* If using DMA to fixed host buffers then allocate memory for them */
+
+ if (sc->nic.mem_mode == DMA_FIXED)
+ lnc_mem_size += (NDESC(sc->nrdre) * RECVBUFSIZE) + (NDESC(sc->ntdre) * TRANSBUFSIZE);
+
+ sc->recv_ring = malloc(lnc_mem_size, M_DEVBUF, M_NOWAIT);
+
+ if (!sc->recv_ring) {
+ log(LOG_ERR, "lnc%d: Couldn't allocate memory for NIC\n", isa_dev->id_unit);
+ return (0); /* XXX -- attach failed -- not tested in
+ * calling routines */
+ }
+ if ((sc->nic.mem_mode != SHMEM) && (kvtop(sc->recv_ring) > 0x1000000)) {
+ log(LOG_ERR, "lnc%d: Memory allocated above 16Mb limit\n", isa_dev->id_unit);
+ return (0);
+ }
+ if (sc->nic.mem_mode != SHMEM)
+ isa_dmacascade(isa_dev->id_drq);
+
+ /* Set default mode */
+ sc->nic.mode = NORMAL;
+
+ /* Fill in arpcom structure entries */
+
+ sc->arpcom.ac_if.if_name = lncdriver.name;
+ sc->arpcom.ac_if.if_unit = isa_dev->id_unit;
+ sc->arpcom.ac_if.if_mtu = ETHERMTU;
+ sc->arpcom.ac_if.if_flags = IFF_BROADCAST | IFF_NOTRAILERS | IFF_SIMPLEX;
+ sc->arpcom.ac_if.if_timer = 0;
+ sc->arpcom.ac_if.if_init = lnc_init;
+ sc->arpcom.ac_if.if_output = ether_output;
+ sc->arpcom.ac_if.if_start = lnc_start;
+ sc->arpcom.ac_if.if_ioctl = lnc_ioctl;
+ sc->arpcom.ac_if.if_reset = lnc_reset;
+ sc->arpcom.ac_if.if_watchdog = lnc_watchdog;
+ sc->arpcom.ac_if.if_type = IFT_ETHER;
+ sc->arpcom.ac_if.if_addrlen = ETHER_ADDR_LEN;
+ sc->arpcom.ac_if.if_hdrlen = ETHER_HDR_LEN;
+
+ /*
+ * XXX -- should check return status of if_attach
+ */
+
+ if_attach(&sc->arpcom.ac_if);
+
+ printf("lnc%d: %s, %s address %s\n",
+ isa_dev->id_unit,
+ nic_ident[sc->nic.ident],
+ ic_ident[sc->nic.ic],
+ ether_sprintf(sc->arpcom.ac_enaddr));
+
+#if NBPFILTER > 0
+ bpfattach(&sc->bpf, &sc->arpcom.ac_if, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+
+ return (1);
+}
+
+void
+lnc_init(int unit)
+{
+ struct lnc_softc *sc = &lnc_softc[unit];
+ int s, i;
+ char *lnc_mem;
+ struct mbuf *m = 0;
+
+ /* Check that interface has valid address */
+
+ if (!sc->arpcom.ac_if.if_addrlist)
+ return;
+
+ /* Shut down interface */
+
+ s = splimp();
+ lnc_stop(unit);
+ sc->arpcom.ac_if.if_flags |= IFF_BROADCAST | IFF_NOTRAILERS | IFF_SIMPLEX;
+
+ /*
+ * This sets up the memory area for the controller. Memory is set up for
+ * the initialisation block (12 words of contiguous memory starting
+ * on a word boundary),the transmit and receive ring structures (each
+ * entry is 4 words long and must start on a quadword boundary) and
+ * the data buffers.
+ *
+ * The alignment tests are particularly paranoid.
+ */
+
+
+
+ sc->recv_next = 0;
+ sc->trans_ring = sc->recv_ring + NDESC(sc->nrdre);
+ sc->trans_next = 0;
+
+ if (sc->nic.mem_mode == SHMEM)
+ lnc_mem = (char *) sc->nic.iobase;
+ else
+ lnc_mem = (char *) (sc->trans_ring + NDESC(sc->ntdre));
+
+ lnc_mem = (char *)(((int)lnc_mem + 1) & ~1);
+ sc->init_block = (struct init_block *) ((int) lnc_mem & ~1);
+ lnc_mem = (char *) (sc->init_block + 1);
+ lnc_mem = (char *)(((int)lnc_mem + 7) & ~7);
+
+ /* Initialise pointers to descriptor entries */
+ for (i = 0; i < NDESC(sc->nrdre); i++) {
+ (sc->recv_ring + i)->md = (struct mds *) lnc_mem;
+ lnc_mem += sizeof(struct mds);
+ }
+ for (i = 0; i < NDESC(sc->ntdre); i++) {
+ (sc->trans_ring + i)->md = (struct mds *) lnc_mem;
+ lnc_mem += sizeof(struct mds);
+ }
+
+ /* Initialise the remaining ring entries */
+
+ if (sc->nic.mem_mode == DMA_MBUF) {
+
+ sc->mbufs = 0;
+ sc->mbuf_count = 0;
+
+ /* Free previously allocated mbufs */
+ if (sc->initialised)
+ lnc_free_mbufs(sc);
+
+
+ for (i = 0; i < NDESC(sc->nrdre); i++) {
+ if (alloc_mbuf_cluster(sc, sc->recv_ring+i)) {
+ log(LOG_ERR, "Initialisation failed -- no mbufs\n");
+ splx(s);
+ return;
+ }
+ }
+
+ for (i = 0; i < NDESC(sc->ntdre); i++) {
+ (sc->trans_ring + i)->buff.mbuf = 0;
+ (sc->trans_ring + i)->md->md0 = 0;
+ (sc->trans_ring + i)->md->md1 = 0;
+ (sc->trans_ring + i)->md->md2 = 0;
+ (sc->trans_ring + i)->md->md3 = 0;
+ }
+ } else {
+ for (i = 0; i < NDESC(sc->nrdre); i++) {
+ (sc->recv_ring + i)->md->md0 = kvtop(lnc_mem);
+ (sc->recv_ring + i)->md->md1 = ((kvtop(lnc_mem) >> 16) & 0xff) | OWN;
+ (sc->recv_ring + i)->md->md2 = -RECVBUFSIZE;
+ (sc->recv_ring + i)->md->md3 = 0;
+ (sc->recv_ring + i)->buff.data = lnc_mem;
+ lnc_mem += RECVBUFSIZE;
+ }
+ for (i = 0; i < NDESC(sc->ntdre); i++) {
+ (sc->trans_ring + i)->md->md0 = kvtop(lnc_mem);
+ (sc->trans_ring + i)->md->md1 = ((kvtop(lnc_mem) >> 16) & 0xff);
+ (sc->trans_ring + i)->md->md2 = 0;
+ (sc->trans_ring + i)->md->md3 = 0;
+ (sc->trans_ring + i)->buff.data = lnc_mem;
+ lnc_mem += TRANSBUFSIZE;
+ }
+ }
+
+ sc->next_to_send = 0;
+
+ /* Set up initialisation block */
+
+ sc->init_block->mode = sc->nic.mode;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ sc->init_block->padr[i] = sc->arpcom.ac_enaddr[i];
+
+ for (i = 0; i < MULTICAST_ADDR_LEN; i++)
+ sc->init_block->ladrf[i] = MULTI_INIT_ADDR;
+
+ sc->init_block->rdra = kvtop(sc->recv_ring->md);
+ sc->init_block->rlen = ((kvtop(sc->recv_ring->md) >> 16) & 0xff) | (sc->nrdre << 13);
+ sc->init_block->tdra = kvtop(sc->trans_ring->md);
+ sc->init_block->tlen = ((kvtop(sc->trans_ring->md) >> 16) & 0xff) | (sc->ntdre << 13);
+
+
+ /* Set initialised to show that the memory area is valid */
+ sc->initialised = 1;
+
+ sc->pending_transmits = 0;
+
+ /* Give the LANCE the physical address of the initialisation block */
+
+ write_csr(unit, CSR1, kvtop(sc->init_block));
+ write_csr(unit, CSR2, (kvtop(sc->init_block) >> 16) & 0xff);
+
+ /*
+ * Depending on which controller this is, CSR3 has different meanings.
+ * For the Am7990 it controls DMA operations, for the Am79C960 it
+ * controls interrupt masks and transmitter algorithms. In either
+ * case, none of the flags are set.
+ *
+ */
+
+ write_csr(unit, CSR3, 0);
+
+ /* Let's see if it starts */
+
+ write_csr(unit, CSR0, INIT);
+ for (i = 0; i < 1000; i++)
+ if (read_csr(unit, CSR0) & IDON)
+ break;
+
+ /*
+ * Now that the initialisation is complete there's no reason to
+ * access anything except CSR0, so we leave RAP pointing there
+ * so we can just access RDP from now on, saving an outw each
+ * time.
+ */
+
+ if (read_csr(unit, CSR0) & IDON) {
+ /*
+ * Enable interrupts, start the LANCE, mark the interface as
+ * running and transmit any pending packets.
+ */
+ write_csr(unit, CSR0, STRT | INEA);
+ sc->arpcom.ac_if.if_flags |= IFF_RUNNING;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+ lnc_start(&sc->arpcom.ac_if);
+ } else
+ log(LOG_ERR, "lnc%d: Initialisation failed\n", unit);
+
+ splx(s);
+}
+
+/*
+ * The interrupt flag (INTR) will be set and provided that the interrupt enable
+ * flag (INEA) is also set, the interrupt pin will be driven low when any of
+ * the following occur:
+ *
+ * 1) Completion of the initialisation routine (IDON). 2) The reception of a
+ * packet (RINT). 3) The transmission of a packet (TINT). 4) A transmitter
+ * timeout error (BABL). 5) A missed packet (MISS). 6) A memory error (MERR).
+ *
+ * The interrupt flag is cleared when all of the above conditions are cleared.
+ *
+ * If the driver is reset from this routine then it first checks to see if any
+ * interrupts have ocurred since the reset and handles them before returning.
+ * This is because the NIC may signify a pending interrupt in CSR0 using the
+ * INTR flag even if a hardware interrupt is currently inhibited (at least I
+ * think it does from reading the data sheets). We may as well deal with
+ * these pending interrupts now rather than get the overhead of another
+ * hardware interrupt immediately upon returning from the interrupt handler.
+ *
+ */
+
+void
+lncintr(int unit)
+{
+ struct lnc_softc *sc = &lnc_softc[unit];
+ u_short csr0;
+
+ /*
+ * INEA is the only bit that can be cleared by writing a 0 to it so
+ * we have to include it in any writes that clear other flags.
+ */
+
+ while ((csr0 = inw(sc->rdp)) & INTR) {
+
+ /*
+ * Clear interrupt flags early to avoid race conditions. The
+ * controller can still set these flags even while we're in
+ * this interrupt routine. If the flag is still set from the
+ * event that caused this interrupt any new events will
+ * be missed.
+ */
+
+ outw(sc->rdp, IDON | CERR | BABL | MISS | MERR | RINT | TINT | INEA);
+
+ /* We don't do anything with the IDON flag */
+
+ if (csr0 & ERR) {
+ if (csr0 & CERR) {
+ log(LOG_ERR, "lnc%d: Heartbeat error -- SQE test failed\n", unit);
+ LNCSTATS(cerr)
+ }
+ if (csr0 & BABL) {
+ log(LOG_ERR, "lnc%d: Babble error - more than 1519 bytes transmitted\n", unit);
+ LNCSTATS(babl)
+ sc->arpcom.ac_if.if_oerrors++;
+ }
+ if (csr0 & MISS) {
+ log(LOG_ERR, "lnc%d: Missed packet -- no receive buffer\n", unit);
+ LNCSTATS(miss)
+ sc->arpcom.ac_if.if_ierrors++;
+ }
+ if (csr0 & MERR) {
+ log(LOG_ERR, "lnc%d: Memory error -- Resetting\n", unit);
+ LNCSTATS(merr)
+ lnc_reset(unit);
+ continue;
+ }
+ }
+ if (csr0 & RINT) {
+ LNCSTATS(rint)
+ lnc_rint(unit);
+ }
+ if (csr0 & TINT) {
+ LNCSTATS(tint)
+ sc->arpcom.ac_if.if_timer = 0;
+ lnc_tint(unit);
+ }
+
+ /*
+ * If there's room in the transmit descriptor ring then queue
+ * some more transmit packets.
+ */
+
+ if (!(sc->arpcom.ac_if.if_flags & IFF_OACTIVE))
+ lnc_start(&sc->arpcom.ac_if);
+ }
+}
+
+
+
+
+inline int
+mbuf_to_buffer(struct mbuf *m, char *buffer)
+{
+
+ int len=0;
+
+ for( ; m; m = m->m_next) {
+ bcopy(mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+
+ return(len);
+}
+
+inline struct mbuf *
+chain_to_cluster(struct mbuf *m)
+{
+ struct mbuf *new;
+
+ MGET(new, M_DONTWAIT, MT_DATA);
+ if (new) {
+ MCLGET(new, M_DONTWAIT);
+ if (new->m_ext.ext_buf) {
+ new->m_len = mbuf_to_buffer(m, new->m_data);
+ m_freem(m);
+ return(new);
+ } else
+ m_free(new);
+ }
+ return(0);
+}
+
+/*
+ * IFF_OACTIVE and IFF_RUNNING are checked in ether_output so it's redundant
+ * to check them again since we wouldn't have got here if they were not
+ * appropriately set. This is also called from lnc_init and lncintr but the
+ * flags should be ok at those points too.
+ */
+
+void
+lnc_start(struct ifnet *ifp)
+{
+
+ struct lnc_softc *sc = &lnc_softc[ifp->if_unit];
+ struct host_ring_entry *desc;
+ int tmp;
+ int end_of_packet;
+ struct mbuf *head, *m;
+ int len, chunk;
+ char *buffer;
+ int addr;
+ int no_entries_needed;
+
+ do {
+
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, head);
+ if (!head)
+ return;
+
+ if (sc->nic.mem_mode == DMA_MBUF) {
+
+ no_entries_needed = 0;
+ for (m=head; m; m = m->m_next)
+ no_entries_needed++;
+
+ /*
+ * We try and avoid bcopy as much as possible
+ * but there are two cases when we use it.
+ *
+ * 1) If there are not enough free entries in the ring
+ * to hold each mbuf in the chain then compact the
+ * chain into a single cluster.
+ *
+ * 2) The Am7990 and Am79C90 must not have less than
+ * 100 bytes in the first descriptor of a chained
+ * packet so it's necessary to shuffle the mbuf
+ * contents to ensure this.
+ */
+
+
+ if (no_entries_needed > (NDESC(sc->ntdre) - sc->pending_transmits))
+ if (!(head = chain_to_cluster(head))) {
+ log(LOG_ERR, "lnc%d: Couldn't get mbuf for transmit packet -- Resetting \n ",ifp->if_unit);
+ lnc_reset(ifp->if_unit);
+ return;
+ }
+ else if ((sc->nic.ic == LANCE) || (sc->nic.ic == C_LANCE)) {
+ if ((head->m_len < 100) && (head->m_next)) {
+ len = 100 - head->m_len;
+ if (M_TRAILINGSPACE(head) < len) {
+ /*
+ * Move data to start of data
+ * area. We assume the first
+ * mbuf has a packet header
+ * and is not a cluster.
+ */
+ bcopy((caddr_t)head->m_data, (caddr_t)head->m_pktdat, head->m_len);
+ head->m_data = head->m_pktdat;
+ }
+ m = head->m_next;
+ while (m && (len > 0)) {
+ chunk = min(len, m->m_len);
+ bcopy(mtod(m, caddr_t), mtod(head, caddr_t) + head->m_len, chunk);
+ len -= chunk;
+ head->m_len += chunk;
+ m->m_len -= chunk;
+ m->m_data += chunk;
+ if (m->m_len <= 0) {
+ MFREE(m, head->m_next);
+ m = head->m_next;
+ }
+ }
+ }
+ }
+
+ tmp = sc->next_to_send;
+
+ /*
+ * On entering this loop we know that tmp points to a
+ * descriptor with a clear OWN bit.
+ */
+
+ desc = sc->trans_ring + tmp;
+ len = ETHER_MIN_LEN;
+ for (m = head; m; m = m->m_next) {
+ desc->buff.mbuf = m;
+ addr = kvtop(m->m_data);
+ desc->md->md0 = addr;
+ desc->md->md1 = ((addr >> 16) & 0xff);
+ desc->md->md3 = 0;
+ desc->md->md2 = -m->m_len;
+ sc->pending_transmits++;
+ len -= m->m_len;
+
+ INC_MD_PTR(tmp, sc->ntdre)
+ desc = sc->trans_ring + tmp;
+ }
+
+ end_of_packet = tmp;
+ DEC_MD_PTR(tmp, sc->ntdre)
+ desc = sc->trans_ring + tmp;
+ desc->md->md1 |= ENP;
+
+ if (len > 0)
+ desc->md->md2 -= len;
+
+ /*
+ * Set OWN bits in reverse order, otherwise the Lance
+ * could start sending the packet before all the
+ * buffers have been relinquished by the host.
+ */
+
+ while (tmp != sc->next_to_send) {
+ desc->md->md1 |= OWN;
+ DEC_MD_PTR(tmp, sc->ntdre)
+ desc = sc->trans_ring + tmp;
+ }
+ sc->next_to_send = end_of_packet;
+ desc->md->md1 |= STP | OWN;
+ } else {
+ sc->pending_transmits++;
+ desc = sc->trans_ring + sc->next_to_send;
+ len = mbuf_to_buffer(head, desc->buff.data);
+ desc->md->md3 = 0;
+ desc->md->md2 = -max(len, ETHER_MIN_LEN);
+ desc->md->md1 |= OWN | STP | ENP;
+ INC_MD_PTR(sc->next_to_send, sc->ntdre)
+ }
+
+ /* Force an immediate poll of the transmit ring */
+ outw(sc->rdp, TDMD | INEA);
+
+ /*
+ * Set a timer so if the buggy Am7990.h shuts
+ * down we can wake it up.
+ */
+
+ ifp->if_timer = 2;
+
+#if NBPFILTER > 0
+ if (sc->bpf)
+ bpf_mtap(sc->bpf, head);
+#endif
+
+ if (sc->nic.mem_mode != DMA_MBUF)
+ m_freem(head);
+
+ } while (sc->pending_transmits < NDESC(sc->ntdre));
+
+ /*
+ * Transmit ring is full so set IFF_OACTIVE
+ * since we can't buffer any more packets.
+ */
+
+ sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
+ LNCSTATS(trans_ring_full)
+}
+
+int
+lnc_ioctl(struct ifnet * ifp, int command, caddr_t data)
+{
+
+ struct lnc_softc *sc = &lnc_softc[ifp->if_unit];
+ struct ifaddr *ifa = (struct ifaddr *) data;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int s, error = 0;
+
+ s = splimp();
+
+ switch (command) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ lnc_init(ifp->if_unit);
+ ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+ default:
+ lnc_init(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+#ifdef DEBUG
+ if (ifp->if_flags & IFF_DEBUG)
+ sc->lnc_debug = 1;
+ else
+ sc->lnc_debug = 0;
+#endif
+ if (ifp->if_flags & IFF_PROMISC) {
+ if (!(sc->nic.mode & PROM)) {
+ sc->nic.mode |= PROM;
+ lnc_init(ifp->if_unit);
+ }
+ } else if (sc->nic.mode & PROM) {
+ sc->nic.mode &= ~PROM;
+ lnc_init(ifp->if_unit);
+ }
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ (ifp->if_flags & IFF_RUNNING) != 0) {
+ /*
+ * If interface is marked down and it is running,
+ * then stop it.
+ */
+ lnc_stop(ifp->if_unit);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else if ((ifp->if_flags & IFF_UP) != 0 &&
+ (ifp->if_flags & IFF_RUNNING) == 0) {
+ /*
+ * If interface is marked up and it is stopped, then
+ * start it.
+ */
+ lnc_init(ifp->if_unit);
+ }
+ break;
+#ifdef notyet
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ error = (command == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &sc->arpcom) :
+ ether_delmulti(ifr, &sc->arpcom);
+
+ if (error == ENETRESET) {
+ lnc_setladrf(ifp,sc);
+ error = 0;
+ }
+ break;
+#endif
+ case SIOCSIFMTU:
+ /*
+ * Set the interface MTU.
+ */
+
+ if (ifr->ifr_mtu > ETHERMTU) {
+ error = EINVAL;
+ } else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return error;
+}
+
+void
+lnc_watchdog(int unit)
+{
+ log(LOG_ERR, "lnc%d: Device timeout -- Resetting\n", unit);
+ ++lnc_softc[unit].arpcom.ac_if.if_oerrors;
+ lnc_reset(unit);
+}
+
+#ifdef DEBUG
+void
+lnc_dump_state(int unit)
+{
+ struct lnc_softc *sc = &lnc_softc[unit];
+ int i;
+
+ log(LOG_DEBUG, "\nDriver/NIC [%d] state dump\n", unit);
+ log(LOG_DEBUG, "Memory access mode: %b\n", sc->nic.mem_mode, MEM_MODES);
+ log(LOG_DEBUG, "Host memory\n");
+ log(LOG_DEBUG, "-----------\n");
+
+ log(LOG_DEBUG, "Receive ring: base = %x, next = %x\n",
+ sc->recv_ring, (sc->recv_ring + sc->recv_next));
+ for (i = 0; i < NDESC(sc->nrdre); i++)
+ log(LOG_DEBUG, "\t%d:%x md = %x buff = %x\n",
+ i, sc->recv_ring + i, (sc->recv_ring + i)->md,
+ (sc->recv_ring + i)->buff);
+
+ log(LOG_DEBUG, "Transmit ring: base = %x, next = %x\n",
+ sc->trans_ring, (sc->trans_ring + sc->trans_next));
+ for (i = 0; i < NDESC(sc->ntdre); i++)
+ log(LOG_DEBUG, "\t%d:%x md = %x buff = %x\n",
+ i, sc->trans_ring + i, (sc->trans_ring + i)->md,
+ (sc->trans_ring + i)->buff);
+ log(LOG_DEBUG, "Lance memory (may be on host(DMA) or card(SHMEM))\n");
+ log(LOG_DEBUG, "Init block = %x\n", sc->init_block);
+ log(LOG_DEBUG, "\tmode = %b rlen:rdra = %x:%x tlen:tdra = %x:%x\n",
+ sc->init_block->mode, INIT_MODE, sc->init_block->rlen,
+ sc->init_block->rdra, sc->init_block->tlen, sc->init_block->tdra);
+ log(LOG_DEBUG, "Receive descriptor ring\n");
+ for (i = 0; i < NDESC(sc->nrdre); i++)
+ log(LOG_DEBUG, "\t%d buffer = 0x%x%x, BCNT = %d,\tMCNT = %u,\tflags = %b\n",
+ i, ((sc->recv_ring + i)->md->md1 & HADR),
+ (sc->recv_ring + i)->md->md0,
+ -(short) (sc->recv_ring + i)->md->md2,
+ (sc->recv_ring + i)->md->md3,
+ (((sc->recv_ring + i)->md->md1 & ~HADR) >> 8), RECV_MD1);
+ log(LOG_DEBUG, "Transmit descriptor ring\n");
+ for (i = 0; i < NDESC(sc->ntdre); i++)
+ log(LOG_DEBUG, "\t%d buffer = 0x%x%x, BCNT = %d,\tflags = %b %b\n",
+ i, ((sc->trans_ring + i)->md->md1 & HADR),
+ (sc->trans_ring + i)->md->md0,
+ -(short) (sc->trans_ring + i)->md->md2,
+ ((sc->trans_ring + i)->md->md1 >> 8), TRANS_MD1,
+ ((sc->trans_ring + i)->md->md3 >> 10), TRANS_MD3);
+ log(LOG_DEBUG, "\nnext_to_send = %x\n", sc->next_to_send);
+ log(LOG_DEBUG, "\n CSR0 = %b CSR1 = %x CSR2 = %x CSR3 = %x\n\n", read_csr(unit, CSR0), CSR0_FLAGS, read_csr(unit, CSR1), read_csr(unit, CSR2), read_csr(unit, CSR3));
+ /* Set RAP back to CSR0 */
+ outw(sc->rap, CSR0);
+}
+
+void
+mbuf_dump_chain(struct mbuf * m)
+{
+
+#define MBUF_FLAGS \
+ "\20\1M_EXT\2M_PKTHDR\3M_EOR\4UNKNOWN\5M_BCAST\6M_MCAST"
+
+ if (!m)
+ log(LOG_DEBUG, "m == NULL\n");
+ do {
+ log(LOG_DEBUG, "m = %x\n", m);
+ log(LOG_DEBUG, "m_hdr.mh_next = %x\n", m->m_hdr.mh_next);
+ log(LOG_DEBUG, "m_hdr.mh_nextpkt = %x\n", m->m_hdr.mh_nextpkt);
+ log(LOG_DEBUG, "m_hdr.mh_len = %d\n", m->m_hdr.mh_len);
+ log(LOG_DEBUG, "m_hdr.mh_data = %x\n", m->m_hdr.mh_data);
+ log(LOG_DEBUG, "m_hdr.mh_type = %d\n", m->m_hdr.mh_type);
+ log(LOG_DEBUG, "m_hdr.mh_flags = %b\n", m->m_hdr.mh_flags, MBUF_FLAGS);
+ if (!(m->m_hdr.mh_flags & (M_PKTHDR | M_EXT)))
+ log(LOG_DEBUG, "M_dat.M_databuf = %x\n", m->M_dat.M_databuf);
+ else {
+ if (m->m_hdr.mh_flags & M_PKTHDR) {
+ log(LOG_DEBUG, "M_dat.MH.MH_pkthdr.len = %d\n", m->M_dat.MH.MH_pkthdr.len);
+ log(LOG_DEBUG, "M_dat.MH.MH_pkthdr.rcvif = %x\n", m->M_dat.MH.MH_pkthdr.rcvif);
+ if (!(m->m_hdr.mh_flags & M_EXT))
+ log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_databuf = %x\n", m->M_dat.MH.MH_dat.MH_databuf);
+ }
+ if (m->m_hdr.mh_flags & M_EXT) {
+ log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_buff %x\n", m->M_dat.MH.MH_dat.MH_ext.ext_buf);
+ log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_free %x\n", m->M_dat.MH.MH_dat.MH_ext.ext_free);
+ log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_size %d\n", m->M_dat.MH.MH_dat.MH_ext.ext_size);
+ }
+ }
+ } while (m = m->m_next);
+}
+#endif
+
+#endif
diff --git a/sys/dev/lnc/if_lncvar.h b/sys/dev/lnc/if_lncvar.h
new file mode 100644
index 000000000000..c555ad8dd6e6
--- /dev/null
+++ b/sys/dev/lnc/if_lncvar.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 1994, Paul Richards. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ */
+
+#include "ic/Am7990.h"
+
+/*
+ * Initialize multicast address hashing registers to accept
+ * all multicasts (only used when in promiscuous mode)
+ */
+#if NBPFILTER > 0
+#define MULTI_INIT_ADDR 0xff
+#else
+#define MULTI_INIT_ADDR 0
+#endif
+
+#define NORMAL 0
+
+#define NRDRE 3
+#define NTDRE 3
+#define RECVBUFSIZE 1518 /* Packet size rounded to dword boundary */
+#define TRANSBUFSIZE 1518
+#define MBUF_CACHE_LIMIT 0
+
+#define MEM_SLEW 8
+
+
+/* BICC port addresses */
+#define BICC_IOSIZE 16
+#define BICC_RDP 0x0c /* Register Data Port */
+#define BICC_RAP 0x0e /* Register Address Port */
+
+/* NE2100 port addresses */
+#define NE2100_IOSIZE 24
+#define PCNET_RDP 0x10 /* Register Data Port */
+#define PCNET_RAP 0x12 /* Register Address Port */
+#define PCNET_RESET 0x14
+#define PCNET_IDP 0x16
+#define PCNET_VSW 0x18
+
+/* DEPCA port addresses */
+#define DEPCA_IOSIZE 16
+#define DEPCA_CTRL 0x00 /* NIC Control and status register */
+#define DEPCA_RDP 0x04 /* Register Data Port */
+#define DEPCA_RAP 0x06 /* Register Address Port */
+#define DEPCA_ADP 0x0c
+
+/* DEPCA specific defines */
+#define DEPCA_ADDR_ROM_SIZE 32
+
+/* Chip types */
+#define LANCE 1 /* Am7990 */
+#define C_LANCE 2 /* Am79C90 */
+#define PCnet_ISA 3 /* Am79C960 */
+#define PCnet_ISAplus 4 /* Am79C961 */
+#define PCnet_32 5 /* Am79C965 */
+#define PCnet_PCI 6 /* Am79C970 */
+
+/* CSR88-89: Chip ID masks */
+#define AMD_MASK 0x003
+#define PART_MASK 0xffff
+#define Am79C960 0x0003
+#define Am79C961 0x2260
+#define Am79C965 0x2430
+#define Am79C970 0x0242
+
+/* Board types */
+#define UNKNOWN 0
+#define BICC 1
+#define NE2100 2
+#define DEPCA 3
+
+/* mem_mode values */
+#define DMA_FIXED 1
+#define DMA_MBUF 2
+#define SHMEM 4
+
+#define MEM_MODES \
+ "\20\3SHMEM\2DMA_MBUF\1DMA_FIXED"
+
+#define CSR0_FLAGS \
+ "\20\20ERR\17BABL\16CERR\15MISS\14MERR\13RINT\12TINT\11IDON\
+ \10INTR\07INEA\06RXON\05TXON\04TDMD\03STOP\02STRT\01INIT"
+
+#define INIT_MODE \
+ "\20\20PROM\07INTL\06DRTY\05COLL\04DTCR\03LOOP\02DTX\01DRX"
+
+#define RECV_MD1 \
+ "\20\10OWN\7ERR\6FRAM\5OFLO\4CRC\3BUFF\2STP\1ENP"
+
+#define TRANS_MD1 \
+ "\20\10OWN\7ERR\6RES\5MORE\4ONE\3DEF\2STP\1ENP"
+
+#define TRANS_MD3 \
+ "\20\6BUFF\5UFLO\4RES\3LCOL\2LCAR\1RTRY"
+
+char *nic_ident[] = {"Unknown",
+ "BICC",
+ "NE2100",
+ "DEPCA"};
+
+char *ic_ident[] = {"Unknown",
+ "LANCE, Am7990",
+ "C-LANCE, Am79C90",
+ "PCnet-ISA, Am79C960",
+ "PCnet-ISA+, Am79C961",
+ "PCnet-32, Am79C965",
+ "PCnet-PCI, Am79C970"};
+
+struct nic_info {
+ int ident; /* Type of card */
+ int ic; /* Type of ic, Am7990, Am79C960 etc. */
+ int mem_mode;
+ int iobase;
+ int mode; /* Mode setting at initialisation */
+};
+
+struct host_ring_entry {
+ struct mds *md;
+ union {
+ struct mbuf *mbuf;
+ char *data;
+ }buff;
+};
+
+#ifdef LNC_KEEP_STATS
+#define LNCSTATS_STRUCT \
+ struct lnc_stats { \
+ int idon; \
+ int rint; \
+ int tint; \
+ int cerr; \
+ int babl; \
+ int miss; \
+ int merr; \
+ int rxoff; \
+ int txoff; \
+ int terr; \
+ int lcol; \
+ int lcar; \
+ int tbuff; \
+ int def; \
+ int more; \
+ int one; \
+ int uflo; \
+ int rtry; \
+ int rerr; \
+ int fram; \
+ int oflo; \
+ int crc; \
+ int rbuff; \
+ int drop_packet; \
+ int trans_ring_full; \
+ } lnc_stats;
+#define LNCSTATS(X) ++(sc->lnc_stats.X);
+#else
+#define LNCSTATS_STRUCT
+#define LNCSTATS(X)
+#endif
+
+#define NDESC(len2) (1 << len2)
+
+#define INC_MD_PTR(ptr, no_entries) \
+ if (++ptr >= NDESC(no_entries)) \
+ ptr = 0;
+
+#define DEC_MD_PTR(ptr, no_entries) \
+ if (--ptr < 0) \
+ ptr = NDESC(no_entries) - 1;
+
+#define RECV_NEXT (sc->recv_ring->base + sc->recv_next)
+#define TRANS_NEXT (sc->trans_ring->base + sc->trans_next)
diff --git a/sys/i386/isa/if_lnc.c b/sys/i386/isa/if_lnc.c
new file mode 100644
index 000000000000..89cec80d0680
--- /dev/null
+++ b/sys/i386/isa/if_lnc.c
@@ -0,0 +1,1659 @@
+/*
+ * Copyright (c) 1994, Paul Richards.
+ *
+ * All rights reserved.
+ *
+ * This software may be used, modified, copied, distributed, and
+ * sold, in both source and binary form provided that the above
+ * copyright and these terms are retained, verbatim, as the first
+ * lines of this file. Under no circumstances is the author
+ * responsible for the proper functioning of this software, nor does
+ * the author assume any responsibility for damages incurred with
+ * its use.
+ */
+
+/*
+#define DIAGNOSTIC
+#define DEBUG
+ *
+ * TODO ----
+ *
+ * This driver will need bounce buffer support when dma'ing to mbufs above the
+ * 16Mb mark.
+ *
+ * Check all the XXX comments -- some of them are just things I've left
+ * unfinished rather than "difficult" problems that were hacked around.
+ *
+ * Check log settings.
+ *
+ * Check how all the arpcom flags get set and used.
+ *
+ * Re-inline and re-static all routines after debugging.
+ *
+ * Remember to assign iobase in SHMEM probe routines.
+ *
+ * Replace all occurences of LANCE-controller-card etc in prints by the name
+ * strings of the appropriate type -- nifty window dressing
+ *
+ * Add DEPCA support -- mostly done.
+ *
+ */
+
+#include "lnc.h"
+#if NLNC > 0
+
+#include "bpfilter.h"
+
+/* Some defines that should really be in generic locations */
+#define FCS_LEN 4
+#define ETHER_ADDR_LEN 6
+#define ETHER_HDR_LEN 14
+#define MULTICAST_ADDR_LEN 8
+#define ETHER_MIN_LEN 64
+
+#include "param.h"
+#include "systm.h"
+#include "errno.h"
+#include "ioctl.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "syslog.h"
+#include "net/if.h"
+#include "net/if_dl.h"
+#include "net/if_types.h"
+#ifdef INET
+#include "netinet/in.h"
+#include "netinet/in_systm.h"
+#include "netinet/in_var.h"
+#include "netinet/ip.h"
+#include "netinet/if_ether.h"
+#endif
+
+#if NBPFILTER > 0
+#include "net/bpf.h"
+#include "net/bpfdesc.h"
+#endif
+
+#include "i386/isa/isa_device.h"
+#include "i386/isa/if_lnc.h"
+
+struct lnc_softc {
+ struct arpcom arpcom; /* see ../../netinet/if_ether.h */
+ struct nic_info nic; /* NIC specific info */
+ int nrdre;
+ struct host_ring_entry *recv_ring; /* start of alloc'd mem */
+ int recv_next;
+ int ntdre;
+ struct host_ring_entry *trans_ring;
+ int trans_next;
+ struct init_block *init_block; /* Initialisation block */
+ int pending_transmits; /* No. of transmit descriptors in use */
+ int next_to_send;
+ struct mbuf *mbufs;
+ int mbuf_count;
+ int initialised;
+ int rap;
+ int rdp;
+#ifdef DEBUG
+ int lnc_debug;
+#endif
+#if NBPFILTER > 0
+ caddr_t bpf; /* XXX bpf magic cookie - move to arpcom */
+#endif
+ LNCSTATS_STRUCT
+} lnc_softc[NLNC];
+
+/* Function prototypes */
+void lnc_init(int);
+void lnc_start(struct ifnet *);
+int lnc_ioctl(struct ifnet *, int, caddr_t);
+void lnc_watchdog(int);
+int lnc_probe(struct isa_device *);
+int lnc_attach(struct isa_device *);
+void lnc_dump_state(int);
+
+struct isa_driver lncdriver = {lnc_probe, lnc_attach, "lnc"};
+
+inline void
+write_csr(int unit, u_short port, u_short val)
+{
+ outw(lnc_softc[unit].rap, port);
+ outw(lnc_softc[unit].rdp, val);
+}
+
+inline u_short
+read_csr(int unit, u_short port)
+{
+ outw(lnc_softc[unit].rap, port);
+ return (inw(lnc_softc[unit].rdp));
+}
+
+
+void
+lnc_setladrf(struct ifnet *ifp, struct lnc_softc *sc)
+{
+
+}
+
+void
+lnc_stop(int unit)
+{
+ write_csr(unit, CSR0, STOP);
+}
+
+void
+lnc_reset(int unit)
+{
+ int s;
+
+ lnc_init(unit);
+}
+
+void
+lnc_free_mbufs(struct lnc_softc *sc)
+{
+ int i;
+
+ /*
+ * We rely on other routines to keep the buff.mbuf field valid. If
+ * it's not NULL then we assume it points to an allocated mbuf.
+ */
+
+ for (i = 0; i < NDESC(sc->nrdre); i++)
+ if ((sc->recv_ring + i)->buff.mbuf)
+ m_free((sc->recv_ring + i)->buff.mbuf);
+
+ for (i = 0; i < NDESC(sc->ntdre); i++)
+ if ((sc->trans_ring + i)->buff.mbuf)
+ m_free((sc->trans_ring + i)->buff.mbuf);
+
+ if (sc->mbuf_count)
+ m_freem(sc->mbufs);
+}
+
+inline int
+alloc_mbuf_cluster(struct lnc_softc *sc, struct host_ring_entry *desc)
+{
+ register struct mds *md = desc->md;
+ struct mbuf *m=0;
+ int addr;
+
+ /* Try and get cluster off local cache */
+ if (sc->mbuf_count) {
+ sc->mbuf_count--;
+ m = sc->mbufs;
+ sc->mbufs = m->m_next;
+ /* XXX m->m_data = m->m_ext.ext_buf;*/
+ } else {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (!m)
+ return(1);
+ MCLGET(m, M_DONTWAIT);
+ if (!m->m_ext.ext_buf) {
+ m_free(m);
+ return(1);
+ }
+ }
+
+ desc->buff.mbuf = m;
+ addr = kvtop(m->m_data);
+ md->md0 = addr;
+ md->md1= ((addr >> 16) & 0xff) | OWN;
+ md->md2 = -(short)(MCLBYTES - sizeof(struct pkthdr));
+ md->md3 = 0;
+ return(0);
+}
+
+inline struct mbuf *
+chain_mbufs(struct lnc_softc *sc, int start_of_packet, int pkt_len)
+{
+ struct mbuf *head, *m;
+ struct host_ring_entry *desc;
+
+ /*
+ * Turn head into a pkthdr mbuf --
+ * assumes a pkthdr type mbuf was
+ * allocated to the descriptor
+ * originally.
+ */
+
+ desc = sc->recv_ring + start_of_packet;
+
+ head = desc->buff.mbuf;
+ head->m_flags |= M_PKTHDR;
+
+ m = head;
+ do {
+ m = desc->buff.mbuf;
+ m->m_len = min(-(desc->md->md2), pkt_len);
+ pkt_len -= m->m_len;
+ if (alloc_mbuf_cluster(sc, desc))
+ return((struct mbuf *)NULL);
+ INC_MD_PTR(start_of_packet, sc->nrdre)
+ desc = sc->recv_ring + start_of_packet;
+ m->m_next = desc->buff.mbuf;
+ } while (start_of_packet != sc->recv_next);
+
+ m->m_next = 0;
+ return(head);
+}
+
+inline struct mbuf *
+mbuf_packet(struct lnc_softc *sc, int start_of_packet, int pkt_len)
+{
+
+ struct host_ring_entry *start;
+ struct mbuf *head,*m,*m_prev;
+ char *data,*mbuf_data;
+ short blen;
+ int amount;
+
+ /* Get a pkthdr mbuf for the start of packet */
+ MGETHDR(head, M_DONTWAIT, MT_DATA);
+ if (!head) {
+ LNCSTATS(drop_packet)
+ return(0);
+ }
+
+ m = head;
+ m->m_len = 0;
+ start = sc->recv_ring + start_of_packet;
+ blen = -(start->md->md2);
+ data = start->buff.data;
+ mbuf_data = m->m_data;
+
+ while (start_of_packet != sc->recv_next) {
+ /*
+ * If the data left fits in a single buffer then set
+ * blen to the size of the data left.
+ */
+ if (pkt_len < blen)
+ blen = pkt_len;
+
+ /*
+ * amount is least of data in current ring buffer and
+ * amount of space left in current mbuf.
+ */
+ amount = min(blen, M_TRAILINGSPACE(m));
+ if (amount == 0) {
+ /* mbuf must be empty */
+ m_prev = m;
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (!m) {
+ m_freem(head);
+ return(0);
+ }
+ if (pkt_len >= MINCLSIZE)
+ MCLGET(m, M_DONTWAIT);
+ m->m_len = 0;
+ m_prev->m_next = m;
+ amount = min(blen, M_TRAILINGSPACE(m));
+ mbuf_data = m->m_data;
+ }
+ bcopy(data, mbuf_data, amount);
+ blen -= amount;
+ pkt_len -= amount;
+ m->m_len += amount;
+ data += amount;
+ mbuf_data += amount;
+
+ if (blen == 0) {
+ start->md->md1 &= HADR;
+ start->md->md1 |= OWN;
+ INC_MD_PTR(start_of_packet, sc->nrdre)
+ start = sc->recv_ring + start_of_packet;
+ data = start->buff.data;
+ blen = -(start->md->md2);
+ }
+ }
+ return(head);
+}
+
+
+inline void
+lnc_rint(int unit)
+{
+ register struct lnc_softc *sc = &lnc_softc[unit];
+ struct host_ring_entry *next, *start;
+ int start_of_packet;
+ struct mbuf *head;
+ struct ether_header *eh;
+ int lookahead;
+ int flags;
+ int pkt_len;
+
+ /*
+ * The LANCE will issue a RINT interrupt when the ownership of the
+ * last buffer of a receive packet has been relinquished by the LANCE.
+ * Therefore, it can be assumed that a complete packet can be found
+ * before hitting buffers that are still owned by the LANCE, if not
+ * then there is a bug in the driver that is causing the descriptors
+ * to get out of sync.
+ */
+
+#ifdef DIAGNOSTIC
+ if ((sc->recv_ring + sc->recv_next)->md->md1 & OWN) {
+ log(LOG_ERR, "lnc%d: Receive interrupt with buffer still owned by controller -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ if (!((sc->recv_ring + sc->recv_next)->md->md1 & STP)) {
+ log(LOG_ERR, "lnc%d: Receive interrupt but not start of packet -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+#endif
+
+ lookahead = 0;
+ next = sc->recv_ring + sc->recv_next;
+ while ((flags = next->md->md1) & STP) {
+
+ /* Make a note of the start of the packet */
+ start_of_packet = sc->recv_next;
+
+ /*
+ * Find the end of the packet. Even if not data chaining,
+ * jabber packets can overrun into a second descriptor.
+ * If there is no error, then the ENP flag is set in the last
+ * descriptor of the packet. If there is an error then the ERR
+ * flag will be set in the descriptor where the error occured.
+ * Therefore, to find the last buffer of a packet we search for
+ * either ERR or ENP.
+ */
+
+ if (!(flags & (ENP | MDERR))) {
+ do {
+ INC_MD_PTR(sc->recv_next, sc->nrdre)
+ next = sc->recv_ring + sc->recv_next;
+ flags = next->md->md1;
+ } while (!(flags & (STP | OWN | ENP | MDERR)));
+
+ if (flags & STP) {
+ log(LOG_ERR, "lnc%d: Start of packet found before end of previous in receive ring -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ if (flags & OWN) {
+ if (lookahead) {
+ /*
+ * Looked ahead into a packet still
+ * being received
+ */
+ sc->recv_next = start_of_packet;
+ break;
+ } else {
+ log(LOG_ERR, "lnc%d: End of received packet not found-- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ }
+ }
+
+ pkt_len = next->md->md3 & MCNT - FCS_LEN;
+
+ /* Move pointer onto start of next packet */
+ INC_MD_PTR(sc->recv_next, sc->nrdre)
+ next = sc->recv_ring + sc->recv_next;
+
+ if (flags & MDERR) {
+ if (flags & RBUFF) {
+ LNCSTATS(rbuff)
+ log(LOG_ERR, "lnc%d: Receive buffer error\n", unit);
+ }
+ if (flags & OFLO) {
+ /* OFLO only valid if ENP is not set */
+ if (!(flags & ENP)) {
+ LNCSTATS(oflo)
+ log(LOG_ERR, "lnc%d: Receive overflow error \n", unit);
+ }
+ } else {
+ /*
+ * FRAM and CRC are valid only if OFLO is not
+ * set and ENP is
+ */
+ if (flags & (FRAM | ENP)) {
+ LNCSTATS(fram)
+ log(LOG_ERR, "lnc%d: Framming error\n", unit);
+ /*
+ * FRAM is only set if there's a CRC
+ * error so avoid multiple messages
+ */
+ } else if (flags & (CRC | ENP)) {
+ LNCSTATS(crc)
+ log(LOG_ERR, "lnc%d: Receive CRC error\n", unit);
+ }
+ }
+
+ /* Drop packet */
+ LNCSTATS(rerr)
+ sc->arpcom.ac_if.if_ierrors++;
+ while (start_of_packet != sc->recv_next) {
+ start = sc->recv_ring + start_of_packet;
+ start->md->md1 &= HADR;
+ start->md->md1 |= OWN;
+ INC_MD_PTR(start_of_packet, sc->nrdre)
+ }
+ } else { /* Valid packet */
+
+ sc->arpcom.ac_if.if_ipackets++;
+
+
+ if (sc->nic.mem_mode == DMA_MBUF)
+ head = chain_mbufs(sc,start_of_packet,pkt_len);
+ else
+ head = mbuf_packet(sc,start_of_packet,pkt_len);
+
+ if (head) {
+ /*
+ * First mbuf in packet holds the
+ * ethernet and packet headers
+ */
+ head->m_pkthdr.rcvif = &sc->arpcom.ac_if;
+ head->m_pkthdr.len = pkt_len - sizeof(*eh);
+ eh = (struct ether_header *) head->m_data;
+ eh->ether_type = ntohs(eh->ether_type);
+ head->m_data += sizeof *eh;
+ head->m_len -= sizeof *eh;
+
+#if NBPFILTER > 0
+ if (sc->bpf)
+ bpf_mtap(sc->bpf, head);
+
+ /* Check this packet is really for us */
+ /* XXX -- this doesn't look right */
+
+ if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) &&
+ (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
+ sizeof(eh->ether_dhost)) != 0) &&
+ (bcmp(eh->ether_dhost, etherbroadcastaddr,
+ sizeof(eh->ether_dhost)) != 0))
+ m_freem(head);
+ else
+#endif
+ ether_input(&sc->arpcom.ac_if, eh, head);
+
+ } else {
+ log(LOG_ERR,"lnc%d: Packet dropped, no mbufs\n",unit);
+ LNCSTATS(drop_packet)
+ }
+ }
+
+ lookahead++;
+ }
+
+ /*
+ * At this point all completely received packets have been processed
+ * so clear RINT since any packets that have arrived while we were in
+ * here have been dealt with.
+ */
+
+ outw(sc->rdp, RINT | INEA);
+}
+
+inline void
+lnc_tint(int unit)
+{
+ register struct lnc_softc *sc = &lnc_softc[unit];
+ struct host_ring_entry *next, *start;
+ int start_of_packet;
+ int lookahead;
+
+ /*
+ * If the driver is reset in this routine then we return immediately to
+ * the interrupt driver routine. Any interrupts that have occured
+ * since the reset will be dealt with there. sc->trans_next
+ * should point to the start of the first packet that was awaiting
+ * transmission after the last transmit interrupt was dealt with. The
+ * LANCE should have relinquished ownership of that descriptor before
+ * the interrupt. Therefore, sc->trans_next should point to a
+ * descriptor with STP set and OWN cleared. If not then the driver's
+ * pointers are out of sync with the LANCE, which signifies a bug in
+ * the driver. Therefore, the following two checks are really
+ * diagnostic, since if the driver is working correctly they should
+ * never happen.
+ */
+
+#ifdef DIAGNOSTIC
+ if ((sc->trans_ring + sc->trans_next)->md->md1 & OWN) {
+ log(LOG_ERR, "lnc%d: Transmit interrupt with buffer still owned by controller -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+#endif
+
+
+ /*
+ * The LANCE will write the status information for the packet it just
+ * tried to transmit in one of two places. If the packet was
+ * transmitted successfully then the status will be written into the
+ * last descriptor of the packet. If the transmit failed then the
+ * status will be written into the descriptor that was being accessed
+ * when the error occured and all subsequent descriptors in that
+ * packet will have been relinquished by the LANCE.
+ *
+ * At this point we know that sc->trans_next points to the start
+ * of a packet that the LANCE has just finished trying to transmit.
+ * We now search for a buffer with either ENP or ERR set.
+ */
+
+ lookahead = 0;
+
+ do {
+ start_of_packet = sc->trans_next;
+ next = sc->trans_ring + sc->trans_next;
+
+#ifdef DIAGNOSTIC
+ if (!(next->md->md1 & STP)) {
+ log(LOG_ERR, "lnc%d: Transmit interrupt but not start of packet -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+#endif
+
+ /*
+ * Find end of packet.
+ */
+
+ if (!(next->md->md1 & (ENP | MDERR))) {
+ do {
+ INC_MD_PTR(sc->trans_next, sc->ntdre)
+ next = sc->trans_ring + sc->trans_next;
+ } while (!(next->md->md1 & (STP | OWN | ENP | MDERR)));
+
+ if (next->md->md1 & STP) {
+ log(LOG_ERR, "lnc%d: Start of packet found before end of previous in transmit ring -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ if (next->md->md1 & OWN) {
+ if (lookahead) {
+ /*
+ * Looked ahead into a packet still
+ * being transmitted
+ */
+ sc->trans_next = start_of_packet;
+ break;
+ } else {
+ log(LOG_ERR, "lnc%d: End of transmitted packet not found -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ }
+ }
+ /*
+ * Check for ERR first since other flags are irrelevant if an
+ * error occurred.
+ */
+ if (next->md->md1 & MDERR) {
+
+ LNCSTATS(terr)
+ sc->arpcom.ac_if.if_oerrors++;
+
+ if (next->md->md3 & LCOL) {
+ LNCSTATS(lcol)
+ log(LOG_ERR, "lnc%d: Transmit late collision -- Net error?\n", unit);
+ sc->arpcom.ac_if.if_collisions++;
+ /*
+ * Clear TBUFF since it's not valid when LCOL
+ * set
+ */
+ next->md->md3 &= ~TBUFF;
+ }
+ if (next->md->md3 & LCAR) {
+ LNCSTATS(lcar)
+ log(LOG_ERR, "lnc%d: Loss of carrier during transmit -- Net error?\n", unit);
+ }
+ if (next->md->md3 & RTRY) {
+ LNCSTATS(rtry)
+ log(LOG_ERR, "lnc%d: Transmit of packet failed after 16 attempts -- TDR = %d\n", unit, ((sc->trans_ring + sc->trans_next)->md->md3 & TDR));
+ sc->arpcom.ac_if.if_collisions += 16;
+ /*
+ * Clear TBUFF since it's not valid when RTRY
+ * set
+ */
+ next->md->md3 &= ~TBUFF;
+ }
+ /*
+ * TBUFF is only valid if neither LCOL nor RTRY are set.
+ * We need to check UFLO after LCOL and RTRY so that we
+ * know whether or not TBUFF is valid. If either are
+ * set then TBUFF will have been cleared above. A
+ * UFLO error will turn off the transmitter so we
+ * have to reset.
+ *
+ */
+
+ if (next->md->md3 & UFLO) {
+ LNCSTATS(uflo)
+ /*
+ * If an UFLO has occured it's possibly due
+ * to a TBUFF error
+ */
+ if (next->md->md3 & TBUFF) {
+ LNCSTATS(tbuff)
+ log(LOG_ERR, "lnc%d: Transmit buffer error -- Resetting\n", unit);
+ } else
+ log(LOG_ERR, "lnc%d: Transmit underflow error -- Resetting\n", unit);
+ lnc_reset(unit);
+ return;
+ }
+ do {
+ INC_MD_PTR(sc->trans_next, sc->ntdre)
+ next = sc->trans_ring + sc->trans_next;
+ } while (!(next->md->md1 & STP) && (sc->trans_next != sc->next_to_send));
+
+ } else {
+ /*
+ * Since we check for ERR first then if we get here
+ * the packet was transmitted correctly. There may
+ * still have been non-fatal errors though.
+ * Don't bother checking for DEF, waste of time.
+ */
+
+ sc->arpcom.ac_if.if_opackets++;
+
+ if (next->md->md1 & MORE) {
+ LNCSTATS(more)
+ sc->arpcom.ac_if.if_collisions += 2;
+ }
+
+ /*
+ * ONE is invalid if LCOL is set. If LCOL was set then
+ * ERR would have also been set and we would have
+ * returned from lnc_tint above. Therefore we can
+ * assume if we arrive here that ONE is valid.
+ *
+ */
+
+ if (next->md->md1 & ONE) {
+ LNCSTATS(one)
+ sc->arpcom.ac_if.if_collisions++;
+ }
+ INC_MD_PTR(sc->trans_next, sc->ntdre)
+ next = sc->trans_ring + sc->trans_next;
+ }
+
+ /*
+ * Clear descriptors and free any mbufs.
+ */
+
+ do {
+ start = sc->trans_ring + start_of_packet;
+ start->md->md1 &= HADR;
+ if (sc->nic.mem_mode == DMA_MBUF) {
+ /* Cache clusters on a local queue */
+ if ((start->buff.mbuf->m_flags & M_EXT) && (sc->mbuf_count < MBUF_CACHE_LIMIT)) {
+ if (sc->mbuf_count) {
+ start->buff.mbuf->m_next = sc->mbufs;
+ sc->mbufs = start->buff.mbuf;
+ } else
+ sc->mbufs = start->buff.mbuf;
+ sc->mbuf_count++;
+ start->buff.mbuf = 0;
+ } else {
+ struct mbuf *junk;
+ MFREE(start->buff.mbuf, junk);
+ start->buff.mbuf = 0;
+ }
+ }
+ sc->pending_transmits--;
+ INC_MD_PTR(start_of_packet, sc->ntdre)
+ }while (start_of_packet != sc->trans_next);
+
+ /*
+ * There's now at least one free descriptor
+ * in the ring so indicate that we can accept
+ * more packets again.
+ */
+
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+
+ lookahead++;
+
+ } while (sc->pending_transmits && !(next->md->md1 & OWN));
+
+ /*
+ * Clear TINT since we've dealt with all
+ * the completed transmissions.
+ */
+
+ outw(sc->rdp, TINT | INEA);
+
+ /* XXX only while doing if_is comparisons */
+ if (!(sc->arpcom.ac_if.if_flags & IFF_OACTIVE))
+ lnc_start(&sc->arpcom.ac_if);
+
+}
+
+int
+lnc_probe(struct isa_device * isa_dev)
+{
+int nports;
+int vsw;
+
+#ifdef DEBUG
+{
+ int i;
+ log(LOG_DEBUG, "Dumping io space for lnc%d starting at %x\n", isa_dev->id_unit, isa_dev->id_iobase);
+ for (i = 0; i < 32; i++)
+ log(LOG_DEBUG, " %x ", inb(isa_dev->id_iobase + i));
+}
+#endif
+
+#ifdef DIAGNOSTIC
+ /* First check the Vendor Specific Word */
+ vsw = inw(isa_dev->id_iobase + PCNET_VSW);
+ printf("Vendor Specific Word = %x\n", vsw);
+#endif
+
+ if (nports = bicc_probe(isa_dev))
+ return (nports);
+ if (nports = ne2100_probe(isa_dev))
+ return (nports);
+ if (nports = depca_probe(isa_dev))
+ return (nports);
+
+ return (0);
+}
+
+int
+ne2100_probe(struct isa_device * isa_dev)
+{
+ struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit];
+ int i;
+
+ sc->rap = isa_dev->id_iobase + PCNET_RAP;
+ sc->rdp = isa_dev->id_iobase + PCNET_RDP;
+
+ if (sc->nic.ic = lance_probe(isa_dev->id_unit)) {
+ sc->nic.ident = NE2100;
+ sc->nic.mem_mode = DMA_FIXED;
+
+ /* Extract MAC address from PROM */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ sc->arpcom.ac_enaddr[i] = inb(isa_dev->id_iobase + i);
+
+ return (NE2100_IOSIZE);
+
+ } else {
+ return (0);
+ }
+}
+
+int
+bicc_probe(struct isa_device * isa_dev)
+{
+struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit];
+int i;
+
+ /*
+ * There isn't any way to determine if a NIC is a BICC. Basically, if
+ * the lance probe succeeds using the i/o addresses of the BICC then
+ * we assume it's a BICC.
+ *
+ */
+
+ sc->rap = isa_dev->id_iobase + BICC_RAP;
+ sc->rdp = isa_dev->id_iobase + BICC_RDP;
+
+ /* I think all these cards us the Am7990 */
+
+ if (sc->nic.ic = lance_probe(isa_dev->id_unit)) {
+ sc->nic.ident = BICC;
+ sc->nic.mem_mode = DMA_FIXED;
+
+ /* XXX - For now just use the defines */
+ sc->nrdre = NRDRE;
+ sc->ntdre = NTDRE;
+
+ /* Extract MAC address from PROM */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ sc->arpcom.ac_enaddr[i] = inb(isa_dev->id_iobase + (i * 2));
+ return (BICC_IOSIZE);
+ } else {
+ return (0);
+ }
+}
+
+/*
+ * I don't have data sheets for the dec cards but it looks like the mac
+ * address is contained in a 32 byte ring. Each time you read from the port
+ * you get the next byte in the ring. The mac address is stored after a
+ * signature so keep searching for the signature first.
+ */
+int
+dec_macaddr_extract(u_char ring[], struct lnc_softc * sc)
+{
+ const unsigned char signature[] = {0xff, 0x00, 0x55, 0xaa, 0xff, 0x00, 0x55, 0xaa};
+
+ int i, j, rindex;
+
+ for (i = 0; i < sizeof ring; i++) {
+ for (j = 0, rindex = i; j < sizeof signature; j++) {
+ if (ring[rindex] != signature[j])
+ break;
+ if (++rindex > sizeof ring)
+ rindex = 0;
+ }
+ if (j == sizeof signature) {
+ for (j = 0, rindex = i; j < ETHER_ADDR_LEN; j++) {
+ sc->arpcom.ac_enaddr[j] = ring[rindex];
+ if (++rindex > sizeof ring)
+ rindex = 0;
+ }
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int
+depca_probe(struct isa_device * isa_dev)
+{
+ int i;
+ struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit];
+ unsigned char maddr_ring[DEPCA_ADDR_ROM_SIZE];
+
+ sc->rap = isa_dev->id_iobase + DEPCA_RAP;
+ sc->rdp = isa_dev->id_iobase + DEPCA_RDP;
+
+ if (sc->nic.ic = lance_probe(isa_dev->id_unit)) {
+ sc->nic.ident = DEPCA;
+ sc->nic.mem_mode = SHMEM;
+
+ /* Extract MAC address from PROM */
+ for (i = 0; i < DEPCA_ADDR_ROM_SIZE; i++)
+ maddr_ring[i] = inb(isa_dev->id_iobase + DEPCA_ADP);
+ if (dec_macaddr_extract(maddr_ring, sc))
+ return (DEPCA_IOSIZE);
+ }
+ return (0);
+}
+
+int
+lance_probe(int unit)
+{
+ write_csr(unit, CSR0, STOP);
+
+ if ((inw(lnc_softc[unit].rdp) & STOP) && !(read_csr(unit, CSR3))) {
+ /*
+ * Check to see if it's a C-LANCE. For the LANCE the INEA bit
+ * cannot be set while the STOP bit is. This restriction is
+ * removed for the C-LANCE.
+ */
+ write_csr(unit, CSR0, INEA);
+ if (read_csr(unit, CSR0) & INEA)
+ return (C_LANCE);
+ else
+ return (LANCE);
+ } else
+ return (UNKNOWN);
+}
+
+int
+pcnet_probe(int unit)
+{
+ u_long chip_id;
+ int type;
+
+ /*
+ * The PCnet family don't reset the RAP register on reset so we'll
+ * have to write during the probe :-) It does have an ID register
+ * though so the probe is just a matter of reading it.
+ */
+
+ if (type = lance_probe(unit)) {
+
+ chip_id = read_csr(unit, CSR88);
+ chip_id <<= 16;
+ chip_id |= read_csr(unit, CSR89);
+
+ if (chip_id & AMD_MASK) {
+ chip_id >>= 12;
+ switch (chip_id & PART_MASK) {
+ case Am79C960:
+ return (PCnet_ISA);
+ case Am79C961:
+ return (PCnet_ISAplus);
+ case Am79C965:
+ return (PCnet_32);
+ case Am79C970:
+ return (PCnet_PCI);
+ default:
+ break;
+ }
+ }
+ }
+ return (type);
+}
+
+int
+lnc_attach(struct isa_device * isa_dev)
+{
+ struct lnc_softc *sc = &lnc_softc[isa_dev->id_unit];
+ int lnc_mem_size;
+
+ /*
+ * Allocate memory for use by the controller.
+ *
+ * XXX -- the Am7990 and Am79C960 only have 24 address lines and so can
+ * only access the lower 16Mb of physical memory. For the moment we
+ * assume that malloc will allocate memory within the lower 16Mb
+ * range. This is not a very valid assumption but there's nothing
+ * that can be done about it yet. For shared memory NICs this isn't
+ * relevant.
+ *
+ */
+
+ lnc_mem_size = ((NDESC(sc->nrdre) + NDESC(sc->ntdre)) *
+ sizeof(struct host_ring_entry));
+
+ if (sc->nic.mem_mode != SHMEM)
+ lnc_mem_size += sizeof(struct init_block) + (sizeof(struct mds) *
+ (NDESC(sc->nrdre) + NDESC(sc->ntdre))) +
+ MEM_SLEW;
+
+ /* If using DMA to fixed host buffers then allocate memory for them */
+
+ if (sc->nic.mem_mode == DMA_FIXED)
+ lnc_mem_size += (NDESC(sc->nrdre) * RECVBUFSIZE) + (NDESC(sc->ntdre) * TRANSBUFSIZE);
+
+ sc->recv_ring = malloc(lnc_mem_size, M_DEVBUF, M_NOWAIT);
+
+ if (!sc->recv_ring) {
+ log(LOG_ERR, "lnc%d: Couldn't allocate memory for NIC\n", isa_dev->id_unit);
+ return (0); /* XXX -- attach failed -- not tested in
+ * calling routines */
+ }
+ if ((sc->nic.mem_mode != SHMEM) && (kvtop(sc->recv_ring) > 0x1000000)) {
+ log(LOG_ERR, "lnc%d: Memory allocated above 16Mb limit\n", isa_dev->id_unit);
+ return (0);
+ }
+ if (sc->nic.mem_mode != SHMEM)
+ isa_dmacascade(isa_dev->id_drq);
+
+ /* Set default mode */
+ sc->nic.mode = NORMAL;
+
+ /* Fill in arpcom structure entries */
+
+ sc->arpcom.ac_if.if_name = lncdriver.name;
+ sc->arpcom.ac_if.if_unit = isa_dev->id_unit;
+ sc->arpcom.ac_if.if_mtu = ETHERMTU;
+ sc->arpcom.ac_if.if_flags = IFF_BROADCAST | IFF_NOTRAILERS | IFF_SIMPLEX;
+ sc->arpcom.ac_if.if_timer = 0;
+ sc->arpcom.ac_if.if_init = lnc_init;
+ sc->arpcom.ac_if.if_output = ether_output;
+ sc->arpcom.ac_if.if_start = lnc_start;
+ sc->arpcom.ac_if.if_ioctl = lnc_ioctl;
+ sc->arpcom.ac_if.if_reset = lnc_reset;
+ sc->arpcom.ac_if.if_watchdog = lnc_watchdog;
+ sc->arpcom.ac_if.if_type = IFT_ETHER;
+ sc->arpcom.ac_if.if_addrlen = ETHER_ADDR_LEN;
+ sc->arpcom.ac_if.if_hdrlen = ETHER_HDR_LEN;
+
+ /*
+ * XXX -- should check return status of if_attach
+ */
+
+ if_attach(&sc->arpcom.ac_if);
+
+ printf("lnc%d: %s, %s address %s\n",
+ isa_dev->id_unit,
+ nic_ident[sc->nic.ident],
+ ic_ident[sc->nic.ic],
+ ether_sprintf(sc->arpcom.ac_enaddr));
+
+#if NBPFILTER > 0
+ bpfattach(&sc->bpf, &sc->arpcom.ac_if, DLT_EN10MB, sizeof(struct ether_header));
+#endif
+
+ return (1);
+}
+
+void
+lnc_init(int unit)
+{
+ struct lnc_softc *sc = &lnc_softc[unit];
+ int s, i;
+ char *lnc_mem;
+ struct mbuf *m = 0;
+
+ /* Check that interface has valid address */
+
+ if (!sc->arpcom.ac_if.if_addrlist)
+ return;
+
+ /* Shut down interface */
+
+ s = splimp();
+ lnc_stop(unit);
+ sc->arpcom.ac_if.if_flags |= IFF_BROADCAST | IFF_NOTRAILERS | IFF_SIMPLEX;
+
+ /*
+ * This sets up the memory area for the controller. Memory is set up for
+ * the initialisation block (12 words of contiguous memory starting
+ * on a word boundary),the transmit and receive ring structures (each
+ * entry is 4 words long and must start on a quadword boundary) and
+ * the data buffers.
+ *
+ * The alignment tests are particularly paranoid.
+ */
+
+
+
+ sc->recv_next = 0;
+ sc->trans_ring = sc->recv_ring + NDESC(sc->nrdre);
+ sc->trans_next = 0;
+
+ if (sc->nic.mem_mode == SHMEM)
+ lnc_mem = (char *) sc->nic.iobase;
+ else
+ lnc_mem = (char *) (sc->trans_ring + NDESC(sc->ntdre));
+
+ lnc_mem = (char *)(((int)lnc_mem + 1) & ~1);
+ sc->init_block = (struct init_block *) ((int) lnc_mem & ~1);
+ lnc_mem = (char *) (sc->init_block + 1);
+ lnc_mem = (char *)(((int)lnc_mem + 7) & ~7);
+
+ /* Initialise pointers to descriptor entries */
+ for (i = 0; i < NDESC(sc->nrdre); i++) {
+ (sc->recv_ring + i)->md = (struct mds *) lnc_mem;
+ lnc_mem += sizeof(struct mds);
+ }
+ for (i = 0; i < NDESC(sc->ntdre); i++) {
+ (sc->trans_ring + i)->md = (struct mds *) lnc_mem;
+ lnc_mem += sizeof(struct mds);
+ }
+
+ /* Initialise the remaining ring entries */
+
+ if (sc->nic.mem_mode == DMA_MBUF) {
+
+ sc->mbufs = 0;
+ sc->mbuf_count = 0;
+
+ /* Free previously allocated mbufs */
+ if (sc->initialised)
+ lnc_free_mbufs(sc);
+
+
+ for (i = 0; i < NDESC(sc->nrdre); i++) {
+ if (alloc_mbuf_cluster(sc, sc->recv_ring+i)) {
+ log(LOG_ERR, "Initialisation failed -- no mbufs\n");
+ splx(s);
+ return;
+ }
+ }
+
+ for (i = 0; i < NDESC(sc->ntdre); i++) {
+ (sc->trans_ring + i)->buff.mbuf = 0;
+ (sc->trans_ring + i)->md->md0 = 0;
+ (sc->trans_ring + i)->md->md1 = 0;
+ (sc->trans_ring + i)->md->md2 = 0;
+ (sc->trans_ring + i)->md->md3 = 0;
+ }
+ } else {
+ for (i = 0; i < NDESC(sc->nrdre); i++) {
+ (sc->recv_ring + i)->md->md0 = kvtop(lnc_mem);
+ (sc->recv_ring + i)->md->md1 = ((kvtop(lnc_mem) >> 16) & 0xff) | OWN;
+ (sc->recv_ring + i)->md->md2 = -RECVBUFSIZE;
+ (sc->recv_ring + i)->md->md3 = 0;
+ (sc->recv_ring + i)->buff.data = lnc_mem;
+ lnc_mem += RECVBUFSIZE;
+ }
+ for (i = 0; i < NDESC(sc->ntdre); i++) {
+ (sc->trans_ring + i)->md->md0 = kvtop(lnc_mem);
+ (sc->trans_ring + i)->md->md1 = ((kvtop(lnc_mem) >> 16) & 0xff);
+ (sc->trans_ring + i)->md->md2 = 0;
+ (sc->trans_ring + i)->md->md3 = 0;
+ (sc->trans_ring + i)->buff.data = lnc_mem;
+ lnc_mem += TRANSBUFSIZE;
+ }
+ }
+
+ sc->next_to_send = 0;
+
+ /* Set up initialisation block */
+
+ sc->init_block->mode = sc->nic.mode;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ sc->init_block->padr[i] = sc->arpcom.ac_enaddr[i];
+
+ for (i = 0; i < MULTICAST_ADDR_LEN; i++)
+ sc->init_block->ladrf[i] = MULTI_INIT_ADDR;
+
+ sc->init_block->rdra = kvtop(sc->recv_ring->md);
+ sc->init_block->rlen = ((kvtop(sc->recv_ring->md) >> 16) & 0xff) | (sc->nrdre << 13);
+ sc->init_block->tdra = kvtop(sc->trans_ring->md);
+ sc->init_block->tlen = ((kvtop(sc->trans_ring->md) >> 16) & 0xff) | (sc->ntdre << 13);
+
+
+ /* Set initialised to show that the memory area is valid */
+ sc->initialised = 1;
+
+ sc->pending_transmits = 0;
+
+ /* Give the LANCE the physical address of the initialisation block */
+
+ write_csr(unit, CSR1, kvtop(sc->init_block));
+ write_csr(unit, CSR2, (kvtop(sc->init_block) >> 16) & 0xff);
+
+ /*
+ * Depending on which controller this is, CSR3 has different meanings.
+ * For the Am7990 it controls DMA operations, for the Am79C960 it
+ * controls interrupt masks and transmitter algorithms. In either
+ * case, none of the flags are set.
+ *
+ */
+
+ write_csr(unit, CSR3, 0);
+
+ /* Let's see if it starts */
+
+ write_csr(unit, CSR0, INIT);
+ for (i = 0; i < 1000; i++)
+ if (read_csr(unit, CSR0) & IDON)
+ break;
+
+ /*
+ * Now that the initialisation is complete there's no reason to
+ * access anything except CSR0, so we leave RAP pointing there
+ * so we can just access RDP from now on, saving an outw each
+ * time.
+ */
+
+ if (read_csr(unit, CSR0) & IDON) {
+ /*
+ * Enable interrupts, start the LANCE, mark the interface as
+ * running and transmit any pending packets.
+ */
+ write_csr(unit, CSR0, STRT | INEA);
+ sc->arpcom.ac_if.if_flags |= IFF_RUNNING;
+ sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
+ lnc_start(&sc->arpcom.ac_if);
+ } else
+ log(LOG_ERR, "lnc%d: Initialisation failed\n", unit);
+
+ splx(s);
+}
+
+/*
+ * The interrupt flag (INTR) will be set and provided that the interrupt enable
+ * flag (INEA) is also set, the interrupt pin will be driven low when any of
+ * the following occur:
+ *
+ * 1) Completion of the initialisation routine (IDON). 2) The reception of a
+ * packet (RINT). 3) The transmission of a packet (TINT). 4) A transmitter
+ * timeout error (BABL). 5) A missed packet (MISS). 6) A memory error (MERR).
+ *
+ * The interrupt flag is cleared when all of the above conditions are cleared.
+ *
+ * If the driver is reset from this routine then it first checks to see if any
+ * interrupts have ocurred since the reset and handles them before returning.
+ * This is because the NIC may signify a pending interrupt in CSR0 using the
+ * INTR flag even if a hardware interrupt is currently inhibited (at least I
+ * think it does from reading the data sheets). We may as well deal with
+ * these pending interrupts now rather than get the overhead of another
+ * hardware interrupt immediately upon returning from the interrupt handler.
+ *
+ */
+
+void
+lncintr(int unit)
+{
+ struct lnc_softc *sc = &lnc_softc[unit];
+ u_short csr0;
+
+ /*
+ * INEA is the only bit that can be cleared by writing a 0 to it so
+ * we have to include it in any writes that clear other flags.
+ */
+
+ while ((csr0 = inw(sc->rdp)) & INTR) {
+
+ /*
+ * Clear interrupt flags early to avoid race conditions. The
+ * controller can still set these flags even while we're in
+ * this interrupt routine. If the flag is still set from the
+ * event that caused this interrupt any new events will
+ * be missed.
+ */
+
+ outw(sc->rdp, IDON | CERR | BABL | MISS | MERR | RINT | TINT | INEA);
+
+ /* We don't do anything with the IDON flag */
+
+ if (csr0 & ERR) {
+ if (csr0 & CERR) {
+ log(LOG_ERR, "lnc%d: Heartbeat error -- SQE test failed\n", unit);
+ LNCSTATS(cerr)
+ }
+ if (csr0 & BABL) {
+ log(LOG_ERR, "lnc%d: Babble error - more than 1519 bytes transmitted\n", unit);
+ LNCSTATS(babl)
+ sc->arpcom.ac_if.if_oerrors++;
+ }
+ if (csr0 & MISS) {
+ log(LOG_ERR, "lnc%d: Missed packet -- no receive buffer\n", unit);
+ LNCSTATS(miss)
+ sc->arpcom.ac_if.if_ierrors++;
+ }
+ if (csr0 & MERR) {
+ log(LOG_ERR, "lnc%d: Memory error -- Resetting\n", unit);
+ LNCSTATS(merr)
+ lnc_reset(unit);
+ continue;
+ }
+ }
+ if (csr0 & RINT) {
+ LNCSTATS(rint)
+ lnc_rint(unit);
+ }
+ if (csr0 & TINT) {
+ LNCSTATS(tint)
+ sc->arpcom.ac_if.if_timer = 0;
+ lnc_tint(unit);
+ }
+
+ /*
+ * If there's room in the transmit descriptor ring then queue
+ * some more transmit packets.
+ */
+
+ if (!(sc->arpcom.ac_if.if_flags & IFF_OACTIVE))
+ lnc_start(&sc->arpcom.ac_if);
+ }
+}
+
+
+
+
+inline int
+mbuf_to_buffer(struct mbuf *m, char *buffer)
+{
+
+ int len=0;
+
+ for( ; m; m = m->m_next) {
+ bcopy(mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ len += m->m_len;
+ }
+
+ return(len);
+}
+
+inline struct mbuf *
+chain_to_cluster(struct mbuf *m)
+{
+ struct mbuf *new;
+
+ MGET(new, M_DONTWAIT, MT_DATA);
+ if (new) {
+ MCLGET(new, M_DONTWAIT);
+ if (new->m_ext.ext_buf) {
+ new->m_len = mbuf_to_buffer(m, new->m_data);
+ m_freem(m);
+ return(new);
+ } else
+ m_free(new);
+ }
+ return(0);
+}
+
+/*
+ * IFF_OACTIVE and IFF_RUNNING are checked in ether_output so it's redundant
+ * to check them again since we wouldn't have got here if they were not
+ * appropriately set. This is also called from lnc_init and lncintr but the
+ * flags should be ok at those points too.
+ */
+
+void
+lnc_start(struct ifnet *ifp)
+{
+
+ struct lnc_softc *sc = &lnc_softc[ifp->if_unit];
+ struct host_ring_entry *desc;
+ int tmp;
+ int end_of_packet;
+ struct mbuf *head, *m;
+ int len, chunk;
+ char *buffer;
+ int addr;
+ int no_entries_needed;
+
+ do {
+
+ IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, head);
+ if (!head)
+ return;
+
+ if (sc->nic.mem_mode == DMA_MBUF) {
+
+ no_entries_needed = 0;
+ for (m=head; m; m = m->m_next)
+ no_entries_needed++;
+
+ /*
+ * We try and avoid bcopy as much as possible
+ * but there are two cases when we use it.
+ *
+ * 1) If there are not enough free entries in the ring
+ * to hold each mbuf in the chain then compact the
+ * chain into a single cluster.
+ *
+ * 2) The Am7990 and Am79C90 must not have less than
+ * 100 bytes in the first descriptor of a chained
+ * packet so it's necessary to shuffle the mbuf
+ * contents to ensure this.
+ */
+
+
+ if (no_entries_needed > (NDESC(sc->ntdre) - sc->pending_transmits))
+ if (!(head = chain_to_cluster(head))) {
+ log(LOG_ERR, "lnc%d: Couldn't get mbuf for transmit packet -- Resetting \n ",ifp->if_unit);
+ lnc_reset(ifp->if_unit);
+ return;
+ }
+ else if ((sc->nic.ic == LANCE) || (sc->nic.ic == C_LANCE)) {
+ if ((head->m_len < 100) && (head->m_next)) {
+ len = 100 - head->m_len;
+ if (M_TRAILINGSPACE(head) < len) {
+ /*
+ * Move data to start of data
+ * area. We assume the first
+ * mbuf has a packet header
+ * and is not a cluster.
+ */
+ bcopy((caddr_t)head->m_data, (caddr_t)head->m_pktdat, head->m_len);
+ head->m_data = head->m_pktdat;
+ }
+ m = head->m_next;
+ while (m && (len > 0)) {
+ chunk = min(len, m->m_len);
+ bcopy(mtod(m, caddr_t), mtod(head, caddr_t) + head->m_len, chunk);
+ len -= chunk;
+ head->m_len += chunk;
+ m->m_len -= chunk;
+ m->m_data += chunk;
+ if (m->m_len <= 0) {
+ MFREE(m, head->m_next);
+ m = head->m_next;
+ }
+ }
+ }
+ }
+
+ tmp = sc->next_to_send;
+
+ /*
+ * On entering this loop we know that tmp points to a
+ * descriptor with a clear OWN bit.
+ */
+
+ desc = sc->trans_ring + tmp;
+ len = ETHER_MIN_LEN;
+ for (m = head; m; m = m->m_next) {
+ desc->buff.mbuf = m;
+ addr = kvtop(m->m_data);
+ desc->md->md0 = addr;
+ desc->md->md1 = ((addr >> 16) & 0xff);
+ desc->md->md3 = 0;
+ desc->md->md2 = -m->m_len;
+ sc->pending_transmits++;
+ len -= m->m_len;
+
+ INC_MD_PTR(tmp, sc->ntdre)
+ desc = sc->trans_ring + tmp;
+ }
+
+ end_of_packet = tmp;
+ DEC_MD_PTR(tmp, sc->ntdre)
+ desc = sc->trans_ring + tmp;
+ desc->md->md1 |= ENP;
+
+ if (len > 0)
+ desc->md->md2 -= len;
+
+ /*
+ * Set OWN bits in reverse order, otherwise the Lance
+ * could start sending the packet before all the
+ * buffers have been relinquished by the host.
+ */
+
+ while (tmp != sc->next_to_send) {
+ desc->md->md1 |= OWN;
+ DEC_MD_PTR(tmp, sc->ntdre)
+ desc = sc->trans_ring + tmp;
+ }
+ sc->next_to_send = end_of_packet;
+ desc->md->md1 |= STP | OWN;
+ } else {
+ sc->pending_transmits++;
+ desc = sc->trans_ring + sc->next_to_send;
+ len = mbuf_to_buffer(head, desc->buff.data);
+ desc->md->md3 = 0;
+ desc->md->md2 = -max(len, ETHER_MIN_LEN);
+ desc->md->md1 |= OWN | STP | ENP;
+ INC_MD_PTR(sc->next_to_send, sc->ntdre)
+ }
+
+ /* Force an immediate poll of the transmit ring */
+ outw(sc->rdp, TDMD | INEA);
+
+ /*
+ * Set a timer so if the buggy Am7990.h shuts
+ * down we can wake it up.
+ */
+
+ ifp->if_timer = 2;
+
+#if NBPFILTER > 0
+ if (sc->bpf)
+ bpf_mtap(sc->bpf, head);
+#endif
+
+ if (sc->nic.mem_mode != DMA_MBUF)
+ m_freem(head);
+
+ } while (sc->pending_transmits < NDESC(sc->ntdre));
+
+ /*
+ * Transmit ring is full so set IFF_OACTIVE
+ * since we can't buffer any more packets.
+ */
+
+ sc->arpcom.ac_if.if_flags |= IFF_OACTIVE;
+ LNCSTATS(trans_ring_full)
+}
+
+int
+lnc_ioctl(struct ifnet * ifp, int command, caddr_t data)
+{
+
+ struct lnc_softc *sc = &lnc_softc[ifp->if_unit];
+ struct ifaddr *ifa = (struct ifaddr *) data;
+ struct ifreq *ifr = (struct ifreq *) data;
+ int s, error = 0;
+
+ s = splimp();
+
+ switch (command) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ switch (ifa->ifa_addr->sa_family) {
+#ifdef INET
+ case AF_INET:
+ lnc_init(ifp->if_unit);
+ ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+ default:
+ lnc_init(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+#ifdef DEBUG
+ if (ifp->if_flags & IFF_DEBUG)
+ sc->lnc_debug = 1;
+ else
+ sc->lnc_debug = 0;
+#endif
+ if (ifp->if_flags & IFF_PROMISC) {
+ if (!(sc->nic.mode & PROM)) {
+ sc->nic.mode |= PROM;
+ lnc_init(ifp->if_unit);
+ }
+ } else if (sc->nic.mode & PROM) {
+ sc->nic.mode &= ~PROM;
+ lnc_init(ifp->if_unit);
+ }
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ (ifp->if_flags & IFF_RUNNING) != 0) {
+ /*
+ * If interface is marked down and it is running,
+ * then stop it.
+ */
+ lnc_stop(ifp->if_unit);
+ ifp->if_flags &= ~IFF_RUNNING;
+ } else if ((ifp->if_flags & IFF_UP) != 0 &&
+ (ifp->if_flags & IFF_RUNNING) == 0) {
+ /*
+ * If interface is marked up and it is stopped, then
+ * start it.
+ */
+ lnc_init(ifp->if_unit);
+ }
+ break;
+#ifdef notyet
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ error = (command == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &sc->arpcom) :
+ ether_delmulti(ifr, &sc->arpcom);
+
+ if (error == ENETRESET) {
+ lnc_setladrf(ifp,sc);
+ error = 0;
+ }
+ break;
+#endif
+ case SIOCSIFMTU:
+ /*
+ * Set the interface MTU.
+ */
+
+ if (ifr->ifr_mtu > ETHERMTU) {
+ error = EINVAL;
+ } else
+ ifp->if_mtu = ifr->ifr_mtu;
+ break;
+ default:
+ error = EINVAL;
+ }
+ (void) splx(s);
+ return error;
+}
+
+void
+lnc_watchdog(int unit)
+{
+ log(LOG_ERR, "lnc%d: Device timeout -- Resetting\n", unit);
+ ++lnc_softc[unit].arpcom.ac_if.if_oerrors;
+ lnc_reset(unit);
+}
+
+#ifdef DEBUG
+void
+lnc_dump_state(int unit)
+{
+ struct lnc_softc *sc = &lnc_softc[unit];
+ int i;
+
+ log(LOG_DEBUG, "\nDriver/NIC [%d] state dump\n", unit);
+ log(LOG_DEBUG, "Memory access mode: %b\n", sc->nic.mem_mode, MEM_MODES);
+ log(LOG_DEBUG, "Host memory\n");
+ log(LOG_DEBUG, "-----------\n");
+
+ log(LOG_DEBUG, "Receive ring: base = %x, next = %x\n",
+ sc->recv_ring, (sc->recv_ring + sc->recv_next));
+ for (i = 0; i < NDESC(sc->nrdre); i++)
+ log(LOG_DEBUG, "\t%d:%x md = %x buff = %x\n",
+ i, sc->recv_ring + i, (sc->recv_ring + i)->md,
+ (sc->recv_ring + i)->buff);
+
+ log(LOG_DEBUG, "Transmit ring: base = %x, next = %x\n",
+ sc->trans_ring, (sc->trans_ring + sc->trans_next));
+ for (i = 0; i < NDESC(sc->ntdre); i++)
+ log(LOG_DEBUG, "\t%d:%x md = %x buff = %x\n",
+ i, sc->trans_ring + i, (sc->trans_ring + i)->md,
+ (sc->trans_ring + i)->buff);
+ log(LOG_DEBUG, "Lance memory (may be on host(DMA) or card(SHMEM))\n");
+ log(LOG_DEBUG, "Init block = %x\n", sc->init_block);
+ log(LOG_DEBUG, "\tmode = %b rlen:rdra = %x:%x tlen:tdra = %x:%x\n",
+ sc->init_block->mode, INIT_MODE, sc->init_block->rlen,
+ sc->init_block->rdra, sc->init_block->tlen, sc->init_block->tdra);
+ log(LOG_DEBUG, "Receive descriptor ring\n");
+ for (i = 0; i < NDESC(sc->nrdre); i++)
+ log(LOG_DEBUG, "\t%d buffer = 0x%x%x, BCNT = %d,\tMCNT = %u,\tflags = %b\n",
+ i, ((sc->recv_ring + i)->md->md1 & HADR),
+ (sc->recv_ring + i)->md->md0,
+ -(short) (sc->recv_ring + i)->md->md2,
+ (sc->recv_ring + i)->md->md3,
+ (((sc->recv_ring + i)->md->md1 & ~HADR) >> 8), RECV_MD1);
+ log(LOG_DEBUG, "Transmit descriptor ring\n");
+ for (i = 0; i < NDESC(sc->ntdre); i++)
+ log(LOG_DEBUG, "\t%d buffer = 0x%x%x, BCNT = %d,\tflags = %b %b\n",
+ i, ((sc->trans_ring + i)->md->md1 & HADR),
+ (sc->trans_ring + i)->md->md0,
+ -(short) (sc->trans_ring + i)->md->md2,
+ ((sc->trans_ring + i)->md->md1 >> 8), TRANS_MD1,
+ ((sc->trans_ring + i)->md->md3 >> 10), TRANS_MD3);
+ log(LOG_DEBUG, "\nnext_to_send = %x\n", sc->next_to_send);
+ log(LOG_DEBUG, "\n CSR0 = %b CSR1 = %x CSR2 = %x CSR3 = %x\n\n", read_csr(unit, CSR0), CSR0_FLAGS, read_csr(unit, CSR1), read_csr(unit, CSR2), read_csr(unit, CSR3));
+ /* Set RAP back to CSR0 */
+ outw(sc->rap, CSR0);
+}
+
+void
+mbuf_dump_chain(struct mbuf * m)
+{
+
+#define MBUF_FLAGS \
+ "\20\1M_EXT\2M_PKTHDR\3M_EOR\4UNKNOWN\5M_BCAST\6M_MCAST"
+
+ if (!m)
+ log(LOG_DEBUG, "m == NULL\n");
+ do {
+ log(LOG_DEBUG, "m = %x\n", m);
+ log(LOG_DEBUG, "m_hdr.mh_next = %x\n", m->m_hdr.mh_next);
+ log(LOG_DEBUG, "m_hdr.mh_nextpkt = %x\n", m->m_hdr.mh_nextpkt);
+ log(LOG_DEBUG, "m_hdr.mh_len = %d\n", m->m_hdr.mh_len);
+ log(LOG_DEBUG, "m_hdr.mh_data = %x\n", m->m_hdr.mh_data);
+ log(LOG_DEBUG, "m_hdr.mh_type = %d\n", m->m_hdr.mh_type);
+ log(LOG_DEBUG, "m_hdr.mh_flags = %b\n", m->m_hdr.mh_flags, MBUF_FLAGS);
+ if (!(m->m_hdr.mh_flags & (M_PKTHDR | M_EXT)))
+ log(LOG_DEBUG, "M_dat.M_databuf = %x\n", m->M_dat.M_databuf);
+ else {
+ if (m->m_hdr.mh_flags & M_PKTHDR) {
+ log(LOG_DEBUG, "M_dat.MH.MH_pkthdr.len = %d\n", m->M_dat.MH.MH_pkthdr.len);
+ log(LOG_DEBUG, "M_dat.MH.MH_pkthdr.rcvif = %x\n", m->M_dat.MH.MH_pkthdr.rcvif);
+ if (!(m->m_hdr.mh_flags & M_EXT))
+ log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_databuf = %x\n", m->M_dat.MH.MH_dat.MH_databuf);
+ }
+ if (m->m_hdr.mh_flags & M_EXT) {
+ log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_buff %x\n", m->M_dat.MH.MH_dat.MH_ext.ext_buf);
+ log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_free %x\n", m->M_dat.MH.MH_dat.MH_ext.ext_free);
+ log(LOG_DEBUG, "M_dat.MH.MH_dat.MH_ext.ext_size %d\n", m->M_dat.MH.MH_dat.MH_ext.ext_size);
+ }
+ }
+ } while (m = m->m_next);
+}
+#endif
+
+#endif
diff --git a/sys/i386/isa/if_lnc.h b/sys/i386/isa/if_lnc.h
new file mode 100644
index 000000000000..c555ad8dd6e6
--- /dev/null
+++ b/sys/i386/isa/if_lnc.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 1994, Paul Richards. This software may be used, modified,
+ * copied, distributed, and sold, in both source and binary form provided
+ * that the above copyright and these terms are retained. Under no
+ * circumstances is the author responsible for the proper functioning
+ * of this software, nor does the author assume any responsibility
+ * for damages incurred with its use.
+ *
+ */
+
+#include "ic/Am7990.h"
+
+/*
+ * Initialize multicast address hashing registers to accept
+ * all multicasts (only used when in promiscuous mode)
+ */
+#if NBPFILTER > 0
+#define MULTI_INIT_ADDR 0xff
+#else
+#define MULTI_INIT_ADDR 0
+#endif
+
+#define NORMAL 0
+
+#define NRDRE 3
+#define NTDRE 3
+#define RECVBUFSIZE 1518 /* Packet size rounded to dword boundary */
+#define TRANSBUFSIZE 1518
+#define MBUF_CACHE_LIMIT 0
+
+#define MEM_SLEW 8
+
+
+/* BICC port addresses */
+#define BICC_IOSIZE 16
+#define BICC_RDP 0x0c /* Register Data Port */
+#define BICC_RAP 0x0e /* Register Address Port */
+
+/* NE2100 port addresses */
+#define NE2100_IOSIZE 24
+#define PCNET_RDP 0x10 /* Register Data Port */
+#define PCNET_RAP 0x12 /* Register Address Port */
+#define PCNET_RESET 0x14
+#define PCNET_IDP 0x16
+#define PCNET_VSW 0x18
+
+/* DEPCA port addresses */
+#define DEPCA_IOSIZE 16
+#define DEPCA_CTRL 0x00 /* NIC Control and status register */
+#define DEPCA_RDP 0x04 /* Register Data Port */
+#define DEPCA_RAP 0x06 /* Register Address Port */
+#define DEPCA_ADP 0x0c
+
+/* DEPCA specific defines */
+#define DEPCA_ADDR_ROM_SIZE 32
+
+/* Chip types */
+#define LANCE 1 /* Am7990 */
+#define C_LANCE 2 /* Am79C90 */
+#define PCnet_ISA 3 /* Am79C960 */
+#define PCnet_ISAplus 4 /* Am79C961 */
+#define PCnet_32 5 /* Am79C965 */
+#define PCnet_PCI 6 /* Am79C970 */
+
+/* CSR88-89: Chip ID masks */
+#define AMD_MASK 0x003
+#define PART_MASK 0xffff
+#define Am79C960 0x0003
+#define Am79C961 0x2260
+#define Am79C965 0x2430
+#define Am79C970 0x0242
+
+/* Board types */
+#define UNKNOWN 0
+#define BICC 1
+#define NE2100 2
+#define DEPCA 3
+
+/* mem_mode values */
+#define DMA_FIXED 1
+#define DMA_MBUF 2
+#define SHMEM 4
+
+#define MEM_MODES \
+ "\20\3SHMEM\2DMA_MBUF\1DMA_FIXED"
+
+#define CSR0_FLAGS \
+ "\20\20ERR\17BABL\16CERR\15MISS\14MERR\13RINT\12TINT\11IDON\
+ \10INTR\07INEA\06RXON\05TXON\04TDMD\03STOP\02STRT\01INIT"
+
+#define INIT_MODE \
+ "\20\20PROM\07INTL\06DRTY\05COLL\04DTCR\03LOOP\02DTX\01DRX"
+
+#define RECV_MD1 \
+ "\20\10OWN\7ERR\6FRAM\5OFLO\4CRC\3BUFF\2STP\1ENP"
+
+#define TRANS_MD1 \
+ "\20\10OWN\7ERR\6RES\5MORE\4ONE\3DEF\2STP\1ENP"
+
+#define TRANS_MD3 \
+ "\20\6BUFF\5UFLO\4RES\3LCOL\2LCAR\1RTRY"
+
+char *nic_ident[] = {"Unknown",
+ "BICC",
+ "NE2100",
+ "DEPCA"};
+
+char *ic_ident[] = {"Unknown",
+ "LANCE, Am7990",
+ "C-LANCE, Am79C90",
+ "PCnet-ISA, Am79C960",
+ "PCnet-ISA+, Am79C961",
+ "PCnet-32, Am79C965",
+ "PCnet-PCI, Am79C970"};
+
+struct nic_info {
+ int ident; /* Type of card */
+ int ic; /* Type of ic, Am7990, Am79C960 etc. */
+ int mem_mode;
+ int iobase;
+ int mode; /* Mode setting at initialisation */
+};
+
+struct host_ring_entry {
+ struct mds *md;
+ union {
+ struct mbuf *mbuf;
+ char *data;
+ }buff;
+};
+
+#ifdef LNC_KEEP_STATS
+#define LNCSTATS_STRUCT \
+ struct lnc_stats { \
+ int idon; \
+ int rint; \
+ int tint; \
+ int cerr; \
+ int babl; \
+ int miss; \
+ int merr; \
+ int rxoff; \
+ int txoff; \
+ int terr; \
+ int lcol; \
+ int lcar; \
+ int tbuff; \
+ int def; \
+ int more; \
+ int one; \
+ int uflo; \
+ int rtry; \
+ int rerr; \
+ int fram; \
+ int oflo; \
+ int crc; \
+ int rbuff; \
+ int drop_packet; \
+ int trans_ring_full; \
+ } lnc_stats;
+#define LNCSTATS(X) ++(sc->lnc_stats.X);
+#else
+#define LNCSTATS_STRUCT
+#define LNCSTATS(X)
+#endif
+
+#define NDESC(len2) (1 << len2)
+
+#define INC_MD_PTR(ptr, no_entries) \
+ if (++ptr >= NDESC(no_entries)) \
+ ptr = 0;
+
+#define DEC_MD_PTR(ptr, no_entries) \
+ if (--ptr < 0) \
+ ptr = NDESC(no_entries) - 1;
+
+#define RECV_NEXT (sc->recv_ring->base + sc->recv_next)
+#define TRANS_NEXT (sc->trans_ring->base + sc->trans_next)
diff --git a/sys/i386/isa/npx.c.orig b/sys/i386/isa/npx.c.orig
new file mode 100644
index 000000000000..dd425d7ed84c
--- /dev/null
+++ b/sys/i386/isa/npx.c.orig
@@ -0,0 +1,555 @@
+/*-
+ * Copyright (c) 1990 William Jolitz.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: @(#)npx.c 7.2 (Berkeley) 5/12/91
+ * $Id: npx.c,v 1.11 1994/09/09 23:12:32 wollman Exp $
+ */
+
+#include "npx.h"
+#if NNPX > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <machine/cpu.h>
+#include <machine/pcb.h>
+#include <machine/trap.h>
+#include <sys/ioctl.h>
+#include <machine/specialreg.h>
+#include <i386/isa/icu.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/isa.h>
+
+/*
+ * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
+ */
+
+#ifdef __GNUC__
+
+#define disable_intr() __asm("cli")
+#define enable_intr() __asm("sti")
+#define fldcw(addr) __asm("fldcw %0" : : "m" (*addr))
+#define fnclex() __asm("fnclex")
+#define fninit() __asm("fninit")
+#define fnsave(addr) __asm("fnsave %0" : "=m" (*addr) : "0" (*addr))
+#define fnstcw(addr) __asm("fnstcw %0" : "=m" (*addr) : "0" (*addr))
+#define fnstsw(addr) __asm("fnstsw %0" : "=m" (*addr) : "0" (*addr))
+#define fp_divide_by_0() __asm("fldz; fld1; fdiv %st,%st(1); fwait")
+#define frstor(addr) __asm("frstor %0" : : "m" (*addr))
+#define fwait() __asm("fwait")
+#define read_eflags() ({u_long ef; \
+ __asm("pushf; popl %0" : "=a" (ef)); \
+ ef; })
+#define start_emulating() __asm("smsw %%ax; orb %0,%%al; lmsw %%ax" \
+ : : "n" (CR0_TS) : "ax")
+#define stop_emulating() __asm("clts")
+#define write_eflags(ef) __asm("pushl %0; popf" : : "a" ((u_long) ef))
+
+#else /* not __GNUC__ */
+
+void disable_intr __P((void));
+void enable_intr __P((void));
+void fldcw __P((caddr_t addr));
+void fnclex __P((void));
+void fninit __P((void));
+void fnsave __P((caddr_t addr));
+void fnstcw __P((caddr_t addr));
+void fnstsw __P((caddr_t addr));
+void fp_divide_by_0 __P((void));
+void frstor __P((caddr_t addr));
+void fwait __P((void));
+u_long read_eflags __P((void));
+void start_emulating __P((void));
+void stop_emulating __P((void));
+void write_eflags __P((u_long ef));
+
+#endif /* __GNUC__ */
+
+typedef u_char bool_t;
+
+extern struct gate_descriptor idt[];
+
+int npxdna __P((void));
+void npxexit __P((struct proc *p));
+void npxinit __P((u_int control));
+void npxintr __P((struct intrframe frame));
+void npxsave __P((struct save87 *addr));
+static int npxattach __P((struct isa_device *dvp));
+static int npxprobe __P((struct isa_device *dvp));
+static int npxprobe1 __P((struct isa_device *dvp));
+
+struct isa_driver npxdriver = {
+ npxprobe, npxattach, "npx",
+};
+
+u_int npx0_imask;
+struct proc *npxproc;
+
+static bool_t npx_ex16;
+static bool_t npx_exists;
+int hw_float;
+static struct gate_descriptor npx_idt_probeintr;
+static int npx_intrno;
+static volatile u_int npx_intrs_while_probing;
+static bool_t npx_irq13;
+static volatile u_int npx_traps_while_probing;
+
+/*
+ * Special interrupt handlers. Someday intr0-intr15 will be used to count
+ * interrupts. We'll still need a special exception 16 handler. The busy
+ * latch stuff in probintr() can be moved to npxprobe().
+ */
+void probeintr(void);
+asm
+("
+ .text
+_probeintr:
+ ss
+ incl _npx_intrs_while_probing
+ pushl %eax
+ movb $0x20,%al # EOI (asm in strings loses cpp features)
+ outb %al,$0xa0 # IO_ICU2
+ outb %al,$0x20 #IO_ICU1
+ movb $0,%al
+ outb %al,$0xf0 # clear BUSY# latch
+ popl %eax
+ iret
+");
+
+void probetrap(void);
+asm
+("
+ .text
+_probetrap:
+ ss
+ incl _npx_traps_while_probing
+ fnclex
+ iret
+");
+
+/*
+ * Probe routine. Initialize cr0 to give correct behaviour for [f]wait
+ * whether the device exists or not (XXX should be elsewhere). Set flags
+ * to tell npxattach() what to do. Modify device struct if npx doesn't
+ * need to use interrupts. Return 1 if device exists.
+ */
+static int
+npxprobe(dvp)
+ struct isa_device *dvp;
+{
+ int result;
+ u_long save_eflags;
+ u_char save_icu1_mask;
+ u_char save_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+ struct gate_descriptor save_idt_npxtrap;
+ /*
+ * This routine is now just a wrapper for npxprobe1(), to install
+ * special npx interrupt and trap handlers, to enable npx interrupts
+ * and to disable other interrupts. Someday isa_configure() will
+ * install suitable handlers and run with interrupts enabled so we
+ * won't need to do so much here.
+ */
+ npx_intrno = NRSVIDT + ffs(dvp->id_irq) - 1;
+ save_eflags = read_eflags();
+ disable_intr();
+ save_icu1_mask = inb(IO_ICU1 + 1);
+ save_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ save_idt_npxtrap = idt[16];
+ outb(IO_ICU1 + 1, ~(IRQ_SLAVE | dvp->id_irq));
+ outb(IO_ICU2 + 1, ~(dvp->id_irq >> 8));
+ setidt(16, probetrap, SDT_SYS386TGT, SEL_KPL);
+ setidt(npx_intrno, probeintr, SDT_SYS386IGT, SEL_KPL);
+ npx_idt_probeintr = idt[npx_intrno];
+ enable_intr();
+ result = npxprobe1(dvp);
+ disable_intr();
+ outb(IO_ICU1 + 1, save_icu1_mask);
+ outb(IO_ICU2 + 1, save_icu2_mask);
+ idt[npx_intrno] = save_idt_npxintr;
+ idt[16] = save_idt_npxtrap;
+ write_eflags(save_eflags);
+ return (result);
+}
+
+static int
+npxprobe1(dvp)
+ struct isa_device *dvp;
+{
+ int control;
+ int status;
+#ifdef lint
+ npxintr();
+#endif
+ /*
+ * Partially reset the coprocessor, if any. Some BIOS's don't reset
+ * it after a warm boot.
+ */
+ outb(0xf1, 0); /* full reset on some systems, NOP on others */
+ outb(0xf0, 0); /* clear BUSY# latch */
+ /*
+ * Prepare to trap all ESC (i.e., NPX) instructions and all WAIT
+ * instructions. We must set the CR0_MP bit and use the CR0_TS
+ * bit to control the trap, because setting the CR0_EM bit does
+ * not cause WAIT instructions to trap. It's important to trap
+ * WAIT instructions - otherwise the "wait" variants of no-wait
+ * control instructions would degenerate to the "no-wait" variants
+ * after FP context switches but work correctly otherwise. It's
+ * particularly important to trap WAITs when there is no NPX -
+ * otherwise the "wait" variants would always degenerate.
+ *
+ * Try setting CR0_NE to get correct error reporting on 486DX's.
+ * Setting it should fail or do nothing on lesser processors.
+ */
+ load_cr0(rcr0() | CR0_MP | CR0_NE);
+ /*
+ * But don't trap while we're probing.
+ */
+ stop_emulating();
+ /*
+ * Finish resetting the coprocessor, if any. If there is an error
+ * pending, then we may get a bogus IRQ13, but probeintr() will handle
+ * it OK. Bogus halts have never been observed, but we enabled
+ * IRQ13 and cleared the BUSY# latch early to handle them anyway.
+ */
+ fninit();
+ DELAY(1000); /* wait for any IRQ13 (fwait might hang) */
+#ifdef DIAGNOSTIC
+ if (npx_intrs_while_probing != 0)
+ printf("fninit caused %u bogus npx interrupt(s)\n",
+ npx_intrs_while_probing);
+ if (npx_traps_while_probing != 0)
+ printf("fninit caused %u bogus npx trap(s)\n",
+ npx_traps_while_probing);
+#endif
+ /*
+ * Check for a status of mostly zero.
+ */
+ status = 0x5a5a;
+ fnstsw(&status);
+ if ((status & 0xb8ff) == 0) {
+ /*
+ * Good, now check for a proper control word.
+ */
+ control = 0x5a5a;
+ fnstcw(&control);
+ if ((control & 0x1f3f) == 0x033f) {
+ hw_float = npx_exists = 1;
+ /*
+ * We have an npx, now divide by 0 to see if exception
+ * 16 works.
+ */
+ control &= ~(1 << 2); /* enable divide by 0 trap */
+ fldcw(&control);
+ npx_traps_while_probing = npx_intrs_while_probing = 0;
+ fp_divide_by_0();
+ if (npx_traps_while_probing != 0) {
+ /*
+ * Good, exception 16 works.
+ */
+ npx_ex16 = 1;
+ dvp->id_irq = 0; /* zap the interrupt */
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+ }
+ if (npx_intrs_while_probing != 0) {
+ /*
+ * Bad, we are stuck with IRQ13.
+ */
+ npx_irq13 = 1;
+ npx0_imask = dvp->id_irq; /* npxattach too late */
+ return (IO_NPXSIZE);
+ }
+ /*
+ * Worse, even IRQ13 is broken. Use emulator.
+ */
+ }
+ }
+ /*
+ * Probe failed, but we want to get to npxattach to initialize the
+ * emulator and say that it has been installed. XXX handle devices
+ * that aren't really devices better.
+ */
+ dvp->id_irq = 0;
+ /*
+ * special return value to flag that we do not
+ * actually use any I/O registers
+ */
+ return (-1);
+}
+
+/*
+ * Attach routine - announce which it is, and wire into system
+ */
+int
+npxattach(dvp)
+ struct isa_device *dvp;
+{
+ if (!npx_ex16 && !npx_irq13) {
+ if (npx_exists) {
+ printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit);
+ hw_float = npx_exists = 0;
+ } else {
+ printf("npx%d: 387 Emulator\n",dvp->id_unit);
+ }
+ }
+ npxinit(__INITIAL_NPXCW__);
+ return (1); /* XXX unused */
+}
+
+/*
+ * Initialize floating point unit.
+ */
+void
+npxinit(control)
+ u_int control;
+{
+ struct save87 dummy;
+
+ if (!npx_exists)
+ return;
+ /*
+ * fninit has the same h/w bugs as fnsave. Use the detoxified
+ * fnsave to throw away any junk in the fpu. fnsave initializes
+ * the fpu and sets npxproc = NULL as important side effects.
+ */
+ npxsave(&dummy);
+ stop_emulating();
+ fldcw(&control);
+ if (curpcb != NULL)
+ fnsave(&curpcb->pcb_savefpu);
+ start_emulating();
+}
+
+/*
+ * Free coprocessor (if we have it).
+ */
+void
+npxexit(p)
+ struct proc *p;
+{
+
+ if (p == npxproc) {
+ start_emulating();
+ npxproc = NULL;
+ }
+}
+
+/*
+ * Record the FPU state and reinitialize it all except for the control word.
+ * Then generate a SIGFPE.
+ *
+ * Reinitializing the state allows naive SIGFPE handlers to longjmp without
+ * doing any fixups.
+ *
+ * XXX there is currently no way to pass the full error state to signal
+ * handlers, and if this is a nested interrupt there is no way to pass even
+ * a status code! So there is no way to have a non-naive SIGFPE handler. At
+ * best a handler could do an fninit followed by an fldcw of a static value.
+ * fnclex would be of little use because it would leave junk on the FPU stack.
+ * Returning from the handler would be even less safe than usual because
+ * IRQ13 exception handling makes exceptions even less precise than usual.
+ */
+void
+npxintr(frame)
+ struct intrframe frame;
+{
+ int code;
+
+ if (npxproc == NULL || !npx_exists) {
+ /* XXX no %p in stand/printf.c. Cast to quiet gcc -Wall. */
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from nowhere");
+ }
+ if (npxproc != curproc) {
+ printf("npxintr: npxproc = %lx, curproc = %lx, npx_exists = %d\n",
+ (u_long) npxproc, (u_long) curproc, npx_exists);
+ panic("npxintr from non-current process");
+ }
+ /*
+ * Save state. This does an implied fninit. It had better not halt
+ * the cpu or we'll hang.
+ */
+ outb(0xf0, 0);
+ fnsave(&curpcb->pcb_savefpu);
+ fwait();
+ /*
+ * Restore control word (was clobbered by fnsave).
+ */
+ fldcw(&curpcb->pcb_savefpu.sv_env.en_cw);
+ fwait();
+ /*
+ * Remember the exception status word and tag word. The current
+ * (almost fninit'ed) fpu state is in the fpu and the exception
+ * state just saved will soon be junk. However, the implied fninit
+ * doesn't change the error pointers or register contents, and we
+ * preserved the control word and will copy the status and tag
+ * words, so the complete exception state can be recovered.
+ */
+ curpcb->pcb_savefpu.sv_ex_sw = curpcb->pcb_savefpu.sv_env.en_sw;
+ curpcb->pcb_savefpu.sv_ex_tw = curpcb->pcb_savefpu.sv_env.en_tw;
+
+ /*
+ * Pass exception to process.
+ */
+ if (ISPL(frame.if_cs) == SEL_UPL) {
+ /*
+ * Interrupt is essentially a trap, so we can afford to call
+ * the SIGFPE handler (if any) as soon as the interrupt
+ * returns.
+ *
+ * XXX little or nothing is gained from this, and plenty is
+ * lost - the interrupt frame has to contain the trap frame
+ * (this is otherwise only necessary for the rescheduling trap
+ * in doreti, and the frame for that could easily be set up
+ * just before it is used).
+ */
+ curproc->p_md.md_regs = (int *)&frame.if_es;
+#ifdef notyet
+ /*
+ * Encode the appropriate code for detailed information on
+ * this exception.
+ */
+ code = XXX_ENCODE(curpcb->pcb_savefpu.sv_ex_sw);
+#else
+ code = 0; /* XXX */
+#endif
+ trapsignal(curproc, SIGFPE, code);
+ } else {
+ /*
+ * Nested interrupt. These losers occur when:
+ * o an IRQ13 is bogusly generated at a bogus time, e.g.:
+ * o immediately after an fnsave or frstor of an
+ * error state.
+ * o a couple of 386 instructions after
+ * "fstpl _memvar" causes a stack overflow.
+ * These are especially nasty when combined with a
+ * trace trap.
+ * o an IRQ13 occurs at the same time as another higher-
+ * priority interrupt.
+ *
+ * Treat them like a true async interrupt.
+ */
+ psignal(npxproc, SIGFPE);
+ }
+}
+
+/*
+ * Implement device not available (DNA) exception
+ *
+ * It would be better to switch FP context here (only). This would require
+ * saving the state in the proc table instead of in the pcb.
+ */
+int
+npxdna()
+{
+ if (!npx_exists)
+ return (0);
+ if (npxproc != NULL) {
+ printf("npxdna: npxproc = %lx, curproc = %lx\n",
+ (u_long) npxproc, (u_long) curproc);
+ panic("npxdna");
+ }
+ stop_emulating();
+ /*
+ * Record new context early in case frstor causes an IRQ13.
+ */
+ npxproc = curproc;
+ /*
+ * The following frstor may cause an IRQ13 when the state being
+ * restored has a pending error. The error will appear to have been
+ * triggered by the current (npx) user instruction even when that
+ * instruction is a no-wait instruction that should not trigger an
+ * error (e.g., fnclex). On at least one 486 system all of the
+ * no-wait instructions are broken the same as frstor, so our
+ * treatment does not amplify the breakage. On at least one
+ * 386/Cyrix 387 system, fnclex works correctly while frstor and
+ * fnsave are broken, so our treatment breaks fnclex if it is the
+ * first FPU instruction after a context switch.
+ */
+ frstor(&curpcb->pcb_savefpu);
+
+ return (1);
+}
+
+/*
+ * Wrapper for fnsave instruction to handle h/w bugs. If there is an error
+ * pending, then fnsave generates a bogus IRQ13 on some systems. Force
+ * any IRQ13 to be handled immediately, and then ignore it. This routine is
+ * often called at splhigh so it must not use many system services. In
+ * particular, it's much easier to install a special handler than to
+ * guarantee that it's safe to use npxintr() and its supporting code.
+ */
+void
+npxsave(addr)
+ struct save87 *addr;
+{
+ u_char icu1_mask;
+ u_char icu2_mask;
+ u_char old_icu1_mask;
+ u_char old_icu2_mask;
+ struct gate_descriptor save_idt_npxintr;
+
+ disable_intr();
+ old_icu1_mask = inb(IO_ICU1 + 1);
+ old_icu2_mask = inb(IO_ICU2 + 1);
+ save_idt_npxintr = idt[npx_intrno];
+ outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask));
+ outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8));
+ idt[npx_intrno] = npx_idt_probeintr;
+ enable_intr();
+ stop_emulating();
+ fnsave(addr);
+ fwait();
+ start_emulating();
+ npxproc = NULL;
+ disable_intr();
+ icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */
+ icu2_mask = inb(IO_ICU2 + 1);
+ outb(IO_ICU1 + 1,
+ (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask));
+ outb(IO_ICU2 + 1,
+ (icu2_mask & ~(npx0_imask >> 8))
+ | (old_icu2_mask & (npx0_imask >> 8)));
+ idt[npx_intrno] = save_idt_npxintr;
+ enable_intr(); /* back to usual state */
+}
+
+#endif /* NNPX > 0 */