/* * SCCSID: @(#)if_de.c 1.1 (2.11BSD GTE) 12/31/93 * 2.11BSD - Remove dereset since 1) it was never called, and 2) * wouldn't work if it were called. Also uballoc and * ubmalloc calling convention changed. - sms */ #include "de.h" #if NDE > 0 /* * DEC DEUNA interface * * Lou Salkind * New York University * * TODO: * timeout routine (get statistics) */ #include "param.h" #include "../machine/seg.h" #include "systm.h" #include "mbuf.h" #include "domain.h" #include "protosw.h" #include "ioctl.h" #include "errno.h" #include "time.h" /* * Define DE_DO_BCTRS to get the DEUNA/DELUA to add code to allow * ioctl() for clearing/getting I/O stats from the board. #define DE_DO_BCTRS */ /* * Define DE_DO_PHYSADDR to get the DEUNA/DELUA to add code to allow * ioctl() for setting/getting physical hardware address. #define DE_DO_PHYSADDR */ /* * Define DE_DO_MULTI to get the DEUNA/DELUA to handle multi-cast addresses #define DE_DO_MULTI */ /* * Define DE_INT_LOOPBACK to get the DEUNA/DELUA internal loopback code turned on #define DE_INT_LOOPBACK */ /* * Define DE_DEBUG to get the DEUNA/DELUA debug code turned on #define DE_DEBUG */ #include "../pdpif/if_de.h" #ifndef YES #define YES 1 #else /* YES */ #undef YES #define YES 1 #endif /* YES */ #ifndef NO #define NO 0 #else /* NO */ #undef NO #define NO 0 #endif /* NO */ #define MAPBUFDESC (((btoc(ETHERMTU+sizeof(struct ether_header)) - 1) << 8 ) | RW) #define CRC_LEN 4 /* Length of CRC added to packet by board */ struct de_softc de_softc[NDE]; #ifdef DE_DEBUG /* * Setting dedebug turns on the level of debugging, iff DE_DEBUG * was defined at compile time. The current levels are: * 1 - added informative messages and error messages * 2 - more added messages * 3 - still more messages, like one for every loop packet seen, etc. */ #define DE_DEBUG_LEVEL 1 int dedebug = DE_DEBUG_LEVEL; #endif /* DE_DEBUG */ #ifdef DE_DO_MULTI u_char unused_multi[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; #endif /* DE_DO_MULTI */ #ifdef not_on_pdp extern struct protosw *iftype_to_proto(), *iffamily_to_proto(); #endif /* not_on_pdp */ int deprobe(), deattach(), deintr(), deinit(), deoutput(), deioctl(); struct mbuf *deget(); u_short destd[] = { 0174510 }; struct uba_device *deinfo[NDE]; struct uba_driver dedriver = { deprobe, 0, deattach, 0, destd, "de", deinfo }; #ifdef DE_INT_LOOPBACK #define DELUA_LOOP_LEN (32 - sizeof(struct ether_header)) #endif /* DE_INT_LOOPBACK */ #ifdef DE_DO_BCTRS extern struct timeval time; struct timeval atv; #endif /* DE_DO_BCTRS */ deprobe(reg) caddr_t reg; { #ifdef not_on_pdp register int br, cvec; /* r11, r10 value-result */ register struct dedevice *addr = (struct dedevice *)reg; register i; #ifdef lint br = 0; cvec = br; br = cvec; i = 0; derint(i); deintr(i); #endif /* lint */ /* * Make sure self-test is finished * Self-test on a DELUA can take 15 seconds. */ for (i = 0; i < 160 && (addr->pcsr0 & PCSR0_FATI) == 0 && (addr->pcsr1 & PCSR1_STMASK) == STAT_RESET; ++i) DELAY(100000); if ((addr->pcsr0 & PCSR0_FATI) != 0 || (addr->pcsr1 & PCSR1_STMASK) != STAT_READY) return(0); addr->pcsr0 = 0; addr->pcsr0 = PCSR0_RSET; /* * just in case this is not a deuna or delua * dont wait for more than 30 secs */ for (i = 0; i < 300 && (addr->pcsr0 & PCSR0_INTR) == 0; i++) DELAY(100000); if ((addr->pcsr0 & PCSR0_INTR) == 0) return(0); /* make board interrupt by executing a GETPCBB command */ addr->pcsr0 = PCSR0_INTE; addr->pcsr2 = 0; addr->pcsr3 = 0; addr->pcsr0 = PCSR0_INTE|CMD_GETPCBB; DELAY(100000); #endif /* not_on_pdp */ return(1); } /* * 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(ui) struct uba_device *ui; { register struct de_softc *ds = &de_softc[ui->ui_unit]; register struct ifnet *ifp = &ds->ds_if; register struct dedevice *addr = (struct dedevice *)ui->ui_addr; int i; u_short csr1; /* * Is it a DEUNA or a DELULA? Save the device id. */ csr1 = addr->pcsr1; ds->ds_devid = (csr1 & PCSR1_DEVID) >> 4; #ifdef DE_DEBUG if (dedebug >= 1) printf("de%d: Device Type: %s\n", ui->ui_unit, (ds->ds_devid == DEUNA) ? "DEUNA" : "DELUA"); #endif /* DE_DEBUG */ /* * Board Status Check */ if (csr1 & 0xff80) { if (ds->ds_devid == DEUNA) printf("de%d: hardware error, pcsr1=%b\n", ui->ui_unit, csr1, PCSR1_BITS); else printf("de%d: hardware error, pcsr1=%b\n", ui->ui_unit, csr1, PCSR1_BITS_DELUA); } ifp->if_unit = ui->ui_unit; ifp->if_name = "de"; ifp->if_mtu = ETHERMTU; ifp->if_flags |= IFF_BROADCAST; #ifdef DE_DEBUG ifp->if_flags |= IFF_DEBUG; printf("de%d: DEBUG enabled level=%d\n",ifp->if_unit,dedebug); #endif /* DE_DEBUG */ #ifdef DE_DO_MULTI /* * 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,(u_char *)&ds->ds_multicast[i],MULTISIZE); #endif /* DE_DO_MULTI */ /* * Reset the board and map the pcbb buffer onto the Unibus. */ addr->pcsr0 = PCSR0_RSET; (void) dewait(ui, "board reset", YES, NO); ds->ds_ubaddr = uballoc(INCORE_BASE(ds), INCORE_SIZE); addr->pcsr2 = ds->ds_ubaddr & 0xffff; addr->pcsr3 = (ds->ds_ubaddr >> 16) & 0x3; addr->pclow = CMD_GETPCBB; (void) dewait(ui, "get pcbb", YES, NO); ds->ds_pcbb.pcbb0 = FC_RDPHYAD; addr->pclow = CMD_GETCMD; (void) dewait(ui, "read addr", YES, NO); bcopy((caddr_t)&ds->ds_pcbb.pcbb2,(caddr_t)ds->ds_addr, sizeof (ds->ds_addr)); #ifdef DE_DEBUG if (dedebug >= 1) printf("de%d: hardware address %s\n", ui->ui_unit, ether_sprintf(ds->ds_addr)); #endif /* DE_DEBUG */ ifp->if_init = deinit; ifp->if_output = deoutput; ifp->if_ioctl = deioctl; ifp->if_reset = 0; ds->ds_deuba.difu_flags = UBA_CANTWAIT; if_attach(ifp); } /* * 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 uba_device *ui = deinfo[unit]; register struct dedevice *addr; register struct ifrw *ifrw; struct ifnet *ifp = &ds->ds_if; int s; struct de_ring *rp; ubadr_t incaddr; /* not yet, if address still unknown */ /* DECnet must set this somewhere to make device happy */ if (ifp->if_addrlist == (struct ifaddr *)0) return; if (ds->ds_flags & DSF_RUNNING) return; if ((ifp->if_flags & IFF_RUNNING) == 0) { /* First time */ /* * Only allocate the resources ONCE. * ~IFF_RUNNING && ~DSF_RUNNING */ if (de_ubainit(&ds->ds_deuba, ui->ui_ubanum, sizeof (struct ether_header), (int) btoc(ETHERMTU)) == 0) { printf("de%d: can't initialize\n", unit); ds->ds_if.if_flags &= ~IFF_UP; return; } } addr = (struct dedevice *)ui->ui_addr; /* * Set up the PCBB - this is done in deattach() also, but * has to be redone here in case the board is reset (PCSR0_RSET) * and this routine is called. Note that ds->ds_ubaddr is set * in deattach() and all we do here is tell the * DEUNA/DELUA where it can find its PCBB. */ addr->pcsr2 = ds->ds_ubaddr & 0xffff; addr->pcsr3 = (ds->ds_ubaddr >> 16) & 0x3; addr->pclow = CMD_GETPCBB; (void) dewait(ui, "get pcbb", YES, NO); /* * Set the transmit and receive ring header addresses */ incaddr = ds->ds_ubaddr + UDBBUF_OFFSET; ds->ds_pcbb.pcbb0 = FC_WTRING; ds->ds_pcbb.pcbb2 = incaddr & 0xffff; ds->ds_pcbb.pcbb4 = (incaddr >> 16) & 0x3; incaddr = ds->ds_ubaddr + XRENT_OFFSET; ds->ds_udbbuf.b_tdrbl = incaddr & 0xffff; ds->ds_udbbuf.b_tdrbh = (incaddr >> 16) & 0x3; ds->ds_udbbuf.b_telen = sizeof (struct de_ring) / sizeof (short); ds->ds_udbbuf.b_trlen = NXMT; incaddr = ds->ds_ubaddr + RRENT_OFFSET; ds->ds_udbbuf.b_rdrbl = incaddr & 0xffff; ds->ds_udbbuf.b_rdrbh = (incaddr >> 16) & 0x3; ds->ds_udbbuf.b_relen = sizeof (struct de_ring) / sizeof (short); ds->ds_udbbuf.b_rrlen = NRCV; addr->pclow = CMD_GETCMD; (void) dewait(ui, "wtring", NO, NO); /* * Initialize the board's mode */ 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; (void) dewait(ui, "wtmode", NO, NO); /* * Set up the receive and transmit ring entries */ ifrw = &ds->ds_deuba.difu_w[0]; for (rp = &ds->ds_xrent[0]; rp < &ds->ds_xrent[NXMT]; rp++) { rp->r_segbl = ifrw->ifrw_info & 0xffff; rp->r_segbh = (ifrw->ifrw_info >> 16) & 0x3; rp->r_flags = 0; ifrw++; } ifrw = &ds->ds_deuba.difu_r[0]; for (rp = &ds->ds_rrent[0]; rp < &ds->ds_rrent[NRCV]; rp++) { rp->r_slen = sizeof (struct de_buf); rp->r_segbl = ifrw->ifrw_info & 0xffff; rp->r_segbh = (ifrw->ifrw_info >> 16) & 0x3; rp->r_flags = RFLG_OWN; /* hang receive */ ifrw++; } /* * Start up the board (rah rah) */ s = splimp(); ds->ds_nxmit = ds->ds_rindex = ds->ds_xindex = ds->ds_xfree = 0; ds->ds_if.if_flags |= IFF_RUNNING; destart(unit); /* queue output packets */ addr->pclow = PCSR0_INTE; /* avoid interlock */ ds->ds_flags |= DSF_RUNNING; #ifdef NS if (ds->ds_flags & DSF_SETADDR) de_setaddr(ds->ds_addr, unit); #endif /* NS */ addr->pclow = CMD_START | PCSR0_INTE; splx(s); #ifdef DE_DO_BCTRS cpfromkern(&time, &atv, sizeof(struct timeval)); ds->ds_ztime = atv.tv_sec; #endif /* DE_DO_BCTRS */ } /* * 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 uba_device *ui = deinfo[unit]; struct dedevice *addr = (struct dedevice *)ui->ui_addr; register struct de_softc *ds = &de_softc[unit]; register struct de_ring *rp; struct mbuf *m; register int nxmit; if (ds->ds_flags & DSF_LOCK) return; 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) { /* * poke device if we have something to send and * transmit ring is full. */ #ifdef DE_DEBUG if (dedebug >= 1) { rp = &ds->ds_xrent[0]; printf("de%d: did not transmit: %d, %d, %d, flag0=%x, flag1=%x\n", unit, ds->ds_xindex, ds->ds_nxmit, ds->ds_xfree, rp->r_flags, (rp+1)->r_flags); } #endif /* DE_DEBUG */ if (ds->ds_flags & DSF_RUNNING) addr->pclow = PCSR0_INTE|CMD_PDMD; } } /* * Command done interrupt. */ deintr(unit) int unit; { struct uba_device *ui = deinfo[unit]; register struct dedevice *addr = (struct dedevice *)ui->ui_addr; register struct de_softc *ds = &de_softc[unit]; register struct de_ring *rp; register struct ifrw *ifrw; short csr0; /* save flags right away - clear out interrupt bits */ csr0 = addr->pcsr0; addr->pchigh = csr0 >> 8; if (csr0 & PCSR0_FATI) { if (ds->ds_devid == DEUNA) printf( "de%d: unsolicited state change, csr0=%b, csr1=%b\n", unit, csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS); else printf( "de%d: unsolicited state change, csr0=%b, csr1=%b\n", unit, csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS_DELUA); } if (csr0 & PCSR0_SERI) { if (ds->ds_devid == DEUNA) printf("de%d: status error, csr0=%b, csr1=%b\n", unit, csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS); else printf("de%d: status error, csr0=%b, csr1=%b\n", unit, csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS_DELUA); } ds->ds_flags |= DSF_LOCK; /* * 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(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++; ifrw = &ds->ds_deuba.difu_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 >= 1) printf("de%d: oerror, flags=%b tdrerr=%b (len=%d)\n", unit, rp->r_flags, XFLG_BITS, rp->r_tdrerr, XERR_BITS, 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++; #ifdef DE_DEBUG if (dedebug >= 3) printf("de%d: loopback packet\n", unit); #endif /* DE_DEBUG */ deread(ds, ifrw, 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; } ds->ds_flags &= ~DSF_LOCK; destart(unit); if (csr0 & PCSR0_RCBI) { ds->ds_if.if_ierrors++; #ifdef DE_DEBUG if (dedebug >= 1) printf("de%d: buffer unavailable\n", ui->ui_unit); #endif /* DE_DEBUG */ addr->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(unit) int unit; { register struct de_softc *ds = &de_softc[unit]; register struct de_ring *rp; register int len; struct ether_header *eh; rp = &ds->ds_rrent[ds->ds_rindex]; while ((rp->r_flags & RFLG_OWN) == 0) { ds->ds_if.if_ipackets++; len = (rp->r_lenerr&RERR_MLEN) - sizeof (struct ether_header) - CRC_LEN; /* 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_BUFL|RERR_UBTO|RERR_NCHN)) || len < ETHERMIN || len > ETHERMTU) { ds->ds_if.if_ierrors++; #ifdef DE_DEBUG if (dedebug >= 1) printf("de%d: ierror, flags=%b lenerr=%b (len=%d)\n", unit, rp->r_flags, RFLG_BITS, rp->r_lenerr, RERR_BITS, len); #endif /* DE_DEBUG */ } else deread(ds, &ds->ds_deuba.difu_r[ds->ds_rindex], len); } else { int ret; segm sav5; saveseg5(sav5); mapseg5(ds->ds_deuba.difu_r[ds->ds_rindex].ifrw_click, MAPBUFDESC); eh = (struct ether_header *) SEG5; ret = bcmp(eh->ether_dhost, ds->ds_addr, 6); restorseg5(sav5); if (ret == NULL) deread(ds, &ds->ds_deuba.difu_r[ds->ds_rindex], len); } /* hang the receive buffer again */ rp->r_lenerr = 0; rp->r_flags = RFLG_OWN; /* check next receive buffer */ ds->ds_rindex++; if (ds->ds_rindex >= NRCV) ds->ds_rindex = 0; rp = &ds->ds_rrent[ds->ds_rindex]; } } /* * 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, s; struct ifqueue *inq; segm sav5; 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(sav5); mapseg5(ifrw->ifrw_click, MAPBUFDESC); eh = (struct ether_header *) SEG5; type = eh->ether_type = ntohs((u_short)eh->ether_type); #define dedataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) if (type >= ETHERTYPE_TRAIL && type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { off = (type - ETHERTYPE_TRAIL) * 512; if (off >= ETHERMTU) { restorseg5(sav5); return; /* sanity */ } type = ntohs(*dedataaddr(eh, off, u_short *)); resid = ntohs(*(dedataaddr(eh, off+2, u_short *))); if (off + resid > len) { restorseg5(sav5); return; /* sanity */ } len = off + resid; } else off = 0; if (len == 0) { restorseg5(sav5); return; } restorseg5(sav5); /* * 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, &ds->ds_if); if (m == 0) return; 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; } switch (type) { #ifdef INET case ETHERTYPE_IP: schednetisr(NETISR_IP); inq = &ipintrq; break; case ETHERTYPE_ARP: arpinput(&ds->ds_ac, m); return; #endif /* INET */ default: #ifdef not_on_pdp /* * see if other protocol families defined * and call protocol specific routines. * If no other protocols defined then dump message. */ if (pr=iftype_to_proto(type)) { if ((m = (struct mbuf *)(*pr->pr_ifinput)(m, &ds->ds_if, &inq)) == 0) return; } else { #endif /* not_on_pdp */ #ifdef DE_DEBUG if(dedebug >= 1) printf("de%d: Unknow Packet Type 0%o(%d)\n", ds->ds_if.if_unit, type, type); #endif /* DE_DEBUG */ #ifdef DE_DO_BCTRS if (ds->ds_unrecog != 0xffff) ds->ds_unrecog++; #endif /* DE_DO_BCTRS */ m_freem(m); return; #ifdef not_on_pdp } #endif /* not_on_pdp */ } s = splimp(); if (IF_QFULL(inq)) { IF_DROP(inq); splx(s); #ifdef DE_DEBUG if (dedebug >= 1) printf("de%d: Packet Dropped(deread)\n", ds->ds_if.if_unit); #endif /* DE_DEBUG */ m_freem(m); return; } IF_ENQUEUE(inq, m); splx(s); } /* * 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; int usetrailers; 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(&ds->ds_ac, m, &idst, edst, &usetrailers)) return (0); /* if not yet resolved */ 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); *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); goto gottrailertype; } type = ETHERTYPE_IP; off = 0; goto gottype; #endif /* INET */ #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 /* NS */ 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: #ifdef DE_INT_LOOPBACK /* * 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); #endif /* DE_INT_LOOPBACK */ #ifdef not_on_pdp /* * 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 { #endif /* not_on_pdp */ printf("de%d: can't handle af%d", ifp->if_unit, dst->sa_family); error = EAFNOSUPPORT; goto bad; #ifdef not_on_pdp } #endif /* not_on_pdp */ } 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 */ bcopy((caddr_t)ds->ds_addr, (caddr_t)eh->ether_shost, sizeof(ds->ds_addr)); /* * 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); #ifdef DE_DEBUG if (dedebug >= 1) printf("de%d: Packet Dropped(deoutput)\n", ifp->if_unit); #endif /* DE_DEBUG */ m_freem(m); return (ENOBUFS); } IF_ENQUEUE(&ifp->if_snd, m); destart(ifp->if_unit); splx(s); return (0); bad: 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, uban, hlen, nmr) register struct deuba *ifu; int uban, hlen, nmr; { register caddr_t cp, dp; register struct ifrw *ifrw; int i, ncl; /* * If the ring already has core allocated, quit now. */ if (ifu->difu_r[0].ifrw_click) return(1); nmr = ctob(nmr); nmr += hlen; for (i = 0; i < NRCV; i++) { ifu->difu_r[i].ifrw_click = m_ioget(nmr); if (ifu->difu_r[i].ifrw_click == 0) { ifu->difu_r[0].ifrw_click = 0; if (i) printf("de: lost some space\n"); return(0); } } for (i = 0; i < NXMT; i++) { ifu->difu_w[i].ifrw_click = m_ioget(nmr); if (ifu->difu_w[i].ifrw_click == 0) { ifu->difu_w[0].ifrw_click = 0; ifu->difu_r[0].ifrw_click = 0; if (i) printf("de: lost some space\n"); return(0); } } for (i = 0; i < NRCV; i++) ifu->difu_r[i].ifrw_info = ubmalloc(ifu->difu_r[i].ifrw_click); for (i = 0; i < NXMT; i++) ifu->difu_w[i].ifrw_info = ubmalloc(ifu->difu_w[i].ifrw_click); ifu->ifu_hlen = hlen; 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, ifp) register struct deuba *ifu; register struct ifrw *ifrw; int totlen, off0; struct ifnet *ifp; { struct mbuf *top, **mp, *m; int off = off0, len; register caddr_t cp = (caddr_t) ifu->ifu_hlen; u_int click; top = 0; mp = ⊤ click = ifrw->ifrw_click; while (totlen > 0) { MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) goto bad; if (off) { len = totlen - off; cp = (caddr_t)(ifu->ifu_hlen + off); } else len = totlen; m->m_off = MMINOFF; if (ifp) { /* * Leave Room for the ifp */ m->m_len = MIN(MLEN - sizeof(ifp), len); m->m_off += sizeof(ifp); } else m->m_len = MIN(MLEN, len); mbcopyin(click, cp, mtod(m, char *), (u_int) 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 = (caddr_t) ifu->ifu_hlen; off = 0; totlen = off0; } } else totlen -= m->m_len; if (ifp) { /* * Prepend interface pointer to first mbuf */ m->m_len += sizeof(ifp); m->m_off -= sizeof(ifp); *(mtod(m, struct ifnet **)) = ifp; ifp = (struct ifnet *) 0; } } 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 u_short off, click; click = ifu->difu_w[n].ifrw_click; off = 0; while (m) { mbcopyout(mtod(m, char *), click, off, (u_int) m->m_len); off += m->m_len; MFREE(m, mp); m = mp; } return (off); } /* * 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 uba_device *ui = deinfo[ifp->if_unit]; register struct dedevice *addr = (struct dedevice *)ui->ui_addr; 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; switch (cmd) { #ifdef DE_INT_LOOPBACK case SIOCENABLBACK: printf("de%d: internal loopback enable requested\n", ifp->if_unit); if ( (error = deloopback(ifp, ds, addr, YES)) != NULL ) break; ifp->if_flags |= IFF_LOOPBACK; break; case SIOCDISABLBACK: printf("de%d: internal loopback disable requested\n", ifp->if_unit); if ( (error = deloopback(ifp, ds, addr, NO)) != NULL ) break; ifp->if_flags &= ~IFF_LOOPBACK; deinit(ifp->if_unit); break; #endif /* DE_INT_LOOPBACK */ #ifdef DE_DO_PHYSADDR case SIOCRPHYSADDR: /* * read default hardware address. */ ds->ds_pcbb.pcbb0 = FC_RDDEFAULT; addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0); if (dewait(ui, "read default hardware address", NO, YES)) { 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); (void) dewait(ui, "write physical address", NO, YES); bcopy((caddr_t)&ds->ds_pcbb.pcbb2,(caddr_t)ds->ds_addr, sizeof (ds->ds_addr)); deinit(ifp->if_unit); break; #endif /* DE_DO_PHYSADDR */ #ifdef DE_DO_MULTI 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) { printf("de%d: mtmulti failed, multicast list full: %d\n", 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); (void) dewait(ui, "set multicast address", NO, YES); break; } #endif /* DE_DO_MULTI */ #ifdef DE_DO_BCTRS case SIOCRDCTRS: case SIOCRDZCTRS: { register struct ctrreq *ctr = (struct ctrreq *)data; ubadr_t incaddr; ds->ds_pcbb.pcbb0 = cmd == SIOCRDCTRS ? FC_RDCNTS : FC_RCCNTS; incaddr = ds->ds_ubaddr + COUNTER_OFFSET; ds->ds_pcbb.pcbb2 = incaddr & 0xffff; ds->ds_pcbb.pcbb4 = (incaddr >> 16) & 0x3; ds->ds_pcbb.pcbb6 = sizeof(struct de_counters) / sizeof (short); addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0); if (dewait(ui, "read counts", NO, YES)) { error = ENOBUFS; break; } bzero(&ctr->ctr_ether, sizeof(struct estat)); ctr->ctr_type = CTR_ETHER; cpfromkern(&time, &atv, sizeof(struct timeval)); ctr->ctr_ether.est_seconds = (atv.tv_sec - ds->ds_ztime) > 0xfffe ? 0xffff : (atv.tv_sec - ds->ds_ztime); ctr->ctr_ether.est_byrcvd = *(long *)ds->ds_counters.c_brcvd; ctr->ctr_ether.est_bysent = *(long *)ds->ds_counters.c_bsent; ctr->ctr_ether.est_mbyrcvd = *(long *)ds->ds_counters.c_mbrcvd; ctr->ctr_ether.est_blrcvd = *(long *)ds->ds_counters.c_prcvd; ctr->ctr_ether.est_blsent = *(long *)ds->ds_counters.c_psent; ctr->ctr_ether.est_mblrcvd = *(long *)ds->ds_counters.c_mprcvd; ctr->ctr_ether.est_deferred = *(long *)ds->ds_counters.c_defer; ctr->ctr_ether.est_single = *(long *)ds->ds_counters.c_single; ctr->ctr_ether.est_multiple = *(long *)ds->ds_counters.c_multiple; ctr->ctr_ether.est_sf = ds->ds_counters.c_snderr; ctr->ctr_ether.est_sfbm = ds->ds_counters.c_sbm & 0xff; ctr->ctr_ether.est_collis = ds->ds_counters.c_collis; ctr->ctr_ether.est_rf = ds->ds_counters.c_rcverr; ctr->ctr_ether.est_rfbm = 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) { cpfromkern(&time, &atv, sizeof(struct timeval)); ds->ds_ztime = atv.tv_sec; ds->ds_unrecog = 0; } break; } #endif /* DE_DO_BCTRS */ 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 /* INET */ #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 /* NS */ default: #ifdef not_on_pdp if (pr=iffamily_to_proto(ifa->ifa_addr.sa_family)) { error = (*pr->pr_ifioctl)(ifp, cmd, data); } #endif /* not_on_pdp */ #ifdef DE_DEBUG if (dedebug >= 1) printf("de%d: SIOCSIFADDR Unknown address family\n", ifp->if_unit); #endif /* DE_DEBUG */ error = EAFNOSUPPORT; break; } break; case SIOCSIFFLAGS: if ((ifp->if_flags & IFF_UP) == 0 && ds->ds_flags & DSF_RUNNING) { ((struct dedevice *) (deinfo[ifp->if_unit]->ui_addr))->pclow = PCSR0_RSET; (void) dewait(deinfo[ifp->if_unit], "board reset(deioctl)", YES, NO); ds->ds_flags &= ~(DSF_LOCK | DSF_RUNNING); #ifdef DE_DEBUG if (dedebug >= 1) printf("de%d: reset and marked down\n",ifp->if_unit); #endif /* DE_DEBUG */ } else if (ifp->if_flags & IFF_UP && (ds->ds_flags & DSF_RUNNING) == 0) { deinit(ifp->if_unit); #ifdef DE_DEBUG if (dedebug >= 1) printf("de%d: reinitialized and marked up\n",ifp->if_unit); #endif /* DE_DEBUG */ } #ifdef DE_DEBUG if ((ifp->if_flags & IFF_DEBUG) == 0) { if (dedebug != 0) { dedebug = 0; printf("de%d: DEBUG disabled\n",ifp->if_unit); } } else { if (dedebug == 0) { dedebug = DE_DEBUG_LEVEL; printf("de%d: DEBUG enabled level=%d\n",ifp->if_unit,dedebug); } } #endif /* DE_DEBUG */ break; default: #ifdef not_on_pdp if (pr=iffamily_to_proto(ifa->ifa_addr.sa_family)) error = (*pr->pr_ifioctl)(ifp, cmd, data); else #endif /* not_on_pdp */ error = EINVAL; } done: splx(s); return (error); } #ifdef DE_INT_LOOPBACK /* * enable or disable internal loopback */ static deloopback(ifp, ds, addr, lb_ctl ) register struct ifnet *ifp; register struct de_softc *ds; register struct dedevice *addr; u_char lb_ctl; { register struct uba_device *ui = deinfo[ifp->if_unit]; /* * read current mode register. */ ds->ds_pcbb.pcbb0 = FC_RDMODE; addr->pclow = CMD_GETCMD|((ds->ds_flags & DSF_RUNNING) ? PCSR0_INTE : 0); if (dewait(ui, "read mode register", NO, YES)) return(EIO); /* * set or clear the loopback bit as a function of lb_ctl and * return mode register to driver. */ if ( lb_ctl == YES ) { 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); if(dewait(ui, "write mode register", NO, YES)) return(EIO); return(NULL); } #endif /* DE_INT_LOOPBACK */ dewait(ui, fn, no_port, only_dni) register struct uba_device *ui; char *fn; int no_port; int only_dni; { register struct de_softc *ds = &de_softc[ui->ui_unit]; register struct dedevice *addr = (struct dedevice *)ui->ui_addr; register csr0; if (only_dni) while ((addr->pcsr0 & PCSR0_DNI) == 0) ; else while ((addr->pcsr0 & PCSR0_INTR) == 0) ; csr0 = addr->pcsr0; addr->pchigh = csr0 >> 8; if ((csr0 & PCSR0_PCEI) || (no_port && (csr0 & PCSR0_DNI)==0)) { if (ds->ds_devid == DEUNA) printf("de%d: %s failed, csr0=%b, csr1=%b\n", ui->ui_unit, fn, csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS); else printf("de%d: %s failed, csr0=%b, csr1=%b\n", ui->ui_unit, fn, csr0, PCSR0_BITS, addr->pcsr1, PCSR1_BITS_DELUA); } return(csr0 & PCSR0_PCEI); } #ifdef NS /* * Set the ethernet address for the unit, this sets the board's * hardware address to physaddr. This is only used if you have * NS defined. */ de_setaddr(physaddr, unit) u_char *physaddr; int unit; { register struct de_softc *ds = &de_softc[unit]; struct uba_device *ui = deinfo[unit]; register struct dedevice *addr = (struct dedevice *)ui->ui_addr; if (! (ds->ds_flags & DSF_RUNNING)) return; bcopy(physaddr, &ds->ds_pcbb.pcbb2, 6); ds->ds_pcbb.pcbb0 = FC_WTPHYAD; addr->pclow = PCSR0_INTE | CMD_GETCMD; if (dewait(ui, "address changed", NO, NO) == 0) { ds->ds_flags |= DSF_SETADDR; bcopy(physaddr, ds->ds_addr, 6); } } #endif /* NS */ #endif /* NDE > 0 */