summaryrefslogtreecommitdiff
path: root/chips/lance.c
diff options
context:
space:
mode:
Diffstat (limited to 'chips/lance.c')
-rw-r--r--chips/lance.c1570
1 files changed, 1570 insertions, 0 deletions
diff --git a/chips/lance.c b/chips/lance.c
new file mode 100644
index 0000000..750d4c5
--- /dev/null
+++ b/chips/lance.c
@@ -0,0 +1,1570 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: lance.c
+ * Author: Robert V. Baron & Alessandro Forin
+ * Date: 5/90
+ *
+ * Driver for the DEC LANCE Ethernet Controller.
+ */
+
+/*
+
+ Byte ordering issues.
+
+ The lance sees data naturally as half word (16 bit) quantitites.
+ Bit 2 (BSWP) in control register 3 (CSR3) controls byte swapping.
+ To quote the spec:
+
+ 02 BSWP BYTE SWAP allows the chip to
+ operate in systems that consdier bits (15:08) of data pointers
+ by an even addressa and bits (7:0) to be pointed by an
+ odd address.
+
+ When BSWP=1, the chip will swap the high and low bytes on DMA
+ data transfers between the silo and bus memory. Only data from
+ silo transfers is swapped; the Initialization Block data and
+ the Descriptor Ring entries are NOT swapped. (emphasis theirs)
+
+
+ So on systems with BYTE_MSF=1, the BSWP bit should be set. Note,
+ however, that all shorts in the descriptor ring and initialization
+ block need to be swapped. The BITFIELD macros in lance.h handle this
+ magic.
+
+*/
+
+#include <ln.h>
+#if NLN > 0
+#include <platforms.h>
+
+/*
+ * AMD Am7990 LANCE (Ethernet Interface)
+ */
+#include <sys/ioctl.h>
+#include <vm/vm_kern.h>
+
+#include <machine/machspl.h> /* spl definitions */
+#include <kern/time_out.h>
+#include <sys/syslog.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_kmsg.h>
+
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+
+#ifdef FLAMINGO
+#define se_reg_type unsigned int
+#endif
+
+#include <chips/lance.h>
+#include <chips/busses.h>
+
+#define private static
+#define public
+
+typedef struct se_softc *se_softc_t; /* move above prototypes */
+
+void se_write_reg(); /* forwards */
+void se_read();
+void se_rint();
+void se_tint();
+
+private vm_offset_t se_Hmem_nogap(), se_Hmem_gap16();
+private vm_offset_t se_malloc();
+
+
+/* This config section should go into a separate file */
+
+#ifdef LUNA88K
+# include <luna88k/board.h>
+# define MAPPED 1
+ #undef bcopy
+ extern void bcopy(), bzero();
+
+#define wbflush()
+#define Hmem(lna) (vm_offset_t)((lna) + sc->lnbuf)
+#define Lmem(lna) (vm_offset_t)((lna) + sc->lnoffset)
+
+#define SPACE (TRI_PORT_RAM_SPACE>>1)
+private struct se_switch se_switch[] = {
+ { LANCE_ADDR - TRI_PORT_RAM, /* pointer */
+ SPACE /* host side */,
+ SPACE /* lance side */,
+ - TRI_PORT_RAM,
+ 0, /* romstride */
+ 0, /* ramstride */
+ SPACE,
+ /* desc_copyin */ bcopy,
+ /* desc_copyout */ bcopy,
+ /* data_copyin */ bcopy,
+ /* data_copyout */ bcopy,
+ /* bzero */ bzero,
+ /* mapaddr */ se_Hmem_nogap,
+ /* mapoffs */ se_Hmem_nogap
+ },
+};
+
+#endif
+
+#ifdef DECSTATION
+#include <mips/mips_cpu.h>
+#include <mips/PMAX/pmad_aa.h>
+
+#define MAPPED 1
+
+/*
+ * The LANCE buffer memory as seen from the Pmax cpu is funny.
+ * It is viewed as short words (16bits), spaced at word (32bits)
+ * intervals. The same applies to the registers. From the LANCE
+ * point of view memory is instead contiguous.
+ * The ROM that contains the station address is in the space belonging
+ * to the clock/battery backup memory. This space is again 16 bits
+ * in a 32bit envelope. And the ether address is stored in the "high"
+ * byte of 6 consecutive quantities.
+ *
+ * But Pmaxen and 3maxen (and..) map lance space differently.
+ * This requires dynamic adaptation of the driver, which
+ * is done via the following switches.
+ * For convenience, the switch holds information about
+ * the location of the lance control registers as well.
+ * This could be either absolute (pmax) or relative to
+ * some register base (3max, turbochannel)
+ */
+void copyin_gap16(), copyout_gap16(), bzero_gap16();
+extern void bcopy(), bzero();
+void copyin_gap32(), copyout_gap32();
+
+private struct se_switch se_switch[] = {
+/* pmax */
+ { 0x00000000, 0x01000000, 0x0, 0x05000000, 8, 16, 64*1024,
+ copyin_gap16, copyout_gap16, copyin_gap16, copyout_gap16,
+ bzero_gap16, se_Hmem_gap16, se_Hmem_gap16},
+/* 3max */
+ { PMAD_OFFSET_LANCE, PMAD_OFFSET_RAM, PMAD_OFFSET_RAM, PMAD_OFFSET_ROM,
+ 16, 0, PMAD_RAM_SIZE,
+ bcopy, bcopy, bcopy, bcopy, bzero, se_Hmem_nogap, se_Hmem_nogap},
+/* 3min */
+/* XXX re-use other 64k */
+ { 0/*later*/, 0/*later*/, 0x0, 0/*later*/, 0, 128, 64*1024,
+ copyin_gap16, copyout_gap16, copyin_gap32, copyout_gap32,
+ bzero_gap16, se_Hmem_gap16, se_Hmem_nogap},
+};
+
+/*
+ * "lna" is what se_malloc hands back. They are offsets using
+ * the sizing that the Lance would use. The Lance space is
+ * mapped somewhere in the I/O space, as indicated by the softc.
+ * Hence we have these two macros:
+ */
+/* H & L are not hi and lo but
+ H = HOST == addresses for host to reference board memory
+ L = LOCAL == addresses on board
+ */
+#define Hmem(lna) (vm_offset_t)((se_sw->mapaddr)(lna) + sc->lnbuf)
+#define Lmem(lna) (vm_offset_t)((vm_offset_t)lna + sc->lnoffset)
+#endif /*DECSTATION*/
+
+
+#ifdef VAXSTATION
+#include <vax/ka3100.h>
+
+#define wbflush()
+
+void xzero(x, l) vm_offset_t x; int l; { blkclr(x, l); }
+void xcopy(f, t, l) vm_offset_t f, t; int l; { bcopy(f, t, l); }
+
+private struct se_switch se_switch[] = {
+ /* pvax sees contiguous bits in lower 16Meg of memory */
+ { 0, 0, 0, 0, 0, 0, 64*1024,
+ xcopy, xcopy, xcopy, xcopy, xzero, se_Hmem_nogap, se_Hmem_nogap},
+};
+
+/*
+ * "lna" is what se_malloc hands back. They are offsets using
+ * the sizing that the Lance would use. The Lance space is
+ * mapped somewhere in the I/O space, as indicated by the softc.
+ * Hence we have these two macros:
+ */
+/* H & L are not hi and lo but
+ H = HOST == addresses for host to reference board memory
+ L = LOCAL == addresses on board
+ */
+ /*
+ * This does not deal with > 16 Meg physical memory, where
+ * Hmem != Lmem
+ */
+#define Hmem(lna) (vm_offset_t)((lna) + sc->lnbuf)
+#define Lmem(lna) (vm_offset_t)((lna) + sc->lnoffset)
+
+#endif /*VAXSTATION*/
+
+
+#ifdef FLAMINGO
+#include <alpha/alpha_cpu.h>
+
+/* XXX might be wrong, mostly stolen from kmin */
+extern void copyin_gap16(), copyout_gap16(), bzero_gap16();
+extern void copyin_gap32(), copyout_gap32();
+extern void bcopy(), bzero();
+
+private struct se_switch se_switch[] = {
+/* XXX re-use other 64k */
+ { 0/*later*/, 0/*later*/, 0x0, 0/*later*/, 0, 128, 64*1024,
+ copyin_gap16, copyout_gap16, copyin_gap32, copyout_gap32,
+ bzero_gap16, se_Hmem_gap16, se_Hmem_nogap},
+};
+
+/*
+ * "lna" is what se_malloc hands back. They are offsets using
+ * the sizing that the Lance would use. The Lance space is
+ * mapped somewhere in the I/O space, as indicated by the softc.
+ * Hence we have these two macros:
+ */
+/* H & L are not hi and lo but
+ H = HOST == addresses for host to reference board memory
+ L = LOCAL == addresses on board
+ */
+#define Hmem(lna) (vm_offset_t)((se_sw->mapaddr)(lna) + sc->lnbuf)
+#define Lmem(lna) (vm_offset_t)((vm_offset_t)lna + sc->lnoffset)
+#endif /*FLAMINGO*/
+
+
+/*
+ * Map a lance-space offset into an host-space one
+ */
+private vm_offset_t se_Hmem_nogap( vm_offset_t lna) { return lna;}
+private vm_offset_t se_Hmem_gap16( vm_offset_t lna) { return lna << 1;}
+
+/*
+ * Memory addresses for LANCE are 24 bits wide.
+ */
+#define Addr_lo(y) ((unsigned short)((vm_offset_t)(y) & 0xffff))
+#define Addr_hi(y) ((unsigned short)(((vm_offset_t)(y)>>16) & 0xff))
+
+#define LN_MEMORY_SIZE (se_sw->ramsize)
+
+/* XXX to accomodate heterogeneity this should be made per-drive */
+/* XXX and then some more */
+
+struct se_switch *se_sw = se_switch;
+
+void set_se_switch(n)
+int n;
+{
+ se_sw = &se_switch[n];
+}
+
+#ifndef LUNA88K
+void setse_switch(n, r, b, l, o)
+ vm_offset_t r, b, l, o;
+ int n;
+{
+ se_switch[n].regspace = r;
+ se_switch[n].bufspace = b;
+ se_switch[n].ln_bufspace = l;
+ se_switch[n].romspace = o;
+
+ /* make sure longword aligned */
+ if (se_switch[n].bufspace & 0x7) {
+ se_switch[n].bufspace = (se_switch[n].bufspace+0x7) & ~0x7;
+ }
+
+ set_se_switch(n);
+}
+#endif
+
+/*
+ * Autoconf info
+ */
+
+private vm_offset_t se_std[NLN] = { 0 };
+private struct bus_device *se_info[NLN];
+private int se_probe();
+private void se_attach();
+
+struct bus_driver se_driver =
+ { se_probe, 0, se_attach, 0, se_std, "se", se_info, };
+
+/*
+ * Externally visible functions
+ */
+char *se_unprobed_addr = 0;
+void se_intr(); /* kernel */
+
+int se_open(), se_output(), se_get_status(), /* user */
+ se_set_status(), se_setinput(), se_restart();
+
+/*
+ *
+ * Internal functions & definitions
+ *
+ */
+
+private int se_probe();
+private void se_init();
+private void init_lance_space();
+private void se_desc_set_status();
+private volatile long *se_desc_alloc(); /* must be aligned! */
+void se_start();
+private void copy_from_lance();
+private int copy_to_lance();
+
+int se_verbose = 0; /* debug flag */
+
+#define RLOG 4 /* 2**4 = 16 receive descriptors */
+#define TLOG 4 /* 2**4 = 16 transmit descriptors */
+#define NRCV (1<<RLOG) /* Receive descriptors */
+#define NXMT (1<<TLOG) /* Transmit descriptors */
+
+#define LN_BUFFER_SIZE (0x800-0x80)
+
+/*
+ * Ethernet software status per interface.
+ *
+ * Each interface is referenced by a network interface structure,
+ * is_if, which contains the output queue for the interface, its address, ...
+ */
+int se_loopback_hack = 1;
+
+struct se_softc {
+ struct ifnet is_if; /* generic interface header */
+ unsigned char is_addr[6]; /* ethernet hardware address */
+ unsigned short pad;
+ se_reg_t lnregs; /* Lance registers */
+ vm_offset_t lnbuf; /* Lance memory, Host offset */
+ vm_offset_t lnoffset; /* Lance memory, Lance offset */
+ vm_offset_t lnrom;
+ vm_offset_t lnsbrk; /* Lance memory allocator */
+ vm_offset_t lninit_block; /* Init block address */
+ se_desc_t lnrring[NRCV]; /* Receive ring desc. */
+ volatile long *lnrbuf[NRCV]; /* Receive buffers */
+ se_desc_t lntring[NXMT]; /* Transmit ring desc. */
+ volatile long *lntbuf[NXMT]; /* Transmit buffers */
+
+ int rcv_last; /* Rcv buffer last read */
+
+ io_req_t tpkt[NXMT+1]; /* Xmt pkt queue */
+ int xmt_count; /* Xmt queue size */
+ int xmt_last; /* Xmt queue head (insert) */
+ int xmt_complete; /* Xmt queue tail (remove) */
+
+ int se_flags; /* Flags for SIOCSIFFLAGS */
+ int counters[4]; /* error counters */
+#define bablcnt counters[0]
+#define misscnt counters[1]
+#define merrcnt counters[2]
+#define rstrtcnt counters[3]
+} se_softc_data[NLN];
+
+se_softc_t se_softc[NLN]; /* quick access */
+
+/*
+ * Probe the Lance to see if it's there
+ */
+private int se_open_state = 0;
+
+private int se_probe(
+ vm_offset_t reg,
+ register struct bus_device *ui)
+{
+ register se_softc_t sc;
+ se_reg_t rdp, rap;
+ int unit = ui->unit;
+
+ /*
+ * See if the interface is there by reading the lance CSR. On pmaxen
+ * and 3maxen this is superfluous, but..
+ */
+ rdp = (se_reg_t) (reg + se_sw->regspace);
+#ifdef DECSTATION
+ if (check_memory(rdp, 0))
+ return 0;
+#endif /*DECSTATION*/
+#ifdef MAPPED
+ SE_probe(reg,ui);
+#endif /*MAPPED*/
+ rap = rdp + 2; /* XXX might not be true in the future XXX */
+ /* rdp and rap are "shorts" on consecutive
+ "long" word boundaries */
+
+ /*
+ * Bind this interface to the softc.
+ */
+ sc = &se_softc_data[unit];
+ se_softc[unit] = sc;
+ sc->lnregs = (se_reg_t) (reg + se_sw->regspace);
+ sc->lnbuf = (vm_offset_t) (reg + se_sw->bufspace);
+ sc->lnoffset = (vm_offset_t) (se_sw->ln_bufspace);
+ sc->lnrom = (vm_offset_t) (reg + se_sw->romspace);
+
+ /*
+ * Reset the interface, and make sure we really do it! (the 3max
+ * seems quite stubborn about these registers)
+ */
+ se_write_reg(rap, CSR0_SELECT, CSR0_SELECT, "RAP");
+ se_write_reg(rdp, LN_CSR0_STOP, LN_CSR0_STOP, "csr0");
+
+ /*
+ * Allocate lance RAM buffer memory
+ */
+ init_lance_space(sc);
+
+ /*
+ * Initialize the chip
+ *
+ * NOTE: From now on we will only touch csr0
+ */
+ if (se_ship_init_block(sc, unit))
+ return 0;
+
+ /*
+ * Tell the world we are alive and well
+ */
+ se_open_state++;
+ return 1;
+}
+
+int se_ship_init_block(
+ register se_softc_t sc,
+ int unit)
+{
+ se_reg_t rdp = sc->lnregs;
+ se_reg_t rap;
+ register int i = 0;
+
+ rap = rdp + 2; /* XXX might not be true in the future XXX */
+
+ /*
+ * Load LANCE control block.
+ */
+
+#ifdef LUNA88K
+ /* turn on byte swap bit in csr3, set bcon bit - as in 2.5 */
+ se_write_reg(rap, CSR3_SELECT, CSR3_SELECT, "RAP");
+ se_write_reg(rdp, LN_CSR3_BSWP|LN_CSR3_BCON,
+ LN_CSR3_BSWP|LN_CSR3_BCON, "csr3");
+#endif
+
+ se_write_reg(rap, CSR1_SELECT, CSR1_SELECT, "RAP");
+ se_write_reg(rdp, Addr_lo(Lmem(sc->lninit_block)),
+ Addr_lo(Lmem(sc->lninit_block)), "csr1");
+
+ se_write_reg(rap, CSR2_SELECT, CSR2_SELECT, "RAP");
+ se_write_reg(rdp, Addr_hi(Lmem(sc->lninit_block)),
+ Addr_hi(Lmem(sc->lninit_block)), "csr2");
+
+ /*
+ * Start the INIT sequence now
+ */
+ se_write_reg(rap, CSR0_SELECT, CSR0_SELECT, "RAP");
+ *rdp = (LN_CSR0_IDON | LN_CSR0_INIT);
+ wbflush();
+
+ /* give it plenty of time to settle */
+ while (i++ < 10000) {
+ delay(100);
+ if ((*rdp & LN_CSR0_IDON) != 0)
+ break;
+ }
+ /* make sure got out okay */
+ if ((*rdp & LN_CSR0_IDON) == 0) {
+ printf("se%d: cannot initialize\n", unit);
+ if (*rdp & LN_CSR0_ERR)
+ printf("se%d: initialization error, csr = %04x\n",
+ unit, (*rdp & 0xffff));
+ return 1;
+ }
+ /*
+ * Do not enable interrupts just yet.
+ */
+ /* se_write_reg(rdp, LN_CSR0_STOP, LN_CSR0_STOP, "csr0"); */
+
+ return 0;
+}
+
+void
+se_write_reg(
+ register se_reg_t regptr,
+ register int val,
+ register int result,
+ char *regname)
+{
+ register int i = 0;
+
+ while ((unsigned short)(*regptr) != (unsigned short)result) {
+ *regptr = (se_reg_type)val;
+ wbflush();
+ if (++i > 10000) {
+ printf("se: %s did not settle (to x%x): x%x\n",
+ regname, result, (unsigned short)(*regptr));
+ return;
+ }
+ delay(100);
+ }
+}
+
+unsigned short
+se_read_reg(
+ register se_reg_t regptr)
+{
+ return (unsigned short) (*regptr);
+}
+
+private void
+init_lance_space(
+ register se_softc_t sc)
+{
+ register int lptr; /* Generic lance pointer */
+ se_desc_t ringaddr;
+ long *rom_eaddress = (long *) sc->lnrom;
+ int i;
+ struct se_init_block init_block;
+
+ /*
+ * Allocate local RAM buffer memory for the init block,
+ * fill in our local copy then copyout.
+ */
+
+ sc->lninit_block = se_malloc(sc, sizeof (struct se_init_block));
+
+ /*
+ * Set values on stack, then copyout en-masse
+ */
+ bzero(&init_block, sizeof(init_block));
+ init_block.mode = 0;
+
+ /* byte swapping between host and lance */
+
+ init_block.phys_addr_low = ((rom_eaddress[0]>>se_sw->romstride)&0xff) |
+ (((rom_eaddress[1]>>se_sw->romstride)&0xff) << 8);
+ init_block.phys_addr_med = ((rom_eaddress[2]>>se_sw->romstride)&0xff) |
+ (((rom_eaddress[3]>>se_sw->romstride)&0xff) << 8);
+ init_block.phys_addr_high = ((rom_eaddress[4]>>se_sw->romstride)&0xff) |
+ (((rom_eaddress[5]>>se_sw->romstride)&0xff) << 8);
+
+ /*
+ * Allocate both descriptor rings at once.
+ * Note that the quadword alignment requirement is
+ * inherent in the way we perform allocation,
+ * but it does depend on the size of the init block.
+ */
+ lptr = se_malloc(sc, sizeof (struct se_desc) * (NXMT + NRCV));
+
+ /*
+ * Initialize the buffer descriptors
+ */
+ init_block.recv_ring_pointer_lo = Addr_lo(Lmem(lptr));
+ init_block.recv_ring_pointer_hi = Addr_hi(Lmem(lptr));
+ init_block.recv_ring_len = RLOG;
+
+ for ( i = 0; i < NRCV ; i++, lptr += sizeof(struct se_desc)) {
+ ringaddr = (se_desc_t)Hmem(lptr);
+ sc->lnrring[i] = ringaddr;
+ sc->lnrbuf[i] = se_desc_alloc (sc, ringaddr);
+ }
+
+ init_block.xmit_ring_pointer_lo = Addr_lo(Lmem(lptr));
+ init_block.xmit_ring_pointer_hi = Addr_hi(Lmem(lptr));
+ init_block.xmit_ring_len = TLOG;
+
+ for ( i = 0 ; i < NXMT ; i++, lptr += sizeof(struct se_desc)) {
+ ringaddr = (se_desc_t)Hmem(lptr);
+ sc->lntring[i] = ringaddr;
+ sc->lntbuf[i] = se_desc_alloc (sc, ringaddr);
+ }
+
+ /*
+ * No logical address filtering
+ */
+ init_block.logical_addr_filter0 = 0;
+ init_block.logical_addr_filter1 = 0;
+ init_block.logical_addr_filter2 = 0;
+ init_block.logical_addr_filter3 = 0;
+
+ /*
+ * Move init block into lance space
+ */
+ (se_sw->desc_copyout)((vm_offset_t)&init_block, Hmem(sc->lninit_block), sizeof(init_block));
+ wbflush();
+}
+
+/*
+ * Interface exists: make available by filling in network interface
+ * record. System will initialize the interface when it is ready
+ * to accept packets.
+ */
+private void
+se_attach(
+ register struct bus_device *ui)
+{
+ unsigned char *enaddr;
+ struct ifnet *ifp;
+ long *rom_eaddress;
+ int unit = ui->unit;
+ se_softc_t sc = se_softc[unit];
+
+ rom_eaddress = (long *) sc->lnrom;
+
+ /*
+ * Read the address from the prom and save it.
+ */
+ enaddr = sc->is_addr;
+ enaddr[0] = (unsigned char) ((rom_eaddress[0] >> se_sw->romstride) & 0xff);
+ enaddr[1] = (unsigned char) ((rom_eaddress[1] >> se_sw->romstride) & 0xff);
+ enaddr[2] = (unsigned char) ((rom_eaddress[2] >> se_sw->romstride) & 0xff);
+ enaddr[3] = (unsigned char) ((rom_eaddress[3] >> se_sw->romstride) & 0xff);
+ enaddr[4] = (unsigned char) ((rom_eaddress[4] >> se_sw->romstride) & 0xff);
+ enaddr[5] = (unsigned char) ((rom_eaddress[5] >> se_sw->romstride) & 0xff);
+
+ printf(": %x-%x-%x-%x-%x-%x",
+ (rom_eaddress[0] >> se_sw->romstride) & 0xff,
+ (rom_eaddress[1] >> se_sw->romstride) & 0xff,
+ (rom_eaddress[2] >> se_sw->romstride) & 0xff,
+ (rom_eaddress[3] >> se_sw->romstride) & 0xff,
+ (rom_eaddress[4] >> se_sw->romstride) & 0xff,
+ (rom_eaddress[5] >> se_sw->romstride) & 0xff);
+
+ /*
+ * Initialize the standard interface descriptor
+ */
+ ifp = &sc->is_if;
+ ifp->if_unit = unit;
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = 6;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags |= IFF_BROADCAST;
+
+ ifp->if_address = (char *) enaddr;
+
+ if_init_queues(ifp);
+#ifdef MAPPED
+ SE_attach(ui);
+#endif /*MAPPED*/
+
+}
+
+/*
+ * Use a different hardware address for interface
+ */
+void
+se_setaddr(
+ unsigned char eaddr[6],
+ int unit)
+{
+ register se_softc_t sc = se_softc[unit];
+ struct se_init_block init_block;
+
+ /*
+ * Modify initialization block accordingly
+ */
+ (se_sw->desc_copyin) (Hmem(sc->lninit_block), (vm_offset_t)&init_block, sizeof(init_block));
+ bcopy(eaddr, &init_block.phys_addr_low, sizeof(*eaddr));
+ (se_sw->desc_copyout)((vm_offset_t)&init_block, Hmem(sc->lninit_block), sizeof(init_block));
+ /*
+ * Make a note of it
+ */
+ bcopy(eaddr, sc->is_addr, sizeof(*eaddr));
+
+ /*
+ * Restart the interface
+ */
+ se_restart(&sc->is_if);
+ se_init(unit);
+}
+
+/*
+ * Restart interface
+ *
+ * We use this internally on those errors that hang the chip,
+ * not sure yet what use the MI code will make of it.
+ *
+ * After stopping the chip and effectively turning off the interface
+ * we release all pending buffers and cause the chip to init
+ * itself. We do not enable interrupts here.
+ */
+int
+se_restart( register struct ifnet *ifp )
+{
+ register se_softc_t sc = se_softc[ifp->if_unit];
+ se_reg_t rdp;
+ register int i;
+
+ rdp = sc->lnregs;
+
+ /*
+ * stop the chip
+ */
+ se_write_reg(rdp, LN_CSR0_STOP, LN_CSR0_STOP, "csr0");
+
+ /*
+ * stop network activity
+ */
+ if (ifp->if_flags & IFF_RUNNING) {
+ ifp->if_flags &= ~(IFF_UP | IFF_RUNNING);
+ sc->se_flags &= ~(IFF_UP | IFF_RUNNING);
+ }
+ sc->rstrtcnt++;
+
+ if (se_verbose)
+ printf("se%d: %d restarts\n", ifp->if_unit, sc->rstrtcnt);
+
+ /*
+ * free up any buffers currently in use
+ */
+ for (i = 0; i < NXMT; i++)
+ if (sc->tpkt[i]) {
+ iodone(sc->tpkt[i]);
+ sc->tpkt[i] = (io_req_t) 0;
+ }
+ /*
+ * INIT the chip again, no need to reload init block address.
+ */
+ se_ship_init_block(sc, ifp->if_unit);
+
+ return (0);
+}
+
+/*
+ * Initialize the interface.
+ */
+private void
+se_init( int unit )
+{
+ register se_softc_t sc = se_softc[unit];
+ register se_desc_t *rp;
+ register struct ifnet *ifp = &sc->is_if;
+ se_reg_t rdp;
+ short mode;
+ spl_t s;
+ int i;
+
+ if (ifp->if_flags & IFF_RUNNING)
+ return;
+
+ rdp = sc->lnregs;
+
+ /*
+ * Init the buffer descriptors and indexes for each of the rings.
+ */
+ for (i = 0, rp = sc->lnrring; i < NRCV; i++, rp++)
+ se_desc_set_status(*rp, LN_RSTATE_OWN);
+
+ for (i = 0, rp = sc->lntring; i < NXMT; i++, rp++)
+ se_desc_set_status(*rp, 0);
+
+ sc->xmt_count = sc->xmt_complete = sc->xmt_last = sc->rcv_last = 0;
+
+ /*
+ * Deal with loopback mode operation
+ */
+ s = splimp();
+
+ (se_sw->desc_copyin) (Hmem(sc->lninit_block), (vm_offset_t)&mode, sizeof(mode));
+
+ if (ifp->if_flags & IFF_LOOPBACK
+ && ((mode & LN_MODE_LOOP) == 0)) {
+ /* if not already in loopback mode, do external loopback */
+ mode &= ~LN_MODE_INTL;
+ mode |= LN_MODE_LOOP;
+ (se_sw->desc_copyout) ((vm_offset_t)&mode, Hmem(sc->lninit_block), sizeof(mode));
+ se_restart(ifp);
+ se_init(ifp->if_unit);
+ splx(s);
+ return;
+ }
+
+ ifp->if_flags |= (IFF_UP | IFF_RUNNING);
+ sc->se_flags |= (IFF_UP | IFF_RUNNING);
+
+ /*
+ * Start the Lance and enable interrupts
+ */
+ *rdp = (LN_CSR0_STRT | LN_CSR0_INEA);
+ wbflush();
+
+ /*
+ * See if anything is already queued
+ */
+ se_start(unit);
+ splx(s);
+}
+
+
+/*
+ * Shut off the lance
+ */
+void
+se_stop(int unit)
+{
+ se_reg_t rdp = se_softc[unit]->lnregs;
+
+ se_write_reg(rdp, LN_CSR0_STOP, LN_CSR0_STOP, "csr0");
+}
+
+
+/*
+ * Open the device, declaring the interface up
+ * and enabling lance interrupts.
+ */
+/*ARGSUSED*/
+int
+se_open(
+ int unit,
+ int flag)
+{
+ register se_softc_t sc = se_softc[unit];
+
+ if (unit >= NLN)
+ return EINVAL;
+ if (!se_open_state)
+ return ENXIO;
+
+ sc->is_if.if_flags |= IFF_UP;
+ se_open_state++;
+ se_init(unit);
+ return (0);
+}
+
+#ifdef MAPPED
+int se_use_mapped_interface[NLN];
+#endif /*MAPPED*/
+
+void
+se_normal(int unit)
+{
+#ifdef MAPPED
+ se_use_mapped_interface[unit] = 0;
+#endif /*MAPPED*/
+ if (se_softc[unit]) {
+ se_restart((struct ifnet *)se_softc[unit]);
+ se_init(unit);
+ }
+}
+
+/*
+ * Ethernet interface interrupt routine
+ */
+void
+se_intr(
+ int unit,
+ spl_t spllevel)
+{
+ register se_softc_t sc = se_softc[unit];
+ se_reg_t rdp;
+ register struct ifnet *ifp = &sc->is_if;
+ register unsigned short csr;
+
+#ifdef MAPPED
+ if (se_use_mapped_interface[unit])
+ {
+ SE_intr(unit,spllevel);
+ return;
+ }
+#endif /*MAPPED*/
+
+ if (se_open_state < 2) { /* Stray, or not open for business */
+ rdp = (sc ? sc->lnregs : (se_reg_t)se_unprobed_addr);
+ *rdp |= LN_CSR0_STOP;
+ wbflush();
+ return;
+ }
+ rdp = sc->lnregs;
+
+ /*
+ * Read the CSR and process any error condition.
+ * Later on, restart the lance by writing back
+ * the CSR (for set-to-clear bits).
+ */
+ csr = *rdp; /* pick up the csr */
+
+ /* drop spurious interrupts */
+ if ((csr & LN_CSR0_INTR) == 0)
+ return;
+
+#ifdef DECSTATION
+ splx(spllevel); /* drop priority now */
+#endif /*DECSTATION*/
+again:
+ /*
+ * Check for errors first
+ */
+ if ( csr & LN_CSR0_ERR ) {
+ if (csr & LN_CSR0_MISS) {
+ /*
+ * Stop the chip to prevent a corrupt packet from
+ * being transmitted. There is a known problem with
+ * missed packet errors causing corrupted data to
+ * be transmitted to the same host as was just
+ * transmitted, with a valid crc appended to the
+ * packet. The only solution is to stop the chip,
+ * which will clear the Lance silo, thus preventing
+ * the corrupt data from being sent.
+ */
+ se_write_reg(rdp, LN_CSR0_STOP, LN_CSR0_STOP, "csr0");
+
+ sc->misscnt++;
+ if (se_verbose) {
+ int me = 0, lance = 0, index;
+ struct se_desc r;
+ for (index = 0; index < NRCV; index++) {
+ (se_sw->desc_copyin)(
+ (vm_offset_t)sc->lnrring[index],
+ (vm_offset_t)&r,
+ sizeof(r));
+ if (r.status & LN_RSTATE_OWN)
+ lance++;
+ else
+ me++;
+ }
+ printf("se%d: missed packet (%d) csr = %x, Lance %x, me %x\n",
+ unit, sc->misscnt, csr, lance, me);
+ }
+ se_restart(ifp);
+ se_init(unit);
+ return;
+ }
+ if (csr & LN_CSR0_BABL) {
+ sc->bablcnt++;
+ if (se_verbose)
+ printf("se%d: xmt timeout (%d)\n",
+ unit, sc->bablcnt);
+ }
+ if (csr & LN_CSR0_MERR) {
+ sc->merrcnt++;
+ printf("se%d: memory error (%d)\n",
+ unit, sc->merrcnt);
+
+ if (((csr & LN_CSR0_RXON) == 0)
+ || ((csr & LN_CSR0_TXON) == 0)) {
+ se_restart(ifp);
+ se_init(unit);
+ return;
+ }
+ }
+ }
+
+ *rdp = LN_CSR0_INEA | (csr & LN_CSR0_WTC);
+ wbflush();
+
+ if ( csr & LN_CSR0_RINT )
+ se_rint( unit );
+
+ if ( csr & LN_CSR0_TINT )
+ se_tint( unit );
+
+ if ((csr = *rdp) & (LN_CSR0_RINT | LN_CSR0_TINT))
+ goto again;
+}
+
+/*
+ * Handle a transmitter complete interrupt.
+ */
+void
+se_tint(int unit)
+{
+ register se_softc_t sc = se_softc[unit];
+ register index;
+ register status;
+ io_req_t request;
+ struct se_desc r;
+
+ /*
+ * Free up descriptors for all packets in queue for which
+ * transmission is complete. Start from queue tail, stop at first
+ * descriptor we do not OWN, or which is in an inconsistent state
+ * (lance still working).
+ */
+
+ while ((sc->xmt_complete != sc->xmt_last) && (sc->xmt_count > 0)) {
+
+ index = sc->xmt_complete;
+ (se_sw->desc_copyin) ((vm_offset_t)sc->lntring[index],
+ (vm_offset_t)&r, sizeof(r));
+ status = r.status;
+
+ /*
+ * Does lance still own it ?
+ */
+ if (status & LN_TSTATE_OWN)
+ break;
+
+ /*
+ * Packet sent allright, release queue slot.
+ */
+ request = sc->tpkt[index];
+ sc->tpkt[index] = (io_req_t) 0;
+ sc->xmt_complete = ++index & (NXMT - 1);
+ --sc->xmt_count;
+
+ sc->is_if.if_opackets++;
+ if (status & (LN_TSTATE_DEF|LN_TSTATE_ONE|LN_TSTATE_MORE))
+ sc->is_if.if_collisions++;
+
+ /*
+ * Check for transmission errors.
+ */
+ if (!se_loopback_hack && status & LN_TSTATE_ERR) {
+ sc->is_if.if_oerrors++;
+ if (se_verbose)
+ printf("se%d: xmt error (x%x)\n", unit, r.status2);
+
+ if (r.status2 & (LN_TSTATE2_RTRY|LN_TSTATE2_LCOL))
+ sc->is_if.if_collisions++;
+
+ /*
+ * Restart chip on errors that disable the
+ * transmitter.
+ */
+ iodone(request);
+ if (r.status2 & LN_TSTATE2_DISABLE) {
+ register struct ifnet *ifp = &sc->is_if;
+ se_restart(ifp);
+ se_init(ifp->if_unit);
+ return;
+ }
+ } else if (request) {
+ /*
+ * If this was a broadcast packet loop it back.
+ * Signal successful transmission of the packet.
+ */
+ register struct ether_header *eh;
+ register int i;
+
+ eh = (struct ether_header *) request->io_data;
+ /* ether broadcast address is in the spec */
+ for (i = 0; (i < 6) && (eh->ether_dhost[i] == 0xff); i++)
+ ; /* nop */
+ /* sending to ourselves makes sense sometimes */
+ if (i != 6 && se_loopback_hack)
+ for (i = 0;
+ (i < 6) && (eh->ether_dhost[i] == sc->is_addr[i]);
+ i++)
+ ; /* nop */
+ if (i == 6)
+ se_read(sc, 0, request->io_count, request);
+ iodone(request);
+ }
+ }
+ /*
+ * Dequeue next transmit request, if any.
+ */
+ if (sc->xmt_count <= 0)
+ se_start(unit);
+}
+
+/*
+ * Handle a receiver complete interrupt.
+ */
+void
+se_rint(int unit)
+{
+ register se_softc_t sc = se_softc[unit];
+ register index, first, len;
+ unsigned char status, status1;
+ int ring_cnt;
+ struct se_desc r;
+
+ /*
+ * Starting from where we left off, look around the receive ring and
+ * pass on all complete packets.
+ */
+
+ for (;; sc->rcv_last = ++index & (NRCV - 1)) {
+
+ /*
+ * Read in current descriptor
+ */
+read_descriptor:
+ (se_sw->desc_copyin) ((vm_offset_t)sc->lnrring[sc->rcv_last],
+ (vm_offset_t)&r, sizeof(r));
+ status = r.status;
+ if (status & LN_RSTATE_OWN)
+ break;
+ first = index = sc->rcv_last;
+
+ /*
+ * If not the start of a packet, error
+ */
+ if (!(status & LN_RSTATE_STP)) {
+ if (se_verbose)
+ printf("se%d: Rring #%d, status=%x !STP\n",
+ unit, index, status);
+ break;
+ }
+ /*
+ * See if packet is chained (should not) by looking at
+ * the last descriptor (OWN clear and ENP set).
+ * Remember the status info in this last descriptor.
+ */
+ ring_cnt = 1, status1 = status;
+ while (((status1 & (LN_RSTATE_ERR | LN_RSTATE_OWN | LN_RSTATE_ENP)) == 0) &&
+ (ring_cnt++ <= NRCV)) {
+ struct se_desc r1;
+ index = (index + 1) & (NRCV - 1);
+ (se_sw->desc_copyin) ((vm_offset_t)sc->lnrring[index],
+ (vm_offset_t)&r1, sizeof(r1));
+ status1 = r1.status;
+ }
+
+ /*
+ * Chained packet (--> illegally sized!); re-init the
+ * descriptors involved and ignore this bogus packet. I
+ * donno how, but it really happens that we get these
+ * monsters.
+ */
+ if (ring_cnt > 1) {
+ /*
+ * Return all descriptors to lance
+ */
+ se_desc_set_status(sc->lnrring[first], LN_RSTATE_OWN);
+ while (first != index) {
+ first = (first + 1) & (NRCV - 1);
+ se_desc_set_status(sc->lnrring[first], LN_RSTATE_OWN);
+ }
+ if ((status1 & LN_RSTATE_ERR) && se_verbose)
+ printf("se%d: rcv error %x (chained)\n", unit, status1);
+ continue;
+ }
+
+ /*
+ * Good packets must be owned by us and have the end of
+ * packet flag. And nothing else.
+ */
+ if ((status & ~LN_RSTATE_STP) == LN_RSTATE_ENP) {
+ sc->is_if.if_ipackets++;
+
+ if ((len = r.message_size) == 0)
+ /* race seen on pmaxen: the lance
+ * has not updated the size yet ??
+ */
+ goto read_descriptor;
+ /*
+ * Drop trailing CRC bytes from len and ship packet
+ * up
+ */
+ se_read(sc, (volatile char*)sc->lnrbuf[first], len-4,0);
+
+ /*
+ * Return descriptor to lance, and move on to next
+ * packet
+ */
+ r.status = LN_RSTATE_OWN;
+ (se_sw->desc_copyout)((vm_offset_t)&r,
+ (vm_offset_t)sc->lnrring[first],
+ sizeof(r));
+ continue;
+ }
+ /*
+ * Not a good packet, see what is wrong
+ */
+ if (status & LN_RSTATE_ERR) {
+ sc->is_if.if_ierrors++;
+
+ if (se_verbose)
+ printf("se%d: rcv error (x%x)\n", unit, status);
+
+ /*
+ * Return descriptor to lance
+ */
+ se_desc_set_status(sc->lnrring[first], LN_RSTATE_OWN);
+ } else {
+ /*
+ * Race condition viz lance, Wait for the next
+ * interrupt.
+ */
+ return;
+ }
+ }
+}
+
+/*
+ * Output routine.
+ * Call common function for wiring memory,
+ * come back later (to se_start) to get
+ * things going.
+ */
+io_return_t
+se_output(
+ int dev,
+ io_req_t ior)
+{
+ return net_write(&se_softc[dev]->is_if, (int(*)())se_start, ior);
+}
+
+/*
+ * Start output on interface.
+ *
+ */
+void
+se_start(int unit)
+{
+ register se_softc_t sc = se_softc[unit];
+ io_req_t request;
+ struct se_desc r;
+ int tlen;
+ spl_t s;
+ register int index;
+
+ s = splimp();
+
+ for (index = sc->xmt_last;
+ sc->xmt_count < (NXMT - 1);
+ sc->xmt_last = index = (index + 1) & (NXMT - 1)) {
+ /*
+ * Dequeue the next transmit request, if any.
+ */
+ IF_DEQUEUE(&sc->is_if.if_snd, request);
+ if (request == 0) {
+ /*
+ * Tell the lance to send the packet now
+ * instead of waiting until the next 1.6 ms
+ * poll interval expires.
+ */
+ *sc->lnregs = LN_CSR0_TDMD | LN_CSR0_INEA;
+ splx(s);
+ return; /* Nothing on the queue */
+ }
+
+ /*
+ * Keep request around until transmission complete
+ */
+ sc->tpkt[index] = request;
+ tlen = copy_to_lance(request, sc->lntbuf[index]);
+
+ /*
+ * Give away buffer. Must copyin/out, set len,
+ * and set the OWN flag. We do not do chaining.
+ */
+ (se_sw->desc_copyin)((vm_offset_t)sc->lntring[index],
+ (vm_offset_t)&r, sizeof(r));
+ r.buffer_size = -(tlen) | 0xf000;
+ r.status = (LN_TSTATE_OWN | LN_TSTATE_STP | LN_TSTATE_ENP);
+ (se_sw->desc_copyout)((vm_offset_t)&r,
+ (vm_offset_t)sc->lntring[index],
+ sizeof(r));
+ wbflush();
+
+ sc->xmt_count++;
+ }
+ /*
+ * Since we actually have queued new packets, tell
+ * the chip to rescan the descriptors _now_.
+ * It is quite unlikely that the ring be filled,
+ * but if it is .. the more reason to do it!
+ */
+ *sc->lnregs = LN_CSR0_TDMD | LN_CSR0_INEA;
+ splx(s);
+}
+
+
+/*
+ * Pull a packet off the interface and
+ * hand it up to the higher levels.
+ *
+ * Simulate broadcast packets in software.
+ */
+void
+se_read(
+ register se_softc_t sc,
+ volatile char *lnrbuf,
+ int len,
+ io_req_t loop_back)
+{
+ register struct ifnet *ifp = &sc->is_if;
+ register ipc_kmsg_t new_kmsg;
+ char *hdr, *pkt;
+
+ if (len <= sizeof(struct ether_header))
+ return; /* sanity */
+
+ /*
+ * Get a new kmsg to put data into.
+ */
+ new_kmsg = net_kmsg_get();
+ if (new_kmsg == IKM_NULL) {
+ /*
+ * No room, drop the packet
+ */
+ ifp->if_rcvdrops++;
+ return;
+ }
+
+ hdr = net_kmsg(new_kmsg)->header;
+ pkt = net_kmsg(new_kmsg)->packet;
+
+#define OFF0 (sizeof(struct ether_header) - sizeof(struct packet_header))
+#define OFF1 (OFF0 & ~3)
+ if (loop_back) {
+ bcopy(loop_back->io_data, hdr, sizeof(struct ether_header));
+ bcopy(loop_back->io_data + OFF0,
+ pkt, len - OFF0);
+ } else
+ copy_from_lance(lnrbuf, len, (struct ether_header*)hdr,
+ (struct packet_header*)pkt);
+
+ /*
+ * Set up the 'fake' header with length. Type has been left
+ * in the correct place.
+ */
+ len = len - OFF0;
+ ((struct packet_header *)pkt)->length = len;
+
+ /*
+ * Hand the packet to the network module.
+ */
+ net_packet(ifp, new_kmsg, len, ethernet_priority(new_kmsg));
+}
+
+
+/*
+ * Get a packet out of Lance memory and into main memory.
+ */
+private void
+copy_from_lance(
+ register volatile unsigned char *rbuf,
+ register unsigned int nbytes,
+ struct ether_header *hdr,
+ struct packet_header *pkt)
+{
+ /*
+ * Read in ethernet header
+ */
+ (se_sw->data_copyin) ((vm_offset_t)rbuf, (vm_offset_t)hdr, sizeof(struct ether_header));
+
+ nbytes -= sizeof(struct ether_header);
+ rbuf += (se_sw->mapoffs) (sizeof(struct ether_header));
+
+ pkt->type = (unsigned short) hdr->ether_type;
+
+ (se_sw->data_copyin) ((vm_offset_t)rbuf, (vm_offset_t)(pkt + 1), nbytes);
+}
+
+
+/*
+ * Move a packet into Lance space
+ */
+private int
+copy_to_lance(
+ register io_req_t request,
+ volatile char *sbuf)
+{
+ register unsigned short *dp;
+ register int len;
+
+ dp = (unsigned short *) request->io_data;
+ len = request->io_count;
+
+ if (len > (int)(ETHERMTU + sizeof(struct ether_header))) {
+ printf("se: truncating HUGE packet\n");
+ len = ETHERMTU + sizeof(struct ether_header);
+ }
+
+ (se_sw->data_copyout) ((vm_offset_t)dp, (vm_offset_t)sbuf, len);
+
+ if (len < LN_MINBUF_NOCH)
+ /*
+ * The lance needs at least this much data in a packet. Who
+ * cares if I send some garbage that was left in the lance
+ * buffer ? If one can spoof packets then one can spoof
+ * packets!
+ */
+ len = LN_MINBUF_NOCH;
+ return len;
+}
+
+/*
+ * Reset a descriptor's flags.
+ * Optionally give the descriptor to the lance
+ */
+private void
+se_desc_set_status (
+ register se_desc_t lndesc,
+ int val)
+{
+ struct se_desc desc;
+
+ (se_sw->desc_copyin) ((vm_offset_t)lndesc, (vm_offset_t)&desc, sizeof(desc));
+ desc.desc4.bits = 0;
+ desc.status = val;
+ (se_sw->desc_copyout) ((vm_offset_t)&desc, (vm_offset_t)lndesc, sizeof(desc));
+ wbflush();
+}
+
+/*
+ * Set/Get status functions
+ */
+int
+se_get_status(
+ int dev,
+ dev_flavor_t flavor,
+ dev_status_t status, /* pointer to OUT array */
+ natural_t *status_count) /* out */
+{
+ return (net_getstat(&se_softc[dev]->is_if,
+ flavor, status, status_count));
+}
+
+int
+se_set_status(
+ int unit,
+ dev_flavor_t flavor,
+ dev_status_t status,
+ natural_t status_count)
+{
+ register se_softc_t sc;
+
+ sc = se_softc[unit];
+
+
+ switch (flavor) {
+
+ case NET_STATUS:
+ break;
+
+ case NET_ADDRESS: {
+
+ register union ether_cvt {
+ unsigned char addr[6];
+ int lwd[2];
+ } *ec = (union ether_cvt *) status;
+
+ if (status_count < sizeof(*ec) / sizeof(int))
+ return (D_INVALID_SIZE);
+
+ ec->lwd[0] = ntohl(ec->lwd[0]);
+ ec->lwd[1] = ntohl(ec->lwd[1]);
+
+ se_setaddr(ec->addr, unit);
+
+ break;
+ }
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+
+ return (D_SUCCESS);
+}
+
+
+/*
+ * Install new filter.
+ * Nothing special needs to be done here.
+ */
+io_return_t
+se_setinput(
+ int dev,
+ ipc_port_t receive_port,
+ int priority,
+ filter_t *filter,
+ natural_t filter_count)
+{
+ return (net_set_filter(&se_softc[dev]->is_if,
+ receive_port, priority,
+ filter, filter_count));
+}
+
+/*
+ * Allocate and initialize a ring descriptor.
+ * Allocates a buffer from the lance memory and writes a descriptor
+ * for that buffer to the host virtual address LNDESC.
+ */
+private volatile long
+*se_desc_alloc (
+ register se_softc_t sc,
+ register se_desc_t lndesc)
+{
+ register vm_offset_t dp; /* data pointer */
+ struct se_desc desc;
+
+ /*
+ * Allocate buffer in lance space
+ */
+ dp = se_malloc(sc, LN_BUFFER_SIZE);
+
+ /*
+ * Build a descriptor pointing to it
+ */
+ desc.addr_low = Addr_lo(Lmem(dp));
+ desc.addr_hi = Addr_hi(Lmem(dp));
+ desc.status = 0;
+ desc.buffer_size = -LN_BUFFER_SIZE;
+ desc.desc4.bits = 0;
+
+ /*
+ * Copy the descriptor to lance space
+ */
+ (se_sw->desc_copyout) ((vm_offset_t)&desc, (vm_offset_t)lndesc, sizeof(desc));
+ wbflush();
+
+ return (volatile long *) Hmem(dp);
+}
+
+/*
+ * Allocate a chunk of lance RAM buffer. Since we never
+ * give lance RAM buffer memory back, we'll just step up the
+ * byte-count on a per-unit basis.
+ *
+ * The return value is an index into the lance memory, which can be
+ * passed with Hmem() and Lmem() to get the host and chip virtual addresses.
+ */
+private vm_offset_t
+se_malloc(
+ se_softc_t sc,
+ int size)
+{
+ register vm_offset_t ret;
+
+ /*
+ * On first call, zero lance memory
+ */
+ if (sc->lnsbrk == 0)
+ (se_sw->bzero) (Hmem(0), LN_MEMORY_SIZE);
+
+ /*
+ * Start out on the first double longword boundary
+ * (this accomodates some machines, with minimal loss)
+ */
+ if (sc->lnsbrk & 0xf)
+ sc->lnsbrk = (sc->lnsbrk + 0x10) & ~0xf;
+
+ ret = sc->lnsbrk;
+ sc->lnsbrk += size;
+
+ if (sc->lnsbrk > LN_MEMORY_SIZE)
+ panic("se_malloc");
+
+ return ret;
+}
+
+#endif NLN > 0