1*29408Ssam /* if_ace.c 1.9 86/06/09 */ 224007Ssam 324007Ssam /* 424007Ssam * ACC VERSAbus Ethernet controller 524007Ssam */ 624007Ssam #include "ace.h" 724007Ssam #if NACE > 0 824007Ssam 924007Ssam #include "../machine/pte.h" 1024007Ssam 1125694Ssam #include "param.h" 1225694Ssam #include "systm.h" 1325694Ssam #include "mbuf.h" 1425694Ssam #include "buf.h" 1525694Ssam #include "protosw.h" 1625694Ssam #include "socket.h" 1725694Ssam #include "vmmac.h" 1825694Ssam #include "ioctl.h" 1925694Ssam #include "errno.h" 2025694Ssam #include "vmparam.h" 2125855Ssam #include "syslog.h" 2224007Ssam 2324007Ssam #include "../net/if.h" 2424007Ssam #include "../net/netisr.h" 2524007Ssam #include "../net/route.h" 2625855Ssam #ifdef INET 2724007Ssam #include "../netinet/in.h" 2824007Ssam #include "../netinet/in_systm.h" 2925694Ssam #include "../netinet/in_var.h" 3024007Ssam #include "../netinet/ip.h" 3124007Ssam #include "../netinet/ip_var.h" 3224007Ssam #include "../netinet/if_ether.h" 3325855Ssam #endif 3425855Ssam #ifdef NS 3525855Ssam #include "../netns/ns.h" 3625855Ssam #include "../netns/ns_if.h" 3725855Ssam #endif 3824007Ssam 3924007Ssam #include "../tahoe/mtpr.h" 4024007Ssam #include "../tahoeif/if_acereg.h" 4125694Ssam #include "../tahoevba/vbavar.h" 4224007Ssam 4324007Ssam int aceprobe(), aceattach(), acerint(), acecint(); 4424007Ssam struct vba_device *aceinfo[NACE]; 4525983Ssam long acestd[] = { 0 }; 4624007Ssam struct vba_driver acedriver = 4725927Ssam { aceprobe, 0, aceattach, 0, acestd, "ace", aceinfo, "v/eiu", 0 }; 4824007Ssam 4924007Ssam int aceinit(), aceoutput(), aceioctl(), acereset(); 5024007Ssam struct mbuf *aceget(); 5124007Ssam 5224007Ssam /* 5324007Ssam * Ethernet software status per interface. 5424007Ssam * 5524007Ssam * Each interface is referenced by a network interface structure, 5624007Ssam * is_if, which the routing code uses to locate the interface. 5724007Ssam * This structure contains the output queue for the interface, its address, ... 5824007Ssam */ 5924007Ssam struct ace_softc { 6024007Ssam struct arpcom is_ac; /* Ethernet common part */ 6124007Ssam #define is_if is_ac.ac_if /* network-visible interface */ 6224007Ssam #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ 6324007Ssam short is_flags; 6424007Ssam #define ACEF_OACTIVE 0x1 /* output is active */ 6524007Ssam #define ACEF_RCVPENDING 0x2 /* start rcv in acecint */ 6624007Ssam short is_promiscuous; /* true is enabled */ 6724007Ssam short is_segboundry; /* first TX Seg in dpm */ 6824007Ssam short is_eictr; /* Rx segment tracking ctr */ 6924007Ssam short is_eoctr; /* Tx segment tracking ctr */ 7024007Ssam short is_txnext; /* Next available Tx segment */ 7124007Ssam short is_currnd; /* current random backoff */ 7224007Ssam struct ace_stats is_stats; /* holds board statistics */ 7324007Ssam short is_xcnt; /* count xmitted segments to be acked 7424007Ssam by the controller */ 7525855Ssam long is_ivec; /* autoconfig interrupt vector base */ 7625927Ssam struct pte *is_map; /* pte map for dual ported memory */ 7725927Ssam caddr_t is_dpm; /* address of mapped memory */ 7824007Ssam } ace_softc[NACE]; 7924007Ssam extern struct ifnet loif; 8024007Ssam 8125855Ssam aceprobe(reg, vi) 8224007Ssam caddr_t reg; 8325855Ssam struct vba_device *vi; 8424007Ssam { 8525855Ssam register br, cvec; /* must be r12, r11 */ 8625855Ssam struct acedevice *ap = (struct acedevice *)reg; 8725855Ssam struct ace_softc *is = &ace_softc[vi->ui_unit]; 8824007Ssam 8924007Ssam #ifdef lint 9024007Ssam acerint(0); acecint(0); 9124007Ssam #endif 9224007Ssam if (badaddr(reg, 2)) 9325855Ssam return (0); 9425855Ssam movow(&ap->csr, CSR_RESET); 9524007Ssam DELAY(10000); 9625855Ssam #ifdef notdef 9725855Ssam /* 9825855Ssam * Select two spaces for the interrupts aligned to an 9925855Ssam * eight vector boundary and fitting in 8 bits (as 10025855Ssam * required by the controller) -- YECH. The controller 10125855Ssam * will be notified later at initialization time. 10225855Ssam */ 10325855Ssam if ((vi->ui_hd->vh_lastiv -= 2) > 0xff) 10425855Ssam vi->ui_hd->vh_lastiv = 0x200; 10525855Ssam is->is_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x7; 10625855Ssam #else 10725855Ssam is->is_ivec = 0x90+vi->ui_unit*8; 10825855Ssam #endif 10925855Ssam br = 0x14, cvec = is->is_ivec; /* XXX */ 11025855Ssam return (sizeof (*ap)); 11124007Ssam } 11224007Ssam 11324007Ssam /* 11424007Ssam * Interface exists: make available by filling in network interface 11524007Ssam * record. System will initialize the interface when it is ready 11624007Ssam * to accept packets. 11724007Ssam */ 11824007Ssam aceattach(ui) 11924007Ssam struct vba_device *ui; 12024007Ssam { 12124007Ssam register short unit = ui->ui_unit; 12224007Ssam register struct ace_softc *is = &ace_softc[unit]; 12324007Ssam register struct ifnet *ifp = &is->is_if; 12424007Ssam register struct acedevice *addr = (struct acedevice *)ui->ui_addr; 12524007Ssam register short *wp, i; 126*29408Ssam char *cp; 12724007Ssam 12824007Ssam ifp->if_unit = unit; 12924007Ssam ifp->if_name = "ace"; 13024007Ssam ifp->if_mtu = ETHERMTU; 13124007Ssam /* 132*29408Ssam * Get station's addresses and set multicast hash table. 13324007Ssam */ 134*29408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 135*29408Ssam is->is_addr[i] = ~*wp++; 136*29408Ssam printf("ace%d: hardware address %s\n", unit, 137*29408Ssam ether_sprintf(is->is_addr)); 13824007Ssam is->is_promiscuous = 0; 139*29408Ssam for (wp = (short *)addr->hash, i = 0; i < 8; i++) 140*29408Ssam movow(wp++, ~0xf); 14125694Ssam movow(&addr->bcastena[0], ~0xffff); 14225694Ssam movow(&addr->bcastena[1], ~0xffff); 14325927Ssam /* 14425927Ssam * Allocate and map dual ported VERSAbus memory. 14525927Ssam */ 14625927Ssam vbmemalloc(32, (caddr_t)ui->ui_flags, &is->is_map, &is->is_dpm); 14725927Ssam 14824007Ssam ifp->if_init = aceinit; 14924007Ssam ifp->if_output = aceoutput; 15024007Ssam ifp->if_ioctl = aceioctl; 15124007Ssam ifp->if_reset = acereset; 15225694Ssam ifp->if_flags = IFF_BROADCAST; 15324007Ssam if_attach(ifp); 15424007Ssam } 15524007Ssam 15624007Ssam /* 15724007Ssam * Reset of interface after "system" reset. 15824007Ssam */ 15924007Ssam acereset(unit, vban) 16024007Ssam int unit, vban; 16124007Ssam { 16224007Ssam register struct vba_device *ui; 16324007Ssam 16424007Ssam if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 || 16524007Ssam ui->ui_vbanum != vban) 16624007Ssam return; 16724007Ssam printf(" ace%d", unit); 16824007Ssam aceinit(unit); 16924007Ssam } 17024007Ssam 17124007Ssam /* 17224007Ssam * Initialization of interface; clear recorded pending operations 17324007Ssam */ 17424007Ssam aceinit(unit) 17524007Ssam int unit; 17624007Ssam { 17724007Ssam register struct ace_softc *is = &ace_softc[unit]; 17824007Ssam register struct vba_device *ui = aceinfo[unit]; 17924007Ssam register struct acedevice *addr; 18024007Ssam register struct ifnet *ifp = &is->is_if; 18124007Ssam register short Csr; 18225694Ssam register int s; 18324007Ssam 18425694Ssam if (ifp->if_addrlist == (struct ifaddr *)0) 18524007Ssam return; 18624007Ssam if ((ifp->if_flags & IFF_RUNNING) == 0) { 18724007Ssam /* 18824007Ssam * Reset the controller, initialize the recieve buffers, 18924007Ssam * and turn the controller on again and set board online. 19024007Ssam */ 19124007Ssam addr = (struct acedevice *)ui->ui_addr; 19224007Ssam s = splimp(); 19325694Ssam movow(&addr->csr, CSR_RESET); 19424007Ssam DELAY(10000); 19524007Ssam 19624007Ssam /* 19725927Ssam * Clean up dpm since the controller might 19825927Ssam * jumble dpm after reset. 19924007Ssam */ 20025927Ssam acesetup(unit); 20125694Ssam movow(&addr->csr, CSR_GO); 20224007Ssam Csr = addr->csr; 20324007Ssam if (Csr & CSR_ACTIVE) { 20425855Ssam movow(&addr->ivct, is->is_ivec); 20524007Ssam Csr |= CSR_IENA | is->is_promiscuous; 20625694Ssam movow(&addr->csr, Csr); 20724007Ssam is->is_flags = 0; 20824007Ssam is->is_xcnt = 0; 20925694Ssam is->is_if.if_flags |= IFF_RUNNING; 21024007Ssam } 21124007Ssam splx(s); 21224007Ssam } 21325694Ssam if (is->is_if.if_snd.ifq_head) 21425927Ssam acestart(unit); 21524007Ssam } 21624007Ssam 21724007Ssam /* 21824007Ssam * Start output on interface. 21924007Ssam * Get another datagram to send off of the interface queue, 22024007Ssam * and map it to the interface before starting the output. 22124007Ssam * 22224007Ssam */ 22325927Ssam acestart(unit) 22425927Ssam int unit; 22524007Ssam { 22624007Ssam register struct tx_segment *txs; 22725694Ssam register long len; 22825694Ssam register int s; 22924007Ssam register struct ace_softc *is = &ace_softc[unit]; 23024007Ssam struct mbuf *m; 23125694Ssam short retries; 23224007Ssam 23325927Ssam if (is->is_flags & ACEF_OACTIVE) 23425927Ssam return; 23525927Ssam is->is_flags |= ACEF_OACTIVE; 23624007Ssam again: 23724007Ssam txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11)); 23824007Ssam if (txs->tx_csr & TCS_TBFULL) { 23924007Ssam is->is_stats.tx_busy++; 24025927Ssam is->is_flags &= ~ACEF_OACTIVE; 24124007Ssam return; 24224007Ssam } 24325694Ssam s = splimp(); 24424007Ssam IF_DEQUEUE(&is->is_if.if_snd, m); 24525694Ssam splx(s); 24625927Ssam if (m == 0) { 24725927Ssam is->is_flags &= ~ACEF_OACTIVE; 24824007Ssam return; 24925927Ssam } 25024007Ssam len = aceput(unit, txs->tx_data, m); 25124007Ssam retries = txs->tx_csr & TCS_RTC; 25224007Ssam if (retries > 0) 25324007Ssam acebakoff(is, txs, retries); 25424007Ssam 25524007Ssam /* 25624007Ssam * Ensure minimum packet length. 25724007Ssam * This makes the safe assumtion that there are no virtual holes 25824007Ssam * after the data. 25924007Ssam * For security, it might be wise to zero out the added bytes, 26024007Ssam * but we're mainly interested in speed at the moment. 26124007Ssam */ 26224007Ssam if (len - sizeof (struct ether_header) < ETHERMIN) 26324007Ssam len = ETHERMIN + sizeof (struct ether_header); 26424007Ssam if (++is->is_txnext > SEG_MAX) 26524007Ssam is->is_txnext = is->is_segboundry; 26624007Ssam is->is_if.if_opackets++; 26724007Ssam is->is_xcnt++; 26824007Ssam len = (len & 0x7fff) | TCS_TBFULL; 26925694Ssam movow(txs, len); 27024007Ssam goto again; 27124007Ssam } 27224007Ssam 27324007Ssam /* 27424007Ssam * Transmit done interrupt. 27524007Ssam */ 27624007Ssam acecint(unit) 27724007Ssam int unit; 27824007Ssam { 27924007Ssam register struct ace_softc *is = &ace_softc[unit]; 28024007Ssam register struct tx_segment *txseg; 28125694Ssam short eostat; 28224007Ssam 28324007Ssam if (is->is_xcnt <= 0) { 28425855Ssam log(LOG_ERR, "ace%d: stray xmit interrupt, xcnt %d\n", 28524007Ssam unit, is->is_xcnt); 28624007Ssam is->is_xcnt = 0; 28725694Ssam if (is->is_if.if_snd.ifq_head) 28825927Ssam acestart(unit); 28924007Ssam return; 29024007Ssam } 29124007Ssam is->is_xcnt--; 29224007Ssam txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm); 29324007Ssam eostat = txseg->tx_csr; 29424007Ssam if ((eostat & TCS_TBFULL) == 0) { 29524007Ssam is->is_stats.tx_retries += eostat & TCS_RTC; 29624007Ssam if (eostat & TCS_RTFAIL) { 29724007Ssam is->is_stats.tx_discarded++; 29824007Ssam is->is_if.if_oerrors++; 29924007Ssam } else 30024007Ssam is->is_stats.tx_datagrams++; 30124007Ssam if (++is->is_eoctr >= 16) 30224007Ssam is->is_eoctr = is->is_segboundry; 30324007Ssam } 30425694Ssam if (is->is_if.if_snd.ifq_head) 30525927Ssam acestart(unit); 30624007Ssam } 30724007Ssam 30824007Ssam /* 30924007Ssam * Ethernet interface receiver interrupt. 31024007Ssam * If input error just drop packet. 31124007Ssam * Otherwise purge input buffered data path and examine 31224007Ssam * packet to determine type. If can't determine length 31324007Ssam * from type, then have to drop packet. Othewise decapsulate 31424007Ssam * packet based on type and pass to type specific higher-level 31524007Ssam * input routine. 31624007Ssam */ 31724007Ssam acerint(unit) 31824007Ssam int unit; 31924007Ssam { 32024007Ssam register struct ace_softc *is = &ace_softc[unit]; 32124007Ssam register struct ifqueue *inq; 32224007Ssam register struct ether_header *ace; 32324007Ssam register struct rx_segment *rxseg; 32424007Ssam int len, s, off, resid; 32524007Ssam struct mbuf *m; 32624007Ssam short eistat; 32724007Ssam 32825694Ssam if ((is->is_if.if_flags&IFF_RUNNING) == 0) 32925694Ssam return; 33024007Ssam again: 33124007Ssam rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm); 33224007Ssam eistat = rxseg->rx_csr; 33324007Ssam if ((eistat & RCS_RBFULL) == 0) 33424007Ssam return; 33524007Ssam is->is_if.if_ipackets++; 33624007Ssam if (++is->is_eictr >= is->is_segboundry) 33724007Ssam is->is_eictr = 0; 33824007Ssam len = eistat & RCS_RBC; 33924007Ssam if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) || 34024007Ssam len < ET_MINLEN || len > ET_MAXLEN+CRC_SIZE) { 34124007Ssam if (eistat & RCS_ROVRN) 34224007Ssam is->is_stats.rx_overruns++; 34324007Ssam if (eistat & RCS_RCRC) 34424007Ssam is->is_stats.rx_crc_errors++; 34524007Ssam if (eistat & RCS_RODD) 34624007Ssam is->is_stats.rx_align_errors++; 34724007Ssam if (len < ET_MINLEN) 34824007Ssam is->is_stats.rx_underruns++; 34924007Ssam if (len > ET_MAXLEN+CRC_SIZE) 35024007Ssam is->is_stats.rx_overruns++; 35124007Ssam is->is_if.if_ierrors++; 35224007Ssam rxseg->rx_csr = 0; 35324007Ssam return; 35424007Ssam } else 35524007Ssam is->is_stats.rx_datagrams++; 35624007Ssam ace = (struct ether_header *)rxseg->rx_data; 35724007Ssam len -= sizeof (struct ether_header); 35824007Ssam /* 35925694Ssam * Deal with trailer protocol: if type is trailer 36024007Ssam * get true type from first 16-bit word past data. 36124007Ssam * Remember that type was trailer by setting off. 36224007Ssam */ 36324007Ssam ace->ether_type = ntohs((u_short)ace->ether_type); 36424007Ssam #define acedataaddr(ace, off, type) \ 36524007Ssam ((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off)))) 36625694Ssam if (ace->ether_type >= ETHERTYPE_TRAIL && 36725694Ssam ace->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 36825694Ssam off = (ace->ether_type - ETHERTYPE_TRAIL) * 512; 36924007Ssam if (off >= ETHERMTU) 37024007Ssam goto setup; /* sanity */ 37124007Ssam ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *)); 37224007Ssam resid = ntohs(*(acedataaddr(ace, off+2, u_short *))); 37324007Ssam if (off + resid > len) 37424007Ssam goto setup; /* sanity */ 37524007Ssam len = off + resid; 37624007Ssam } else 37724007Ssam off = 0; 37824007Ssam if (len == 0) 37924007Ssam goto setup; 38024007Ssam 38124007Ssam /* 38224007Ssam * Pull packet off interface. Off is nonzero if packet 38324007Ssam * has trailing header; aceget will then force this header 38424007Ssam * information to be at the front, but we still have to drop 38524007Ssam * the type and length which are at the front of any trailer data. 38624007Ssam */ 38725855Ssam m = aceget((u_char *)rxseg->rx_data, len, off, &is->is_if); 38824007Ssam if (m == 0) 38924007Ssam goto setup; 39024007Ssam if (off) { 39125855Ssam struct ifnet *ifp; 39225855Ssam 39325855Ssam ifp = *(mtod(m, struct ifnet **)); 39424007Ssam m->m_off += 2 * sizeof (u_short); 39524007Ssam m->m_len -= 2 * sizeof (u_short); 39625855Ssam *(mtod(m, struct ifnet **)) = ifp; 39724007Ssam } 39824007Ssam switch (ace->ether_type) { 39924007Ssam 40024007Ssam #ifdef INET 40125694Ssam case ETHERTYPE_IP: 40224007Ssam schednetisr(NETISR_IP); 40324007Ssam inq = &ipintrq; 40424007Ssam break; 40525855Ssam #endif 40624007Ssam 40725694Ssam case ETHERTYPE_ARP: 40824007Ssam arpinput(&is->is_ac, m); 40924007Ssam goto setup; 41025694Ssam #ifdef NS 41125694Ssam case ETHERTYPE_NS: 41225694Ssam schednetisr(NETISR_NS); 41325694Ssam inq = &nsintrq; 41425694Ssam break; 41525694Ssam 41625694Ssam #endif 41724007Ssam default: 41824007Ssam m_freem(m); 41924007Ssam goto setup; 42024007Ssam } 42124007Ssam if (IF_QFULL(inq)) { 42224007Ssam IF_DROP(inq); 42324007Ssam m_freem(m); 42424007Ssam goto setup; 42524007Ssam } 42624007Ssam s = splimp(); 42724007Ssam IF_ENQUEUE(inq, m); 42824007Ssam splx(s); 42924007Ssam setup: 43024007Ssam rxseg->rx_csr = 0; 43124007Ssam goto again; 43224007Ssam } 43324007Ssam 43424007Ssam /* 43524007Ssam * Ethernet output routine. 43624007Ssam * Encapsulate a packet of type family for the local net. 43724007Ssam * Use trailer local net encapsulation if enough data in first 43824007Ssam * packet leaves a multiple of 512 bytes of data in remainder. 43924007Ssam */ 44024007Ssam aceoutput(ifp, m0, dst) 44124007Ssam struct ifnet *ifp; 44224007Ssam struct mbuf *m0; 44324007Ssam struct sockaddr *dst; 44424007Ssam { 44524007Ssam register struct ace_softc *is = &ace_softc[ifp->if_unit]; 44624007Ssam register struct mbuf *m = m0; 44724007Ssam register struct ether_header *ace; 44824007Ssam register int off; 44924007Ssam struct mbuf *mcopy = (struct mbuf *)0; 45025957Ssam int type, s, error, usetrailers; 45125694Ssam u_char edst[6]; 45224007Ssam struct in_addr idst; 45324007Ssam 45425855Ssam if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 45525855Ssam error = ENETDOWN; 45625855Ssam goto bad; 45725855Ssam } 45824007Ssam switch (dst->sa_family) { 45924007Ssam 46024007Ssam #ifdef INET 46124007Ssam case AF_INET: 46224007Ssam idst = ((struct sockaddr_in *)dst)->sin_addr; 46325957Ssam if (!arpresolve(&is->is_ac, m, &idst, edst, &usetrailers)) 46424007Ssam return (0); /* if not yet resolved */ 46525694Ssam if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr, 46625937Ssam sizeof (edst))) 46724007Ssam mcopy = m_copy(m, 0, (int)M_COPYALL); 46824007Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 46924007Ssam /* need per host negotiation */ 47025957Ssam if (usetrailers && off > 0 && (off & 0x1ff) == 0 && 47124007Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 47225694Ssam type = ETHERTYPE_TRAIL + (off>>9); 47324007Ssam m->m_off -= 2 * sizeof (u_short); 47424007Ssam m->m_len += 2 * sizeof (u_short); 47525694Ssam *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); 47624007Ssam *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 47724007Ssam goto gottrailertype; 47824007Ssam } 47925694Ssam type = ETHERTYPE_IP; 48024007Ssam off = 0; 48124007Ssam goto gottype; 48224007Ssam #endif 48325694Ssam #ifdef NS 48425694Ssam case AF_NS: 48525694Ssam bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), 48625694Ssam (caddr_t)edst, sizeof (edst)); 48725694Ssam if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost,sizeof(edst))) 48825694Ssam mcopy = m_copy(m, 0, (int)M_COPYALL); 48925694Ssam else if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, 49025694Ssam sizeof(edst))) 49125694Ssam return(looutput(&loif, m, dst)); 49225694Ssam type = ETHERTYPE_NS; 49325694Ssam off = 0; 49425694Ssam goto gottype; 49525694Ssam #endif 49624007Ssam case AF_UNSPEC: 49724007Ssam ace = (struct ether_header *)dst->sa_data; 49825694Ssam bcopy((caddr_t)ace->ether_dhost, (caddr_t)edst, sizeof (edst)); 49924007Ssam type = ace->ether_type; 50024007Ssam goto gottype; 50124007Ssam 50224007Ssam default: 50325855Ssam log(LOG_ERR, "ace%d: can't handle af%d\n", 50424007Ssam ifp->if_unit, dst->sa_family); 50524007Ssam error = EAFNOSUPPORT; 50624007Ssam goto bad; 50724007Ssam } 50824007Ssam 50924007Ssam gottrailertype: 51024007Ssam /* 51124007Ssam * Packet to be sent as trailer: move first packet 51224007Ssam * (control information) to end of chain. 51324007Ssam */ 51424007Ssam while (m->m_next) 51524007Ssam m = m->m_next; 51624007Ssam m->m_next = m0; 51724007Ssam m = m0->m_next; 51824007Ssam m0->m_next = 0; 51924007Ssam m0 = m; 52024007Ssam 52124007Ssam gottype: 52224007Ssam /* 52324007Ssam * Add local net header. If no space in first mbuf, 52424007Ssam * allocate another. 52524007Ssam */ 52624007Ssam if (m->m_off > MMAXOFF || 52724007Ssam MMINOFF + sizeof (struct ether_header) > m->m_off) { 52824007Ssam m = m_get(M_DONTWAIT, MT_HEADER); 52924007Ssam if (m == 0) { 53024007Ssam error = ENOBUFS; 53124007Ssam goto bad; 53224007Ssam } 53324007Ssam m->m_next = m0; 53424007Ssam m->m_off = MMINOFF; 53524007Ssam m->m_len = sizeof (struct ether_header); 53624007Ssam } else { 53724007Ssam m->m_off -= sizeof (struct ether_header); 53824007Ssam m->m_len += sizeof (struct ether_header); 53924007Ssam } 54024007Ssam ace = mtod(m, struct ether_header *); 54125694Ssam bcopy((caddr_t)edst, (caddr_t)ace->ether_dhost, sizeof (edst)); 54225694Ssam bcopy((caddr_t)is->is_addr, (caddr_t)ace->ether_shost, 54325694Ssam sizeof (is->is_addr)); 54424007Ssam ace->ether_type = htons((u_short)type); 54524007Ssam 54624007Ssam /* 54724007Ssam * Queue message on interface, and start output if interface 54824007Ssam * not yet active. 54924007Ssam */ 55024007Ssam s = splimp(); 55124007Ssam if (IF_QFULL(&ifp->if_snd)) { 55224007Ssam IF_DROP(&ifp->if_snd); 55324007Ssam error = ENOBUFS; 55424007Ssam goto qfull; 55524007Ssam } 55624007Ssam IF_ENQUEUE(&ifp->if_snd, m); 55724007Ssam splx(s); 55825927Ssam acestart(ifp->if_unit); 55924007Ssam return (mcopy ? looutput(&loif, mcopy, dst) : 0); 56024007Ssam qfull: 56124007Ssam m0 = m; 56224007Ssam splx(s); 56324007Ssam bad: 56424007Ssam m_freem(m0); 56524007Ssam if (mcopy) 56624007Ssam m_freem(mcopy); 56724007Ssam return (error); 56824007Ssam } 56924007Ssam 57024007Ssam /* 57124007Ssam * Routine to copy from mbuf chain to transmit buffer on the VERSAbus 57224007Ssam * If packet size is less than the minimum legal size, 57324007Ssam * the buffer is expanded. We probably should zero out the extra 57424007Ssam * bytes for security, but that would slow things down. 57524007Ssam */ 57625694Ssam /*ARGSUSED*/ 57724007Ssam aceput(unit, txbuf, m) 57825855Ssam int unit; 57925694Ssam char *txbuf; 58024007Ssam struct mbuf *m; 58124007Ssam { 58225855Ssam register u_char *bp, *mcp; 58325855Ssam register short *s1, *s2; 58425694Ssam register u_int len; 58524007Ssam register struct mbuf *mp; 58625694Ssam int total; 58724007Ssam 58824007Ssam total = 0; 58925694Ssam bp = (u_char *)txbuf; 59025694Ssam for (mp = m; (mp); mp = mp->m_next) { 59124007Ssam len = mp->m_len; 59224007Ssam if (len == 0) 59324007Ssam continue; 59424007Ssam total += len; 59524007Ssam mcp = mtod(mp, u_char *); 59624007Ssam if (((int)mcp & 01) && ((int)bp & 01)) { 59724007Ssam /* source & destination at odd addresses */ 59825694Ssam movob(bp++, *mcp++); 59924007Ssam --len; 60024007Ssam } 60124007Ssam if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) { 60225694Ssam register u_int l; 60324007Ssam 60424007Ssam s1 = (short *)bp; 60524007Ssam s2 = (short *)mcp; 60624007Ssam l = len >> 1; /* count # of shorts */ 60725694Ssam while (l-- != 0) 60825694Ssam movow(s1++, *s2++); 60924007Ssam len &= 1; /* # remaining bytes */ 61024007Ssam bp = (u_char *)s1; 61124007Ssam mcp = (u_char *)s2; 61224007Ssam } 61325694Ssam while (len-- != 0) 61425694Ssam movob(bp++, *mcp++); 61524007Ssam } 61624007Ssam m_freem(m); 61724007Ssam return (total); 61824007Ssam } 61924007Ssam 62024007Ssam /* 62124007Ssam * Routine to copy from VERSAbus memory into mbufs. 62224007Ssam * 62324007Ssam * Warning: This makes the fairly safe assumption that 62424007Ssam * mbufs have even lengths. 62524007Ssam */ 62625694Ssam /*ARGSUSED*/ 62724007Ssam struct mbuf * 62825855Ssam aceget(rxbuf, totlen, off0, ifp) 62924007Ssam u_char *rxbuf; 63024007Ssam int totlen, off0; 63125855Ssam struct ifnet *ifp; 63224007Ssam { 63325855Ssam register u_char *cp, *mcp; 63424007Ssam register int tlen; 63524007Ssam register struct mbuf *m; 63624007Ssam struct mbuf *top = 0, **mp = ⊤ 63724007Ssam int len, off = off0; 63824007Ssam 63924007Ssam cp = rxbuf + sizeof (struct ether_header); 64024007Ssam while (totlen > 0) { 64124007Ssam MGET(m, M_DONTWAIT, MT_DATA); 64224007Ssam if (m == 0) 64324007Ssam goto bad; 64424007Ssam if (off) { 64524007Ssam len = totlen - off; 64624007Ssam cp = rxbuf + sizeof (struct ether_header) + off; 64724007Ssam } else 64824007Ssam len = totlen; 64925855Ssam if (ifp) 65025855Ssam len += sizeof(ifp); 65124007Ssam if (len >= CLBYTES) { 65224007Ssam struct mbuf *p; 65324007Ssam 65424007Ssam MCLGET(p, 1); 65524007Ssam if (p != 0) { 65624007Ssam m->m_len = len = CLBYTES; 65724007Ssam m->m_off = (int)p - (int)m; 65824007Ssam } else { 65924007Ssam m->m_len = len = MIN(MLEN, len); 66024007Ssam m->m_off = MMINOFF; 66124007Ssam } 66224007Ssam } else { 66324007Ssam m->m_len = len = MIN(MLEN, len); 66424007Ssam m->m_off = MMINOFF; 66524007Ssam } 66624007Ssam mcp = mtod(m, u_char *); 66725855Ssam if (ifp) { 66825855Ssam /* 66925855Ssam * Prepend interface pointer to first mbuf. 67025855Ssam */ 67125855Ssam *(mtod(m, struct ifnet **)) = ifp; 67225855Ssam mcp += sizeof(ifp); 67325855Ssam len -= sizeof(ifp); 67425855Ssam ifp = (struct ifnet *)0; 67525855Ssam } 67624007Ssam /*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/ 67724007Ssam /*cp += len; mcp += len;*/ 67824007Ssam tlen = len; 67924007Ssam if (((int)mcp & 01) && ((int)cp & 01)) { 68024007Ssam /* source & destination at odd addresses */ 68124007Ssam *mcp++ = *cp++; 68224007Ssam --tlen; 68324007Ssam } 68424007Ssam if (tlen > 1 && (((int)mcp&01) == 0) && (((int)cp&01) == 0)) { 68524007Ssam register short *s1, *s2; 68624007Ssam register int l; 68724007Ssam 68824007Ssam s1 = (short *)mcp; 68924007Ssam s2 = (short *)cp; 69024007Ssam l = tlen >> 1; /* count # of shorts */ 69124007Ssam while (l-- > 0) /* copy shorts */ 69224007Ssam *s1++ = *s2++; 69324007Ssam tlen &= 1; /* # remaining bytes */ 69424007Ssam mcp = (u_char *)s1; 69524007Ssam cp = (u_char *)s2; 69624007Ssam } 69724007Ssam while (tlen-- > 0) 69824007Ssam *mcp++ = *cp++; 69924007Ssam *mp = m; 70024007Ssam mp = &m->m_next; 70124007Ssam if (off == 0) { 70224007Ssam totlen -= len; 70324007Ssam continue; 70424007Ssam } 70524007Ssam off += len; 70624007Ssam if (off == totlen) { 70724007Ssam cp = rxbuf + sizeof (struct ether_header); 70824007Ssam off = 0; 70924007Ssam totlen = off0; 71024007Ssam } 71124007Ssam } 71224007Ssam return (top); 71324007Ssam bad: 71424007Ssam m_freem(top); 71524007Ssam return (0); 71624007Ssam } 71724007Ssam 718*29408Ssam /* backoff table masks */ 719*29408Ssam short random_mask_tbl[16] = { 720*29408Ssam 0x0040, 0x00C0, 0x01C0, 0x03C0, 0x07C0, 0x0FC0, 0x1FC0, 0x3FC0, 721*29408Ssam 0x7FC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0 722*29408Ssam }; 723*29408Ssam 72424007Ssam acebakoff(is, txseg, retries) 72524007Ssam struct ace_softc *is; 72624007Ssam struct tx_segment *txseg; 72724007Ssam register int retries; 72824007Ssam { 72924007Ssam register short *pBakNum, random_num; 73024007Ssam short *pMask; 73124007Ssam 73224007Ssam pMask = &random_mask_tbl[0]; 73324007Ssam pBakNum = &txseg->tx_backoff[0]; 73424007Ssam while (--retries >= 0) { 73524007Ssam random_num = (is->is_currnd = (is->is_currnd * 18741)-13849); 73624007Ssam random_num &= *pMask++; 73724007Ssam *pBakNum++ = random_num ^ (short)(0xFF00 | 0x00FC); 73824007Ssam } 73924007Ssam } 74024007Ssam 74124007Ssam /* 74224007Ssam * Process an ioctl request. 74324007Ssam */ 74424007Ssam aceioctl(ifp, cmd, data) 74524007Ssam register struct ifnet *ifp; 74624007Ssam int cmd; 74724007Ssam caddr_t data; 74824007Ssam { 74925694Ssam register struct ifaddr *ifa = (struct ifaddr *)data; 75025855Ssam struct acedevice *addr; 75125694Ssam int s = splimp(), error = 0; 75224007Ssam 75324007Ssam switch (cmd) { 75424007Ssam 75524007Ssam case SIOCSIFADDR: 75625694Ssam ifp->if_flags |= IFF_UP; 75725855Ssam switch (ifa->ifa_addr.sa_family) { 75825855Ssam #ifdef INET 75925855Ssam case AF_INET: 76025855Ssam aceinit(ifp->if_unit); /* before arpwhohas */ 76125855Ssam ((struct arpcom *)ifp)->ac_ipaddr = 76225855Ssam IA_SIN(ifa)->sin_addr; 76325855Ssam arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 76425855Ssam break; 76525855Ssam #endif 76625855Ssam #ifdef NS 76725855Ssam case AF_NS: { 76825937Ssam struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; 76925937Ssam struct ace_softc *is = &ace_softc[ifp->if_unit]; 77025855Ssam 77125855Ssam if (!ns_nullhost(*ina)) { 77225855Ssam ifp->if_flags &= ~IFF_RUNNING; 77325855Ssam addr = (struct acedevice *) 77425937Ssam aceinfo[ifp->if_unit]->ui_addr; 77525855Ssam movow(&addr->csr, CSR_RESET); 77625855Ssam DELAY(10000); 77725855Ssam /* set station address & copy addr to arp */ 778*29408Ssam acesetaddr(ifp->if_unit, addr, 77925855Ssam ina->x_host.c_host); 78025855Ssam } else 78125937Ssam ina->x_host = *(union ns_host *)is->is_addr; 78225855Ssam aceinit(ifp->if_unit); 78325855Ssam break; 78425855Ssam } 78525855Ssam #endif 78625855Ssam default: 78725855Ssam aceinit(ifp->if_unit); 78825855Ssam break; 78925855Ssam } 79024007Ssam break; 79124007Ssam 79225855Ssam case SIOCSIFFLAGS: 79325855Ssam if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) { 79425855Ssam addr = (struct acedevice *) 79525855Ssam (aceinfo[ifp->if_unit]->ui_addr); 79625855Ssam movow(&addr->csr, CSR_RESET); 79725855Ssam ifp->if_flags &= ~IFF_RUNNING; 79825855Ssam } else if (ifp->if_flags&IFF_UP && 79925855Ssam (ifp->if_flags&IFF_RUNNING) == 0) 80025855Ssam aceinit(ifp->if_unit); 80124007Ssam break; 80224007Ssam 80324007Ssam default: 80424007Ssam error = EINVAL; 80524007Ssam } 80624007Ssam splx(s); 80724007Ssam return (error); 80824007Ssam } 80924007Ssam 810*29408Ssam /* 811*29408Ssam * Set the on-board station address, then read it back 812*29408Ssam * to initialize the address used by ARP (among others). 813*29408Ssam */ 814*29408Ssam acesetaddr(unit, addr, station) 815*29408Ssam short unit; 816*29408Ssam struct acedevice *addr; 817*29408Ssam char *station; 818*29408Ssam { 819*29408Ssam struct ace_softc *is = &ace_softc[unit]; 820*29408Ssam register short *wp, i; 821*29408Ssam 822*29408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 823*29408Ssam movow(wp++, ~*station++); 824*29408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 825*29408Ssam is->is_addr[i] = ~*wp++; 826*29408Ssam printf("ace%d: hardware address %s\n", unit, 827*29408Ssam ether_sprintf(is->is_addr)); 828*29408Ssam } 829*29408Ssam 830*29408Ssam /* 831*29408Ssam * Setup the device for use. Initialize dual-ported memory, 832*29408Ssam * backoff parameters, and various other software state. 833*29408Ssam */ 83425927Ssam acesetup(unit) 83524007Ssam int unit; 83624007Ssam { 83724007Ssam register struct ace_softc *is = &ace_softc[unit]; 83825927Ssam register char *pData1; 83925694Ssam register short i; 84025927Ssam struct acedevice *addr; 84124007Ssam 84225927Ssam bzero(is->is_dpm, 16384*2); 84324007Ssam is->is_currnd = 49123; 84425927Ssam addr = (struct acedevice *)aceinfo[unit]->ui_addr; 84524007Ssam is->is_segboundry = (addr->segb >> 11) & 0xf; 84625927Ssam pData1 = is->is_dpm + (is->is_segboundry << 11); 84724007Ssam for (i = SEG_MAX + 1 - is->is_segboundry; --i >= 0;) { 84824007Ssam acebakoff(is, (struct tx_segment *)pData1, 15); 84924007Ssam pData1 += sizeof (struct tx_segment); 85024007Ssam } 85124007Ssam is->is_eictr = 0; 85224007Ssam is->is_eoctr = is->is_txnext = is->is_segboundry; 85324007Ssam bzero((char *)&is->is_stats, sizeof (is->is_stats)); 85424007Ssam } 85524007Ssam #endif 856