* Copyright (c) 1982, 1990 The Regents of the University of California.
* %sccs.include.redist.c%
* @(#)if_le.c 7.1 (Berkeley) %G%
* This driver will generate and 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 "../net/netisr.h"
#include "../net/route.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#include "../netinet/if_ether.h"
#include "../netns/ns_if.h"
#include "../netrmp/rmp.h"
#include "../netrmp/rmp_var.h"
#include "machine/mtpr.h"
/* offsets for: ID, REGS, MEM, NVRAM */
int lestd
[] = { 0, 0x4000, 0x8000, 0xC008 };
struct driver ledriver
= {
int ledebug
= 0; /* console error messages */
int leintr(), leinit(), leioctl(), lestart(), ether_output();
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 lereg0
*sc_r0
; /* DIO registers */
struct lereg1
*sc_r1
; /* LANCE registers */
struct lereg2
*sc_r2
; /* dual-port RAM */
int sc_rmd
; /* predicted next rmd to process */
/* access LANCE registers */
#define LERDWR(cntl, src, dst) \
} while (((cntl)->ler0_status & LE_ACK) == 0);
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register struct lereg0
*ler0
;
register struct lereg2
*ler2
;
struct lereg2
*lemem
= 0;
struct le_softc
*le
= &le_softc
[hd
->hp_unit
];
struct ifnet
*ifp
= &le
->sc_if
;
ler0
= le
->sc_r0
= (struct lereg0
*)(lestd
[0] + (int)hd
->hp_addr
);
le
->sc_r1
= (struct lereg1
*)(lestd
[1] + (int)hd
->hp_addr
);
ler2
= le
->sc_r2
= (struct lereg2
*)(lestd
[2] + (int)hd
->hp_addr
);
if (ler0
->ler0_id
!= LEID
)
le_isr
[hd
->hp_unit
].isr_intr
= leintr
;
hd
->hp_ipl
= le_isr
[hd
->hp_unit
].isr_ipl
= LE_IPL(ler0
->ler0_status
);
le_isr
[hd
->hp_unit
].isr_arg
= hd
->hp_unit
;
* Read the ethernet address off the board, one nibble at a time.
cp
= (char *)(lestd
[3] + (int)hd
->hp_addr
);
for (i
= 0; i
< sizeof(le
->sc_addr
); i
++) {
le
->sc_addr
[i
] = (*++cp
& 0xF) << 4;
le
->sc_addr
[i
] |= *++cp
& 0xF;
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_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];
* 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
= 0x00100000;
ler2
->ler2_rlen
= LE_RLEN
;
ler2
->ler2_rdra
= (int)lemem
->ler2_rmd
;
ler2
->ler2_tlen
= LE_TLEN
;
ler2
->ler2_tdra
= (int)lemem
->ler2_tmd
;
isrlink(&le_isr
[hd
->hp_unit
]);
ler0
->ler0_status
= LE_IE
;
ifp
->if_unit
= hd
->hp_unit
;
ifp
->if_output
= ether_output
;
ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
;
register struct lereg2
*ler2
;
register struct lereg2
*lemem
= 0;
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;
register struct le_softc
*le
= &le_softc
[unit
];
register struct lereg0
*ler0
= le
->sc_r0
;
register struct lereg1
*ler1
= le
->sc_r1
;
register struct lereg2
*lemem
= 0;
register int timo
= 100000;
LERDWR(ler0
, LE_CSR0
, ler1
->ler1_rap
);
LERDWR(ler0
, LE_STOP
, ler1
->ler1_rdp
);
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
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
;
* 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)
IF_DEQUEUE(&le
->sc_if
.if_snd
, m
);
len
= leput(le
->sc_r2
->ler2_tbuf
[0], m
);
tmd
= le
->sc_r2
->ler2_tmd
;
tmd
->tmd1
= LE_OWN
| LE_STP
| LE_ENP
;
le
->sc_if
.if_flags
|= IFF_OACTIVE
;
register struct le_softc
*le
= &le_softc
[unit
];
register struct lereg0
*ler0
= le
->sc_r0
;
register struct lereg1
*ler1
;
if ((ler0
->ler0_status
& LE_IR
) == 0)
if (ler0
->ler0_status
& LE_JAB
) {
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) {
/* 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 struct letmd
*tmd
= le
->sc_r2
->ler2_tmd
;
if ((le
->sc_if
.if_flags
& IFF_OACTIVE
) == 0) {
if (tmd
->tmd1
& LE_OWN
) {
if (tmd
->tmd1
& LE_ERR
) {
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
->tmd3
& LE_TBUFF
)
/* XXX documentation says BUFF not included in ERR */
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;
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;
* 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! */
#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
);
* 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
,
sizeof(et
->ether_shost
));
bcopy(et
->ether_dhost
, rmp_dst
.sa_data
,
sizeof(et
->ether_dhost
));
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 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
);
* Routine to copy from board local memory into mbufs.
leget(lebuf
, totlen
, off0
, ifp
)
struct mbuf
*top
= 0, **mp
= &top
;
register int off
= off0
, len
;
lebuf
+= sizeof (struct ether_header
);
cp
+= 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
);
len
= min(totlen
, epkt
- cp
);
m
->m_len
= len
= min(len
, MCLBYTES
);
* Place initial small packet/header at end of mbuf.
if (top
== 0 && len
+ max_linkhdr
<= m
->m_len
)
m
->m_data
+= max_linkhdr
;
bcopy(cp
, mtod(m
, caddr_t
), (unsigned)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
];
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
;
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)
* 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",
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 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");