* Copyright (c) 1992 Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* %sccs.include.redist.c%
* @(#)if_le.c 7.3 (Berkeley) %G%
* This driver will generate and accept trailer encapsulated packets even
* though it buys us nothing. The motivation was to avoid incompatibilities
* with VAXen, SUNs, and others that handle and benefit from them.
* This reasoning is dubious.
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/if_ether.h"
#include "netrmp/rmp_var.h"
#include "machine/machConst.h"
#include "../net/bpfdesc.h"
struct driver ledriver
= {
"le", leprobe
, 0, 0, leintr
,
int ledebug
= 1; /* console error messages */
* Ethernet software status per interface.
* Each interface is referenced by a network interface structure,
* le_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
struct arpcom sc_ac
; /* common Ethernet structures */
#define sc_if sc_ac.ac_if /* network-visible interface */
#define sc_addr sc_ac.ac_enaddr /* hardware Ethernet address */
volatile struct lereg1
*sc_r1
; /* LANCE registers */
volatile struct lereg2
*sc_r2
; /* dual-port RAM */
int sc_rmd
; /* predicted next rmd to process */
int sc_tmd
; /* last tmd processed */
int sc_tmdnext
; /* next tmd to transmit with */
/* access LANCE registers */
#define LERDWR(cntl, src, dst) { (dst) = (src); DELAY(10); }
#define CPU_TO_CHIP_ADDR(cpu) \
(((unsigned)(&(((struct lereg2 *)0)->cpu))) >> 1)
/* access LANCE registers */
#define LERDWR(cntl, src, dst) (dst) = (src);
#define CPU_TO_CHIP_ADDR(cpu) \
((unsigned)(&(((struct lereg2 *)0)->cpu)))
#define LE_OFFSET_RAM 0x0
#define LE_OFFSET_LANCE 0x100000
#define LE_OFFSET_ROM 0x1c0000
* Test to see if device is present.
* Return true if found and initialized ok.
* If interface exists, make available by filling in network interface
* record. System will initialize the interface when it is ready
volatile struct lereg1
*ler1
;
struct le_softc
*le
= &le_softc
[dp
->pmax_unit
];
struct ifnet
*ifp
= &le
->sc_if
;
extern int leinit(), leioctl(), lestart(), ether_output();
le
->sc_r1
= ler1
= (volatile struct lereg1
*)dp
->pmax_addr
;
le
->sc_r2
= (volatile struct lereg2
*)MACH_NETWORK_BUFFER_ADDR
;
* Read the ethernet address.
* See "DECstation 3100 Desktop Workstation Functional Specification".
cp
= (u_char
*)(MACH_CLOCK_ADDR
+ 1);
for (i
= 0; i
< sizeof(le
->sc_addr
); i
++) {
le
->sc_r1
= ler1
= (volatile struct lereg1
*)
(dp
->pmax_addr
+ LE_OFFSET_LANCE
);
le
->sc_r2
= (volatile struct lereg2
*)(dp
->pmax_addr
+ LE_OFFSET_RAM
);
* Read the ethernet address.
cp
= (u_char
*)(dp
->pmax_addr
+ LE_OFFSET_ROM
+ 2);
for (i
= 0; i
< sizeof(le
->sc_addr
); i
++) {
/* make sure the chip is stopped */
LERDWR(ler0
, LE_CSR0
, ler1
->ler1_rap
);
LERDWR(ler0
, LE_STOP
, ler1
->ler1_rdp
);
ifp
->if_unit
= dp
->pmax_unit
;
ifp
->if_output
= ether_output
;
ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
;
bpfattach(&le
->sc_bpf
, ifp
, DLT_EN10MB
, sizeof(struct ether_header
));
printf("le%d at nexus0 csr 0x%x priority %d ethernet address %s\n",
dp
->pmax_unit
, dp
->pmax_addr
, dp
->pmax_pri
,
ether_sprintf(le
->sc_addr
));
register volatile struct lereg2
*ler2
;
for (i
= 0; i
< LERBUF
; i
++) {
ler2
->ler2_rmd
[i
].rmd0
= CPU_TO_CHIP_ADDR(ler2_rbuf
[i
][0]);
ler2
->ler2_rmd
[i
].rmd1
= LE_OWN
;
ler2
->ler2_rmd
[i
].rmd2
= -LEMTU
;
ler2
->ler2_rmd
[i
].rmd3
= 0;
for (i
= 0; i
< LETBUF
; i
++) {
ler2
->ler2_tmd
[i
].tmd0
= CPU_TO_CHIP_ADDR(ler2_tbuf
[i
][0]);
ler2
->ler2_tmd
[i
].tmd1
= 0;
ler2
->ler2_tmd
[i
].tmd2
= 0;
ler2
->ler2_tmd
[i
].tmd3
= 0;
register struct le_softc
*le
= &le_softc
[unit
];
register volatile struct lereg1
*ler1
= le
->sc_r1
;
register volatile struct lereg2
*ler2
= le
->sc_r2
;
register int timo
= 100000;
if (le
->sc_if
.if_flags
& IFF_PROMISC
)
/* set the promiscuous bit */
le
->sc_r2
->ler2_mode
= LE_MODE
|0x8000;
le
->sc_r2
->ler2_mode
= LE_MODE
;
LERDWR(ler0
, LE_CSR0
, ler1
->ler1_rap
);
LERDWR(ler0
, LE_STOP
, ler1
->ler1_rdp
);
* Setup for transmit/receive
ler2
->ler2_mode
= LE_MODE
;
ler2
->ler2_padr0
= (le
->sc_addr
[1] << 8) | le
->sc_addr
[0];
ler2
->ler2_padr1
= (le
->sc_addr
[3] << 8) | le
->sc_addr
[2];
ler2
->ler2_padr2
= (le
->sc_addr
[5] << 8) | le
->sc_addr
[4];
* Set up logical addr filter to accept multicast 9:0:9:0:0:4
* This should be an ioctl() to the driver. (XXX)
ler2
->ler2_ladrf0
= 0x0010;
ler2
->ler2_rlen
= LE_RLEN
;
ler2
->ler2_rdra
= CPU_TO_CHIP_ADDR(ler2_rmd
[0]);
ler2
->ler2_tlen
= LE_TLEN
;
ler2
->ler2_tdra
= CPU_TO_CHIP_ADDR(ler2_tmd
[0]);
LERDWR(ler0
, LE_CSR1
, ler1
->ler1_rap
);
LERDWR(ler0
, CPU_TO_CHIP_ADDR(ler2_mode
), ler1
->ler1_rdp
);
LERDWR(ler0
, LE_CSR2
, ler1
->ler1_rap
);
LERDWR(ler0
, 0, ler1
->ler1_rdp
);
LERDWR(ler0
, LE_CSR3
, ler1
->ler1_rap
);
LERDWR(ler0
, 0, ler1
->ler1_rdp
);
LERDWR(ler0
, LE_CSR0
, ler1
->ler1_rap
);
LERDWR(ler0
, LE_INIT
, ler1
->ler1_rdp
);
printf("le%d: init timeout, stat = 0x%x\n",
LERDWR(ler0
, ler1
->ler1_rdp
, stat
);
} while ((stat
& LE_IDON
) == 0);
LERDWR(ler0
, LE_IDON
, ler1
->ler1_rdp
);
LERDWR(ler0
, LE_STRT
| LE_INEA
, ler1
->ler1_rdp
);
le
->sc_if
.if_flags
&= ~IFF_OACTIVE
;
* Initialization of interface
struct le_softc
*le
= &le_softc
[unit
];
register struct ifnet
*ifp
= &le
->sc_if
;
/* not yet, if address still unknown */
if (ifp
->if_addrlist
== (struct ifaddr
*)0)
if ((ifp
->if_flags
& IFF_RUNNING
) == 0) {
ifp
->if_flags
|= IFF_RUNNING
;
if (++bix == LETBUF) bix = 0, tmd = le->sc_r2->ler2_tmd; else ++tmd
* Start output on interface. Get another datagram to send
* off of the interface queue, and copy it to the interface
* before starting the output.
register struct le_softc
*le
= &le_softc
[ifp
->if_unit
];
register int bix
= le
->sc_tmdnext
;
register volatile struct letmd
*tmd
= &le
->sc_r2
->ler2_tmd
[bix
];
if ((le
->sc_if
.if_flags
& IFF_RUNNING
) == 0)
while (bix
!= le
->sc_tmd
) {
IF_DEQUEUE(&le
->sc_if
.if_snd
, m
);
len
= leput(le
->sc_r2
->ler2_tbuf
[bix
], m
);
* If bpf is listening on this interface, let it
* see the packet before we commit it to the wire.
bpf_tap(le
->sc_bpf
, le
->sc_r2
->ler2_tbuf
[bix
], len
);
tmd
->tmd1
= LE_OWN
| LE_STP
| LE_ENP
;
le
->sc_if
.if_flags
|= IFF_OACTIVE
;
LERDWR(ler0
, LE_TDMD
| LE_INEA
, le
->sc_r1
->ler1_rdp
);
* Process interrupts from the 7990 chip.
register struct le_softc
*le
;
register volatile struct lereg1
*ler1
;
printf("le%d: spurrious interrupt\n", unit
);
LERDWR(ler0
, LE_BABL
|LE_CERR
|LE_MISS
|LE_INEA
, ler1
->ler1_rdp
);
if ((stat
& LE_RXON
) == 0) {
if ((stat
& LE_TXON
) == 0) {
/* interrupt is cleared in lerint */
LERDWR(ler0
, LE_TINT
|LE_INEA
, ler1
->ler1_rdp
);
* Ethernet interface transmitter interrupt.
* Start another output if more data to send.
register struct le_softc
*le
= &le_softc
[unit
];
register int bix
= le
->sc_tmd
;
register volatile struct letmd
*tmd
= &le
->sc_r2
->ler2_tmd
[bix
];
if ((le
->sc_if
.if_flags
& IFF_OACTIVE
) == 0) {
while (bix
!= le
->sc_tmdnext
&& (tmd
->tmd1
& LE_OWN
) == 0) {
if ((tmd
->tmd1
& LE_ERR
) || (tmd
->tmd3
& LE_TBUFF
)) {
if (tmd
->tmd3
& (LE_TBUFF
|LE_UFLO
)) {
else if (tmd
->tmd3
& LE_LCOL
)
le
->sc_if
.if_collisions
++;
else if (tmd
->tmd3
& LE_RTRY
)
le
->sc_if
.if_collisions
+= 16;
else if (tmd
->tmd1
& LE_ONE
)
le
->sc_if
.if_collisions
++;
else if (tmd
->tmd1
& LE_MORE
)
/* what is the real number? */
le
->sc_if
.if_collisions
+= 2;
if (bix
== le
->sc_tmdnext
)
le
->sc_if
.if_flags
&= ~IFF_OACTIVE
;
(void) lestart(&le
->sc_if
);
if (++bix == LERBUF) bix = 0, rmd = le->sc_r2->ler2_rmd; else ++rmd
* Ethernet interface receiver interrupt.
* If input error just drop packet.
* Decapsulate packet based on type and pass to type specific
* higher-level input routine.
register struct le_softc
*le
= &le_softc
[unit
];
register int bix
= le
->sc_rmd
;
register volatile struct lermd
*rmd
= &le
->sc_r2
->ler2_rmd
[bix
];
* Out of sync with hardware, should never happen?
if (rmd
->rmd1
& LE_OWN
) {
LERDWR(le
->sc_r0
, LE_RINT
|LE_INEA
, le
->sc_r1
->ler1_rdp
);
* Process all buffers with valid data
while ((rmd
->rmd1
& LE_OWN
) == 0) {
/* Clear interrupt to avoid race condition */
LERDWR(le
->sc_r0
, LE_RINT
|LE_INEA
, le
->sc_r1
->ler1_rdp
);
if (rmd
->rmd1
& LE_ERR
) {
lererror(unit
, "bad packet");
} else if ((rmd
->rmd1
& (LE_STP
|LE_ENP
)) != (LE_STP
|LE_ENP
)) {
* Find the end of the packet so we can see how long
* it was. We still throw it away.
LERDWR(le
->sc_r0
, LE_RINT
|LE_INEA
,
} while (!(rmd
->rmd1
& (LE_OWN
|LE_ERR
|LE_STP
|LE_ENP
)));
lererror(unit
, "chained buffer");
* If search terminated without successful completion
* we reset the hardware (conservative).
if ((rmd
->rmd1
& (LE_OWN
|LE_ERR
|LE_STP
|LE_ENP
)) !=
leread(unit
, le
->sc_r2
->ler2_rbuf
[bix
], len
);
MachEmptyWriteBuffer(); /* Paranoia */
* Look at the packet in network buffer memory so we can be smart about how
* we copy the data into mbufs.
* This needs work since we can't just read network buffer memory like
register struct le_softc
*le
= &le_softc
[unit
];
extern struct mbuf
*leget();
CopyFromBuffer(buf
, (char *)&et
, sizeof(et
));
bcopy(buf
, (char *)&et
, sizeof(et
));
et
.ether_type
= ntohs(et
.ether_type
);
/* adjust input length to account for header and CRC */
len
= len
- sizeof(struct ether_header
) - 4;
* If Ethernet Type field is < MaxPacketSize, we probably have
* a IEEE802 packet here. Make sure that the size is at least
* that of the HP LLC. Also do sanity checks on length of LLC
* (old Ethernet Type field) and packet length.
* Provided the above checks succeed, change `len' to reflect
* the length of the LLC (i.e. et.ether_type) and change the
* type field to ETHERTYPE_IEEE so we can switch() on it later.
* Yes, this is a hack and will eventually be done "right".
if (et
.ether_type
<= IEEE802LEN_MAX
&& len
>= sizeof(struct hp_llc
) &&
len
>= et
.ether_type
&& len
>= IEEE802LEN_MIN
) {
et
.ether_type
= ETHERTYPE_IEEE
; /* hack! */
if (et
.ether_type
>= ETHERTYPE_TRAIL
&&
et
.ether_type
< ETHERTYPE_TRAIL
+ETHERTYPE_NTRAILER
) {
off
= (et
.ether_type
- ETHERTYPE_TRAIL
) * 512;
CopyFromBuffer(buf
+ (sizeof(et
) + off
),
(char *)sbuf
, sizeof(sbuf
));
et
.ether_type
= ntohs(sbuf
[0]);
et
.ether_type
= ntohs((u_short
*)(buf
+ (sizeof(et
) + off
))[0]);
resid
= ntohs((u_short
*)(buf
+ (sizeof(et
) + off
))[1]);
"le%d: ierror(runt packet): from %s: len=%d\n",
unit
, ether_sprintf(et
.ether_shost
), len
);
* Check if there's a bpf filter listening on this interface.
* If so, hand off the raw packet to bpf, which must deal with
* trailers in its own way.
bpf_tap(le
->sc_bpf
, buf
, len
+ sizeof(struct ether_header
));
* Note that the interface cannot be in promiscuous mode if
* there are no bpf listeners. And if we are in promiscuous
* mode, we have to check if this packet is really ours.
* XXX This test does not support multicasts.
if ((le
->sc_if
.if_flags
& IFF_PROMISC
)
&& bcmp(et
.ether_dhost
, le
->sc_addr
,
sizeof(et
.ether_dhost
)) != 0
&& bcmp(et
.ether_dhost
, etherbroadcastaddr
,
sizeof(et
.ether_dhost
)) != 0)
* Pull packet off interface. Off is nonzero if packet
* has trailing header; leget will then force this header
* information to be at the front, but we still have to drop
* the type and length which are at the front of any trailer data.
m
= leget(buf
, len
, off
, &le
->sc_if
);
* This needs to be integrated with the ISO stuff in ether_input()
if (et
.ether_type
== ETHERTYPE_IEEE
) {
* Snag the Logical Link Control header (IEEE 802.2).
struct hp_llc
*llc
= &(mtod(m
, struct rmp_packet
*)->hp_llc
);
* If the DSAP (and HP's extended DXSAP) indicate this
* is an RMP packet, hand it to the raw input routine.
if (llc
->dsap
== IEEE_DSAP_HP
&& llc
->dxsap
== HPEXT_DXSAP
) {
static struct sockproto rmp_sp
= {AF_RMP
,RMPPROTO_BOOT
};
static struct sockaddr rmp_src
= {AF_RMP
};
static struct sockaddr rmp_dst
= {AF_RMP
};
bcopy(et
.ether_shost
, rmp_src
.sa_data
,
bcopy(et
.ether_dhost
, rmp_dst
.sa_data
,
raw_input(m
, &rmp_sp
, &rmp_src
, &rmp_dst
);
ether_input(&le
->sc_if
, &et
, m
);
* Routine to copy from mbuf chain to transmit buffer in
* NOTE: On the DS3100, network memory can only be written one short at
register le_buf_t
*lebuf
;
register struct mbuf
*mp
;
register int len
, tlen
= 0;
for (mp
= m
; mp
; mp
= mp
->m_next
) {
/* copy data for this mbuf */
/* handle odd length from previous mbuf */
*lebuf
= (cp
[0] << 8) | tmp
;
*lebuf
= (cp
[1] << 8) | cp
[0];
/* optimize for aligned transfers */
xfer
= (int)((unsigned)len
& ~0x1);
CopyToBuffer((u_short
*)cp
, lebuf
, xfer
);
bcopy(mtod(mp
, char *), lebuf
, len
);
/* handle odd length from previous mbuf */
while (tlen
< LEMINSIZE
) {
bzero(lebuf
, LEMINSIZE
- tlen
);
* Routine to copy from network buffer memory into mbufs.
* NOTE: On the DS3100, network memory can only be written one short at
leget(lebuf
, totlen
, off
, ifp
)
struct mbuf
*top
= 0, **mp
= &top
;
/* NOTE: sizeof(struct ether_header) should be even */
lebuf
+= sizeof(struct ether_header
);
/* NOTE: off should be even */
sp
+= off
+ 2 * sizeof(u_short
);
totlen
-= 2 * sizeof(u_short
);
MGETHDR(m
, M_DONTWAIT
, MT_DATA
);
m
->m_pkthdr
.len
= totlen
;
MGET(m
, M_DONTWAIT
, MT_DATA
);
m
->m_len
= MIN(resid
, MCLBYTES
);
else if (resid
< m
->m_len
) {
* Place initial small packet/header at end of mbuf.
if (top
== 0 && resid
+ max_linkhdr
<= m
->m_len
)
m
->m_data
+= max_linkhdr
;
* Previous len was odd. Copy the single byte specially.
* XXX Can this ever happen??
*mtod(m
, char *) = ((volatile char *)sp
)[-1];
CopyFromBuffer(sp
+ 1, mtod(m
, char *) + 1, len
- 1);
CopyFromBuffer(sp
, mtod(m
, char *), len
);
bcopy(sp
, mtod(m
, char *), len
);
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
struct le_softc
*le
= &le_softc
[ifp
->if_unit
];
volatile struct lereg1
*ler1
= le
->sc_r1
;
switch (ifa
->ifa_addr
->sa_family
) {
leinit(ifp
->if_unit
); /* before arpwhohas */
((struct arpcom
*)ifp
)->ac_ipaddr
=
arpwhohas((struct arpcom
*)ifp
, &IA_SIN(ifa
)->sin_addr
);
register struct ns_addr
*ina
= &(IA_SNS(ifa
)->sns_addr
);
ina
->x_host
= *(union ns_host
*)(le
->sc_addr
);
* The manual says we can't change the address
* while the receiver is armed,
ifp
->if_flags
&= ~IFF_RUNNING
;
bcopy((caddr_t
)ina
->x_host
.c_host
,
(caddr_t
)le
->sc_addr
, sizeof(le
->sc_addr
));
leinit(ifp
->if_unit
); /* does le_setaddr() */
if ((ifp
->if_flags
& IFF_UP
) == 0 &&
ifp
->if_flags
& IFF_RUNNING
) {
LERDWR(le
->sc_r0
, LE_STOP
, ler1
->ler1_rdp
);
ifp
->if_flags
&= ~IFF_RUNNING
;
} else if (ifp
->if_flags
& IFF_UP
&&
(ifp
->if_flags
& IFF_RUNNING
) == 0)
* If the state of the promiscuous bit changes, the interface
* must be reset to effect the change.
if (((ifp
->if_flags
^ le
->sc_iflags
) & IFF_PROMISC
) &&
(ifp
->if_flags
& IFF_RUNNING
)) {
le
->sc_iflags
= ifp
->if_flags
;
* Not all transceivers implement heartbeat
* so we only log CERR once.
if ((stat
& LE_CERR
) && le_softc
[unit
].sc_cerr
)
"le%d: error: stat=%b\n", unit
,
"\20\20ERR\17BABL\16CERR\15MISS\14MERR\13RINT\12TINT\11IDON\10INTR\07INEA\06RXON\05TXON\04TDMD\03STOP\02STRT\01INIT");
register struct le_softc
*le
= &le_softc
[unit
];
register volatile struct lermd
*rmd
;
rmd
= &le
->sc_r2
->ler2_rmd
[le
->sc_rmd
];
"le%d: ierror(%s): from %s: buf=%d, len=%d, rmd1=%b\n",
len
> 11 ? ether_sprintf(&le
->sc_r2
->ler2_rbuf
[le
->sc_rmd
][6]) : "unknown",
"\20\20OWN\17ERR\16FRAM\15OFLO\14CRC\13RBUF\12STP\11ENP");
register struct le_softc
*le
= &le_softc
[unit
];
register volatile struct letmd
*tmd
;
tmd
= le
->sc_r2
->ler2_tmd
;
"le%d: oerror: to %s: buf=%d, len=%d, tmd1=%b, tmd3=%b\n",
len
> 5 ? ether_sprintf(&le
->sc_r2
->ler2_tbuf
[0][0]) : "unknown",
"\20\20OWN\17ERR\16RES\15MORE\14ONE\13DEF\12STP\11ENP",
"\20\20BUFF\17UFLO\16RES\15LCOL\14LCAR\13RTRY");