/* if_ec.c 4.17 82/06/17 */
* 3Com Ethernet Controller interface
#include "../h/protosw.h"
#include "../net/in_systm.h"
#include "../net/if_ec.h"
#include "../net/if_uba.h"
#include "../net/ip_var.h"
#include "../net/route.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? */
caddr_t 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
[0][0600000];
br
= 0; cvec
= br
; br
= cvec
;
ecrint(0); ecxint(0); eccollide(0);
* Make sure memory is turned on
* Check for existence of buffers on Unibus.
* This won't work on a 780 until more work is done.
if (badaddr((caddr_t
) ecbuf
, 2)) {
printf("ec: buffer mem not found");
* Tell the system that the board has memory here, so it won't
* attempt to allocate the addresses later.
ubamem(0, 0600000, 32*2);
* 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) {
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
;
ifp
->if_ubareset
= ecreset
;
es
->es_buf
[i
] = &umem
[ui
->ui_ubanum
][0600000+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 ||
* 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 by faking a transmit complete.
* 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_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.
int unit
= ECUNIT(dev
), dest
;
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
,
* There shouldn't ever be any mbuf's to free, but just in case...
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
)
* 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
];
printf("ec%d: collision\n", 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.
addr
->ec_xcr
= EC_JINTEN
|EC_XINTEN
|EC_JCLR
;
* 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
, buf
;
register struct ifqueue
*inq
;
buf
= addr
->ec_rcr
& EC_RBN
;
if (buf
< ECRLBF
|| buf
> ECRHBF
)
if (ecoff
<= ECRDOFF
|| ecoff
> 2046) {
if (es
->es_if
.if_ierrors
% 100 == 0)
printf("ec%d: += 100 input errors\n", unit
);
printf("ec%d: input error (offset=%d)\n", unit
, ecoff
);
* 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
|buf
;
* 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, 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
= m_free(mp
)) {
mcp
= mtod(mp
, u_char
*);
for (; len
> 1; len
-= sizeof (u_short
)) {
*(u_short
*)bp
= *(u_short
*)mcp
;
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
, **mp
, *m
;
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
);
for (i
= 0; i
< len
; i
+= sizeof (short)) {
*(short *)mcp
= *(short *)cp
;
sizeof (struct ec_header
);