* Copyright (c) 1991 The Regents of the University of California.
* %sccs.include.redist.c%
* @(#)if_ec.c 7.3 (Berkeley) %G%
* Intel 82586/3com Etherlink II controller.
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/if_ether.h"
extern char all_es_snpa
[], all_is_snpa
[], all_l1is_snpa
[], all_l2is_snpa
[];
#include "../net/bpfdesc.h"
int ecdebug
= 0; /* console error messages */
int ecintr(), ecinit(), ecioctl(), ecstart(), ether_output();
int ecattach(), ecprobe(), ecreset(), ecwatchdog();
void ec_idpattern(), ec_reset_all(), ec_getnmdata(), ecread();
extern struct ifnet loif
;
struct ec_82586params ec_82586defaults
=
{ 11, 0xc8, ECMINSIZE
, 0x2e, 0, 0x60, 0, 2, 0, 0, 0x40};
/* 2e == no source insert */
* Ethernet software status per interface.
* Each interface is referenced by a network interface structure,
* sc_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 */
caddr_t sc_device
; /* e.g. isa_device */
caddr_t sc_bpf
; /* for packet filter */
struct ec_ports
*sc_ports
; /* control ports for this unit */
struct ec_mem
*sc_hmem
; /* Host addr for shared memory */
struct ec_mem
*sc_dmem
; /* Device (chip) addr for shared mem */
int sc_msize
; /* How much memory is mapped? */
int sc_iflags
; /* copy of sc_if.if_flags for state */
int sc_rxnum
; /* Last receiver dx we looked at */
int sc_txnum
; /* Last tranmistter dx we stomped on */
int sc_txcnt
; /* Number of packets queued for tx*/
int sc_txbusy
; /* we're confused */
int sc_uflo
; /* DMA Late */
int sc_runt
; /* too short */
struct isa_driver ecdriver
= {
#define TIMO 10000 /* used in ec_uprim */
register struct isa_device
*id
;
int unit
= id
->id_unit
, msize
= id
->id_msize
;
struct ec_ports
*reg
= (struct ec_ports
*)id
->id_iobase
;
register struct ec_softc
*ec
= ec_softc
+ unit
;
bzero((caddr_t
)data
, sizeof(data
));
ec_getnmdata(reg
, R_ECID
, data
);
if (bcmp((caddr_t
)data
, "*3com*", sizeof(data
)) != 0) {
printf("ecprobe: ec%d not matched\n");
ec_getnmdata(reg
, R_ETHER
, ec
->sc_addr
);
ec_getnmdata(reg
, R_REV
, data
);
ec
->sc_hmem
= (struct ec_mem
*) (id
->id_maddr
);
ec
->sc_dmem
= (struct ec_mem
*) (0x10000 - msize
);
ec
->sc_device
= (caddr_t
) id
;
printf("ec%d: hardware address %s, rev info %4.4x%4.4x%4.4x\n",
unit
, ether_sprintf(ec
->sc_addr
),
0[(u_short
*)data
], 1[(u_short
*)data
], 2[(u_short
*)data
]);
register caddr_t p
= (caddr_t
)0x100;
for (n
= 255; n
> 0; n
--) {
register caddr_t p
= (caddr_t
)0x100;
#define ECWR(p, e, d) outb(&(p->e), d)
#define ECRD(p, e) inb(&(p->e))
#define SET_CA ECWR(ec->sc_ports, port_ca, 0)
#define UNLATCH_INT ECWR(ec->sc_ports, port_ic, 0);
ec_getnmdata(p
, which
, data
)
register struct ec_ports
*p
;
for (i
= 0; i
< 6; i
++) {
data
[i
] = ECRD(p
, data
[i
]);
register struct ec_softc
*ec
= &ec_softc
[unit
];
struct ec_ports
*p
= ec
->sc_ports
;
struct ec_mem
*hmem
= ec
->sc_hmem
;
ECWR(p
, creg
, R_LPB
); DELAY(10);
if ((ec
->sc_if
.if_flags
& IFF_RUNNING
) == 0)
ECWR(p
, creg
, R_NORST
); DELAY(10);
ECWR(p
, port_ca
, 0); DELAY(10);
for (timo
= TIMO
; hmem
->iscp
.busy
; )
printf("ec(%d)reset: iscp failed\n", unit
);
hmem
->scb
.command
= CU_START
;
ECWR(p
, port_ca
, 0); DELAY(10);
for (timo
= TIMO
; (hmem
->scb
.status
& CU_STATE
) == CUS_ACTIVE
;)
if (timo
== 0 || hmem
->scb
.status
== CUS_IDLE
) {
printf("ec(%d)reset: setup failed\n");
hmem
->scb
.command
= RU_START
;
ECWR(p
, port_ic
, 0); DELAY(10);
ECWR(p
, creg
, R_NORST
|R_IEN
);
ECWR(p
, port_ca
, 0); DELAY(10);
return 0; /* Keep GCC Happy! */
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register struct isa_device
*id
;
struct ec_softc
*ec
= &ec_softc
[unit
];
struct ifnet
*ifp
= &ec
->sc_if
;
ifp
->if_watchdog
= ecwatchdog
;
ifp
->if_output
= ether_output
;
ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
;
bpfattach(&ec
->sc_bpf
, ifp
, DLT_EN10MB
, sizeof(struct ether_header
));
#define OFF(e) ((u_short)&(((struct ec_mem *)0)->e))
register struct ec_softc
*ec
;
register struct ec_mem
*hmem
= ec
->sc_hmem
;
struct ec_rfd
*rc
= hmem
->rcom
;
struct ec_transmit
*tc
= hmem
->tcom
;
bzero((caddr_t
)hmem
, ec
->sc_msize
);
(ec
->sc_msize
- 4 + (caddr_t
)ec
->sc_hmem
) = ec
->sc_dmem
;
hmem
->iscp
.scb_off
= OFF(scb
);
hmem
->iscp
.scb_base
= (caddr_t
)ec
->sc_dmem
;
hmem
->scb
.rfa_off
= OFF(rcom
[0]);
hmem
->scb
.cbl_off
= OFF(config
);
hmem
->config
.com1
= COM1_CONFIGURE
;
bcopy((caddr_t
)&ec_82586defaults
, (caddr_t
)&hmem
->config
.modes
,
sizeof(hmem
->config
.modes
));
if (ec
->sc_if
.if_flags
& IFF_PROMISC
)
hmem
->config
.modes
.promisc
|= M_PROMISC
;
hmem
->config
.next_off
= OFF(iasetup
);
bcopy((caddr_t
)ec
->sc_addr
, (caddr_t
)hmem
->iasetup
.srcaddr
, 6);
hmem
->iasetup
.com1
= COM1_IASETUP
| COM1_S
| COM1_EL
;
hmem
->iasetup
.com1
= COM1_IASETUP
;
hmem
->iasetup
.next_off
= OFF(mcsetup
);
hmem
->mcsetup
.com1
= COM1_MCSETUP
| COM1_S
| COM1_EL
;
hmem
->mcsetup
.count
= 24;
cp
= (caddr_t
)hmem
->txbuf
[0];
bcopy((caddr_t
)all_es_snpa
, cp
, 6); cp
+= 6;
bcopy((caddr_t
)all_is_snpa
, cp
, 6); cp
+= 6;
bcopy((caddr_t
)all_l1is_snpa
, cp
, 6); cp
+= 6;
bcopy((caddr_t
)all_l2is_snpa
, cp
, 6); cp
+= 6;
for (i
= 0; i
< NTXBUF
; i
++) {
tc
->tbd_off
= OFF(tcom
[i
].count
);
tc
->buffer
= ec
->sc_dmem
->txbuf
[i
];
(tc
++)->com1
= COM1_TRANSMIT
| COM1_S
| COM1_EL
| COM1_I
;
for (i
= 0; i
< NRXBUF
; i
++) {
rc
->next_off
= OFF(rcom
[i
+ 1]);
rc
->rbd_off
= OFF(rcom
[i
].count
);
rc
->buffer
= ec
->sc_dmem
->rxbuf
[i
];
(rc
++)->size
= ECMTU
| COM1_EL
;
(--rc
)->next_off
= OFF(rcom
[0]);
* Initialization of interface
register struct ifnet
*ifp
= &ec_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
;
* Timeout: for now check for a transmit command taking more than 10 seconds.
register struct ec_softc
*ec
= ec_softc
+ unit
;
if (ec
->sc_iflags
& IFF_OACTIVE
) {
ec
->sc_if
.if_flags
&= ~IFF_RUNNING
;
} else if (ec
->sc_txcnt
> 0)
ec
->sc_iflags
|= IFF_OACTIVE
;
register struct ec_softc
*ec
;
struct ec_mem
*hmem
= ec
->sc_hmem
;
if ((i
= ec
->sc_txnum
- ec
->sc_txcnt
) < 0) i
+= NTXBUF
;
hmem
->scb
.cbl_off
= OFF(tcom
[i
]);
hmem
->scb
.command
= CU_START
;
* 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 ec_softc
*ec
= &ec_softc
[ifp
->if_unit
];
register struct ec_transmit
*tmd
;
if ((ec
->sc_if
.if_flags
& IFF_RUNNING
) == 0 || ec
->sc_txcnt
>= NTXBUF
)
tmd
= ec
->sc_hmem
->tcom
+ ec
->sc_txnum
;
if (tmd
->com0
& (COM0_B
| COM0_C
))
return (ec
->sc_txbusy
++, 0);
IF_DEQUEUE(&ec
->sc_if
.if_snd
, m
);
len
= ecput(ec
->sc_hmem
->txbuf
[ec
->sc_txnum
], m
);
* If bpf is listening on this interface, let it
* see the packet before we commit it to the wire.
bpf_tap(ec
->sc_bpf
, ec
->sc_hmem
->txbuf
[ec
->sc_txnum
], len
);
tmd
->count
= len
| COM1_EL
;
if (++ec
->sc_txnum
>= NTXBUF
)
if (++ec
->sc_txcnt
>= NTXBUF
) {
ec
->sc_if
.if_flags
|= IFF_OACTIVE
;
struct ec_softc
*ec
= &ec_softc
[unit
];
struct ec_mem
*hmem
= ec
->sc_hmem
;
register int stat
= hmem
->scb
.status
;
hmem
->scb
.command
= stat
& 0xf000; /* Ack interrupt cause */
* Ethernet interface transmitter interrupt.
* Start another output if more data to send.
register struct ec_softc
*ec
= &ec_softc
[unit
];
register struct ec_transmit
*tmd
;
ec
->sc_xint
++; /* unexpected transmit interrupt */
ec
->sc_iflags
&= ~IFF_OACTIVE
; /* clear deadman indication */
if ((i
= ec
->sc_txnum
- ec
->sc_txcnt
) < 0) i
+= NTXBUF
;
tmd
= ec
->sc_hmem
->tcom
+ i
;
ec
->sc_if
.if_collisions
+= tmd
->com0
& 0xf;
if ((tmd
->com0
& EXCOL
) && (tmd
->com0
& 0xf) == 0)
ec
->sc_if
.if_collisions
+= 16;
if ((tmd
->com0
& COM0_OK
) == 0) {
if (tmd
->com0
& DMALATE
) {
ec
->sc_if
.if_flags
&= ~IFF_OACTIVE
;
(void) ecstart(&ec
->sc_if
);
if (++bix == NRXBUF) bix = 0, rmd = ec->sc_hmem->rcom; 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 ec_softc
*ec
= &ec_softc
[unit
];
register int bix
= ec
->sc_rxnum
;
register struct ec_rfd
*rmd
= ec
->sc_hmem
->rcom
+ bix
;
* Out of sync with hardware, should never happen?
if ((rmd
->rfd0
& COM0_C
) == 0 || (rmd
->count
& RBD_F
) == 0) {
ecrerror(unit
, "out of sync, resetting");
* Process all buffers with valid data
while ((rmd
->rfd0
& COM0_C
) && (rmd
->count
& RBD_F
)) {
if (rmd
->rfd0
& (COM0_C
|COM0_B
|COM0_OK
) != (COM0_C
|COM0_OK
)) {
ecrerror(unit
, "bad packet");
if ((rmd
->count
& (RBD_F
|RBD_EOF
)) != (RBD_F
|RBD_EOF
)) {
ecrerror(unit
, "chained buffer");
ecread(ec
, ec
->sc_hmem
->txbuf
[bix
], rmd
->count
& 0x2f);
register struct ec_softc
*ec
;
struct ether_header
*et
, eh
;
int off
, resid
, unit
= ec
->sc_if
.if_unit
;
et
= (struct ether_header
*)buf
;
et
->ether_type
= ntohs((u_short
)et
->ether_type
);
bcopy((caddr_t
)et
, &eh
, sizeof(eh
));
/* adjust input length to account for header */
len
= len
- sizeof(struct ether_header
);
#define ecdataaddr(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(*ecdataaddr(et
, off
, u_short
*));
resid
= ntohs(*(ecdataaddr(et
, off
+2, u_short
*)));
"ec%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(ec
->sc_bpf
, buf
, len
+ sizeof(struct ether_header
));
#if defined(ISO) || NBPFILTER > 0
* Note that the interface cannot be in promiscuous mode if
* there are no bpf listeners. If we are in promiscuous
* mode, we have to check if this packet is really ours.
* However, there may be appropriate multicate addresses involved
#define NOT_TO(p) (bcmp(et->ether_dhost, p, sizeof(et->ether_dhost)) != 0)
if (et
->ether_dhost
[0] & 1) {
if (NOT_TO(etherbroadcastaddr
)
&& NOT_TO(all_es_snpa
) && NOT_TO(all_is_snpa
)
&& NOT_TO(all_l1is_snpa
) && NOT_TO(all_l2is_snpa
)
} else if ((ec
->sc_if
.if_flags
& IFF_PROMISC
) && NOT_TO(ec
->sc_addr
))
* 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
, &ec
->sc_if
, 0);
ether_input(&ec
->sc_if
, &eh
, 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 *), ecbuf
, len
);
bzero(ecbuf
, ECMINSIZE
- tlen
);
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
struct ec_softc
*ec
= &ec_softc
[ifp
->if_unit
];
int s
= splimp(), error
= 0;
switch (ifa
->ifa_addr
->sa_family
) {
ecinit(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
*)(ec
->sc_addr
);
ifp
->if_flags
&= ~IFF_RUNNING
;
bcopy((caddr_t
)ina
->x_host
.c_host
,
(caddr_t
)ec
->sc_addr
, sizeof(ec
->sc_addr
));
if ((ifp
->if_flags
& IFF_UP
) == 0 &&
ifp
->if_flags
& IFF_RUNNING
) {
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
^ ec
->sc_iflags
) & IFF_PROMISC
) &&
(ifp
->if_flags
& IFF_RUNNING
)) {
ec
->sc_iflags
= ifp
->if_flags
& ~IFF_OACTIVE
;
register struct ec_softc
*ec
= &ec_softc
[unit
];
register struct ec_rfd
*rmd
;
rmd
= &ec
->sc_hmem
->rcom
[ec
->sc_rxnum
];
"ec%d: ierror(%s): from %s: buf=%d, len=%d, rmd1=%b\n",
len
> 11 ? ether_sprintf(&ec
->sc_hmem
->rxbuf
[ec
->sc_rxnum
][6]) : "unknown",
rmd
->rfd0
, "\14\14LEN\13CRC\12ALGN\11NBUF\10DMAL\07SHRT");
register struct ec_softc
*ec
= &ec_softc
[unit
];
register struct ec_transmit
*tmd
;
tmd
= &ec
->sc_hmem
->tcom
[ec
->sc_txnum
];
"ec%d: oerror: to %s: buf=%d, len=%d, com0=%b\n",
len
> 5 ? ether_sprintf(ec
->sc_hmem
->txbuf
[ec
->sc_txnum
]) : "unknown",
"\14\14ABRT\13LCOLL\12NCAR\11NCTS\10DMAL\07TDEF\06HRBT\05XCOL");