1*34405Skarels /* 2*34405Skarels * Copyright (c) 1988 Regents of the University of California. 3*34405Skarels * All rights reserved. 4*34405Skarels * 5*34405Skarels * Redistribution and use in source and binary forms are permitted 6*34405Skarels * provided that this notice is preserved and that due credit is given 7*34405Skarels * to the University of California at Berkeley. The name of the University 8*34405Skarels * may not be used to endorse or promote products derived from this 9*34405Skarels * software without specific prior written permission. This software 10*34405Skarels * is provided ``as is'' without express or implied warranty. 11*34405Skarels * 12*34405Skarels * @(#)if_ace.c 7.1 (Berkeley) 05/21/88 13*34405Skarels */ 1424007Ssam 1524007Ssam /* 1624007Ssam * ACC VERSAbus Ethernet controller 1724007Ssam */ 1824007Ssam #include "ace.h" 1924007Ssam #if NACE > 0 2024007Ssam 2125694Ssam #include "param.h" 2225694Ssam #include "systm.h" 2325694Ssam #include "mbuf.h" 2425694Ssam #include "buf.h" 2525694Ssam #include "protosw.h" 2625694Ssam #include "socket.h" 2725694Ssam #include "vmmac.h" 2825694Ssam #include "ioctl.h" 2925694Ssam #include "errno.h" 3025694Ssam #include "vmparam.h" 3125855Ssam #include "syslog.h" 3224007Ssam 3324007Ssam #include "../net/if.h" 3424007Ssam #include "../net/netisr.h" 3524007Ssam #include "../net/route.h" 3625855Ssam #ifdef INET 3724007Ssam #include "../netinet/in.h" 3824007Ssam #include "../netinet/in_systm.h" 3925694Ssam #include "../netinet/in_var.h" 4024007Ssam #include "../netinet/ip.h" 4124007Ssam #include "../netinet/ip_var.h" 4224007Ssam #include "../netinet/if_ether.h" 4325855Ssam #endif 4425855Ssam #ifdef NS 4525855Ssam #include "../netns/ns.h" 4625855Ssam #include "../netns/ns_if.h" 4725855Ssam #endif 4824007Ssam 4930229Ssam #include "../machine/cpu.h" 5030229Ssam #include "../machine/pte.h" 5130229Ssam 5224007Ssam #include "../tahoe/mtpr.h" 5324007Ssam #include "../tahoeif/if_acereg.h" 5425694Ssam #include "../tahoevba/vbavar.h" 5524007Ssam 5624007Ssam int aceprobe(), aceattach(), acerint(), acecint(); 5724007Ssam struct vba_device *aceinfo[NACE]; 5825983Ssam long acestd[] = { 0 }; 5924007Ssam struct vba_driver acedriver = 6025927Ssam { aceprobe, 0, aceattach, 0, acestd, "ace", aceinfo, "v/eiu", 0 }; 6124007Ssam 6224007Ssam int aceinit(), aceoutput(), aceioctl(), acereset(); 6324007Ssam struct mbuf *aceget(); 6424007Ssam 6524007Ssam /* 6624007Ssam * Ethernet software status per interface. 6724007Ssam * 6824007Ssam * Each interface is referenced by a network interface structure, 6924007Ssam * is_if, which the routing code uses to locate the interface. 7024007Ssam * This structure contains the output queue for the interface, its address, ... 7124007Ssam */ 7224007Ssam struct ace_softc { 7324007Ssam struct arpcom is_ac; /* Ethernet common part */ 7424007Ssam #define is_if is_ac.ac_if /* network-visible interface */ 7524007Ssam #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ 7624007Ssam short is_flags; 7724007Ssam #define ACEF_OACTIVE 0x1 /* output is active */ 7824007Ssam #define ACEF_RCVPENDING 0x2 /* start rcv in acecint */ 7924007Ssam short is_promiscuous; /* true is enabled */ 8024007Ssam short is_segboundry; /* first TX Seg in dpm */ 8124007Ssam short is_eictr; /* Rx segment tracking ctr */ 8224007Ssam short is_eoctr; /* Tx segment tracking ctr */ 8324007Ssam short is_txnext; /* Next available Tx segment */ 8424007Ssam short is_currnd; /* current random backoff */ 8524007Ssam struct ace_stats is_stats; /* holds board statistics */ 8624007Ssam short is_xcnt; /* count xmitted segments to be acked 8724007Ssam by the controller */ 8825855Ssam long is_ivec; /* autoconfig interrupt vector base */ 8925927Ssam struct pte *is_map; /* pte map for dual ported memory */ 9025927Ssam caddr_t is_dpm; /* address of mapped memory */ 9124007Ssam } ace_softc[NACE]; 9224007Ssam extern struct ifnet loif; 9324007Ssam 9425855Ssam aceprobe(reg, vi) 9524007Ssam caddr_t reg; 9625855Ssam struct vba_device *vi; 9724007Ssam { 9825855Ssam register br, cvec; /* must be r12, r11 */ 9925855Ssam struct acedevice *ap = (struct acedevice *)reg; 10025855Ssam struct ace_softc *is = &ace_softc[vi->ui_unit]; 10124007Ssam 10224007Ssam #ifdef lint 10330295Ssam br = 0; cvec = br; br = cvec; 10424007Ssam acerint(0); acecint(0); 10524007Ssam #endif 10624007Ssam if (badaddr(reg, 2)) 10725855Ssam return (0); 10825855Ssam movow(&ap->csr, CSR_RESET); 10924007Ssam DELAY(10000); 11025855Ssam #ifdef notdef 11125855Ssam /* 11225855Ssam * Select two spaces for the interrupts aligned to an 11325855Ssam * eight vector boundary and fitting in 8 bits (as 11425855Ssam * required by the controller) -- YECH. The controller 11525855Ssam * will be notified later at initialization time. 11625855Ssam */ 11725855Ssam if ((vi->ui_hd->vh_lastiv -= 2) > 0xff) 11825855Ssam vi->ui_hd->vh_lastiv = 0x200; 11925855Ssam is->is_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x7; 12025855Ssam #else 12125855Ssam is->is_ivec = 0x90+vi->ui_unit*8; 12225855Ssam #endif 12325855Ssam br = 0x14, cvec = is->is_ivec; /* XXX */ 12425855Ssam return (sizeof (*ap)); 12524007Ssam } 12624007Ssam 12724007Ssam /* 12824007Ssam * Interface exists: make available by filling in network interface 12924007Ssam * record. System will initialize the interface when it is ready 13024007Ssam * to accept packets. 13124007Ssam */ 13224007Ssam aceattach(ui) 13324007Ssam struct vba_device *ui; 13424007Ssam { 13524007Ssam register short unit = ui->ui_unit; 13624007Ssam register struct ace_softc *is = &ace_softc[unit]; 13724007Ssam register struct ifnet *ifp = &is->is_if; 13824007Ssam register struct acedevice *addr = (struct acedevice *)ui->ui_addr; 13924007Ssam register short *wp, i; 14024007Ssam 14124007Ssam ifp->if_unit = unit; 14224007Ssam ifp->if_name = "ace"; 14324007Ssam ifp->if_mtu = ETHERMTU; 14424007Ssam /* 14529408Ssam * Get station's addresses and set multicast hash table. 14624007Ssam */ 14729408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 14829408Ssam is->is_addr[i] = ~*wp++; 14929408Ssam printf("ace%d: hardware address %s\n", unit, 15029408Ssam ether_sprintf(is->is_addr)); 15124007Ssam is->is_promiscuous = 0; 15229408Ssam for (wp = (short *)addr->hash, i = 0; i < 8; i++) 15329408Ssam movow(wp++, ~0xf); 15425694Ssam movow(&addr->bcastena[0], ~0xffff); 15525694Ssam movow(&addr->bcastena[1], ~0xffff); 15625927Ssam /* 15725927Ssam * Allocate and map dual ported VERSAbus memory. 15825927Ssam */ 15931735Skarels if (vbmemalloc(32, (caddr_t)ui->ui_flags, 16031735Skarels &is->is_map, &is->is_dpm) == 0) { 16131735Skarels printf("ace%d: can't allocate VERSAbus memory map\n", unit); 16231735Skarels return; 16331735Skarels } 16425927Ssam 16524007Ssam ifp->if_init = aceinit; 16624007Ssam ifp->if_output = aceoutput; 16724007Ssam ifp->if_ioctl = aceioctl; 16824007Ssam ifp->if_reset = acereset; 16925694Ssam ifp->if_flags = IFF_BROADCAST; 17024007Ssam if_attach(ifp); 17124007Ssam } 17224007Ssam 17324007Ssam /* 17424007Ssam * Reset of interface after "system" reset. 17524007Ssam */ 17624007Ssam acereset(unit, vban) 17724007Ssam int unit, vban; 17824007Ssam { 17924007Ssam register struct vba_device *ui; 18024007Ssam 18124007Ssam if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 || 18224007Ssam ui->ui_vbanum != vban) 18324007Ssam return; 18424007Ssam printf(" ace%d", unit); 18524007Ssam aceinit(unit); 18624007Ssam } 18724007Ssam 18824007Ssam /* 18924007Ssam * Initialization of interface; clear recorded pending operations 19024007Ssam */ 19124007Ssam aceinit(unit) 19224007Ssam int unit; 19324007Ssam { 19424007Ssam register struct ace_softc *is = &ace_softc[unit]; 19524007Ssam register struct vba_device *ui = aceinfo[unit]; 19624007Ssam register struct acedevice *addr; 19724007Ssam register struct ifnet *ifp = &is->is_if; 19824007Ssam register short Csr; 19925694Ssam register int s; 20024007Ssam 20125694Ssam if (ifp->if_addrlist == (struct ifaddr *)0) 20224007Ssam return; 20324007Ssam if ((ifp->if_flags & IFF_RUNNING) == 0) { 20424007Ssam /* 20524007Ssam * Reset the controller, initialize the recieve buffers, 20624007Ssam * and turn the controller on again and set board online. 20724007Ssam */ 20824007Ssam addr = (struct acedevice *)ui->ui_addr; 20924007Ssam s = splimp(); 21025694Ssam movow(&addr->csr, CSR_RESET); 21124007Ssam DELAY(10000); 21224007Ssam 21324007Ssam /* 21425927Ssam * Clean up dpm since the controller might 21525927Ssam * jumble dpm after reset. 21624007Ssam */ 21725927Ssam acesetup(unit); 21825694Ssam movow(&addr->csr, CSR_GO); 21924007Ssam Csr = addr->csr; 22024007Ssam if (Csr & CSR_ACTIVE) { 22125855Ssam movow(&addr->ivct, is->is_ivec); 22224007Ssam Csr |= CSR_IENA | is->is_promiscuous; 22325694Ssam movow(&addr->csr, Csr); 22424007Ssam is->is_flags = 0; 22524007Ssam is->is_xcnt = 0; 22625694Ssam is->is_if.if_flags |= IFF_RUNNING; 22724007Ssam } 22824007Ssam splx(s); 22924007Ssam } 23025694Ssam if (is->is_if.if_snd.ifq_head) 23125927Ssam acestart(unit); 23224007Ssam } 23324007Ssam 23424007Ssam /* 23524007Ssam * Start output on interface. 23624007Ssam * Get another datagram to send off of the interface queue, 23724007Ssam * and map it to the interface before starting the output. 23824007Ssam */ 23925927Ssam acestart(unit) 24025927Ssam int unit; 24124007Ssam { 24224007Ssam register struct tx_segment *txs; 24325694Ssam register long len; 24425694Ssam register int s; 24524007Ssam register struct ace_softc *is = &ace_softc[unit]; 24624007Ssam struct mbuf *m; 24725694Ssam short retries; 24824007Ssam 24925927Ssam if (is->is_flags & ACEF_OACTIVE) 25025927Ssam return; 25125927Ssam is->is_flags |= ACEF_OACTIVE; 25224007Ssam again: 25324007Ssam txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11)); 25424007Ssam if (txs->tx_csr & TCS_TBFULL) { 25524007Ssam is->is_stats.tx_busy++; 25625927Ssam is->is_flags &= ~ACEF_OACTIVE; 25724007Ssam return; 25824007Ssam } 25925694Ssam s = splimp(); 26024007Ssam IF_DEQUEUE(&is->is_if.if_snd, m); 26125694Ssam splx(s); 26225927Ssam if (m == 0) { 26325927Ssam is->is_flags &= ~ACEF_OACTIVE; 26424007Ssam return; 26525927Ssam } 26624007Ssam len = aceput(unit, txs->tx_data, m); 26724007Ssam retries = txs->tx_csr & TCS_RTC; 26824007Ssam if (retries > 0) 26924007Ssam acebakoff(is, txs, retries); 27024007Ssam 27124007Ssam /* 27224007Ssam * Ensure minimum packet length. 27324007Ssam * This makes the safe assumtion that there are no virtual holes 27424007Ssam * after the data. 27524007Ssam * For security, it might be wise to zero out the added bytes, 27624007Ssam * but we're mainly interested in speed at the moment. 27724007Ssam */ 27824007Ssam if (len - sizeof (struct ether_header) < ETHERMIN) 27924007Ssam len = ETHERMIN + sizeof (struct ether_header); 28024007Ssam if (++is->is_txnext > SEG_MAX) 28124007Ssam is->is_txnext = is->is_segboundry; 28224007Ssam is->is_if.if_opackets++; 28324007Ssam is->is_xcnt++; 28424007Ssam len = (len & 0x7fff) | TCS_TBFULL; 28525694Ssam movow(txs, len); 28624007Ssam goto again; 28724007Ssam } 28824007Ssam 28924007Ssam /* 29024007Ssam * Transmit done interrupt. 29124007Ssam */ 29224007Ssam acecint(unit) 29324007Ssam int unit; 29424007Ssam { 29524007Ssam register struct ace_softc *is = &ace_softc[unit]; 29624007Ssam register struct tx_segment *txseg; 29725694Ssam short eostat; 29824007Ssam 29924007Ssam if (is->is_xcnt <= 0) { 30025855Ssam log(LOG_ERR, "ace%d: stray xmit interrupt, xcnt %d\n", 30124007Ssam unit, is->is_xcnt); 30224007Ssam is->is_xcnt = 0; 30325694Ssam if (is->is_if.if_snd.ifq_head) 30425927Ssam acestart(unit); 30524007Ssam return; 30624007Ssam } 30724007Ssam is->is_xcnt--; 30824007Ssam txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm); 30924007Ssam eostat = txseg->tx_csr; 31024007Ssam if ((eostat & TCS_TBFULL) == 0) { 31124007Ssam is->is_stats.tx_retries += eostat & TCS_RTC; 31224007Ssam if (eostat & TCS_RTFAIL) { 31324007Ssam is->is_stats.tx_discarded++; 31424007Ssam is->is_if.if_oerrors++; 31524007Ssam } else 31624007Ssam is->is_stats.tx_datagrams++; 31724007Ssam if (++is->is_eoctr >= 16) 31824007Ssam is->is_eoctr = is->is_segboundry; 31924007Ssam } 32025694Ssam if (is->is_if.if_snd.ifq_head) 32125927Ssam acestart(unit); 32224007Ssam } 32324007Ssam 32424007Ssam /* 32524007Ssam * Ethernet interface receiver interrupt. 32624007Ssam * If input error just drop packet. 32724007Ssam * Otherwise purge input buffered data path and examine 32824007Ssam * packet to determine type. If can't determine length 32924007Ssam * from type, then have to drop packet. Othewise decapsulate 33024007Ssam * packet based on type and pass to type specific higher-level 33124007Ssam * input routine. 33224007Ssam */ 33324007Ssam acerint(unit) 33424007Ssam int unit; 33524007Ssam { 33624007Ssam register struct ace_softc *is = &ace_softc[unit]; 33724007Ssam register struct ifqueue *inq; 33824007Ssam register struct ether_header *ace; 33924007Ssam register struct rx_segment *rxseg; 34024007Ssam int len, s, off, resid; 34124007Ssam struct mbuf *m; 34224007Ssam short eistat; 34324007Ssam 34425694Ssam if ((is->is_if.if_flags&IFF_RUNNING) == 0) 34525694Ssam return; 34624007Ssam again: 34724007Ssam rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm); 34824007Ssam eistat = rxseg->rx_csr; 34924007Ssam if ((eistat & RCS_RBFULL) == 0) 35024007Ssam return; 35124007Ssam is->is_if.if_ipackets++; 35224007Ssam if (++is->is_eictr >= is->is_segboundry) 35324007Ssam is->is_eictr = 0; 35424007Ssam len = eistat & RCS_RBC; 35524007Ssam if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) || 35624007Ssam len < ET_MINLEN || len > ET_MAXLEN+CRC_SIZE) { 35724007Ssam if (eistat & RCS_ROVRN) 35824007Ssam is->is_stats.rx_overruns++; 35924007Ssam if (eistat & RCS_RCRC) 36024007Ssam is->is_stats.rx_crc_errors++; 36124007Ssam if (eistat & RCS_RODD) 36224007Ssam is->is_stats.rx_align_errors++; 36324007Ssam if (len < ET_MINLEN) 36424007Ssam is->is_stats.rx_underruns++; 36524007Ssam if (len > ET_MAXLEN+CRC_SIZE) 36624007Ssam is->is_stats.rx_overruns++; 36724007Ssam is->is_if.if_ierrors++; 36824007Ssam rxseg->rx_csr = 0; 36924007Ssam return; 37024007Ssam } else 37124007Ssam is->is_stats.rx_datagrams++; 37224007Ssam ace = (struct ether_header *)rxseg->rx_data; 37324007Ssam len -= sizeof (struct ether_header); 37424007Ssam /* 37525694Ssam * Deal with trailer protocol: if type is trailer 37624007Ssam * get true type from first 16-bit word past data. 37724007Ssam * Remember that type was trailer by setting off. 37824007Ssam */ 37924007Ssam ace->ether_type = ntohs((u_short)ace->ether_type); 38024007Ssam #define acedataaddr(ace, off, type) \ 38124007Ssam ((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off)))) 38225694Ssam if (ace->ether_type >= ETHERTYPE_TRAIL && 38325694Ssam ace->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 38425694Ssam off = (ace->ether_type - ETHERTYPE_TRAIL) * 512; 38524007Ssam if (off >= ETHERMTU) 38624007Ssam goto setup; /* sanity */ 38724007Ssam ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *)); 38824007Ssam resid = ntohs(*(acedataaddr(ace, off+2, u_short *))); 38924007Ssam if (off + resid > len) 39024007Ssam goto setup; /* sanity */ 39124007Ssam len = off + resid; 39224007Ssam } else 39324007Ssam off = 0; 39424007Ssam if (len == 0) 39524007Ssam goto setup; 39624007Ssam 39724007Ssam /* 39824007Ssam * Pull packet off interface. Off is nonzero if packet 39924007Ssam * has trailing header; aceget will then force this header 40024007Ssam * information to be at the front, but we still have to drop 40124007Ssam * the type and length which are at the front of any trailer data. 40224007Ssam */ 40325855Ssam m = aceget((u_char *)rxseg->rx_data, len, off, &is->is_if); 40424007Ssam if (m == 0) 40524007Ssam goto setup; 40624007Ssam if (off) { 40725855Ssam struct ifnet *ifp; 40825855Ssam 40925855Ssam ifp = *(mtod(m, struct ifnet **)); 41024007Ssam m->m_off += 2 * sizeof (u_short); 41124007Ssam m->m_len -= 2 * sizeof (u_short); 41225855Ssam *(mtod(m, struct ifnet **)) = ifp; 41324007Ssam } 41424007Ssam switch (ace->ether_type) { 41524007Ssam 41624007Ssam #ifdef INET 41725694Ssam case ETHERTYPE_IP: 41824007Ssam schednetisr(NETISR_IP); 41924007Ssam inq = &ipintrq; 42024007Ssam break; 42125855Ssam #endif 42224007Ssam 42325694Ssam case ETHERTYPE_ARP: 42424007Ssam arpinput(&is->is_ac, m); 42524007Ssam goto setup; 42625694Ssam #ifdef NS 42725694Ssam case ETHERTYPE_NS: 42825694Ssam schednetisr(NETISR_NS); 42925694Ssam inq = &nsintrq; 43025694Ssam break; 43125694Ssam 43225694Ssam #endif 43324007Ssam default: 43424007Ssam m_freem(m); 43524007Ssam goto setup; 43624007Ssam } 43724007Ssam if (IF_QFULL(inq)) { 43824007Ssam IF_DROP(inq); 43924007Ssam m_freem(m); 44024007Ssam goto setup; 44124007Ssam } 44224007Ssam s = splimp(); 44324007Ssam IF_ENQUEUE(inq, m); 44424007Ssam splx(s); 44524007Ssam setup: 44624007Ssam rxseg->rx_csr = 0; 44724007Ssam goto again; 44824007Ssam } 44924007Ssam 45024007Ssam /* 45124007Ssam * Ethernet output routine. 45224007Ssam * Encapsulate a packet of type family for the local net. 45324007Ssam * Use trailer local net encapsulation if enough data in first 45424007Ssam * packet leaves a multiple of 512 bytes of data in remainder. 45524007Ssam */ 45624007Ssam aceoutput(ifp, m0, dst) 45724007Ssam struct ifnet *ifp; 45824007Ssam struct mbuf *m0; 45924007Ssam struct sockaddr *dst; 46024007Ssam { 46124007Ssam register struct ace_softc *is = &ace_softc[ifp->if_unit]; 46224007Ssam register struct mbuf *m = m0; 46324007Ssam register struct ether_header *ace; 46424007Ssam register int off; 46524007Ssam struct mbuf *mcopy = (struct mbuf *)0; 46625957Ssam int type, s, error, usetrailers; 46725694Ssam u_char edst[6]; 46824007Ssam struct in_addr idst; 46924007Ssam 47025855Ssam if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 47125855Ssam error = ENETDOWN; 47225855Ssam goto bad; 47325855Ssam } 47424007Ssam switch (dst->sa_family) { 47524007Ssam 47624007Ssam #ifdef INET 47724007Ssam case AF_INET: 47824007Ssam idst = ((struct sockaddr_in *)dst)->sin_addr; 47925957Ssam if (!arpresolve(&is->is_ac, m, &idst, edst, &usetrailers)) 48024007Ssam return (0); /* if not yet resolved */ 48125694Ssam if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr, 48225937Ssam sizeof (edst))) 48324007Ssam mcopy = m_copy(m, 0, (int)M_COPYALL); 48424007Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 48525957Ssam if (usetrailers && off > 0 && (off & 0x1ff) == 0 && 48624007Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 48725694Ssam type = ETHERTYPE_TRAIL + (off>>9); 48824007Ssam m->m_off -= 2 * sizeof (u_short); 48924007Ssam m->m_len += 2 * sizeof (u_short); 49025694Ssam *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); 49124007Ssam *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 49224007Ssam goto gottrailertype; 49324007Ssam } 49425694Ssam type = ETHERTYPE_IP; 49524007Ssam off = 0; 49624007Ssam goto gottype; 49724007Ssam #endif 49825694Ssam #ifdef NS 49925694Ssam case AF_NS: 50025694Ssam bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), 50125694Ssam (caddr_t)edst, sizeof (edst)); 50225694Ssam if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost,sizeof(edst))) 50325694Ssam mcopy = m_copy(m, 0, (int)M_COPYALL); 50425694Ssam else if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, 50525694Ssam sizeof(edst))) 50625694Ssam return(looutput(&loif, m, dst)); 50725694Ssam type = ETHERTYPE_NS; 50825694Ssam off = 0; 50925694Ssam goto gottype; 51025694Ssam #endif 51124007Ssam case AF_UNSPEC: 51224007Ssam ace = (struct ether_header *)dst->sa_data; 51325694Ssam bcopy((caddr_t)ace->ether_dhost, (caddr_t)edst, sizeof (edst)); 51424007Ssam type = ace->ether_type; 51524007Ssam goto gottype; 51624007Ssam 51724007Ssam default: 51825855Ssam log(LOG_ERR, "ace%d: can't handle af%d\n", 51924007Ssam ifp->if_unit, dst->sa_family); 52024007Ssam error = EAFNOSUPPORT; 52124007Ssam goto bad; 52224007Ssam } 52324007Ssam 52424007Ssam gottrailertype: 52524007Ssam /* 52624007Ssam * Packet to be sent as trailer: move first packet 52724007Ssam * (control information) to end of chain. 52824007Ssam */ 52924007Ssam while (m->m_next) 53024007Ssam m = m->m_next; 53124007Ssam m->m_next = m0; 53224007Ssam m = m0->m_next; 53324007Ssam m0->m_next = 0; 53424007Ssam m0 = m; 53524007Ssam 53624007Ssam gottype: 53724007Ssam /* 53824007Ssam * Add local net header. If no space in first mbuf, 53924007Ssam * allocate another. 54024007Ssam */ 54124007Ssam if (m->m_off > MMAXOFF || 54224007Ssam MMINOFF + sizeof (struct ether_header) > m->m_off) { 54324007Ssam m = m_get(M_DONTWAIT, MT_HEADER); 54424007Ssam if (m == 0) { 54524007Ssam error = ENOBUFS; 54624007Ssam goto bad; 54724007Ssam } 54824007Ssam m->m_next = m0; 54924007Ssam m->m_off = MMINOFF; 55024007Ssam m->m_len = sizeof (struct ether_header); 55124007Ssam } else { 55224007Ssam m->m_off -= sizeof (struct ether_header); 55324007Ssam m->m_len += sizeof (struct ether_header); 55424007Ssam } 55524007Ssam ace = mtod(m, struct ether_header *); 55625694Ssam bcopy((caddr_t)edst, (caddr_t)ace->ether_dhost, sizeof (edst)); 55725694Ssam bcopy((caddr_t)is->is_addr, (caddr_t)ace->ether_shost, 55825694Ssam sizeof (is->is_addr)); 55924007Ssam ace->ether_type = htons((u_short)type); 56024007Ssam 56124007Ssam /* 56224007Ssam * Queue message on interface, and start output if interface 56324007Ssam * not yet active. 56424007Ssam */ 56524007Ssam s = splimp(); 56624007Ssam if (IF_QFULL(&ifp->if_snd)) { 56724007Ssam IF_DROP(&ifp->if_snd); 56824007Ssam error = ENOBUFS; 56924007Ssam goto qfull; 57024007Ssam } 57124007Ssam IF_ENQUEUE(&ifp->if_snd, m); 57224007Ssam splx(s); 57325927Ssam acestart(ifp->if_unit); 57424007Ssam return (mcopy ? looutput(&loif, mcopy, dst) : 0); 57524007Ssam qfull: 57624007Ssam m0 = m; 57724007Ssam splx(s); 57824007Ssam bad: 57924007Ssam m_freem(m0); 58024007Ssam if (mcopy) 58124007Ssam m_freem(mcopy); 58224007Ssam return (error); 58324007Ssam } 58424007Ssam 58524007Ssam /* 58624007Ssam * Routine to copy from mbuf chain to transmit buffer on the VERSAbus 58724007Ssam * If packet size is less than the minimum legal size, 58824007Ssam * the buffer is expanded. We probably should zero out the extra 58924007Ssam * bytes for security, but that would slow things down. 59024007Ssam */ 59125694Ssam /*ARGSUSED*/ 59224007Ssam aceput(unit, txbuf, m) 59325855Ssam int unit; 59425694Ssam char *txbuf; 59524007Ssam struct mbuf *m; 59624007Ssam { 59725855Ssam register u_char *bp, *mcp; 59825855Ssam register short *s1, *s2; 59925694Ssam register u_int len; 60024007Ssam register struct mbuf *mp; 60125694Ssam int total; 60224007Ssam 60324007Ssam total = 0; 60425694Ssam bp = (u_char *)txbuf; 60525694Ssam for (mp = m; (mp); mp = mp->m_next) { 60624007Ssam len = mp->m_len; 60724007Ssam if (len == 0) 60824007Ssam continue; 60924007Ssam total += len; 61024007Ssam mcp = mtod(mp, u_char *); 61124007Ssam if (((int)mcp & 01) && ((int)bp & 01)) { 61224007Ssam /* source & destination at odd addresses */ 61325694Ssam movob(bp++, *mcp++); 61424007Ssam --len; 61524007Ssam } 61624007Ssam if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) { 61725694Ssam register u_int l; 61824007Ssam 61924007Ssam s1 = (short *)bp; 62024007Ssam s2 = (short *)mcp; 62124007Ssam l = len >> 1; /* count # of shorts */ 62225694Ssam while (l-- != 0) 62325694Ssam movow(s1++, *s2++); 62424007Ssam len &= 1; /* # remaining bytes */ 62524007Ssam bp = (u_char *)s1; 62624007Ssam mcp = (u_char *)s2; 62724007Ssam } 62825694Ssam while (len-- != 0) 62925694Ssam movob(bp++, *mcp++); 63024007Ssam } 63124007Ssam m_freem(m); 63224007Ssam return (total); 63324007Ssam } 63424007Ssam 63524007Ssam /* 63624007Ssam * Routine to copy from VERSAbus memory into mbufs. 63724007Ssam * 63824007Ssam * Warning: This makes the fairly safe assumption that 63924007Ssam * mbufs have even lengths. 64024007Ssam */ 64125694Ssam /*ARGSUSED*/ 64224007Ssam struct mbuf * 64325855Ssam aceget(rxbuf, totlen, off0, ifp) 64424007Ssam u_char *rxbuf; 64524007Ssam int totlen, off0; 64625855Ssam struct ifnet *ifp; 64724007Ssam { 64825855Ssam register u_char *cp, *mcp; 64924007Ssam register int tlen; 65024007Ssam register struct mbuf *m; 65124007Ssam struct mbuf *top = 0, **mp = ⊤ 65224007Ssam int len, off = off0; 65324007Ssam 65424007Ssam cp = rxbuf + sizeof (struct ether_header); 65524007Ssam while (totlen > 0) { 65624007Ssam MGET(m, M_DONTWAIT, MT_DATA); 65724007Ssam if (m == 0) 65824007Ssam goto bad; 65924007Ssam if (off) { 66024007Ssam len = totlen - off; 66124007Ssam cp = rxbuf + sizeof (struct ether_header) + off; 66224007Ssam } else 66324007Ssam len = totlen; 66425855Ssam if (ifp) 66525855Ssam len += sizeof(ifp); 66629563Ssam if (len >= NBPG) { 66729563Ssam MCLGET(m); 66829563Ssam if (m->m_len == CLBYTES) 66929563Ssam m->m_len = len = MIN(len, CLBYTES); 67029563Ssam else 67124007Ssam m->m_len = len = MIN(MLEN, len); 67224007Ssam } else { 67324007Ssam m->m_len = len = MIN(MLEN, len); 67424007Ssam m->m_off = MMINOFF; 67524007Ssam } 67624007Ssam mcp = mtod(m, u_char *); 67725855Ssam if (ifp) { 67825855Ssam /* 67925855Ssam * Prepend interface pointer to first mbuf. 68025855Ssam */ 68125855Ssam *(mtod(m, struct ifnet **)) = ifp; 68225855Ssam mcp += sizeof(ifp); 68325855Ssam len -= sizeof(ifp); 68425855Ssam ifp = (struct ifnet *)0; 68525855Ssam } 68624007Ssam /*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/ 68724007Ssam /*cp += len; mcp += len;*/ 68824007Ssam tlen = len; 68924007Ssam if (((int)mcp & 01) && ((int)cp & 01)) { 69024007Ssam /* source & destination at odd addresses */ 69124007Ssam *mcp++ = *cp++; 69224007Ssam --tlen; 69324007Ssam } 69424007Ssam if (tlen > 1 && (((int)mcp&01) == 0) && (((int)cp&01) == 0)) { 69524007Ssam register short *s1, *s2; 69624007Ssam register int l; 69724007Ssam 69824007Ssam s1 = (short *)mcp; 69924007Ssam s2 = (short *)cp; 70024007Ssam l = tlen >> 1; /* count # of shorts */ 70124007Ssam while (l-- > 0) /* copy shorts */ 70224007Ssam *s1++ = *s2++; 70324007Ssam tlen &= 1; /* # remaining bytes */ 70424007Ssam mcp = (u_char *)s1; 70524007Ssam cp = (u_char *)s2; 70624007Ssam } 70724007Ssam while (tlen-- > 0) 70824007Ssam *mcp++ = *cp++; 70924007Ssam *mp = m; 71024007Ssam mp = &m->m_next; 71124007Ssam if (off == 0) { 71224007Ssam totlen -= len; 71324007Ssam continue; 71424007Ssam } 71524007Ssam off += len; 71624007Ssam if (off == totlen) { 71724007Ssam cp = rxbuf + sizeof (struct ether_header); 71824007Ssam off = 0; 71924007Ssam totlen = off0; 72024007Ssam } 72124007Ssam } 72224007Ssam return (top); 72324007Ssam bad: 72424007Ssam m_freem(top); 72524007Ssam return (0); 72624007Ssam } 72724007Ssam 72829408Ssam /* backoff table masks */ 72929408Ssam short random_mask_tbl[16] = { 73029563Ssam 0x0040, 0x00c0, 0x01c0, 0x03c0, 0x07c0, 0x0fc0, 0x1fc0, 0x3fc0, 73129563Ssam 0x7fc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0 73229408Ssam }; 73329408Ssam 73424007Ssam acebakoff(is, txseg, retries) 73524007Ssam struct ace_softc *is; 73624007Ssam struct tx_segment *txseg; 73724007Ssam register int retries; 73824007Ssam { 73924007Ssam register short *pBakNum, random_num; 74024007Ssam short *pMask; 74124007Ssam 74224007Ssam pMask = &random_mask_tbl[0]; 74324007Ssam pBakNum = &txseg->tx_backoff[0]; 74424007Ssam while (--retries >= 0) { 74524007Ssam random_num = (is->is_currnd = (is->is_currnd * 18741)-13849); 74624007Ssam random_num &= *pMask++; 74729563Ssam *pBakNum++ = random_num ^ (short)(0xff00 | 0x00fc); 74824007Ssam } 74924007Ssam } 75024007Ssam 75124007Ssam /* 75224007Ssam * Process an ioctl request. 75324007Ssam */ 75424007Ssam aceioctl(ifp, cmd, data) 75524007Ssam register struct ifnet *ifp; 75624007Ssam int cmd; 75724007Ssam caddr_t data; 75824007Ssam { 75925694Ssam register struct ifaddr *ifa = (struct ifaddr *)data; 76025855Ssam struct acedevice *addr; 76125694Ssam int s = splimp(), error = 0; 76224007Ssam 76324007Ssam switch (cmd) { 76424007Ssam 76524007Ssam case SIOCSIFADDR: 76625694Ssam ifp->if_flags |= IFF_UP; 76725855Ssam switch (ifa->ifa_addr.sa_family) { 76825855Ssam #ifdef INET 76925855Ssam case AF_INET: 77025855Ssam aceinit(ifp->if_unit); /* before arpwhohas */ 77125855Ssam ((struct arpcom *)ifp)->ac_ipaddr = 77225855Ssam IA_SIN(ifa)->sin_addr; 77325855Ssam arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 77425855Ssam break; 77525855Ssam #endif 77625855Ssam #ifdef NS 77725855Ssam case AF_NS: { 77825937Ssam struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; 77925937Ssam struct ace_softc *is = &ace_softc[ifp->if_unit]; 78025855Ssam 78125855Ssam if (!ns_nullhost(*ina)) { 78225855Ssam ifp->if_flags &= ~IFF_RUNNING; 78325855Ssam addr = (struct acedevice *) 78425937Ssam aceinfo[ifp->if_unit]->ui_addr; 78525855Ssam movow(&addr->csr, CSR_RESET); 78625855Ssam DELAY(10000); 78725855Ssam /* set station address & copy addr to arp */ 78829408Ssam acesetaddr(ifp->if_unit, addr, 78925855Ssam ina->x_host.c_host); 79025855Ssam } else 79125937Ssam ina->x_host = *(union ns_host *)is->is_addr; 79225855Ssam aceinit(ifp->if_unit); 79325855Ssam break; 79425855Ssam } 79525855Ssam #endif 79625855Ssam default: 79725855Ssam aceinit(ifp->if_unit); 79825855Ssam break; 79925855Ssam } 80024007Ssam break; 80124007Ssam 80225855Ssam case SIOCSIFFLAGS: 80325855Ssam if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) { 80425855Ssam addr = (struct acedevice *) 80525855Ssam (aceinfo[ifp->if_unit]->ui_addr); 80625855Ssam movow(&addr->csr, CSR_RESET); 80725855Ssam ifp->if_flags &= ~IFF_RUNNING; 80825855Ssam } else if (ifp->if_flags&IFF_UP && 80925855Ssam (ifp->if_flags&IFF_RUNNING) == 0) 81025855Ssam aceinit(ifp->if_unit); 81124007Ssam break; 81224007Ssam 81324007Ssam default: 81424007Ssam error = EINVAL; 81524007Ssam } 81624007Ssam splx(s); 81724007Ssam return (error); 81824007Ssam } 81924007Ssam 82029408Ssam /* 82129408Ssam * Set the on-board station address, then read it back 82229408Ssam * to initialize the address used by ARP (among others). 82329408Ssam */ 82429408Ssam acesetaddr(unit, addr, station) 82529408Ssam short unit; 82629408Ssam struct acedevice *addr; 82730295Ssam u_char *station; 82829408Ssam { 82929408Ssam struct ace_softc *is = &ace_softc[unit]; 83029408Ssam register short *wp, i; 83129408Ssam 83229408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 83329408Ssam movow(wp++, ~*station++); 83429408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 83529408Ssam is->is_addr[i] = ~*wp++; 83629408Ssam printf("ace%d: hardware address %s\n", unit, 83729408Ssam ether_sprintf(is->is_addr)); 83829408Ssam } 83929408Ssam 84029408Ssam /* 84129408Ssam * Setup the device for use. Initialize dual-ported memory, 84229408Ssam * backoff parameters, and various other software state. 84329408Ssam */ 84425927Ssam acesetup(unit) 84524007Ssam int unit; 84624007Ssam { 84724007Ssam register struct ace_softc *is = &ace_softc[unit]; 84825927Ssam register char *pData1; 84925694Ssam register short i; 85025927Ssam struct acedevice *addr; 85124007Ssam 85225927Ssam bzero(is->is_dpm, 16384*2); 85324007Ssam is->is_currnd = 49123; 85425927Ssam addr = (struct acedevice *)aceinfo[unit]->ui_addr; 85524007Ssam is->is_segboundry = (addr->segb >> 11) & 0xf; 85625927Ssam pData1 = is->is_dpm + (is->is_segboundry << 11); 85724007Ssam for (i = SEG_MAX + 1 - is->is_segboundry; --i >= 0;) { 85824007Ssam acebakoff(is, (struct tx_segment *)pData1, 15); 85924007Ssam pData1 += sizeof (struct tx_segment); 86024007Ssam } 86124007Ssam is->is_eictr = 0; 86224007Ssam is->is_eoctr = is->is_txnext = is->is_segboundry; 86324007Ssam bzero((char *)&is->is_stats, sizeof (is->is_stats)); 86424007Ssam } 86524007Ssam #endif 866