* Copyright (c) 1982, 1986 Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* @(#)if_en.c 7.7 (Berkeley) 12/16/90
* Xerox prototype (3 Mb) Ethernet interface driver.
#include "../include/pte.h"
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netpup/ether.h"
#include "../include/cpu.h"
#include "../include/mtpr.h"
#include "../uba/ubareg.h"
#include "../uba/ubavar.h"
#define ENMRU (1024+512+16) /* 16 is enough to receive trailer */
int enprobe(), enattach(), enrint(), enxint(), encollide();
struct uba_device
*eninfo
[NEN
];
struct uba_driver endriver
=
{ enprobe
, 0, enattach
, 0, enstd
, "en", eninfo
};
#define ENUNIT(x) minor(x)
int eninit(),oldenoutput(),enreset(),enioctl(), enstart();
* If you need to byte swap IP's in the system, define
* this and do a SIOCSIFFLAGS at boot time.
#define ENF_SWABIPS 0x1000
* Ethernet software status per interface.
* Each interface is referenced by a network interface structure,
* es_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 ifnet es_if
; /* network-visible interface */
struct ifuba es_ifuba
; /* UNIBUS resources */
short es_host
; /* hardware host number */
short es_delay
; /* current output delay */
short es_mask
; /* mask for current output delay */
short es_lastx
; /* host last transmitted to */
short es_oactive
; /* is output active? */
short es_olen
; /* length of last output */
short es_nsactive
; /* is interface enabled for ns? */
* Do output DMA to determine interface presence and
* interrupt vector. DMA is too short to disturb other hosts.
register int br
, cvec
; /* r11, r10 value-result */
register struct endevice
*addr
= (struct endevice
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
enrint(0); enxint(0); encollide(0);
addr
->en_ostat
= EN_IEN
|EN_GO
;
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
register struct en_softc
*es
= &en_softc
[ui
->ui_unit
];
es
->es_if
.if_unit
= ui
->ui_unit
;
es
->es_if
.if_name
= "en";
es
->es_if
.if_mtu
= ENMTU
;
es
->es_if
.if_flags
= IFF_BROADCAST
;
es
->es_if
.if_init
= eninit
;
es
->es_if
.if_output
= oldenoutput
;
es
->es_if
.if_start
= enstart
;
es
->es_if
.if_ioctl
= enioctl
;
es
->es_if
.if_reset
= enreset
;
es
->es_ifuba
.ifu_flags
= UBA_NEEDBDP
| UBA_NEED16
| UBA_CANTWAIT
;
/* don't chew up 750 bdp's */
if (cpu
== VAX_750
&& ui
->ui_unit
> 0)
es
->es_ifuba
.ifu_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
>= NEN
|| (ui
= eninfo
[unit
]) == 0 || ui
->ui_alive
== 0 ||
* Initialization of interface; clear recorded pending
* operations, and reinitialize UNIBUS usage.
register struct en_softc
*es
= &en_softc
[unit
];
register struct uba_device
*ui
= eninfo
[unit
];
register struct endevice
*addr
;
if (es
->es_if
.if_addrlist
== (struct ifaddr
*)0)
if (if_ubainit(&es
->es_ifuba
, ui
->ui_ubanum
,
sizeof (struct en_header
), (int)btoc(ENMRU
)) == 0) {
printf("en%d: can't initialize\n", unit
);
es
->es_if
.if_flags
&= ~IFF_UP
;
addr
= (struct endevice
*)ui
->ui_addr
;
addr
->en_istat
= addr
->en_ostat
= 0;
* Hang a receive and start any
* pending writes by faking a transmit complete.
addr
->en_iba
= es
->es_ifuba
.ifu_r
.ifrw_info
;
addr
->en_iwc
= -(sizeof (struct en_header
) + ENMRU
) >> 1;
addr
->en_istat
= EN_IEN
|EN_GO
;
es
->es_if
.if_flags
|= IFF_RUNNING
;
int enlastmask
= (~0) << 5;
* Start or restart output on interface.
* If interface is already active, then this is a retransmit
* after a collision, and just restuff registers and delay.
* If interface is not already active, get another datagram
* to send off of the interface queue, and map it to the interface
* before starting the output.
struct uba_device
*ui
= eninfo
[unit
];
register struct en_softc
*es
= &en_softc
[unit
];
register struct endevice
*addr
;
register struct en_header
*en
;
* Not already active: dequeue another request
* and map it to the UNIBUS. If no more requests,
IF_DEQUEUE(&es
->es_if
.if_snd
, m
);
en
= mtod(m
, struct en_header
*);
en
->en_shost
= es
->es_host
;
es
->es_olen
= if_wubaput(&es
->es_ifuba
, m
);
* The Xerox interface does word at a time DMA, so
* someone must do byte swapping of user data if high
* and low ender machines are to communicate. It doesn't
* belong here, but certain people depend on it, so...
* Should swab everybody, but this is a kludge anyway.
if (es
->es_if
.if_flags
& ENF_SWABIPS
) {
en
= (struct en_header
*)es
->es_ifuba
.ifu_w
.ifrw_addr
;
if (en
->en_type
== ENTYPE_IP
)
enswab((caddr_t
)(en
+ 1), (caddr_t
)(en
+ 1),
es
->es_olen
- sizeof (struct en_header
) + 1);
* Ethernet cannot take back-to-back packets (no
* buffering in interface. To help avoid overrunning
* receivers, enforce a small delay (about 1ms) in interface:
* * between all packets when enalldelay
* * whenever last packet was broadcast
* * whenever this packet is to same host as last packet
if (enalldelay
|| es
->es_lastx
== 0 || es
->es_lastx
== dest
) {
es
->es_delay
= enlastdel
;
es
->es_mask
= enlastmask
;
* Have request mapped to UNIBUS for transmission.
* Purge any stale data from this BDP, and start the otput.
if (es
->es_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(es
->es_ifuba
.ifu_uba
, es
->es_ifuba
.ifu_w
.ifrw_bdp
);
addr
= (struct endevice
*)ui
->ui_addr
;
addr
->en_oba
= (int)es
->es_ifuba
.ifu_w
.ifrw_info
;
addr
->en_odelay
= es
->es_delay
;
addr
->en_owc
= -((es
->es_olen
+ 1) >> 1);
addr
->en_ostat
= EN_IEN
|EN_GO
;
* Ethernet interface transmitter interrupt.
* Start another output if more data to send.
register struct uba_device
*ui
= eninfo
[unit
];
register struct en_softc
*es
= &en_softc
[unit
];
register struct endevice
*addr
= (struct endevice
*)ui
->ui_addr
;
if (es
->es_mask
&& (addr
->en_ostat
&EN_OERROR
)) {
if (es
->es_ifuba
.ifu_xtofree
) {
m_freem(es
->es_ifuba
.ifu_xtofree
);
es
->es_ifuba
.ifu_xtofree
= 0;
if (es
->es_if
.if_snd
.ifq_head
== 0) {
es
->es_lastx
= 256; /* putatively illegal */
* Collision on ethernet interface. Do exponential
* backoff, and retransmit. If have backed off all
* the way print warning diagnostic, and drop packet.
struct en_softc
*es
= &en_softc
[unit
];
es
->es_if
.if_collisions
++;
register struct en_softc
*es
= &en_softc
[unit
];
* Es_mask is a 16 bit number with n low zero bits, with
* n the number of backoffs. When es_mask is 0 we have
* backed off 16 times, and give up.
printf("en%d: send error\n", unit
);
* Another backoff. Restart with delay based on n low bits
es
->es_delay
= mfpr(ICR
) &~ es
->es_mask
;
struct sockproto enproto
= { AF_ETHERLINK
};
struct sockaddr_en endst
= { sizeof(endst
), AF_ETHERLINK
};
struct sockaddr_en ensrc
= { sizeof(ensrc
), AF_ETHERLINK
};
* 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 en_softc
*es
= &en_softc
[unit
];
struct endevice
*addr
= (struct endevice
*)eninfo
[unit
]->ui_addr
;
register struct en_header
*en
;
register struct ifqueue
*inq
;
* Purge BDP; drop if input error indicated.
if (es
->es_ifuba
.ifu_flags
& UBA_NEEDBDP
)
UBAPURGE(es
->es_ifuba
.ifu_uba
, es
->es_ifuba
.ifu_r
.ifrw_bdp
);
if (addr
->en_istat
&EN_IERROR
) {
* Calculate input data length.
* Get pointer to ethernet header (in input buffer).
* Deal with trailer protocol: if type is PUP trailer
* get true type from first 16-bit word past data.
* Remember that type was trailer by setting off.
len
= (((sizeof (struct en_header
) + ENMRU
) >> 1) + resid
) << 1;
len
-= sizeof (struct en_header
);
en
= (struct en_header
*)(es
->es_ifuba
.ifu_r
.ifrw_addr
);
en
->en_type
= ntohs(en
->en_type
);
#define endataaddr(en, off, type) ((type)(((caddr_t)((en)+1)+(off))))
if (en
->en_type
>= ENTYPE_TRAIL
&&
en
->en_type
< ENTYPE_TRAIL
+ENTYPE_NTRAILER
) {
off
= (en
->en_type
- ENTYPE_TRAIL
) * 512;
en
->en_type
= ntohs(*endataaddr(en
, off
, u_short
*));
resid
= ntohs(*(endataaddr(en
, off
+2, u_short
*)));
if (es
->es_if
.if_flags
& ENF_SWABIPS
&& en
->en_type
== ENTYPE_IP
)
enswab((caddr_t
)(en
+ 1), (caddr_t
)(en
+ 1), len
);
* Pull packet off interface. Off is nonzero if packet
* has trailing header; if_rubaget 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
= if_rubaget(&es
->es_ifuba
, len
, off
, &es
->es_if
);
enproto
.sp_protocol
= en
->en_type
;
endst
.sen_host
= en
->en_dhost
;
endst
.sen_net
= ensrc
.sen_net
= es
->es_if
.if_net
;
ensrc
.sen_host
= en
->en_shost
;
(struct sockaddr
*)&ensrc
, (struct sockaddr
*)&endst
);
addr
->en_iba
= es
->es_ifuba
.ifu_r
.ifrw_info
;
addr
->en_iwc
= -(sizeof (struct en_header
) + ENMRU
) >> 1;
addr
->en_istat
= EN_IEN
|EN_GO
;
* 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.
oldenoutput(ifp
, m0
, dst
)
int type
, dest
, s
, error
;
register struct mbuf
*m
= m0
;
register struct en_header
*en
;
if ((ifp
->if_flags
& (IFF_UP
|IFF_RUNNING
)) != (IFF_UP
|IFF_RUNNING
)) {
switch (dst
->sa_family
) {
in
= ((struct sockaddr_in
*)dst
)->sin_addr
;
off
= m
->m_pkthdr
.len
- m
->m_len
;
/* need per host negotiation */
if ((ifp
->if_flags
& IFF_NOTRAILERS
) == 0)
if (off
> 0 && (off
& 0x1ff) == 0 &&
(m
->m_flags
& M_EXT
) == 0 &&
m
->m_data
>= m
->m_pktdat
+ 2 * sizeof (u_short
)) {
type
= ENTYPE_TRAIL
+ (off
>>9);
m
->m_data
-= 2 * sizeof (u_short
);
m
->m_len
+= 2 * sizeof (u_short
);
*mtod(m
, u_short
*) = htons((u_short
)ENTYPE_IP
);
*(mtod(m
, u_short
*) + 1) = ntohs((u_short
)m
->m_len
);
up
= ((struct sockaddr_ns
*)dst
)->sns_addr
.x_host
.c_host
;
dest
= ((struct sockaddr_pup
*)dst
)->spup_host
;
printf("en%d: can't handle af%d\n", ifp
->if_unit
,
* 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,
M_PREPEND(m
, sizeof (struct en_header
), M_DONTWAIT
);
en
= mtod(m
, struct en_header
*);
en
->en_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
);
if (en_softc
[ifp
->if_unit
].es_oactive
== 0)
* Process an ioctl request.
register struct ifnet
*ifp
;
register struct en_softc
*es
= ((struct en_softc
*)ifp
);
struct ifaddr
*ifa
= (struct ifaddr
*) data
;
int s
= splimp(), error
= 0;
enaddr
= (struct endevice
*)eninfo
[ifp
->if_unit
]->ui_addr
;
es
->es_host
= (~enaddr
->en_addr
) & 0xff;
* Attempt to check agreement of protocol address
switch (ifa
->ifa_addr
->sa_family
) {
if (in_lnaof(IA_SIN(ifa
)->sin_addr
) != es
->es_host
)
if (IA_SNS(ifa
)->sns_addr
.x_host
.c_host
[5]
if ((ifp
->if_flags
& IFF_RUNNING
) == 0)
* Jeffrey Mogul, Stanford
register unsigned char *from
, *to
;
register unsigned long temp
;
if ((n
<= 0) || (n
> 0xFFFF)) {
printf("enswab: bad len %d\n", n
);
#define STEP {temp = *from++;*to++ = *from++;*to++ = temp;}
/* round to multiple of 8 */