/* if_en.c 4.75 82/12/17 */
* Xerox prototype (3 Mb) Ethernet interface driver.
#include "../machine/pte.h"
#include "../h/protosw.h"
#include "../net/netisr.h"
#include "../net/route.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/ip.h"
#include "../netinet/ip_var.h"
#include "../netpup/pup.h"
#include "../vaxif/if_en.h"
#include "../vaxif/if_enreg.h"
#include "../vaxif/if_uba.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/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(),enoutput(),enreset();
* 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_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 */
* 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
];
register struct sockaddr_in
*sin
;
es
->es_if
.if_unit
= ui
->ui_unit
;
es
->es_if
.if_name
= "en";
es
->es_if
.if_mtu
= ENMTU
;
es
->es_if
.if_net
= ui
->ui_flags
;
(~(((struct endevice
*)eninfo
[ui
->ui_unit
]->ui_addr
)->en_addr
)) & 0xff;
sin
= (struct sockaddr_in
*)&es
->es_if
.if_addr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= if_makeaddr(es
->es_if
.if_net
, es
->es_if
.if_host
[0]);
sin
= (struct sockaddr_in
*)&es
->es_if
.if_broadaddr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= if_makeaddr(es
->es_if
.if_net
, 0);
es
->es_if
.if_flags
= IFF_BROADCAST
;
es
->es_if
.if_init
= eninit
;
es
->es_if
.if_output
= enoutput
;
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 (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_UP
;
if_rtinit(&es
->es_if
, RTF_UP
);
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
;
* Not already active: dequeue another request
* and map it to the UNIBUS. If no more requests,
IF_DEQUEUE(&es
->es_if
.if_snd
, m
);
dest
= mtod(m
, struct en_header
*)->en_dhost
;
es
->es_olen
= if_wubaput(&es
->es_ifuba
, m
);
* 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 sockaddr_pup pupsrc
= { AF_PUP
};
struct sockaddr_pup pupdst
= { AF_PUP
};
struct sockproto pupproto
= { PF_PUP
};
* 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
);
#define endataaddr(en, off, type) ((type)(((caddr_t)((en)+1)+(off))))
if (en
->en_type
>= ENPUP_TRAIL
&&
en
->en_type
< ENPUP_TRAIL
+ENPUP_NTRAILER
) {
off
= (en
->en_type
- ENPUP_TRAIL
) * 512;
en
->en_type
= *endataaddr(en
, off
, u_short
*);
resid
= *(endataaddr(en
, off
+2, u_short
*));
* 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
);
m
->m_off
+= 2 * sizeof (u_short
);
m
->m_len
-= 2 * sizeof (u_short
);
struct pup_header
*pup
= mtod(m
, struct pup_header
*);
pupproto
.sp_protocol
= pup
->pup_type
;
pupdst
.spup_addr
= pup
->pup_dport
;
pupsrc
.spup_addr
= pup
->pup_sport
;
raw_input(m
, &pupproto
, (struct sockaddr
*)&pupsrc
,
(struct sockaddr
*)&pupdst
);
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.
int type
, dest
, s
, error
;
register struct mbuf
*m
= m0
;
register struct en_header
*en
;
switch (dst
->sa_family
) {
dest
= ((struct sockaddr_in
*)dst
)->sin_addr
.s_addr
;
if (in_lnaof(*((struct in_addr
*)&dest
)) >= 0x100) {
dest
= (dest
>> 24) & 0xff;
off
= ntohs((u_short
)mtod(m
, struct ip
*)->ip_len
) - m
->m_len
;
if (off
> 0 && (off
& 0x1ff) == 0 &&
m
->m_off
>= MMINOFF
+ 2 * sizeof (u_short
)) {
type
= ENPUP_TRAIL
+ (off
>>9);
m
->m_off
-= 2 * sizeof (u_short
);
m
->m_len
+= 2 * sizeof (u_short
);
*mtod(m
, u_short
*) = ENPUP_IPTYPE
;
*(mtod(m
, u_short
*) + 1) = m
->m_len
;
dest
= ((struct sockaddr_pup
*)dst
)->spup_addr
.pp_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,
if (m
->m_off
> MMAXOFF
||
MMINOFF
+ sizeof (struct en_header
) > m
->m_off
) {
m
= m_get(M_DONTWAIT
, MT_HEADER
);
m
->m_len
= sizeof (struct en_header
);
m
->m_off
-= sizeof (struct en_header
);
m
->m_len
+= sizeof (struct en_header
);
en
= mtod(m
, struct en_header
*);
en
->en_shost
= ifp
->if_host
[0];
* 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)