Ultrix-3.1/sys/net/if_de.c
/**********************************************************************
* Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. *
* All Rights Reserved. *
* Reference "/usr/src/COPYRIGHT" for applicable restrictions. *
**********************************************************************/
/*
* SCCSID: @(#)if_de.c 3.1 10/27/87
* based on "@(#)if_de.c 1.13 (ULTRIX-32) 6/20/85";
*/
/************************************************************************
* Modification History *
* *
* 2-Mar-86 - Fred Canter *
* Install George Mathew's change to fix broken unibus *
* address (buba) allocation code. The DEUNA failed on *
* the 11/24 (just dumb luck it worked on split I/D CPUs). *
* *
* 15-Jul-85 - Dave Borman *
* Frontal lobotomy for the PDP11. There's a lot *
* of VAX code that just doesn't make sense on a PDP. *
* *
* 19-Jun-85 -- jaw *
* VAX8200 name change. *
* *
* 13 Mar 85 - Jaw *
* add support for VAX8200 and bua *
* *
* LSC001 - Larry Cohen, 3/6/85: add internal loopback *
* *
* LSC002 - Larry cohen, 3/685: poke deuna when transmit ring *
* is full and more data to send. Prevents system from *
* dropping off the network and hanging occasionally *
***********************************************************************/
/*
* DEC DEUNA interface
*
* Lou Salkind
* New York University
*
* TODO:
* timeout routine (get statistics)
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/buf.h>
#include <sys/protosw.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/map.h>
#include <net/if_de.h>
/*
* buba - Bastardized UniBus Address
* These macros are used so that we can store unibus address
* in an int. They also allow us to get at the low and high
* parts of a UNIBUS address much easier. These are intended
* only for click boundries, since we store the 16th & 17th bits
* in bits 0 and 1.
*/
/* convert between a click address and a buba */
#define ctobuba(x) ((((x)<<6)&~077)|(((x)>>10)&03))
#define bubatoc(x) ((((x)&03)<<10)|(((x)&~077)>>6))
/* get the low and high parts from a buba */
#define lobuba(x) ((x)&~077)
#define hibuba(x) ((x)&03)
#define MAPIT(x,y) {mapseg5((x), ((btoc(ETHERMTU)-1)<<8)|RW);y=SEG5;}
struct de_softc de_softc[];
#ifdef DE_DEBUG
int dedebug = 0;
#endif DE_DEBUG
#ifdef DOMULTI
u_char unused_multi[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
#endif DOMULTI
extern struct protosw *iftype_to_proto(), *iffamily_to_proto();
int deattach(), deint();
struct uba_device ;
u_short destd[] = { 0 };
int deinit(),deoutput(),deioctl();
struct mbuf *deget();
#define DELUA_LOOP_LEN (32 - sizeof(struct ether_header))
static char DEname[] = { 'd', 'e', '\0' };
unsigned de_dmaa;
int de_dmas;
/*
* Interface exists: make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets. We get the ethernet address here.
*/
deattach(unit, addr, vector)
int unit;
register struct dedevice *addr;
int vector;
{
register struct de_softc *ds;
register struct ifnet *ifp;
struct sockaddr_in *sin;
int csr0,i;
int de_buba2b;
if (badaddr(addr)) {
derr("non-existant address %o", unit, addr);
return;
}
ds = &de_softc[unit];
ifp = &ds->ds_if;
ds->ds_hwaddr = addr;
/*
* Is it a DEUNA or a DELULA? Save the device id.
*/
ds->ds_devid = (addr->pcsr1 & PCSR1_DEVID) >> 4;
ifp->if_unit = unit;
ifp->if_name = DEname;
ifp->if_mtu = ETHERMTU;
ifp->if_flags |= IFF_BROADCAST|IFF_DYNPROTO;
#ifdef DOMULTI
/*
* Fill the multicast address table with unused entries (broadcast
* address) so that we can always give the full table to the device
* and we don't have to worry about gaps.
*/
for (i=0; i < NMULTI; i++)
bcopy(unused_multi,&ds->ds_multicast[i],MULTISIZE);
#endif DOMULTI
/*
* Reset the board and map the pcbb buffer onto the Unibus.
*/
addr->pcsr0 = PCSR0_RSET;
while ((addr->pcsr0 & PCSR0_INTR) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if ((csr0 & PCSR0_PCEI) || (csr0 & PCSR0_DNI)==0)
derr_csr("reset failed", unit, csr0, addr->pcsr1);
de_buba2b = ub_alloc((long)INCORE_BASE(ds), INCORE_SIZE);
ds->ds_ubaddr = ctobuba(de_buba2b);
addr->pcsr2 = lobuba(ds->ds_ubaddr);
addr->pcsr3 = hibuba(ds->ds_ubaddr);
addr->pclow = CMD_GETPCBB;
while ((addr->pcsr0 & PCSR0_INTR) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if ((csr0 & PCSR0_PCEI) || (csr0 & PCSR0_DNI)==0)
derr_csr("pcbb failed", unit, csr0, addr->pcsr1);
ds->ds_pcbb.pcbb0 = FC_RDPHYAD;
addr->pclow = CMD_GETCMD;
while ((addr->pcsr0 & PCSR0_INTR) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if ((csr0 & PCSR0_PCEI) || (csr0 & PCSR0_DNI)==0)
derr_csr("rdphyad failed", unit, csr0, addr->pcsr1);
#ifdef DE_DEBUG
if (dedebug)
derr("addr=%d:%d:%d:%d:%d:%d", unit,
ds->ds_pcbb.pcbb2&0xff, (ds->ds_pcbb.pcbb2>>8)&0xff,
ds->ds_pcbb.pcbb4&0xff, (ds->ds_pcbb.pcbb4>>8)&0xff,
ds->ds_pcbb.pcbb6&0xff, (ds->ds_pcbb.pcbb6>>8)&0xff);
#endif DE_DEBUG
bcopy((caddr_t)&ds->ds_pcbb.pcbb2,(caddr_t)ds->ds_addr,
sizeof (ds->ds_addr));
ifp->if_init = deinit;
ifp->if_output = deoutput;
ifp->if_ioctl = deioctl;
ifp->if_reset = 0;
if_attach(ifp);
ds->ds_deuba.ifu_r[0].ifrw_click = malloc(coremap, NTOT*btoc(ETHERMTU));
de_dmaa = ds->ds_deuba.ifu_r[0].ifrw_click; /* for memstat */
de_dmas = (NTOT*btoc(ETHERMTU)); /* for memstat */
derr("allocated %d bytes for DMA", unit, ctob(NTOT*btoc(ETHERMTU)));
}
/*
* Initialization of interface; clear recorded pending
* operations, and reinitialize UNIBUS usage.
*/
deinit(unit)
int unit;
{
register struct de_softc *ds = &de_softc[unit];
register struct dedevice *addr;
register struct ifrw *ifrw;
register struct ifrw *ifxp;
struct ifnet *ifp = &ds->ds_if;
int s;
struct de_ring *rp;
int csr0;
/* not yet, if address still unknown */
/* DECnet must set this somewhere to make device happy */
if (ifp->if_addrlist == (struct ifaddr *)0)
return;
if (ifp->if_flags & IFF_RUNNING)
return;
if (de_ubainit(&ds->ds_deuba, sizeof (struct ether_header)) == 0) {
derr("can't initialize", unit);
ds->ds_if.if_flags &= ~IFF_UP;
return;
}
addr = ds->ds_hwaddr;
/* set the transmit and receive ring header addresses */
ds->ds_pcbb.pcbb0 = FC_WTRING;
ds->ds_pcbb.pcbb2 = lobuba(ds->ds_ubaddr) + UDBBUF_OFFSET;
ds->ds_pcbb.pcbb4 = hibuba(ds->ds_ubaddr);
ds->ds_udbbuf.b_tdrbl = lobuba(ds->ds_ubaddr) + XRENT_OFFSET;
ds->ds_udbbuf.b_tdrbh = hibuba(ds->ds_ubaddr);
ds->ds_udbbuf.b_telen = sizeof (struct de_ring) / sizeof (short);
ds->ds_udbbuf.b_trlen = NXMT;
ds->ds_udbbuf.b_rdrbl = lobuba(ds->ds_ubaddr) + RRENT_OFFSET;
ds->ds_udbbuf.b_rdrbh = hibuba(ds->ds_ubaddr);
ds->ds_udbbuf.b_relen = sizeof (struct de_ring) / sizeof (short);
ds->ds_udbbuf.b_rrlen = NRCV;
addr->pclow = CMD_GETCMD;
while ((addr->pcsr0 & PCSR0_INTR) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if (csr0 & PCSR0_PCEI)
derr_csr("wtring failed", unit, csr0, addr->pcsr1);
/* initialize the mode - enable hardware padding */
ds->ds_pcbb.pcbb0 = FC_WTMODE;
/* let hardware do padding - set MTCH bit on broadcast */
ds->ds_pcbb.pcbb2 = MOD_TPAD|MOD_HDX;
addr->pclow = CMD_GETCMD;
while ((addr->pcsr0 & PCSR0_INTR) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if (csr0 & PCSR0_PCEI)
derr_csr("wtmode failed", unit, csr0, addr->pcsr1);
/* set up the receive and transmit ring entries */
ifxp = &ds->ds_deuba.ifu_w[0];
for (rp = &ds->ds_xrent[0]; rp < &ds->ds_xrent[NXMT]; rp++) {
rp->r_segbl = lobuba(ifxp->ifrw_buba);
rp->r_segbh = hibuba(ifxp->ifrw_buba);
rp->r_flags = 0;
ifxp++;
}
ifrw = &ds->ds_deuba.ifu_r[0];
for (rp = &ds->ds_rrent[0]; rp < &ds->ds_rrent[NRCV]; rp++) {
rp->r_slen = sizeof (struct de_buf);
rp->r_segbl = lobuba(ifrw->ifrw_buba);
rp->r_segbh = hibuba(ifrw->ifrw_buba);
rp->r_flags = RFLG_OWN; /* hang receive */
ifrw++;
}
/* start up the board (rah rah) */
s = splimp();
ds->ds_rindex = ds->ds_xindex = ds->ds_xfree = 0;
ds->ds_if.if_flags |= IFF_UP|IFF_RUNNING;
destart(unit); /* queue output packets */
addr->pclow = PCSR0_INTE; /* avoid interlock */
addr->pclow = CMD_START | PCSR0_INTE;
ds->ds_flags |= DSF_RUNNING;
ds->ds_ztime = time;
splx(s);
}
/*
* Setup output on interface.
* Get another datagram to send off of the interface queue,
* and map it to the interface before starting the output.
*/
static
destart(unit)
int unit;
{
int len;
register struct de_softc *ds = &de_softc[unit];
struct dedevice *addr = ds->ds_hwaddr;
register struct de_ring *rp;
struct mbuf *m;
register int nxmit;
for (nxmit = ds->ds_nxmit; nxmit < NXMT; nxmit++) {
IF_DEQUEUE(&ds->ds_if.if_snd, m);
if (m == 0)
break;
rp = &ds->ds_xrent[ds->ds_xfree];
if (rp->r_flags & XFLG_OWN)
panic("deuna xmit in progress");
len = deput(&ds->ds_deuba, ds->ds_xfree, m);
rp->r_slen = len;
rp->r_tdrerr = 0;
rp->r_flags = XFLG_STP|XFLG_ENP|XFLG_OWN;
ds->ds_xfree++;
if (ds->ds_xfree == NXMT)
ds->ds_xfree = 0;
}
if (ds->ds_nxmit != nxmit) {
ds->ds_nxmit = nxmit;
if (ds->ds_flags & DSF_RUNNING)
addr->pclow = PCSR0_INTE|CMD_PDMD;
}
else if (ds->ds_nxmit == NXMT) { /* LSC002 */
/*
* poke device if we have something to send and
* transmit ring is full.
*/
#ifdef DE_DEBUG
if (dedebug) {
rp = &ds->ds_xrent[0];
derr("did not xmt: %d, %d, %d, flag0=%x, flag1=%x",
unit, ds->ds_xindex, ds->ds_nxmit, ds->ds_xfree,
rp++->r_flags, rp->r_flags);
}
#endif DE_DEBUG
if (ds->ds_flags & DSF_RUNNING)
addr->pclow = PCSR0_INTE|CMD_PDMD;
}
}
/*
* Command done interrupt.
*/
deint(unit)
int unit;
{
register struct de_softc *ds = &de_softc[unit];
register struct de_ring *rp;
register struct ifrw *ifxp;
short csr0;
/* save flags right away - clear out interrupt bits */
csr0 = ds->ds_hwaddr->pcsr0;
ds->ds_hwaddr->pchigh = csr0 >> 8;
/*
* if receive, put receive buffer on mbuf
* and hang the request again
*/
rp = &ds->ds_rrent[ds->ds_rindex];
if ((rp->r_flags & RFLG_OWN) == 0)
derecv(ds, rp, unit);
/*
* Poll transmit ring and check status.
* Be careful about loopback requests.
* Then free buffer space and check for
* more transmit requests.
*/
for ( ; ds->ds_nxmit > 0; ds->ds_nxmit--) {
rp = &ds->ds_xrent[ds->ds_xindex];
if (rp->r_flags & XFLG_OWN)
break;
ds->ds_if.if_opackets++;
ifxp = &ds->ds_deuba.ifu_w[ds->ds_xindex];
/* check for unusual conditions */
if (rp->r_flags & (XFLG_ERRS|XFLG_MTCH|XFLG_ONE|XFLG_MORE)) {
if (rp->r_flags & XFLG_ERRS) {
/* output error */
ds->ds_if.if_oerrors++;
#ifdef DE_DEBUG
if (dedebug)
derr("oerror, flags=%o tdrerr=%o (len=%d)",
unit, rp->r_flags,
rp->r_tdrerr, rp->r_slen);
#endif DE_DEBUG
} else {
if (rp->r_flags & XFLG_ONE) {
/* one collision */
ds->ds_if.if_collisions++;
} else if (rp->r_flags & XFLG_MORE) {
/* more than one collision */
ds->ds_if.if_collisions += 2; /*guess*/
}
if ((rp->r_flags & XFLG_MTCH) &&
!(ds->ds_if.if_flags & IFF_LOOPBACK)) {
/* received our own packet */
ds->ds_if.if_ipackets++;
deread(ds, ifxp, rp->r_slen -
sizeof (struct ether_header));
}
}
}
/* check if next transmit buffer also finished */
ds->ds_xindex++;
if (ds->ds_xindex == NXMT)
ds->ds_xindex = 0;
}
destart(unit);
if (csr0 & PCSR0_RCBI) {
ds->ds_if.if_ierrors++;
ds->ds_hwaddr->pclow = PCSR0_INTE|CMD_PDMD;
}
}
/*
* Ethernet interface receiver interface.
* 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
* input routine.
*/
static
derecv(ds, rp, unit)
register struct de_softc *ds;
register struct de_ring *rp;
int unit;
{
register int len;
struct ether_header *eh;
do {
ds->ds_if.if_ipackets++;
len = (rp->r_lenerr&RERR_MLEN) - sizeof (struct ether_header)
- 4; /* don't forget checksum! */
if( ! (ds->ds_if.if_flags & IFF_LOOPBACK) ) {
/* check for errors */
if ((rp->r_flags & (RFLG_ERRS|RFLG_FRAM|RFLG_OFLO|RFLG_CRC))
|| (rp->r_flags&(RFLG_STP|RFLG_ENP)) != (RFLG_STP|RFLG_ENP)
|| (rp->r_lenerr & (RERR_OVRN|RERR_BUFL|RERR_UBTO|RERR_NCHN)) ||
len < ETHERMIN || len > ETHERMTU) {
ds->ds_if.if_ierrors++;
#ifdef DE_DEBUG
if (dedebug)
derr("ierror, flags=%o lenerr=%o (len=%d)",
unit, rp->r_flags, rp->r_lenerr, len);
#endif DE_DEBUG
} else
deread(ds, &ds->ds_deuba.ifu_r[ds->ds_rindex], len);
} else {
int ret;
segm map5;
saveseg5(map5);
MAPIT(ds->ds_deuba.ifu_r[ds->ds_rindex].ifrw_click, eh);
ret = bcmp(eh->ether_dhost, ds->ds_addr, 6);
restorseg5(map5);
if (ret == NULL)
deread(ds, &ds->ds_deuba.ifu_r[ds->ds_rindex], len);
}
/* hang the receive buffer again */
rp->r_lenerr = 0;
rp->r_flags = RFLG_OWN;
/* check next receive buffer */
if (++ds->ds_rindex == NRCV) {
ds->ds_rindex = 0;
rp = &ds->ds_rrent[0];
} else
rp = &ds->ds_rrent[ds->ds_rindex];
} while ((rp->r_flags & RFLG_OWN) == 0);
}
/*
* Pass a packet to the higher levels.
* We deal with the trailer protocol here.
*/
static
deread(ds, ifrw, len)
register struct de_softc *ds;
struct ifrw *ifrw;
int len;
{
struct ether_header *eh;
struct mbuf *m;
struct protosw *pr;
int off, resid;
struct ifqueue *inq;
segm map5;
int type;
/*
* Deal with trailer protocol: if type is trailer
* get true type from first 16-bit word past data.
* Remember that type was trailer by setting off.
*/
saveseg5(map5);
MAPIT(ifrw->ifrw_click, eh);
type = eh->ether_type = ntohs((u_short)eh->ether_type);
restorseg5(map5);
if (type >= ETHERTYPE_TRAIL &&
type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
register u_short *p;
off = (type - ETHERTYPE_TRAIL) * 512;
if (off >= ETHERMTU)
return; /* sanity */
mapseg5(ifrw->ifrw_click, ((btoc(ETHERMTU)-1)<<8)|RW)
p = (u_short *)(SEG5 + sizeof(struct ether_header) + off);
type = ntohs(*p++);
resid = ntohs(*p);
restorseg5(map5);
if (off + resid > len)
return; /* sanity */
len = off + resid;
} else
off = 0;
if (len == 0)
return;
/*
* Pull packet off interface. Off is nonzero if packet
* has trailing header; deget 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 = deget(&ds->ds_deuba, ifrw, len, off);
if (m == 0)
return;
if (off) {
m->m_off += 2 * sizeof (u_short);
m->m_len -= 2 * sizeof (u_short);
}
switch (type) {
#ifdef INET
case ETHERTYPE_IP:
#ifdef vax
if (nINET==0) {
m_freem(m);
return;
}
#endif vax
schednetisr(NETISR_IP);
inq = &ipintrq;
break;
case ETHERTYPE_ARP:
#ifdef vax
if (nETHER==0) {
m_freem(m);
return;
}
#endif vax
arpinput(&ds->ds_ac, m);
return;
#endif
default:
/*
* see if other protocol families defined
* and call protocol specific routines.
* If no other protocols defined then dump message.
*/
#ifdef vax
if (pr=iftype_to_proto(eh->ether_type)) {
if ((m = (struct mbuf *)(*pr->pr_ifinput)(m, &ds->ds_if, &inq, eh)) == 0)
#else vax
if (pr=iftype_to_proto(type)) {
if ((m = (struct mbuf *)(*pr->pr_ifinput)(m, &ds->ds_if, &inq)) == 0)
#endif vax
return;
} else {
if (ds->ds_unrecog != 0xffff)
ds->ds_unrecog++;
m_freem(m);
return;
}
}
if (IF_QFULL(inq)) {
IF_DROP(inq);
m_freem(m);
return;
}
IF_ENQUEUE(inq, m);
}
/*
* 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.
*/
deoutput(ifp, m0, dst)
struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
{
int type, s, error;
u_char edst[6];
struct in_addr idst;
struct protosw *pr;
register struct de_softc *ds = &de_softc[ifp->if_unit];
register struct mbuf *m = m0;
register struct ether_header *eh;
register int off;
segm map5;
saveseg5(map5);
switch (dst->sa_family) {
#ifdef INET
case AF_INET:
#ifdef vax
if (nINET == 0) {
derr("can't handle af%d", ifp->if_unit, dst->sa_family);
error = EAFNOSUPPORT;
goto bad;
}
#endif vax
idst = ((struct sockaddr_in *)dst)->sin_addr;
if (!arpresolve(&ds->ds_ac, m, &idst, edst))
return (0); /* if not yet resolved */
/* need per host negotiation */
if ((ifp->if_flags & IFF_NOTRAILERS) == 0) {
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)) {
u_short *p;
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++ = HTONS(ETHERTYPE_IP);
*p = htons((u_short)m->m_len);
goto gottrailertype;
}
}
type = ETHERTYPE_IP;
off = 0;
goto gottype;
#endif
#ifdef NS
case AF_NS:
type = ETHERTYPE_NS;
bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
(caddr_t)edst, sizeof (edst));
off = 0;
goto gottype;
#endif
case AF_UNSPEC:
eh = (struct ether_header *)dst->sa_data;
bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
type = eh->ether_type;
goto gottype;
default:
/*
* If we are in loopback mode check the length and
* device type. DELUA can only loopback frames of 36 bytes
* or less including crc.
*/
if ((ifp->if_flags & IFF_LOOPBACK) &&
(m->m_len > DELUA_LOOP_LEN) && (ds->ds_devid == DELUA))
return(EINVAL);
/*
* try to find other address families and call protocol
* specific output routine.
*/
if (pr=iffamily_to_proto(dst->sa_family)) {
(*pr->pr_ifoutput)(ifp, m0, dst, &type, (char *)edst);
goto gottype;
}
else {
derr("can't handle af%d", 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);
}
eh = mtod(m, struct ether_header *);
eh->ether_type = htons((u_short)type);
bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
/* DEUNA fills in source address */
restorseg5(map5);
/*
* Queue message on interface, and start output if interface
* not yet active.
*/
s = splimp();
if (IF_QFULL(&ifp->if_snd)) {
IF_DROP(&ifp->if_snd);
splx(s);
m_freem(m);
return (ENOBUFS);
}
IF_ENQUEUE(&ifp->if_snd, m);
destart(ifp->if_unit);
splx(s);
return (0);
bad:
restorseg5(map5);
m_freem(m0);
return (error);
}
/*
* Routines supporting UNIBUS network interfaces.
*/
/*
* Init UNIBUS for interface. We map the i/o area
* onto the UNIBUS. We then split up the i/o area among
* all the receive and transmit buffers.
*/
static
de_ubainit(ifu, hlen)
register struct deuba *ifu;
int hlen;
{
register unsigned cp, ubaddr;
int ncl;
/*
* If the second read buffer has a non-zero
* address, it means we have already allocated core
* If the first read buffer has a zero entry,
* it means that deattach couldn't malloc the
* needed core space for dma.
*/
if (ifu->ifu_r[1].ifrw_click)
return(1);
if (ifu->ifu_r[0].ifrw_click == 0)
return(0);
ncl = btoc(ETHERMTU);
cp = ifu->ifu_r[0].ifrw_click;
ifu->ifu_uba = ub_alloc(ctob((long)cp), ctob(NTOT*ncl));
ubaddr = ifu->ifu_uba;
ifu->ifu_hlen = hlen;
{
register struct ifrw *ifrw;
for (ifrw = ifu->ifu_r; ifrw < &ifu->ifu_r[NRCV]; ifrw++) {
ifrw->ifrw_click = cp;
ifrw->ifrw_buba = ctobuba(ubaddr);
cp += ncl;
ubaddr += ncl;
}
}
{
register struct ifrw *ifxp;
for (ifxp = ifu->ifu_w; ifxp < &ifu->ifu_w[NXMT]; ifxp++) {
ifxp->ifrw_click = cp;
ifxp->ifrw_buba = ctobuba(ubaddr);
cp += ncl;
ubaddr += ncl;
}
}
return (1);
}
/*
* Pull read data off a interface.
* Len is length of data, with local net header stripped.
* Off is non-zero if a trailer protocol was used, and
* gives the offset of the trailer information.
* We copy the trailer information and then all the normal
* data into mbufs.
*/
static struct mbuf *
deget(ifu, ifrw, totlen, off0)
register struct deuba *ifu;
register struct ifrw *ifrw;
int totlen, off0;
{
struct mbuf *top, **mp, *m;
int off = off0, len;
register caddr_t cp = ifu->ifu_hlen;
top = 0;
mp = ⊤
while (totlen > 0) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == 0)
goto bad;
if (off) {
len = totlen - off;
cp = ifu->ifu_hlen + off;
} else
len = totlen;
m->m_len = MIN(MLEN, len);
m->m_off = MMINOFF;
copyv(ifrw->ifrw_click, cp, m->m_click, m->m_off, m->m_len);
cp += m->m_len;
*mp = m;
mp = &m->m_next;
if (off) {
/* sort of an ALGOL-W style for statement... */
off += m->m_len;
if (off == totlen) {
cp = ifu->ifu_hlen;
off = 0;
totlen = off0;
}
} else
totlen -= m->m_len;
}
return (top);
bad:
m_freem(top);
return (0);
}
/*
* Map a chain of mbufs onto a network interface
* in preparation for an i/o operation.
* The argument chain of mbufs includes the local network
* header which is copied to be in the mapped, aligned
* i/o space.
*/
static
deput(ifu, n, m)
struct deuba *ifu;
int n;
register struct mbuf *m;
{
register struct mbuf *mp;
register int cc;
int click;
click = ifu->ifu_w[n].ifrw_click;
cc = 0;
while (m) {
copyv(m->m_click, m->m_off, click, cc, m->m_len);
cc += m->m_len;
MFREE(m, mp);
m = mp;
}
return (cc);
}
/*
* Process an ioctl request.
*/
deioctl(ifp, cmd, data)
register struct ifnet *ifp;
int cmd;
caddr_t data;
{
register struct de_softc *ds = &de_softc[ifp->if_unit];
register struct dedevice *addr = ds->ds_hwaddr;
struct protosw *pr;
struct sockaddr *sa;
struct ifreq *ifr = (struct ifreq *)data;
struct ifdevea *ifd = (struct ifdevea *)data;
register struct ifaddr *ifa = (struct ifaddr *)data;
int s = splimp(), error = 0;
int csr0;
switch (cmd) {
/*LSC001*/
case SIOCENABLBACK:
derr("internal loopback enable requested", ifp->if_unit);
if ( (error = deloopback(ifp, ds, addr, 1)) != NULL )
break;
ifp->if_flags |= IFF_LOOPBACK;
break;
case SIOCDISABLBACK:
derr("internal loopback disable requested", ifp->if_unit);
if ( (error = deloopback(ifp, ds, addr, 0)) != NULL )
break;
ifp->if_flags &= ~IFF_LOOPBACK;
deinit(ifp->if_unit);
break;
case SIOCRPHYSADDR:
/*
* read default hardware address.
*/
ds->ds_pcbb.pcbb0 = FC_RDDEFAULT;
addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
while ((addr->pcsr0 & PCSR0_DNI) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if (csr0 & PCSR0_PCEI) {
derr_csr("read default hardware address failed",
ifp->if_unit, csr0, addr->pcsr1);
error = EIO;
break;
}
/*
* copy current physical address and default hardware address
* for requestor.
*/
bcopy(&ds->ds_pcbb.pcbb2, ifd->default_pa, 6);
bcopy(ds->ds_addr, ifd->current_pa, 6);
break;
case SIOCSPHYSADDR:
/* Set the DNA address as the de's physical address */
ds->ds_pcbb.pcbb0 = FC_WTPHYAD;
bcopy (ifr->ifr_addr.sa_data, &ds->ds_pcbb.pcbb2, 6);
addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
while ((addr->pcsr0 & PCSR0_DNI) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if (csr0 & PCSR0_PCEI)
derr_csr("wtphyad failed", ifp->if_unit, csr0,
addr->pcsr1);
bcopy((caddr_t)&ds->ds_pcbb.pcbb2,(caddr_t)ds->ds_addr,
sizeof (ds->ds_addr));
deinit(ifp->if_unit);
break;
#ifdef DOMULTI
case SIOCDELMULTI:
case SIOCADDMULTI:
{
int i,j = -1,incaddr = ds->ds_ubaddr + MULTI_OFFSET;
if (cmd==SIOCDELMULTI) {
for (i = 0; i < NMULTI; i++)
if (bcmp(&ds->ds_multicast[i],ifr->ifr_addr.sa_data,MULTISIZE) == 0) {
if (--ds->ds_muse[i] == 0)
bcopy(unused_multi,&ds->ds_multicast[i],MULTISIZE);
}
} else {
for (i = 0; i < NMULTI; i++) {
if (bcmp(&ds->ds_multicast[i],ifr->ifr_addr.sa_data,MULTISIZE) == 0) {
ds->ds_muse[i]++;
goto done;
}
if (bcmp(&ds->ds_multicast[i],unused_multi,MULTISIZE) == 0)
j = i;
}
if (j == -1) {
derr("mtmulti failed, multicast list full: %d",
ui->ui_unit, NMULTI);
error = ENOBUFS;
goto done;
}
bcopy(ifr->ifr_addr.sa_data, &ds->ds_multicast[j], MULTISIZE);
ds->ds_muse[j]++;
}
ds->ds_pcbb.pcbb0 = FC_WTMULTI;
ds->ds_pcbb.pcbb2 = incaddr & 0xffff;
ds->ds_pcbb.pcbb4 = (NMULTI << 8) | ((incaddr >> 16) & 03);
addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
while ((addr->pcsr0 & PCSR0_DNI) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if (csr0 & PCSR0_PCEI)
derr_csr("wtmulti failed", ui->ui_unit, csr0,
addr->pcsr1);
break;
}
#endif DOMULTI
case SIOCRDCTRS:
case SIOCRDZCTRS:
{
register struct ctrreq *ctr = (struct ctrreq *)data;
ds->ds_pcbb.pcbb0 = cmd == SIOCRDCTRS ? FC_RDCNTS : FC_RCCNTS;
ds->ds_pcbb.pcbb2 = lobuba(ds->ds_ubaddr) + COUNTER_OFFSET;
ds->ds_pcbb.pcbb4 = hibuba(ds->ds_ubaddr);
ds->ds_pcbb.pcbb6 = sizeof(struct de_counters);
addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
while ((addr->pcsr0 & PCSR0_DNI) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if (csr0 & PCSR0_PCEI) {
derr_csr("rdcnts failed", ifp->if_unit, csr0,
addr->pcsr1);
error = ENOBUFS;
break;
}
bzero(&ctr->ctr_ctrs, sizeof(struct estat));
ctr->ctr_type = CTR_ETHER;
ctr->ctr_ether.est_seconds = (time - ds->ds_ztime) > 0xfffe ? 0xffff : (time - ds->ds_ztime);
ctr->ctr_ether.est_bytercvd = *(int *)ds->ds_counters.c_brcvd;
ctr->ctr_ether.est_bytesent = *(int *)ds->ds_counters.c_bsent;
ctr->ctr_ether.est_mbytercvd = *(int *)ds->ds_counters.c_mbrcvd;
ctr->ctr_ether.est_blokrcvd = *(int *)ds->ds_counters.c_prcvd;
ctr->ctr_ether.est_bloksent = *(int *)ds->ds_counters.c_psent;
ctr->ctr_ether.est_mblokrcvd = *(int *)ds->ds_counters.c_mprcvd;
ctr->ctr_ether.est_deferred = *(int *)ds->ds_counters.c_defer;
ctr->ctr_ether.est_single = *(int *)ds->ds_counters.c_single;
ctr->ctr_ether.est_multiple = *(int *)ds->ds_counters.c_multiple;
ctr->ctr_ether.est_sendfail = ds->ds_counters.c_snderr;
ctr->ctr_ether.est_sendfail_bm = ds->ds_counters.c_sbm & 0xff;
ctr->ctr_ether.est_collis = ds->ds_counters.c_collis;
ctr->ctr_ether.est_recvfail = ds->ds_counters.c_rcverr;
ctr->ctr_ether.est_recvfail_bm = ds->ds_counters.c_rbm & 0xff;
ctr->ctr_ether.est_unrecog = ds->ds_unrecog;
ctr->ctr_ether.est_sysbuf = ds->ds_counters.c_ibuferr;
ctr->ctr_ether.est_userbuf = ds->ds_counters.c_lbuferr;
if (cmd == SIOCRDZCTRS) {
ds->ds_ztime = time;
ds->ds_unrecog = 0;
}
break;
}
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
deinit(ifp->if_unit);
switch (ifa->ifa_addr.sa_family) {
#ifdef INET
case AF_INET:
((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 *)
(de_softc[ifp->if_unit].ds_addr);
} else {
de_setaddr(ina->x_host.c_host,ifp->if_unit);
}
break;
}
#endif
default:
if (pr=iffamily_to_proto(ifa->ifa_addr.sa_family)) {
error = (*pr->pr_ifioctl)(ifp, cmd, data);
}
break;
}
break;
default:
#ifdef should_this_be_here
if (pr=iffamily_to_proto(ifa->ifa_addr.sa_family))
error = (*pr->pr_ifioctl)(ifp, cmd, data);
else
#endif
error = EINVAL;
}
done: splx(s);
return (error);
}
/*
* enable or disable internal loopback LSC001
*/
static
deloopback(ifp, ds, addr, lb_ctl )
register struct ifnet *ifp;
register struct de_softc *ds;
register struct dedevice *addr;
u_char lb_ctl;
{
int csr0;
/*
* read current mode register.
*/
ds->ds_pcbb.pcbb0 = FC_RDMODE;
addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
while ((addr->pcsr0 & PCSR0_DNI) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if (csr0 & PCSR0_PCEI) {
derr_csr("read mode register failed",
ifp->if_unit, csr0, addr->pcsr1);
return(EIO);
}
/*
* set or clear the loopback bit as a function of lb_ctl and
* return mode register to driver.
*/
if ( lb_ctl == 1 ) {
ds->ds_pcbb.pcbb2 |= MOD_LOOP;
if (ds->ds_devid == DELUA)
ds->ds_pcbb.pcbb2 |= MOD_INTL;
else
ds->ds_pcbb.pcbb2 &= ~MOD_HDX;
} else {
ds->ds_pcbb.pcbb2 &= ~MOD_LOOP;
if (ds->ds_devid == DELUA)
ds->ds_pcbb.pcbb2 &= ~MOD_INTL;
else
ds->ds_pcbb.pcbb2 |= MOD_HDX;
}
ds->ds_pcbb.pcbb0 = FC_WTMODE;
addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0);
while ((addr->pcsr0 & PCSR0_DNI) == 0)
;
csr0 = addr->pcsr0;
addr->pchigh = csr0 >> 8;
if (csr0 & PCSR0_PCEI) {
derr_csr("write mode register failed",
ifp->if_unit, csr0, addr->pcsr1);
return(EIO);
}
return(NULL);
}
static
derr_csr(msg, unit, csr0, csr1)
char *msg;
{
derr("%s, csr0=%o csr1=%o", unit, msg, csr0, csr1);
}
static
derr(fmt, unit, a1, a2, a3, a4, a5, a6)
{
printf("de%d: ", unit);
printf(fmt, a1, a2, a3, a4, a5, a6);
putchar('\n');
}