/* if_ace.c 1.12 86/12/15 */
* ACC VERSAbus Ethernet controller
#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/ip_var.h"
#include "../netinet/if_ether.h"
#include "../netns/ns_if.h"
#include "../machine/cpu.h"
#include "../machine/pte.h"
#include "../tahoe/mtpr.h"
#include "../tahoeif/if_acereg.h"
#include "../tahoevba/vbavar.h"
int aceprobe(), aceattach(), acerint(), acecint();
struct vba_device
*aceinfo
[NACE
];
struct vba_driver acedriver
=
{ aceprobe
, 0, aceattach
, 0, acestd
, "ace", aceinfo
, "v/eiu", 0 };
int aceinit(), aceoutput(), aceioctl(), acereset();
* Ethernet software status per interface.
* Each interface is referenced by a network interface structure,
* is_if, which the routing code uses to locate the interface.
* This structure contains the output queue for the interface, its address, ...
struct arpcom is_ac
; /* Ethernet common part */
#define is_if is_ac.ac_if /* network-visible interface */
#define is_addr is_ac.ac_enaddr /* hardware Ethernet address */
#define ACEF_OACTIVE 0x1 /* output is active */
#define ACEF_RCVPENDING 0x2 /* start rcv in acecint */
short is_promiscuous
; /* true is enabled */
short is_segboundry
; /* first TX Seg in dpm */
short is_eictr
; /* Rx segment tracking ctr */
short is_eoctr
; /* Tx segment tracking ctr */
short is_txnext
; /* Next available Tx segment */
short is_currnd
; /* current random backoff */
struct ace_stats is_stats
; /* holds board statistics */
short is_xcnt
; /* count xmitted segments to be acked
long is_ivec
; /* autoconfig interrupt vector base */
struct pte
*is_map
; /* pte map for dual ported memory */
caddr_t is_dpm
; /* address of mapped memory */
extern struct ifnet loif
;
register br
, cvec
; /* must be r12, r11 */
struct acedevice
*ap
= (struct acedevice
*)reg
;
struct ace_softc
*is
= &ace_softc
[vi
->ui_unit
];
br
= 0; cvec
= br
; br
= cvec
;
movow(&ap
->csr
, CSR_RESET
);
* Select two spaces for the interrupts aligned to an
* eight vector boundary and fitting in 8 bits (as
* required by the controller) -- YECH. The controller
* will be notified later at initialization time.
if ((vi
->ui_hd
->vh_lastiv
-= 2) > 0xff)
vi
->ui_hd
->vh_lastiv
= 0x200;
is
->is_ivec
= vi
->ui_hd
->vh_lastiv
= vi
->ui_hd
->vh_lastiv
&~ 0x7;
is
->is_ivec
= 0x90+vi
->ui_unit
*8;
br
= 0x14, cvec
= is
->is_ivec
; /* XXX */
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register short unit
= ui
->ui_unit
;
register struct ace_softc
*is
= &ace_softc
[unit
];
register struct ifnet
*ifp
= &is
->is_if
;
register struct acedevice
*addr
= (struct acedevice
*)ui
->ui_addr
;
* Get station's addresses and set multicast hash table.
for (wp
= (short *)addr
->station
, i
= 0; i
< 6; i
++)
printf("ace%d: hardware address %s\n", unit
,
ether_sprintf(is
->is_addr
));
for (wp
= (short *)addr
->hash
, i
= 0; i
< 8; i
++)
movow(&addr
->bcastena
[0], ~0xffff);
movow(&addr
->bcastena
[1], ~0xffff);
* Allocate and map dual ported VERSAbus memory.
vbmemalloc(32, (caddr_t
)ui
->ui_flags
, &is
->is_map
, &is
->is_dpm
);
ifp
->if_output
= aceoutput
;
ifp
->if_ioctl
= aceioctl
;
ifp
->if_reset
= acereset
;
ifp
->if_flags
= IFF_BROADCAST
;
* Reset of interface after "system" reset.
register struct vba_device
*ui
;
if (unit
>= NACE
|| (ui
= aceinfo
[unit
]) == 0 || ui
->ui_alive
== 0 ||
* Initialization of interface; clear recorded pending operations
register struct ace_softc
*is
= &ace_softc
[unit
];
register struct vba_device
*ui
= aceinfo
[unit
];
register struct acedevice
*addr
;
register struct ifnet
*ifp
= &is
->is_if
;
if (ifp
->if_addrlist
== (struct ifaddr
*)0)
if ((ifp
->if_flags
& IFF_RUNNING
) == 0) {
* Reset the controller, initialize the recieve buffers,
* and turn the controller on again and set board online.
addr
= (struct acedevice
*)ui
->ui_addr
;
movow(&addr
->csr
, CSR_RESET
);
* Clean up dpm since the controller might
* jumble dpm after reset.
movow(&addr
->csr
, CSR_GO
);
movow(&addr
->ivct
, is
->is_ivec
);
Csr
|= CSR_IENA
| is
->is_promiscuous
;
is
->is_if
.if_flags
|= IFF_RUNNING
;
if (is
->is_if
.if_snd
.ifq_head
)
* Start output on interface.
* Get another datagram to send off of the interface queue,
* and map it to the interface before starting the output.
register struct tx_segment
*txs
;
register struct ace_softc
*is
= &ace_softc
[unit
];
if (is
->is_flags
& ACEF_OACTIVE
)
is
->is_flags
|= ACEF_OACTIVE
;
txs
= (struct tx_segment
*)(is
->is_dpm
+ (is
->is_txnext
<< 11));
if (txs
->tx_csr
& TCS_TBFULL
) {
is
->is_flags
&= ~ACEF_OACTIVE
;
IF_DEQUEUE(&is
->is_if
.if_snd
, m
);
is
->is_flags
&= ~ACEF_OACTIVE
;
len
= aceput(unit
, txs
->tx_data
, m
);
retries
= txs
->tx_csr
& TCS_RTC
;
acebakoff(is
, txs
, retries
);
* Ensure minimum packet length.
* This makes the safe assumtion that there are no virtual holes
* For security, it might be wise to zero out the added bytes,
* but we're mainly interested in speed at the moment.
if (len
- sizeof (struct ether_header
) < ETHERMIN
)
len
= ETHERMIN
+ sizeof (struct ether_header
);
if (++is
->is_txnext
> SEG_MAX
)
is
->is_txnext
= is
->is_segboundry
;
len
= (len
& 0x7fff) | TCS_TBFULL
;
* Transmit done interrupt.
register struct ace_softc
*is
= &ace_softc
[unit
];
register struct tx_segment
*txseg
;
log(LOG_ERR
, "ace%d: stray xmit interrupt, xcnt %d\n",
if (is
->is_if
.if_snd
.ifq_head
)
txseg
= (struct tx_segment
*)((is
->is_eoctr
<< 11) + is
->is_dpm
);
if ((eostat
& TCS_TBFULL
) == 0) {
is
->is_stats
.tx_retries
+= eostat
& TCS_RTC
;
if (eostat
& TCS_RTFAIL
) {
is
->is_stats
.tx_discarded
++;
is
->is_stats
.tx_datagrams
++;
if (++is
->is_eoctr
>= 16)
is
->is_eoctr
= is
->is_segboundry
;
if (is
->is_if
.if_snd
.ifq_head
)
* Ethernet interface receiver interrupt.
* 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 ace_softc
*is
= &ace_softc
[unit
];
register struct ifqueue
*inq
;
register struct ether_header
*ace
;
register struct rx_segment
*rxseg
;
if ((is
->is_if
.if_flags
&IFF_RUNNING
) == 0)
rxseg
= (struct rx_segment
*)((is
->is_eictr
<< 11) + is
->is_dpm
);
if ((eistat
& RCS_RBFULL
) == 0)
if (++is
->is_eictr
>= is
->is_segboundry
)
if ((eistat
& (RCS_ROVRN
| RCS_RCRC
| RCS_RODD
)) ||
len
< ET_MINLEN
|| len
> ET_MAXLEN
+CRC_SIZE
) {
is
->is_stats
.rx_overruns
++;
is
->is_stats
.rx_crc_errors
++;
is
->is_stats
.rx_align_errors
++;
is
->is_stats
.rx_underruns
++;
if (len
> ET_MAXLEN
+CRC_SIZE
)
is
->is_stats
.rx_overruns
++;
is
->is_stats
.rx_datagrams
++;
ace
= (struct ether_header
*)rxseg
->rx_data
;
len
-= sizeof (struct ether_header
);
* Deal with trailer protocol: if type is trailer
* get true type from first 16-bit word past data.
* Remember that type was trailer by setting off.
ace
->ether_type
= ntohs((u_short
)ace
->ether_type
);
#define acedataaddr(ace, off, type) \
((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off))))
if (ace
->ether_type
>= ETHERTYPE_TRAIL
&&
ace
->ether_type
< ETHERTYPE_TRAIL
+ETHERTYPE_NTRAILER
) {
off
= (ace
->ether_type
- ETHERTYPE_TRAIL
) * 512;
ace
->ether_type
= ntohs(*acedataaddr(ace
, off
, u_short
*));
resid
= ntohs(*(acedataaddr(ace
, off
+2, u_short
*)));
* Pull packet off interface. Off is nonzero if packet
* has trailing header; aceget 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
= aceget((u_char
*)rxseg
->rx_data
, len
, off
, &is
->is_if
);
ifp
= *(mtod(m
, struct ifnet
**));
m
->m_off
+= 2 * sizeof (u_short
);
m
->m_len
-= 2 * sizeof (u_short
);
*(mtod(m
, struct ifnet
**)) = ifp
;
switch (ace
->ether_type
) {
* Ethernet output routine.
* Encapsulate a packet of type family for the local net.
* Use trailer local net encapsulation if enough data in first
* packet leaves a multiple of 512 bytes of data in remainder.
register struct ace_softc
*is
= &ace_softc
[ifp
->if_unit
];
register struct mbuf
*m
= m0
;
register struct ether_header
*ace
;
struct mbuf
*mcopy
= (struct mbuf
*)0;
int type
, s
, error
, usetrailers
;
if ((ifp
->if_flags
& (IFF_UP
|IFF_RUNNING
)) != (IFF_UP
|IFF_RUNNING
)) {
switch (dst
->sa_family
) {
idst
= ((struct sockaddr_in
*)dst
)->sin_addr
;
if (!arpresolve(&is
->is_ac
, m
, &idst
, edst
, &usetrailers
))
return (0); /* if not yet resolved */
if (!bcmp((caddr_t
)edst
, (caddr_t
)etherbroadcastaddr
,
mcopy
= m_copy(m
, 0, (int)M_COPYALL
);
off
= ntohs((u_short
)mtod(m
, struct ip
*)->ip_len
) - m
->m_len
;
if (usetrailers
&& off
> 0 && (off
& 0x1ff) == 0 &&
m
->m_off
>= MMINOFF
+ 2 * sizeof (u_short
)) {
type
= ETHERTYPE_TRAIL
+ (off
>>9);
m
->m_off
-= 2 * sizeof (u_short
);
m
->m_len
+= 2 * sizeof (u_short
);
*mtod(m
, u_short
*) = htons((u_short
)ETHERTYPE_IP
);
*(mtod(m
, u_short
*) + 1) = htons((u_short
)m
->m_len
);
bcopy((caddr_t
)&(((struct sockaddr_ns
*)dst
)->sns_addr
.x_host
),
(caddr_t
)edst
, sizeof (edst
));
if (!bcmp((caddr_t
)edst
, (caddr_t
)&ns_broadhost
,sizeof(edst
)))
mcopy
= m_copy(m
, 0, (int)M_COPYALL
);
else if (!bcmp((caddr_t
)edst
, (caddr_t
)&ns_thishost
,
return(looutput(&loif
, m
, dst
));
ace
= (struct ether_header
*)dst
->sa_data
;
bcopy((caddr_t
)ace
->ether_dhost
, (caddr_t
)edst
, sizeof (edst
));
log(LOG_ERR
, "ace%d: can't handle af%d\n",
ifp
->if_unit
, dst
->sa_family
);
* Packet to be sent as trailer: move first packet
* (control information) to end of chain.
* Add local net header. If no space in first mbuf,
if (m
->m_off
> MMAXOFF
||
MMINOFF
+ sizeof (struct ether_header
) > m
->m_off
) {
m
= m_get(M_DONTWAIT
, MT_HEADER
);
m
->m_len
= sizeof (struct ether_header
);
m
->m_off
-= sizeof (struct ether_header
);
m
->m_len
+= sizeof (struct ether_header
);
ace
= mtod(m
, struct ether_header
*);
bcopy((caddr_t
)edst
, (caddr_t
)ace
->ether_dhost
, sizeof (edst
));
bcopy((caddr_t
)is
->is_addr
, (caddr_t
)ace
->ether_shost
,
ace
->ether_type
= htons((u_short
)type
);
* Queue message on interface, and start output if interface
if (IF_QFULL(&ifp
->if_snd
)) {
IF_ENQUEUE(&ifp
->if_snd
, m
);
return (mcopy
? looutput(&loif
, mcopy
, dst
) : 0);
* Routine to copy from mbuf chain to transmit buffer on the VERSAbus
* If packet size is less than the minimum legal size,
* the buffer is expanded. We probably should zero out the extra
* bytes for security, but that would slow things down.
register u_char
*bp
, *mcp
;
register struct mbuf
*mp
;
for (mp
= m
; (mp
); mp
= mp
->m_next
) {
mcp
= mtod(mp
, u_char
*);
if (((int)mcp
& 01) && ((int)bp
& 01)) {
/* source & destination at odd addresses */
if (len
> 1 && (((int)mcp
& 01)==0) && (((int)bp
& 01)==0)) {
l
= len
>> 1; /* count # of shorts */
len
&= 1; /* # remaining bytes */
* Routine to copy from VERSAbus memory into mbufs.
* Warning: This makes the fairly safe assumption that
* mbufs have even lengths.
aceget(rxbuf
, totlen
, off0
, ifp
)
register u_char
*cp
, *mcp
;
struct mbuf
*top
= 0, **mp
= &top
;
cp
= rxbuf
+ sizeof (struct ether_header
);
MGET(m
, M_DONTWAIT
, MT_DATA
);
cp
= rxbuf
+ sizeof (struct ether_header
) + off
;
m
->m_len
= len
= MIN(len
, CLBYTES
);
m
->m_len
= len
= MIN(MLEN
, len
);
m
->m_len
= len
= MIN(MLEN
, len
);
* Prepend interface pointer to first mbuf.
*(mtod(m
, struct ifnet
**)) = ifp
;
/*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/
/*cp += len; mcp += len;*/
if (((int)mcp
& 01) && ((int)cp
& 01)) {
/* source & destination at odd addresses */
if (tlen
> 1 && (((int)mcp
&01) == 0) && (((int)cp
&01) == 0)) {
l
= tlen
>> 1; /* count # of shorts */
while (l
-- > 0) /* copy shorts */
tlen
&= 1; /* # remaining bytes */
cp
= rxbuf
+ sizeof (struct ether_header
);
/* backoff table masks */
short random_mask_tbl
[16] = {
0x0040, 0x00c0, 0x01c0, 0x03c0, 0x07c0, 0x0fc0, 0x1fc0, 0x3fc0,
0x7fc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0
acebakoff(is
, txseg
, retries
)
struct tx_segment
*txseg
;
register short *pBakNum
, random_num
;
pMask
= &random_mask_tbl
[0];
pBakNum
= &txseg
->tx_backoff
[0];
random_num
= (is
->is_currnd
= (is
->is_currnd
* 18741)-13849);
*pBakNum
++ = random_num
^ (short)(0xff00 | 0x00fc);
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct ifaddr
*ifa
= (struct ifaddr
*)data
;
int s
= splimp(), error
= 0;
switch (ifa
->ifa_addr
.sa_family
) {
aceinit(ifp
->if_unit
); /* before arpwhohas */
((struct arpcom
*)ifp
)->ac_ipaddr
=
arpwhohas((struct arpcom
*)ifp
, &IA_SIN(ifa
)->sin_addr
);
struct ns_addr
*ina
= &IA_SNS(ifa
)->sns_addr
;
struct ace_softc
*is
= &ace_softc
[ifp
->if_unit
];
if (!ns_nullhost(*ina
)) {
ifp
->if_flags
&= ~IFF_RUNNING
;
addr
= (struct acedevice
*)
aceinfo
[ifp
->if_unit
]->ui_addr
;
movow(&addr
->csr
, CSR_RESET
);
/* set station address & copy addr to arp */
acesetaddr(ifp
->if_unit
, addr
,
ina
->x_host
= *(union ns_host
*)is
->is_addr
;
if ((ifp
->if_flags
&IFF_UP
) == 0 && ifp
->if_flags
&IFF_RUNNING
) {
addr
= (struct acedevice
*)
(aceinfo
[ifp
->if_unit
]->ui_addr
);
movow(&addr
->csr
, CSR_RESET
);
ifp
->if_flags
&= ~IFF_RUNNING
;
} else if (ifp
->if_flags
&IFF_UP
&&
(ifp
->if_flags
&IFF_RUNNING
) == 0)
* Set the on-board station address, then read it back
* to initialize the address used by ARP (among others).
acesetaddr(unit
, addr
, station
)
struct ace_softc
*is
= &ace_softc
[unit
];
for (wp
= (short *)addr
->station
, i
= 0; i
< 6; i
++)
movow(wp
++, ~*station
++);
for (wp
= (short *)addr
->station
, i
= 0; i
< 6; i
++)
printf("ace%d: hardware address %s\n", unit
,
ether_sprintf(is
->is_addr
));
* Setup the device for use. Initialize dual-ported memory,
* backoff parameters, and various other software state.
register struct ace_softc
*is
= &ace_softc
[unit
];
bzero(is
->is_dpm
, 16384*2);
addr
= (struct acedevice
*)aceinfo
[unit
]->ui_addr
;
is
->is_segboundry
= (addr
->segb
>> 11) & 0xf;
pData1
= is
->is_dpm
+ (is
->is_segboundry
<< 11);
for (i
= SEG_MAX
+ 1 - is
->is_segboundry
; --i
>= 0;) {
acebakoff(is
, (struct tx_segment
*)pData1
, 15);
pData1
+= sizeof (struct tx_segment
);
is
->is_eoctr
= is
->is_txnext
= is
->is_segboundry
;
bzero((char *)&is
->is_stats
, sizeof (is
->is_stats
));