* Copyright (c) 1982, 1990, 1993
* The Regents of the University of California. All rights reserved.
* %sccs.include.redist.c%
* from: hp300/dev/if_le.c 7.16 (Berkeley) 3/11/93
* @(#)if_le.c 8.1 (Berkeley) %G%
* This driver will accept tailer 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>
#if defined (CCITT) && defined (LLC)
#include <sys/socketvar.h>
#include <netccitt/x25.h>
extern llc_ctlinput(), cons_rtrequest();
#include <machine/mtpr.h>
#include <luna68k/dev/device.h>
#include <luna68k/dev/if_lereg.h>
struct driver ledriver
= {
int ledebug
= 0; /* console error messages */
int leintr(), leinit(), leioctl(), lestart(), ether_output(), lereset();
extern struct ifnet loif
;
* 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 */
struct lereg1
*sc_r1
; /* LANCE registers */
struct lereg2
*sc_r2
; /* dual-port RAM */
int sc_rmd
; /* predicted next rmd to process */
int sc_tmd
; /* next available tmd */
int sc_txcnt
; /* # of transmit buffers in use */
/* access LANCE registers */
#define LERDWR(cntl, src, dst) (dst) = (src)
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register struct lereg2
*ler2
;
struct lereg2
*lemem
= (struct lereg2
*) 0;
struct le_softc
*le
= &le_softc
[hd
->hp_unit
];
struct ifnet
*ifp
= &le
->sc_if
;
le
->sc_r1
= (struct lereg1
*) hd
->hp_addr
;
ler2
= le
->sc_r2
= (struct lereg2
*) 0x71000000;
* Read the ethernet address off the board, one nibble at a time.
if (machineid
== LUNA_II
) {
static char rom_data
[128];
volatile u_int
*from
= (u_int
*)0xf1000004;
for (i
= 0; i
< 128; i
++) {
rom_data
[i
] |= (*from
>> 12) & 0xf0;
*from
= (i
* 2 + 1) << 16;
rom_data
[i
] |= (*from
>> 16) & 0xf;
cp
=&rom_data
[6]; /* ETHER0 must be here */
/* one port only now XXX */
cp
= (char *) 0x4101FFE0;
for (i
= 0; i
< sizeof(le
->sc_addr
); i
++) {
le
->sc_addr
[i
] = (*cp
< 'A' ? (*cp
& 0xF) : (*cp
& 0xF) + 9) << 4;
le
->sc_addr
[i
] |= (*cp
< 'A' ? (*cp
& 0xF) : (*cp
& 0xF) + 9);
printf("le%d: hardware address %s\n", hd
->hp_unit
,
ether_sprintf(le
->sc_addr
));
* Setup for transmit/receive
ler2
->ler2_mode
= LE_MODE
;
ler2
->ler2_rlen
= LE_RLEN
;
ler2
->ler2_rdra
= (int)lemem
->ler2_rmd
;
ler2
->ler2_tlen
= LE_TLEN
;
ler2
->ler2_tdra
= (int)lemem
->ler2_tmd
;
ifp
->if_unit
= hd
->hp_unit
;
ifp
->if_output
= ether_output
;
ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
| IFF_MULTICAST
;
ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
;
bpfattach(&ifp
->if_bpf
, ifp
, DLT_EN10MB
, sizeof(struct ether_header
));
* Setup the logical address filter
register struct le_softc
*sc
;
register volatile struct lereg2
*ler2
= sc
->sc_r2
;
register struct ifnet
*ifp
= &sc
->sc_if
;
register struct ether_multi
*enm
;
struct ether_multistep step
;
* Set up multicast address filter by passing all multicast
* addresses through a crc generator, and then using the high
* order 6 bits as a index into the 64 bit logical address
* filter. The high order two bits select the word, while the
* rest of the bits select the bit within the word.
ifp
->if_flags
&= ~IFF_ALLMULTI
;
ETHER_FIRST_MULTI(step
, &sc
->sc_ac
, enm
);
if (bcmp((caddr_t
)&enm
->enm_addrlo
,
(caddr_t
)&enm
->enm_addrhi
, sizeof(enm
->enm_addrlo
)) == 0) {
* We must listen to a range of multicast
* addresses. For now, just accept all
* multicasts, rather than trying to set only
* those filter bits needed to match the range.
* (At this time, the only use of address
* ranges is for IP multicast routing, for
* which the range is big enough to require all
ler2
->ler2_ladrf
[0] = 0xffffffff;
ler2
->ler2_ladrf
[1] = 0xffffffff;
ifp
->if_flags
|= IFF_ALLMULTI
;
cp
= (unsigned char *)&enm
->enm_addrlo
;
for (i
= 0; i
< 8; i
++) {
if ((c
& 0x01) ^ (crc
& 0x01)) {
/* Just want the 6 most significant bits. */
/* Turn on the corresponding bit in the filter. */
ler2
->ler2_ladrf
[crc
>> 5] |= 1 << (crc
& 0x1f);
ETHER_NEXT_MULTI(step
, enm
);
register struct lereg2
*ler2
;
register struct le_softc
*le
;
register struct lereg2
*lemem
= (struct lereg2
*) 0;
ler2
->ler2_padr
[0] = le
->sc_addr
[1];
ler2
->ler2_padr
[1] = le
->sc_addr
[0];
ler2
->ler2_padr
[2] = le
->sc_addr
[3];
ler2
->ler2_padr
[3] = le
->sc_addr
[2];
ler2
->ler2_padr
[4] = le
->sc_addr
[5];
ler2
->ler2_padr
[5] = le
->sc_addr
[4];
for (i
= 0; i
< LERBUF
; i
++) {
ler2
->ler2_rmd
[i
].rmd0
= (int)lemem
->ler2_rbuf
[i
];
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
= (int)lemem
->ler2_tbuf
[i
];
ler2
->ler2_tmd
[i
].tmd1
= 0;
ler2
->ler2_tmd
[i
].tmd2
= 0;
ler2
->ler2_tmd
[i
].tmd3
= 0;
/* Setup the logical address filter */
register struct le_softc
*le
= &le_softc
[unit
];
register struct lereg1
*ler1
= le
->sc_r1
;
register struct lereg2
*lemem
= (struct lereg2
*) 0;
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
);
le
->sc_rmd
= le
->sc_tmd
= 0;
LERDWR(ler0
, LE_CSR1
, ler1
->ler1_rap
);
LERDWR(ler0
, (int)&lemem
->ler2_mode
, ler1
->ler1_rdp
);
LERDWR(ler0
, LE_CSR2
, 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_STOP
, ler1
->ler1_rdp
);
LERDWR(ler0
, LE_CSR3
, ler1
->ler1_rap
);
LERDWR(ler0
, LE_BSWP
, ler1
->ler1_rdp
);
LERDWR(ler0
, LE_CSR0
, ler1
->ler1_rap
);
LERDWR(ler0
, LE_STRT
| LE_INEA
, ler1
->ler1_rdp
);
le
->sc_if
.if_flags
&= ~IFF_OACTIVE
;
* Initialization of interface
register struct ifnet
*ifp
= &le_softc
[unit
].sc_if
;
register struct ifaddr
*ifa
;
/* not yet, if address still unknown */
for (ifa
= ifp
->if_addrlist
;; ifa
= ifa
->ifa_next
)
else if (ifa
->ifa_addr
&& ifa
->ifa_addr
->sa_family
!= AF_LINK
)
if ((ifp
->if_flags
& IFF_RUNNING
) == 0) {
ifp
->if_flags
|= IFF_RUNNING
;
* 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 struct letmd
*tmd
;
if ((le
->sc_if
.if_flags
& IFF_RUNNING
) == 0)
tmd
= &le
->sc_r2
->ler2_tmd
[le
->sc_tmd
];
if (tmd
->tmd1
& LE_OWN
) {
IF_DEQUEUE(&le
->sc_if
.if_snd
, m
);
len
= leput(le
->sc_r2
->ler2_tbuf
[le
->sc_tmd
], m
);
* If bpf is listening on this interface, let it
* see the packet before we commit it to the wire.
bpf_tap(ifp
->if_bpf
, le
->sc_r2
->ler2_tbuf
[le
->sc_tmd
],
tmd
->tmd1
= LE_OWN
| LE_STP
| LE_ENP
;
if (++le
->sc_tmd
== LETBUF
) {
tmd
= le
->sc_r2
->ler2_tmd
;
} while (++le
->sc_txcnt
< LETBUF
);
le
->sc_if
.if_flags
|= IFF_OACTIVE
;
for (i
= 0; i
< NLE
; i
++) {
register struct le_softc
*le
= &le_softc
[unit
];
register struct lereg1
*ler1
;
LERDWR(ler0
, ler1
->ler1_rdp
, stat
);
LERDWR(ler0
, LE_BABL
|LE_CERR
|LE_MISS
|LE_INEA
, ler1
->ler1_rdp
);
if ((stat
& LE_RXON
) == 0) {
if ((stat
& LE_TXON
) == 0) {
* Ethernet interface transmitter interrupt.
* Start another output if more data to send.
register struct le_softc
*le
= &le_softc
[unit
];
register struct letmd
*tmd
;
if ((i
= le
->sc_tmd
- le
->sc_txcnt
) < 0)
tmd
= &le
->sc_r2
->ler2_tmd
[i
];
if (tmd
->tmd1
& LE_OWN
) {
LERDWR(le
->sc_r0
, LE_TINT
|LE_INEA
, le
->sc_r1
->ler1_rdp
);
/* XXX documentation says BUFF not included in ERR */
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;
} while (--le
->sc_txcnt
> 0);
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 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
);
register struct le_softc
*le
= &le_softc
[unit
];
register struct ether_header
*et
;
et
= (struct ether_header
*)buf
;
et
->ether_type
= ntohs((u_short
)et
->ether_type
);
/* adjust input length to account for header and CRC */
len
= len
- sizeof(struct ether_header
) - 4;
#define ledataaddr(et, off, type) ((type)(((caddr_t)((et)+1)+(off))))
if (et
->ether_type
>= ETHERTYPE_TRAIL
&&
et
->ether_type
< ETHERTYPE_TRAIL
+ETHERTYPE_NTRAILER
) {
off
= (et
->ether_type
- ETHERTYPE_TRAIL
) * 512;
et
->ether_type
= ntohs(*ledataaddr(et
, off
, u_short
*));
resid
= ntohs(*(ledataaddr(et
, off
+2, u_short
*)));
"le%d: ierror(runt packet): from %s: len=%d\n",
unit
, ether_sprintf(et
->ether_shost
), len
);
if (bcmp((caddr_t
)etherbroadcastaddr
,
(caddr_t
)et
->ether_dhost
, sizeof(etherbroadcastaddr
)) == 0)
if (et
->ether_dhost
[0] & 1)
* Check if there's a bpf filter listening on this interface.
* If so, hand off the raw packet to enet.
bpf_tap(le
->sc_if
.if_bpf
, buf
, len
+ sizeof(struct ether_header
));
* Keep the packet if it's a broadcast or has our
* physical ethernet address (or if we support
* multicast and it's one).
(flags
& (M_BCAST
| M_MCAST
)) == 0 &&
(flags
& M_BCAST
) == 0 &&
bcmp(et
->ether_dhost
, le
->sc_addr
,
sizeof(et
->ether_dhost
)) != 0)
* Pull packet off interface. Off is nonzero if packet
* has trailing header; m_devget 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
= m_devget((char *)(et
+ 1), len
, off
, &le
->sc_if
, 0);
ether_input(&le
->sc_if
, et
, m
);
* Routine to copy from mbuf chain to transmit
* buffer in board local memory.
register struct mbuf
*mp
;
register int len
, tlen
= 0;
for (mp
= m
; mp
; mp
= mp
->m_next
) {
bcopy(mtod(mp
, char *), lebuf
, len
);
bzero(lebuf
, LEMINSIZE
- tlen
);
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
struct le_softc
*le
= &le_softc
[ifp
->if_unit
];
struct lereg1
*ler1
= le
->sc_r1
;
int s
= splimp(), error
= 0;
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
;
LERDWR(le
->sc_r0
, LE_STOP
, ler1
->ler1_rdp
);
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 defined (CCITT) && defined (LLC)
ifp
-> if_flags
|= IFF_UP
;
ifa
-> ifa_rtrequest
= cons_rtrequest
;
error
= x25_llcglue(PRC_IFUP
, ifa
-> ifa_addr
);
#endif /* CCITT && LLC */
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
;
/* Update our multicast list */
error
= (cmd
== SIOCADDMULTI
) ?
ether_addmulti((struct ifreq
*)data
, &le
->sc_ac
) :
ether_delmulti((struct ifreq
*)data
, &le
->sc_ac
);
if (error
== ENETRESET
) {
* Multicast list has changed; set the hardware
* 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 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",
ether_sprintf((u_char
*)&le
->sc_r2
->ler2_rbuf
[le
->sc_rmd
][6]) :
"\20\20OWN\17ERR\16FRAM\15OFLO\14CRC\13RBUF\12STP\11ENP");
register struct le_softc
*le
= &le_softc
[unit
];
register struct letmd
*tmd
;
tmd
= le
->sc_r2
->ler2_tmd
;
"le%d: oerror: to %s: buf=%d, len=%d, tmd1=%b, tmd3=%b\n",
ether_sprintf((u_char
*)&le
->sc_r2
->ler2_tbuf
[0][0]) :
"\20\20OWN\17ERR\16RES\15MORE\14ONE\13DEF\12STP\11ENP",
"\20\20BUFF\17UFLO\16RES\15LCOL\14LCAR\13RTRY");