* Copyright (c) 1982, 1986, 1989 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)if_de.c 7.9 (Berkeley) %G%
* timeout routine (get statistics)
#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 "../netiso/iso.h"
#include "../netiso/iso_var.h"
#include "../netiso/iso_snpac.h"
extern struct snpa_cache all_es
, all_is
;
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
#define NXMT 3 /* number of transmit buffers */
#define NRCV 7 /* number of receive buffers (must be > 1) */
int deprobe(), deattach(), deintr();
struct uba_device
*deinfo
[NDE
];
struct uba_driver dedriver
=
{ deprobe
, 0, deattach
, 0, destd
, "de", deinfo
};
int deinit(),ether_output(),deioctl(),dereset(),destart();
* Ethernet software status per interface.
* Each interface is referenced by a network interface structure,
* ds_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
* We also have, for each interface, a UBA interface structure, which
* contains information about the UNIBUS resources held by the interface:
* map registers, buffered data paths, etc. Information is cached in this
* structure for use by the if_uba.c routines in running the interface
struct arpcom ds_ac
; /* Ethernet common part */
#define ds_if ds_ac.ac_if /* network-visible interface */
#define ds_addr ds_ac.ac_enaddr /* hardware Ethernet address */
#define DSF_RUNNING 2 /* board is enabled */
#define DSF_SETADDR 4 /* physical address is changed */
int ds_ubaddr
; /* map info for incore structs */
struct ifubinfo ds_deuba
; /* unibus resource structure */
struct ifrw ds_ifr
[NRCV
]; /* unibus receive maps */
struct ifxmt ds_ifw
[NXMT
]; /* unibus xmt maps */
/* the following structures are always mapped in */
struct de_pcbb ds_pcbb
; /* port control block */
struct de_ring ds_xrent
[NXMT
]; /* transmit ring entrys */
struct de_ring ds_rrent
[NRCV
]; /* receive ring entrys */
struct de_udbbuf ds_udbbuf
; /* UNIBUS data buffer */
#define INCORE_BASE(p) ((char *)&(p)->ds_pcbb)
#define RVAL_OFF(n) ((char *)&de_softc[0].n - INCORE_BASE(&de_softc[0]))
#define LVAL_OFF(n) ((char *)de_softc[0].n - INCORE_BASE(&de_softc[0]))
#define PCBB_OFFSET RVAL_OFF(ds_pcbb)
#define XRENT_OFFSET LVAL_OFF(ds_xrent)
#define RRENT_OFFSET LVAL_OFF(ds_rrent)
#define UDBBUF_OFFSET RVAL_OFF(ds_udbbuf)
#define INCORE_SIZE RVAL_OFF(ds_xindex)
int ds_xindex
; /* UNA index into transmit chain */
int ds_rindex
; /* UNA index into receive chain */
int ds_xfree
; /* index for next transmit buffer */
int ds_nxmit
; /* # of transmits in progress */
register int br
, cvec
; /* r11, r10 value-result */
register struct dedevice
*addr
= (struct dedevice
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
i
= 0; derint(i
); deintr(i
);
* Make sure self-test is finished before we screw with the board.
* Self-test on a DELUA can take 15 seconds (argh).
(addr
->pcsr0
& PCSR0_FATI
) == 0 &&
(addr
->pcsr1
& PCSR1_STMASK
) == STAT_RESET
;
if ((addr
->pcsr0
& PCSR0_FATI
) != 0 ||
(addr
->pcsr1
& PCSR1_STMASK
) != STAT_READY
)
addr
->pcsr0
= PCSR0_RSET
;
while ((addr
->pcsr0
& PCSR0_INTR
) == 0)
/* make board interrupt by executing a GETPCBB command */
addr
->pcsr0
= PCSR0_INTE
;
addr
->pcsr0
= PCSR0_INTE
|CMD_GETPCBB
;
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets. We get the ethernet address here.
register struct de_softc
*ds
= &de_softc
[ui
->ui_unit
];
register struct ifnet
*ifp
= &ds
->ds_if
;
register struct dedevice
*addr
= (struct dedevice
*)ui
->ui_addr
;
ifp
->if_unit
= ui
->ui_unit
;
ifp
->if_flags
= IFF_BROADCAST
;
* What kind of a board is this?
* The error bits 4-6 in pcsr1 are a device id as long as
printf("de%d: broken\n", ui
->ui_unit
);
printf("de%d: delua\n", ui
->ui_unit
);
printf("de%d: deuna\n", ui
->ui_unit
);
* Reset the board and temporarily map
* the pcbb buffer onto the Unibus.
addr
->pcsr0
= 0; /* reset INTE */
addr
->pcsr0
= PCSR0_RSET
;
(void)dewait(ui
, "reset");
ds
->ds_ubaddr
= uballoc(ui
->ui_ubanum
, (char *)&ds
->ds_pcbb
,
sizeof (struct de_pcbb
), 0);
addr
->pcsr2
= ds
->ds_ubaddr
& 0xffff;
addr
->pcsr3
= (ds
->ds_ubaddr
>> 16) & 0x3;
addr
->pclow
= CMD_GETPCBB
;
(void)dewait(ui
, "pcbb");
ds
->ds_pcbb
.pcbb0
= FC_RDPHYAD
;
addr
->pclow
= CMD_GETCMD
;
(void)dewait(ui
, "read addr ");
ubarelse(ui
->ui_ubanum
, &ds
->ds_ubaddr
);
bcopy((caddr_t
)&ds
->ds_pcbb
.pcbb2
, (caddr_t
)ds
->ds_addr
,
printf("de%d: hardware address %s\n", ui
->ui_unit
,
ether_sprintf(ds
->ds_addr
));
ifp
->if_output
= ether_output
;
ds
->ds_deuba
.iff_flags
= UBA_CANTWAIT
;
/* CAN WE USE BDP's ??? */
ds
->ds_deuba
.iff_flags
|= UBA_NEEDBDP
;
* Reset of interface after UNIBUS reset.
* If interface is on specified uba, reset its state.
register struct uba_device
*ui
;
if (unit
>= NDE
|| (ui
= deinfo
[unit
]) == 0 || ui
->ui_alive
== 0 ||
de_softc
[unit
].ds_if
.if_flags
&= ~(IFF_RUNNING
| IFF_OACTIVE
);
de_softc
[unit
].ds_flags
&= ~DSF_RUNNING
;
((struct dedevice
*)ui
->ui_addr
)->pcsr0
= PCSR0_RSET
;
(void)dewait(ui
, "reset");
* Initialization of interface; clear recorded pending
* operations, and reinitialize UNIBUS usage.
register struct de_softc
*ds
= &de_softc
[unit
];
register struct uba_device
*ui
= deinfo
[unit
];
register struct dedevice
*addr
;
register struct ifrw
*ifrw
;
register struct ifxmt
*ifxp
;
struct ifnet
*ifp
= &ds
->ds_if
;
/* not yet, if address still unknown */
if (ifp
->if_addrlist
== (struct ifaddr
*)0)
if (ds
->ds_flags
& DSF_RUNNING
)
if ((ifp
->if_flags
& IFF_RUNNING
) == 0) {
if (if_ubaminit(&ds
->ds_deuba
, ui
->ui_ubanum
,
sizeof (struct ether_header
), (int)btoc(ETHERMTU
),
ds
->ds_ifr
, NRCV
, ds
->ds_ifw
, NXMT
) == 0) {
printf("de%d: can't initialize\n", unit
);
ds
->ds_if
.if_flags
&= ~IFF_UP
;
ds
->ds_ubaddr
= uballoc(ui
->ui_ubanum
, INCORE_BASE(ds
),
addr
= (struct dedevice
*)ui
->ui_addr
;
/* set the pcbb block address */
incaddr
= ds
->ds_ubaddr
+ PCBB_OFFSET
;
addr
->pcsr2
= incaddr
& 0xffff;
addr
->pcsr3
= (incaddr
>> 16) & 0x3;
addr
->pclow
= 0; /* reset INTE */
addr
->pclow
= CMD_GETPCBB
;
(void)dewait(ui
, "pcbb");
/* set the transmit and receive ring header addresses */
incaddr
= ds
->ds_ubaddr
+ UDBBUF_OFFSET
;
ds
->ds_pcbb
.pcbb0
= FC_WTRING
;
ds
->ds_pcbb
.pcbb2
= incaddr
& 0xffff;
ds
->ds_pcbb
.pcbb4
= (incaddr
>> 16) & 0x3;
incaddr
= ds
->ds_ubaddr
+ XRENT_OFFSET
;
ds
->ds_udbbuf
.b_tdrbl
= incaddr
& 0xffff;
ds
->ds_udbbuf
.b_tdrbh
= (incaddr
>> 16) & 0x3;
ds
->ds_udbbuf
.b_telen
= sizeof (struct de_ring
) / sizeof (short);
ds
->ds_udbbuf
.b_trlen
= NXMT
;
incaddr
= ds
->ds_ubaddr
+ RRENT_OFFSET
;
ds
->ds_udbbuf
.b_rdrbl
= incaddr
& 0xffff;
ds
->ds_udbbuf
.b_rdrbh
= (incaddr
>> 16) & 0x3;
ds
->ds_udbbuf
.b_relen
= sizeof (struct de_ring
) / sizeof (short);
ds
->ds_udbbuf
.b_rrlen
= NRCV
;
addr
->pclow
= CMD_GETCMD
;
(void)dewait(ui
, "wtring");
/* initialize the mode - enable hardware padding */
ds
->ds_pcbb
.pcbb0
= FC_WTMODE
;
/* let hardware do padding - set MTCH bit on broadcast */
ds
->ds_pcbb
.pcbb2
= MOD_TPAD
|MOD_HDX
;
addr
->pclow
= CMD_GETCMD
;
(void)dewait(ui
, "wtmode");
/* set up the receive and transmit ring entries */
for (rp
= &ds
->ds_xrent
[0]; rp
< &ds
->ds_xrent
[NXMT
]; rp
++) {
rp
->r_segbl
= ifxp
->ifw_info
& 0xffff;
rp
->r_segbh
= (ifxp
->ifw_info
>> 16) & 0x3;
for (rp
= &ds
->ds_rrent
[0]; rp
< &ds
->ds_rrent
[NRCV
]; rp
++) {
rp
->r_slen
= sizeof (struct de_buf
);
rp
->r_segbl
= ifrw
->ifrw_info
& 0xffff;
rp
->r_segbh
= (ifrw
->ifrw_info
>> 16) & 0x3;
rp
->r_flags
= RFLG_OWN
; /* hang receive */
/* start up the board (rah rah) */
ds
->ds_rindex
= ds
->ds_xindex
= ds
->ds_xfree
= ds
->ds_nxmit
= 0;
ds
->ds_if
.if_flags
|= IFF_RUNNING
;
addr
->pclow
= PCSR0_INTE
; /* avoid interlock */
destart(&ds
->ds_if
); /* queue output packets */
ds
->ds_flags
|= DSF_RUNNING
; /* need before de_setaddr */
if (ds
->ds_flags
& DSF_SETADDR
)
de_setaddr(ds
->ds_addr
, unit
);
addr
->pclow
= CMD_START
| PCSR0_INTE
;
* Setup output on interface.
* Get another datagram to send off of the interface queue,
* and map it to the interface before starting the output.
* Must be called from ipl >= our interrupt level.
struct uba_device
*ui
= deinfo
[unit
];
struct dedevice
*addr
= (struct dedevice
*)ui
->ui_addr
;
register struct de_softc
*ds
= &de_softc
[unit
];
register struct de_ring
*rp
;
* the following test is necessary, since
* the code is not reentrant and we have
* multiple transmission buffers.
if (ds
->ds_if
.if_flags
& IFF_OACTIVE
)
for (nxmit
= ds
->ds_nxmit
; nxmit
< NXMT
; nxmit
++) {
IF_DEQUEUE(&ds
->ds_if
.if_snd
, m
);
rp
= &ds
->ds_xrent
[ds
->ds_xfree
];
if (rp
->r_flags
& XFLG_OWN
)
panic("deuna xmit in progress");
len
= if_ubaput(&ds
->ds_deuba
, &ds
->ds_ifw
[ds
->ds_xfree
], m
);
if (ds
->ds_deuba
.iff_flags
& UBA_NEEDBDP
)
UBAPURGE(ds
->ds_deuba
.iff_uba
,
ds
->ds_ifw
[ds
->ds_xfree
].ifw_bdp
);
rp
->r_flags
= XFLG_STP
|XFLG_ENP
|XFLG_OWN
;
if (ds
->ds_xfree
== NXMT
)
if (ds
->ds_nxmit
!= nxmit
) {
if (ds
->ds_flags
& DSF_RUNNING
)
addr
->pclow
= PCSR0_INTE
|CMD_PDMD
;
* Command done interrupt.
struct uba_device
*ui
= deinfo
[unit
];
register struct dedevice
*addr
= (struct dedevice
*)ui
->ui_addr
;
register struct de_softc
*ds
= &de_softc
[unit
];
register struct de_ring
*rp
;
register struct ifxmt
*ifxp
;
/* save flags right away - clear out interrupt bits */
addr
->pchigh
= csr0
>> 8;
ds
->ds_if
.if_flags
|= IFF_OACTIVE
; /* prevent entering destart */
* if receive, put receive buffer on mbuf
* and hang the request again
* Poll transmit ring and check status.
* Be careful about loopback requests.
* Then free buffer space and check for
* more transmit requests.
for ( ; ds
->ds_nxmit
> 0; ds
->ds_nxmit
--) {
rp
= &ds
->ds_xrent
[ds
->ds_xindex
];
if (rp
->r_flags
& XFLG_OWN
)
ifxp
= &ds
->ds_ifw
[ds
->ds_xindex
];
/* check for unusual conditions */
if (rp
->r_flags
& (XFLG_ERRS
|XFLG_MTCH
|XFLG_ONE
|XFLG_MORE
)) {
if (rp
->r_flags
& XFLG_ERRS
) {
printf("de%d: oerror, flags=%b tdrerr=%b (len=%d)\n",
unit
, rp
->r_flags
, XFLG_BITS
,
rp
->r_tdrerr
, XERR_BITS
, rp
->r_slen
);
} else if (rp
->r_flags
& XFLG_ONE
) {
ds
->ds_if
.if_collisions
++;
} else if (rp
->r_flags
& XFLG_MORE
) {
/* more than one collision */
ds
->ds_if
.if_collisions
+= 2; /* guess */
} else if (rp
->r_flags
& XFLG_MTCH
) {
/* received our own packet */
rp
->r_slen
- sizeof (struct ether_header
));
m_freem(ifxp
->ifw_xtofree
);
/* check if next transmit buffer also finished */
if (ds
->ds_xindex
== NXMT
)
ds
->ds_if
.if_flags
&= ~IFF_OACTIVE
;
log(LOG_WARNING
, "de%d: buffer unavailable\n", unit
);
addr
->pclow
= PCSR0_INTE
|CMD_PDMD
;
* Ethernet interface receiver interface.
* If input error just drop packet.
* Otherwise purge input buffered data path and examine
* packet to determine type. If can't determine length
* from type, then have to drop packet. Othewise decapsulate
* packet based on type and pass to type specific higher-level
register struct de_softc
*ds
= &de_softc
[unit
];
register struct de_ring
*rp
;
rp
= &ds
->ds_rrent
[ds
->ds_rindex
];
while ((rp
->r_flags
& RFLG_OWN
) == 0) {
if (ds
->ds_deuba
.iff_flags
& UBA_NEEDBDP
)
UBAPURGE(ds
->ds_deuba
.iff_uba
,
ds
->ds_ifr
[ds
->ds_rindex
].ifrw_bdp
);
len
= (rp
->r_lenerr
&RERR_MLEN
) - sizeof (struct ether_header
)
- 4; /* don't forget checksum! */
if ((rp
->r_flags
& (RFLG_ERRS
|RFLG_FRAM
|RFLG_OFLO
|RFLG_CRC
)) ||
(rp
->r_flags
&(RFLG_STP
|RFLG_ENP
)) != (RFLG_STP
|RFLG_ENP
) ||
(rp
->r_lenerr
& (RERR_BUFL
|RERR_UBTO
|RERR_NCHN
)) ||
len
< ETHERMIN
|| len
> ETHERMTU
) {
printf("de%d: ierror, flags=%b lenerr=%b (len=%d)\n",
unit
, rp
->r_flags
, RFLG_BITS
, rp
->r_lenerr
,
deread(ds
, &ds
->ds_ifr
[ds
->ds_rindex
], len
);
/* hang the receive buffer again */
/* check next receive buffer */
if (ds
->ds_rindex
== NRCV
)
rp
= &ds
->ds_rrent
[ds
->ds_rindex
];
* Pass a packet to the higher levels.
* We deal with the trailer protocol here.
register struct de_softc
*ds
;
register struct ifqueue
*inq
;
* Deal with trailer protocol: if type is trailer type
* get true type from first 16-bit word past data.
* Remember that type was trailer by setting off.
eh
= (struct ether_header
*)ifrw
->ifrw_addr
;
eh
->ether_type
= ntohs((u_short
)eh
->ether_type
);
#define dedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off))))
if (eh
->ether_type
>= ETHERTYPE_TRAIL
&&
eh
->ether_type
< ETHERTYPE_TRAIL
+ETHERTYPE_NTRAILER
) {
off
= (eh
->ether_type
- ETHERTYPE_TRAIL
) * 512;
eh
->ether_type
= ntohs(*dedataaddr(eh
, off
, u_short
*));
resid
= ntohs(*(dedataaddr(eh
, off
+2, u_short
*)));
* Pull packet off interface. Off is nonzero if packet
* has trailing header; if_ubaget will then force this header
* information to be at the front.
m
= if_ubaget(&ds
->ds_deuba
, ifrw
, len
, off
, &ds
->ds_if
);
ether_input(&ds
->ds_if
, eh
, m
);
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
register struct de_softc
*ds
= &de_softc
[ifp
->if_unit
];
int s
= splimp(), error
= 0;
switch (ifa
->ifa_addr
->sa_family
) {
((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
*)(ds
->ds_addr
);
de_setaddr(ina
->x_host
.c_host
,ifp
->if_unit
);
if ((ifp
->if_flags
& IFF_UP
) == 0 &&
ds
->ds_flags
& DSF_RUNNING
) {
(deinfo
[ifp
->if_unit
]->ui_addr
))->pclow
= 0;
(deinfo
[ifp
->if_unit
]->ui_addr
))->pclow
= PCSR0_RSET
;
ds
->ds_flags
&= ~DSF_RUNNING
;
ds
->ds_if
.if_flags
&= ~IFF_OACTIVE
;
} else if (ifp
->if_flags
& IFF_UP
&&
(ds
->ds_flags
& DSF_RUNNING
) == 0)
* set ethernet address for unit
de_setaddr(physaddr
, unit
)
register struct de_softc
*ds
= &de_softc
[unit
];
struct uba_device
*ui
= deinfo
[unit
];
register struct dedevice
*addr
= (struct dedevice
*)ui
->ui_addr
;
if (! (ds
->ds_flags
& DSF_RUNNING
))
bcopy((caddr_t
) physaddr
, (caddr_t
) &ds
->ds_pcbb
.pcbb2
, 6);
ds
->ds_pcbb
.pcbb0
= FC_WTPHYAD
;
addr
->pclow
= PCSR0_INTE
|CMD_GETCMD
;
if (dewait(ui
, "address change") == 0) {
ds
->ds_flags
|= DSF_SETADDR
;
bcopy((caddr_t
) physaddr
, (caddr_t
) ds
->ds_addr
, 6);
* Await completion of the named function
register struct uba_device
*ui
;
register struct dedevice
*addr
= (struct dedevice
*)ui
->ui_addr
;
while ((addr
->pcsr0
& PCSR0_INTR
) == 0)
addr
->pchigh
= csr0
>> 8;
printf("de%d: %s failed, csr0=%b csr1=%b\n",
ui
->ui_unit
, fn
, csr0
, PCSR0_BITS
,
addr
->pcsr1
, PCSR1_BITS
);
return (csr0
& PCSR0_PCEI
);