2.11BSD/sys/pdpif/if_ec.c
/*
* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)if_ec.c 7.1 (Berkeley) 6/5/86
* 1.0 (2.10BSD EATON IMSD sms@etn-wlv.eaton.com 12/28/87)
*/
#include "ec.h"
#if NEC > 0
/*
* 3Com Ethernet Controller interface
*
* Adapted from the 4.3BSD version to run on 2.10BSD. Major differences:
*
* 1) static instead of autoconfiguration, 'autoconfig' has problems with
* our triple vector.
* 2) reduction of the number of buffers from 16 to 12, this is because
* the unibus memory for this device is 'enabled' by clipping resistors
* on the cpu board and must start on a mod 4 UMR boundary and we only
* have UMRs 28, 29 and 30. The number of UMRs available has to be
* reduced in ubinit() (/sys/pdp/machdep2.c) if this driver is to
* be used.
* 3) Buffers are mapped thru APR5, copyv() is used to go to/from mbufs.
* 4) Exponential backup redone to use a count instead of a mask.
* 5) The ec_softc structure reduced in size to remove unused fields.
* 6) Modified to run in supervisor mode, about all this required was
* changing the macros in seg.h to use supervisor registers if
* SUPERVISOR is defined (from the makefiles) AND changing where
* SEG5 is mapped in/out. Use mbcopyin/out instead of copyv.
* 7) Broken ethernet cable showed up a problem in collision handling,
* the backoff being done via a DELAY loop effectively hangs the system.
* Changed to use a timeout with the number of ticks being the number
* of collisions (up to 16 max).
*
* Who knows if trailers and NS work, i don't. Coding style changed to reflect
* personal preferences, conditional vax code removed to improve readability.
*
* Oh, one more thing. The 3Com board is hardwired to interrupt at spl6 for
* the receiver, spl5 for the collision detect, and spl4 for the transmitter.
* References to splimp() have been replaced in this driver with splhigh().
* you'll have to change splimp() to be spl6 and recompile the whole kernel
* in order to avoid recursive interrupts caused by the receiver using splimp
* anywhere in its path. TRUST ME, you crash if you don't do this. Better
* to lose a few clock ticks than the system!
*/
#include "param.h"
#include "../machine/seg.h"
#include "mbuf.h"
#include "buf.h"
#include "domain.h"
#include "protosw.h"
#include "socket.h"
#include "ioctl.h"
#include "errno.h"
#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"
#ifdef INET
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/in_var.h"
#include "../netinet/ip.h"
#include "../netinet/if_ether.h"
#endif
#ifdef NS
#include "../netns/ns.h"
#include "../netns/ns_if.h"
#endif
#include "if_ecreg.h"
#include "if_uba.h"
#include "../pdpuba/ubavar.h"
#define MAPBUFDESC (((btoc(2048) - 1) << 8 ) | RW)
#define BUFP ((caddr_t)0120000)
#undef ECRHBF
#define ECRHBF 11
#define ECNUMBUFS (ECRHBF + 1)
extern struct ifnet loif;
int ecattach(), ecinit(), ecioctl(), ecoutput(), ecunjam();
struct uba_device *ecinfo[NEC];
u_short ecstd[] = { 0 };
struct uba_driver ecdriver =
{ 0, 0, ecattach, 0, ecstd, "ec", ecinfo };
struct mbuf *ecget();
/*
* 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, ...
*/
struct ec_softc {
struct arpcom es_ac; /* common Ethernet structures */
#define es_if es_ac.ac_if /* network-visible interface */
#define es_addr es_ac.ac_enaddr /* hardware Ethernet address */
u_char es_mask; /* mask for current output delay */
u_char es_oactive; /* is output active? */
memaddr es_buf[ECNUMBUFS]; /* virtual click buffer addresses */
} ec_softc[NEC];
/*
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets.
*/
ecattach(ui)
struct uba_device *ui;
{
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;
int i, j;
u_char *cp;
ifp->if_unit = ui->ui_unit;
ifp->if_name = "ec";
ifp->if_mtu = ETHERMTU;
/* Read the ethernet address off the board, one nibble at a time. */
addr->ec_xcr = EC_UECLR; /* zero address pointer */
addr->ec_rcr = EC_AROM;
cp = es->es_addr;
#define NEXTBIT addr->ec_rcr = EC_AROM|EC_ASTEP; addr->ec_rcr = EC_AROM
for (i=0; i < sizeof (es->es_addr); i++)
{
*cp = 0;
for (j=0; j<=4; j+=4)
{
*cp |= ((addr->ec_rcr >> 8) & 0xf) << j;
NEXTBIT; NEXTBIT; NEXTBIT; NEXTBIT;
}
cp++;
}
printf("ec%d: addr %s\n", ui->ui_unit, ether_sprintf(es->es_addr));
ifp->if_init = ecinit;
ifp->if_ioctl = ecioctl;
ifp->if_output = ecoutput;
ifp->if_reset = 0;
ifp->if_flags = IFF_BROADCAST;
/*
* the (memaddr)(0177000) below is UMR28 translated into clicks.
*/
for (i = 0; i < ECNUMBUFS; i++)
es->es_buf[i] = (memaddr)(0177000) + (memaddr)(btoc(2048) * i);
if_attach(ifp);
}
/*
* Initialization of interface; clear recorded pending operations.
*/
ecinit(unit)
int unit;
{
struct ec_softc *es = &ec_softc[unit];
struct ecdevice *addr;
register struct ifnet *ifp = &es->es_if;
int i, s;
/* not yet, if address still unknown */
if (ifp->if_addrlist == (struct ifaddr *)0)
return;
/*
* Hang receive buffers and start any pending writes.
* Writing into the rcr also makes sure the memory
* is turned on.
*/
if ((ifp->if_flags & IFF_RUNNING) == 0)
{
addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
s = splhigh();
/*
* write our ethernet address into the address recognition ROM
* so we can always use the same EC_READ bits (referencing ROM),
* in case we change the address sometime.
* Note that this is safe here as the receiver is NOT armed.
*/
ec_setaddr(es->es_addr, unit);
/*
* Arm the receiver
*/
for (i = ECRHBF; i >= ECRLBF; i--)
addr->ec_rcr = EC_READ | i;
es->es_oactive = 0;
es->es_mask = 1;
es->es_if.if_flags |= IFF_RUNNING;
if (es->es_if.if_snd.ifq_head)
ecstart(unit);
splx(s);
}
}
/*
* Start output on interface. Get another datagram to send
* off of the interface queue, and copy it to the interface
* before starting the output.
*/
ecstart(unit)
int unit;
{
register struct ec_softc *es = &ec_softc[unit];
struct ecdevice *addr;
struct mbuf *m;
if ((es->es_if.if_flags & IFF_RUNNING) == 0)
return;
IF_DEQUEUE(&es->es_if.if_snd, m);
if (m == 0)
return;
ecput(es->es_buf[ECTBF], m);
addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
addr->ec_xcr = EC_WRITE|ECTBF;
es->es_oactive = 1;
}
/*
* Ethernet interface transmitter interrupt.
* Start another output if more data to send.
*/
ecxint(unit)
int unit;
{
register struct ec_softc *es = &ec_softc[unit];
register struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
register int s;
if (es->es_oactive == 0)
return;
if (!(addr->ec_xcr&EC_XDONE) || (addr->ec_xcr&EC_XBN) != ECTBF)
{
printf("ec%d: stray xint, xcr=%b\n",unit,addr->ec_xcr,EC_XBITS);
es->es_oactive = 0;
addr->ec_xcr = EC_XCLR;
return;
}
es->es_if.if_opackets++;
es->es_oactive = 0;
es->es_mask = 1;
addr->ec_xcr = EC_XCLR;
s = splimp();
if (es->es_if.if_snd.ifq_head)
ecstart(unit);
splx(s);
}
/*
* Collision on ethernet interface. Do exponential
* backoff, and retransmit. If have backed off all
* the way print warning diagnostic, and drop packet.
*/
eccollide(unit)
int unit;
{
register struct ec_softc *es = &ec_softc[unit];
register struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
register int i;
long delay;
es->es_if.if_collisions++;
if (es->es_oactive == 0)
return;
if (es->es_mask++ >= 16)
{
es->es_if.if_oerrors++;
printf("ec%d: send err\n", unit);
/*
* Reset interface, then requeue rcv buffers.
* Some incoming packets may be lost, but that
* can't be helped.
*/
addr->ec_xcr = EC_UECLR;
for (i=ECRHBF; i>=ECRLBF; i--)
addr->ec_rcr = EC_READ|i;
/*
* Reset and transmit next packet (if any).
*/
es->es_oactive = 0;
es->es_mask = 1;
if (es->es_if.if_snd.ifq_head)
ecstart(unit);
return;
}
/*
* use a timeout instead of a delay loop - the loop hung the system
* when someone unscrewed a terminator on the net.
*
* this isn't exponential, but it sure beats a hung up system in the
* face of a broken cable.
*/
TIMEOUT(ecunjam, addr, es->es_mask);
}
ecunjam(addr)
struct ecdevice *addr;
{
/*
* Clear the controller's collision flag, thus enabling retransmit.
*/
addr->ec_xcr = EC_CLEAR;
}
/*
* Ethernet interface receiver interrupt.
* If input error just drop packet.
* Otherwise 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
* input routine.
*/
ecrint(unit)
int unit;
{
struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
while (addr->ec_rcr & EC_RDONE)
ecread(unit);
}
ecread(unit)
int unit;
{
register struct ec_softc *es = &ec_softc[unit];
register struct ether_header *ec;
register struct ifqueue *inq;
struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr;
struct mbuf *m;
int len, off = 0, resid, ecoff, rbuf, type;
u_char *ecbuf;
segm sav5;
es->es_if.if_ipackets++;
rbuf = addr->ec_rcr & EC_RBN;
if (rbuf < ECRLBF || rbuf > ECRHBF)
panic("ecrint"); /* sanity */
/*
* we change SDSA5 only while NOT looking at mbufs (there might be some on SEG5)
* and carefully restore it before calling 'ecget' who uses mbcopyin().
* the save/restore seg routines are ifdef'd on SUPERVISOR (which had better
* be set when compiling the network stuff!!).
*/
saveseg5(sav5);
mapseg5(es->es_buf[rbuf], MAPBUFDESC);
ecbuf = (u_char *)SEG5;
ecoff = *(short *)SEG5;
if (ecoff <= ECRDOFF || ecoff > 2046)
{
es->es_if.if_ierrors++;
#ifdef notyet
printf("ec%d ecoff=0%o rbuf=0%o\n", unit, ecoff, rbuf);
#endif
goto setup;
}
/*
* Get input data length.
* Get pointer to ethernet header (in input buffer).
* Deal with trailer protocol: if type is trailer type
* get true type from first 16-bit word past data.
* Remember that type was trailer by setting off.
*/
len = ecoff - ECRDOFF - sizeof (struct ether_header);
ec = (struct ether_header *)(ecbuf + ECRDOFF);
ec->ether_type = ntohs((u_short)ec->ether_type);
#ifdef weliketrailers
#define ecdataaddr(ec, off, type) ((type)(((caddr_t)((ec)+1)+(off))))
if (ec->ether_type >= ETHERTYPE_TRAIL &&
ec->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER)
{
off = (ec->ether_type - ETHERTYPE_TRAIL) * 512;
if (off >= ETHERMTU)
goto setup; /* sanity */
ec->ether_type = ntohs(*ecdataaddr(ec, off, u_short *));
resid = ntohs(*(ecdataaddr(ec, off+2, u_short *)));
if (off + resid > len)
goto setup; /* sanity */
len = off + resid;
}
else
off = 0;
if (!len)
goto setup;
#else
if (!len || (ec->ether_type >= ETHERTYPE_TRAIL &&
ec->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER))
{
printf("ec%d type=%x len=%d\n", unit, ec->ether_type,len);
es->es_if.if_ierrors++;
goto setup;
}
#endif
/*
* 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.
*/
type = ec->ether_type; /* save before restoring mapping */
restorseg5(sav5); /* put it back now! */
m = ecget(es->es_buf[rbuf], len, off, &es->es_if);
if (m == 0)
goto setup;
#ifdef weliketrailers
if (off)
{
struct ifnet *ifp;
ifp = *(mtod(m, struct ifnet **));
m->m_off += 2 * sizeof (u_short);
m->m_len -= 2 * sizeof (u_short);
*(mtod(m, struct ifnet **)) = ifp;
}
#endif
switch (type)
{
#ifdef INET
case ETHERTYPE_IP:
schednetisr(NETISR_IP);
inq = &ipintrq;
break;
case ETHERTYPE_ARP:
arpinput(&es->es_ac, m);
goto setup;
#endif
#ifdef NS
case ETHERTYPE_NS:
schednetisr(NETISR_NS);
inq = &nsintrq;
break;
#endif
default:
m_freem(m);
goto setup;
}
if (IF_QFULL(inq))
{
IF_DROP(inq);
m_freem(m);
goto setup;
}
IF_ENQUEUE(inq, m);
setup:
/* Reset for next packet. */
restorseg5(sav5); /* put it back before leaving */
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 and trailers
* are allowed (which we should NEVER do).
* If destination is this address or broadcast, send packet to
* loop device to kludge around the fact that 3com interfaces can't
* talk to themselves.
*/
ecoutput(ifp, m0, dst)
struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
{
int type, s, error, usetrailers;
u_char edst[6];
struct in_addr idst;
struct ec_softc *es = &ec_softc[ifp->if_unit];
register struct mbuf *m = m0;
register struct ether_header *ec;
register int off;
u_short *p;
struct mbuf *mcopy = (struct mbuf *)0;
if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
{
error = ENETDOWN;
goto bad;
}
switch (dst->sa_family)
{
#ifdef INET
case AF_INET:
idst = ((struct sockaddr_in *)dst)->sin_addr;
if (!arpresolve(&es->es_ac, m, &idst, edst,
&usetrailers))
return(0); /* if not yet resolved */
if (!bcmp(edst, etherbroadcastaddr, sizeof(edst)))
mcopy = m_copy(m, 0, (int)M_COPYALL);
off = ntohs((u_short)mtod(m, struct ip *)->ip_len)
- m->m_len;
/* need per host negotiation */
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);
p = mtod(m, u_short *);
*p++ =ntohs((u_short)ETHERTYPE_IP);
*p = ntohs((u_short)m->m_len);
goto gottrailertype;
}
type = ETHERTYPE_IP;
off = 0;
goto gottype;
#endif
#ifdef NS
case AF_NS:
bcopy(&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
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,
sizeof(edst)))
return(looutput(&loif, m, dst));
type = ETHERTYPE_NS;
off = 0;
goto gottype;
#endif
case AF_UNSPEC:
ec = (struct ether_header *)dst->sa_data;
bcopy(ec->ether_dhost, (caddr_t)edst, sizeof (edst));
type = ec->ether_type;
goto gottype;
default:
printf("ec%d: af%d\n", ifp->if_unit, dst->sa_family);
error = EAFNOSUPPORT;
goto bad;
}
gottrailertype:
/*
* Packet to be sent as trailer: move first packet
* (control information) to end of chain.
*/
while (m->m_next)
m = m->m_next;
m->m_next = m0;
m = m0->m_next;
m0->m_next = 0;
m0 = m;
gottype:
/*
* Add local net header. If no space in first mbuf,
* allocate another.
*/
if (m->m_off > MMAXOFF ||
MMINOFF + sizeof (struct ether_header) > m->m_off)
{
m = m_get(M_DONTWAIT, MT_HEADER);
if (m == 0)
{
error = ENOBUFS;
goto bad;
}
m->m_next = m0;
m->m_off = MMINOFF;
m->m_len = sizeof (struct ether_header);
}
else
{
m->m_off -= sizeof (struct ether_header);
m->m_len += sizeof (struct ether_header);
}
ec = mtod(m, struct ether_header *);
bcopy((caddr_t)edst, (caddr_t)ec->ether_dhost, sizeof (edst));
bcopy(es->es_addr, (caddr_t)ec->ether_shost, sizeof(ec->ether_shost));
ec->ether_type = htons((u_short)type);
/*
* Queue message on interface, and start output if interface
* not yet active.
*/
s = splhigh();
if (IF_QFULL(&ifp->if_snd))
{
IF_DROP(&ifp->if_snd);
error = ENOBUFS;
goto qfull;
}
IF_ENQUEUE(&ifp->if_snd, m);
if (es->es_oactive == 0)
ecstart(ifp->if_unit);
splx(s);
error = mcopy ? looutput(&loif, mcopy, dst) : 0;
return(error);
qfull:
m0 = m;
splx(s);
bad:
m_freem(m0);
if (mcopy)
m_freem(mcopy);
return(error);
}
/*
* Routine to copy from mbuf chain to transmit
* buffer in UNIBUS memory.
* 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.
*/
ecput(ecbuf, m)
memaddr ecbuf;
struct mbuf *m;
{
register struct mbuf *mp;
register u_short off;
segm sav5;
for (off = 2048, mp = m; mp; mp = mp->m_next)
off -= mp->m_len;
if (2048 - off < ETHERMIN + sizeof (struct ether_header))
off = 2048 - ETHERMIN - sizeof (struct ether_header);
saveseg5(sav5);
mapseg5(ecbuf, MAPBUFDESC);
*(u_short *)SEG5 = off;
restorseg5(sav5);
for (mp = m; mp; mp = mp->m_next)
{
register unsigned len = mp->m_len;
if (len == 0)
continue;
mbcopyout(mtod(mp, caddr_t), ecbuf, off, len);
off += len;
}
m_freem(m);
}
/*
* Routine to copy from UNIBUS memory into mbufs.
*/
struct mbuf *
ecget(ecbuf, totlen, off0, ifp)
memaddr ecbuf;
int totlen, off0;
struct ifnet *ifp;
{
register struct mbuf *m;
register int off = off0, len;
struct mbuf *top = 0, **mp = ⊤
u_short distance;
distance = ECRDOFF + sizeof (struct ether_header);
while (totlen > 0)
{
MGET(m, M_DONTWAIT, MT_DATA);
if (m == 0)
goto bad;
if (off)
{
len = totlen - off;
distance = ECRDOFF + off + sizeof (struct ether_header);
}
else
len = totlen;
if (ifp)
len += sizeof(ifp);
if (len >= NBPG)
{
MCLGET(m);
if (m->m_len == CLBYTES)
m->m_len = len = MIN(len, CLBYTES);
else
m->m_len = len = MIN(MLEN, len);
}
else
m->m_len = len = MIN(MLEN, len);
if (ifp)
{
/* Prepend interface pointer to first mbuf. */
*(mtod(m, struct ifnet **)) = ifp;
len -= sizeof(ifp);
}
mbcopyin(ecbuf, distance,
ifp ? mtod(m,caddr_t)+sizeof(ifp) : mtod(m,caddr_t), len);
ifp = (struct ifnet *)0;
distance += len;
*mp = m;
mp = &m->m_next;
if (off == 0)
{
totlen -= len;
continue;
}
off += len;
if (off == totlen)
{
distance = ECRDOFF + sizeof (struct ether_header);
off = 0;
totlen = off0;
}
}
return(top);
bad:
m_freem(top);
return(0);
}
/*
* Process an ioctl request.
*/
ecioctl(ifp, cmd, data)
register struct ifnet *ifp;
int cmd;
caddr_t data;
{
register struct ifaddr *ifa = (struct ifaddr *)data;
struct ec_softc *es = &ec_softc[ifp->if_unit];
struct ecdevice *addr;
int s = splhigh(), error = 0;
addr = (struct ecdevice *)ecinfo[ifp->if_unit]->ui_addr;
switch (cmd)
{
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
switch (ifa->ifa_addr.sa_family)
{
#ifdef INET
case AF_INET:
ecinit(ifp->if_unit);
((struct arpcom *)ifp)->ac_ipaddr =
IA_SIN(ifa)->sin_addr;
arpwhohas((struct arpcom *)ifp,
&IA_SIN(ifa)->sin_addr);
break;
#endif
#ifdef NS
case AF_NS:
{
register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
if (ns_nullhost(*ina))
ina->x_host = *(union ns_host *)
(es->es_addr);
else
{
/*
* The manual says we can't change the address
* while the receiver is armed,
* so reset everything
*/
ifp->if_flags &= ~IFF_RUNNING;
bcopy(ina->x_host.c_host,
es->es_addr,
sizeof(es->es_addr));
}
ecinit(ifp->if_unit); /* do ec_setaddr*/
break;
}
#endif
default:
ecinit(ifp->if_unit);
break;
}
break;
case SIOCSIFFLAGS:
if ((ifp->if_flags & IFF_UP) == 0 &&
ifp->if_flags & IFF_RUNNING)
{
addr->ec_xcr = EC_UECLR;
ifp->if_flags &= ~IFF_RUNNING;
}
else if (ifp->if_flags & IFF_UP &&
(ifp->if_flags & IFF_RUNNING) == 0)
ecinit(ifp->if_unit);
break;
default:
error = EINVAL;
}
splx(s);
return(error);
}
ec_setaddr(physaddr,unit)
u_char *physaddr;
int unit;
{
struct ec_softc *es = &ec_softc[unit];
struct uba_device *ui = ecinfo[unit];
struct ecdevice *addr = (struct ecdevice *)ui->ui_addr;
register char nibble;
register int i, j;
/*
* Use the ethernet address supplied
* Note that we do a UECLR here, so the receive buffers
* must be requeued.
*/
#ifdef DEBUG
printf("ec_setaddr: setting address for unit %d = %s",
unit, ether_sprintf(physaddr));
#endif
addr->ec_xcr = EC_UECLR;
addr->ec_rcr = 0;
/* load requested address */
for (i = 0; i < 6; i++)
{ /* 6 bytes of address */
es->es_addr[i] = physaddr[i];
nibble = physaddr[i] & 0xf; /* lower nibble */
addr->ec_rcr = (nibble << 8);
addr->ec_rcr = (nibble << 8) + EC_AWCLK; /* latch nibble */
addr->ec_rcr = (nibble << 8);
for (j=0; j < 4; j++)
{
addr->ec_rcr = 0;
addr->ec_rcr = EC_ASTEP; /* step counter */
addr->ec_rcr = 0;
}
nibble = (physaddr[i] >> 4) & 0xf; /* upper nibble */
addr->ec_rcr = (nibble << 8);
addr->ec_rcr = (nibble << 8) + EC_AWCLK; /* latch nibble */
addr->ec_rcr = (nibble << 8);
for (j=0; j < 4; j++)
{
addr->ec_rcr = 0;
addr->ec_rcr = EC_ASTEP; /* step counter */
addr->ec_rcr = 0;
}
}
#ifdef DEBUG
/*
* Read the ethernet address off the board, one nibble at a time.
*/
addr->ec_xcr = EC_UECLR;
addr->ec_rcr = 0; /* read RAM */
cp = es->es_addr;
#undef NEXTBIT
#define NEXTBIT addr->ec_rcr = EC_ASTEP; addr->ec_rcr = 0
for (i=0; i < sizeof (es->es_addr); i++)
{
*cp = 0;
for (j=0; j<=4; j+=4)
{
*cp |= ((addr->ec_rcr >> 8) & 0xf) << j;
NEXTBIT; NEXTBIT; NEXTBIT; NEXTBIT;
}
cp++;
}
printf("ec_setaddr: RAM address for unit %d = %s",
unit, ether_sprintf(physaddr));
#endif
}
#endif