|
|
GNU Mach 1.0
/*
* 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 [email protected]
* 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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.