/* if_ec.c 4.28 82/10/31 */
* 3Com Ethernet Controller interface
#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_ec.h"
#include "../vaxif/if_ecreg.h"
#include "../vaxif/if_uba.h"
#include "../vaxuba/ubareg.h"
#include "../vaxuba/ubavar.h"
int ecprobe(), ecattach(), ecrint(), ecxint(), eccollide();
struct uba_device
*ecinfo
[NEC
];
struct uba_driver ecdriver
=
{ ecprobe
, 0, ecattach
, 0, ecstd
, "ec", ecinfo
};
u_char ec_iltop
[3] = { 0x02, 0x07, 0x01 };
#define ECUNIT(x) minor(x)
int ecinit(),ecoutput(),ecreset();
extern struct ifnet loif
;
* 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_mask
; /* mask for current output delay */
short es_oactive
; /* is output active? */
u_char
*es_buf
[16]; /* virtual addresses of buffers */
u_char es_enaddr
[6]; /* board's ethernet address */
* 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 ecdevice
*addr
= (struct ecdevice
*)reg
;
register caddr_t ecbuf
= (caddr_t
) &umem
[numuba
][ECMEM
];
br
= 0; cvec
= br
; br
= cvec
;
ecrint(0); ecxint(0); eccollide(0);
* Make sure memory is turned on
* Disable map registers for ec unibus space,
* but don't allocate yet.
(void) ubamem(numuba
, ECMEM
, 32*2, 0);
* Check for existence of buffers on Unibus.
if (badaddr((caddr_t
)ecbuf
, 2)) {
printf("ec: buffer mem not found\n");
(void) ubamem(numuba
, 0, 0, 0); /* reenable map (780 only) */
addr
->ec_rcr
= EC_MDISAB
; /* disable memory */
if (cpu
== VAX_780
&& uba_hd
[numuba
].uh_uba
->uba_sr
) {
uba_hd
[numuba
].uh_uba
->uba_sr
= uba_hd
[numuba
].uh_uba
->uba_sr
;
* Tell the system that the board has memory here, so it won't
* attempt to allocate the addresses later.
if (ubamem(numuba
, ECMEM
, 32*2, 1) == 0) {
printf("ecprobe: cannot reserve uba addresses\n");
* Make a one byte packet in what should be buffer #0.
* Submit it for sending. This whould cause an xmit interrupt.
* The xmit interrupt vector is 8 bytes after the receive vector,
* so adjust for this before returning.
*(u_short
*)ecbuf
= (u_short
) 03777;
addr
->ec_xcr
= EC_XINTEN
|EC_XWBN
;
if (cvec
> 0 && cvec
!= 0x200) {
if (cvec
& 04) { /* collision interrupt */
br
+= 1; /* rcv is collision + 1 */
} else { /* xmit interrupt */
br
+= 2; /* rcv is xmit + 2 */
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
struct ec_softc
*es
= &ec_softc
[ui
->ui_unit
];
register struct ifnet
*ifp
= &es
->es_if
;
register struct ecdevice
*addr
= (struct ecdevice
*)ui
->ui_addr
;
ifp
->if_unit
= ui
->ui_unit
;
ifp
->if_net
= ui
->ui_flags
;
* Read the ethernet address off the board, one nibble at a time.
#define NEXTBIT addr->ec_rcr = EC_AROM|EC_ASTEP; addr->ec_rcr = EC_AROM
*cp
|= ((addr
->ec_rcr
>> 8) & 0xf) << j
;
NEXTBIT
; NEXTBIT
; NEXTBIT
; NEXTBIT
;
printf("ec%d: addr=%x:%x:%x:%x:%x:%x\n", ui
->ui_unit
,
es
->es_enaddr
[0]&0xff, es
->es_enaddr
[1]&0xff,
es
->es_enaddr
[2]&0xff, es
->es_enaddr
[3]&0xff,
es
->es_enaddr
[4]&0xff, es
->es_enaddr
[5]&0xff);
ifp
->if_host
[0] = ((es
->es_enaddr
[3]&0xff)<<16) |
((es
->es_enaddr
[4]&0xff)<<8) | (es
->es_enaddr
[5]&0xff);
sin
= (struct sockaddr_in
*)&es
->es_if
.if_addr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= if_makeaddr(ifp
->if_net
, ifp
->if_host
[0]);
sin
= (struct sockaddr_in
*)&ifp
->if_broadaddr
;
sin
->sin_family
= AF_INET
;
sin
->sin_addr
= if_makeaddr(ifp
->if_net
, INADDR_ANY
);
ifp
->if_flags
= IFF_BROADCAST
;
ifp
->if_output
= ecoutput
;
es
->es_buf
[i
] = (u_char
*)&umem
[ui
->ui_ubanum
][ECMEM
+2048*i
];
* Reset of interface after UNIBUS reset.
* If interface is on specified uba, reset its state.
register struct uba_device
*ui
;
if (unit
>= NEC
|| (ui
= ecinfo
[unit
]) == 0 || ui
->ui_alive
== 0 ||
(void) ubamem(uban
, ECMEM
, 32*2, 0); /* mr disable (no alloc) */
* Initialization of interface; clear recorded pending
* operations, and reinitialize UNIBUS usage.
struct ec_softc
*es
= &ec_softc
[unit
];
* Hang receive buffers and start any pending writes.
* Writing into the rcr also makes sure the memory
addr
= (struct ecdevice
*)ecinfo
[unit
]->ui_addr
;
for (i
=ECRHBF
; i
>=ECRLBF
; i
--)
addr
->ec_rcr
= EC_READ
|i
;
es
->es_if
.if_flags
|= IFF_UP
;
if (es
->es_if
.if_snd
.ifq_head
)
if_rtinit(&es
->es_if
, RTF_UP
);
* Start or restart output on interface.
* If interface is already active, then this is a retransmit
* after a collision, and just restuff registers.
* 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 ec_softc
*es
= &ec_softc
[unit
];
IF_DEQUEUE(&es
->es_if
.if_snd
, m
);
ecput(es
->es_buf
[ECTBF
], m
);
addr
= (struct ecdevice
*)ecinfo
[unit
]->ui_addr
;
addr
->ec_xcr
= EC_WRITE
|ECTBF
;
* Ethernet interface transmitter interrupt.
* Start another output if more data to send.
register struct ec_softc
*es
= &ec_softc
[unit
];
register struct ecdevice
*addr
=
(struct ecdevice
*)ecinfo
[unit
]->ui_addr
;
if ((addr
->ec_xcr
&EC_XDONE
) == 0 || (addr
->ec_xcr
&EC_XBN
) != ECTBF
) {
printf("ec%d: stray xmit interrupt, xcr=%b\n", unit
,
if (es
->es_if
.if_snd
.ifq_head
)
* Collision on ethernet interface. Do exponential
* backoff, and retransmit. If have backed off all
* the way print warning diagnostic, and drop packet.
struct ec_softc
*es
= &ec_softc
[unit
];
es
->es_if
.if_collisions
++;
register struct ec_softc
*es
= &ec_softc
[unit
];
register struct ecdevice
*addr
=
(struct ecdevice
*)ecinfo
[unit
]->ui_addr
;
* 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("ec%d: send error\n", unit
);
* Reset interface, then requeue rcv buffers.
* Some incoming packets may be lost, but that
for (i
=ECRHBF
; i
>=ECRLBF
; i
--)
addr
->ec_rcr
= EC_READ
|i
;
* Reset and transmit next packet (if any).
if (es
->es_if
.if_snd
.ifq_head
)
* Do exponential backoff. Compute delay based on low bits
* of the interval timer. Then delay for that number of
* slot times. A slot time is 51.2 microseconds (rounded to 51).
* This does not take into account the time already used to
delay
= mfpr(ICR
) &~ es
->es_mask
;
* Clear the controller's collision flag, thus enabling retransmit.
* 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
struct ecdevice
*addr
= (struct ecdevice
*)ecinfo
[unit
]->ui_addr
;
while (addr
->ec_rcr
& EC_RDONE
)
register struct ec_softc
*es
= &ec_softc
[unit
];
struct ecdevice
*addr
= (struct ecdevice
*)ecinfo
[unit
]->ui_addr
;
register struct ec_header
*ec
;
int len
, off
, resid
, ecoff
, rbuf
;
register struct ifqueue
*inq
;
rbuf
= addr
->ec_rcr
& EC_RBN
;
if (rbuf
< ECRLBF
|| rbuf
> ECRHBF
)
ecbuf
= es
->es_buf
[rbuf
];
if (ecoff
<= ECRDOFF
|| ecoff
> 2046) {
if (es
->es_if
.if_ierrors
% 100 == 0)
printf("ec%d: += 100 input errors\n", unit
);
* 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
= ecoff
- ECRDOFF
- sizeof (struct ec_header
);
ec
= (struct ec_header
*)(ecbuf
+ ECRDOFF
);
#define ecdataaddr(ec, off, type) ((type)(((caddr_t)((ec)+1)+(off))))
if (ec
->ec_type
>= ECPUP_TRAIL
&&
ec
->ec_type
< ECPUP_TRAIL
+ECPUP_NTRAILER
) {
off
= (ec
->ec_type
- ECPUP_TRAIL
) * 512;
ec
->ec_type
= *ecdataaddr(ec
, off
, u_short
*);
resid
= *(ecdataaddr(ec
, off
+2, u_short
*));
* Pull packet off interface. Off is nonzero if packet
* has trailing header; ecget 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
= ecget(ecbuf
, len
, off
);
m
->m_off
+= 2 * sizeof (u_short
);
m
->m_len
-= 2 * sizeof (u_short
);
addr
->ec_rcr
= EC_READ
|EC_RCLR
|rbuf
;
* 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.
* If destination is this address or broadcast, send packet to
* loop device to kludge around the fact that 3com interfaces can't
int type
, dest
, s
, error
;
register struct ec_softc
*es
= &ec_softc
[ifp
->if_unit
];
register struct mbuf
*m
= m0
;
register struct ec_header
*ec
;
struct mbuf
*mcopy
= (struct mbuf
*) 0; /* Null */
switch (dst
->sa_family
) {
dest
= ((struct sockaddr_in
*)dst
)->sin_addr
.s_addr
;
mcopy
= m_copy(m
, 0, (int)M_COPYALL
);
else if (dest
== ((struct sockaddr_in
*)&es
->es_if
.if_addr
)->
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
= ECPUP_TRAIL
+ (off
>>9);
m
->m_off
-= 2 * sizeof (u_short
);
m
->m_len
+= 2 * sizeof (u_short
);
*mtod(m
, u_short
*) = ECPUP_IPTYPE
;
*(mtod(m
, u_short
*) + 1) = m
->m_len
;
printf("ec%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 ec_header
) > m
->m_off
) {
m
->m_len
= sizeof (struct ec_header
);
m
->m_off
-= sizeof (struct ec_header
);
m
->m_len
+= sizeof (struct ec_header
);
ec
= mtod(m
, struct ec_header
*);
ec
->ec_shost
[i
] = es
->es_enaddr
[i
];
ec
->ec_dhost
[0] = ec_iltop
[0];
ec
->ec_dhost
[1] = ec_iltop
[1];
ec
->ec_dhost
[2] = ec_iltop
[2];
ec
->ec_dhost
[0] = es
->es_enaddr
[0];
ec
->ec_dhost
[1] = es
->es_enaddr
[1];
ec
->ec_dhost
[2] = es
->es_enaddr
[2];
ec
->ec_dhost
[3] = (dest
>>8) & 0x7f;
ec
->ec_dhost
[4] = (dest
>>16) & 0xff;
ec
->ec_dhost
[5] = (dest
>>24) & 0xff;
* 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 transmitter
* buffer in UNIBUS memory.
register struct mbuf
*mp
;
for (off
= 2048, mp
= m
; mp
; mp
= mp
->m_next
)
bp
= (u_char
*)(ecbuf
+ off
);
for (mp
= m
; mp
; mp
= mp
->m_next
) {
register unsigned len
= mp
->m_len
;
mcp
= mtod(mp
, u_char
*);
register u_short
*to
, *from
;
printf("ec: bad ecput, diff=%d\n", bp
-ecbuf
);
* Routine to copy from UNIBUS memory into mbufs.
* Similar in spirit to if_rubaget.
* Warning: This makes the fairly safe assumption that
* mbufs have even lengths.
ecget(ecbuf
, totlen
, off0
)
struct mbuf
*top
= 0, **mp
= &top
;
register int off
= off0
, len
;
cp
= ecbuf
+ ECRDOFF
+ sizeof (struct ec_header
);
cp
= ecbuf
+ ECRDOFF
+ sizeof (struct ec_header
) + off
;
m
->m_len
= len
= CLBYTES
;
m
->m_off
= (int)p
- (int)m
;
m
->m_len
= len
= MIN(MLEN
, len
);
m
->m_len
= len
= MIN(MLEN
, len
);
if (words
= (len
>> 1)) {
register u_short
*to
, *from
;
cp
= ecbuf
+ ECRDOFF
+ sizeof (struct ec_header
);