134405Skarels /* 234405Skarels * Copyright (c) 1988 Regents of the University of California. 334405Skarels * All rights reserved. 434405Skarels * 534405Skarels * Redistribution and use in source and binary forms are permitted 6*34864Sbostic * provided that the above copyright notice and this paragraph are 7*34864Sbostic * duplicated in all such forms and that any documentation, 8*34864Sbostic * advertising materials, and other materials related to such 9*34864Sbostic * distribution and use acknowledge that the software was developed 10*34864Sbostic * by the University of California, Berkeley. The name of the 11*34864Sbostic * University may not be used to endorse or promote products derived 12*34864Sbostic * from this software without specific prior written permission. 13*34864Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34864Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34864Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1634405Skarels * 17*34864Sbostic * @(#)if_ace.c 7.2 (Berkeley) 06/29/88 1834405Skarels */ 1924007Ssam 2024007Ssam /* 2124007Ssam * ACC VERSAbus Ethernet controller 2224007Ssam */ 2324007Ssam #include "ace.h" 2424007Ssam #if NACE > 0 2524007Ssam 2625694Ssam #include "param.h" 2725694Ssam #include "systm.h" 2825694Ssam #include "mbuf.h" 2925694Ssam #include "buf.h" 3025694Ssam #include "protosw.h" 3125694Ssam #include "socket.h" 3225694Ssam #include "vmmac.h" 3325694Ssam #include "ioctl.h" 3425694Ssam #include "errno.h" 3525694Ssam #include "vmparam.h" 3625855Ssam #include "syslog.h" 3724007Ssam 3824007Ssam #include "../net/if.h" 3924007Ssam #include "../net/netisr.h" 4024007Ssam #include "../net/route.h" 4125855Ssam #ifdef INET 4224007Ssam #include "../netinet/in.h" 4324007Ssam #include "../netinet/in_systm.h" 4425694Ssam #include "../netinet/in_var.h" 4524007Ssam #include "../netinet/ip.h" 4624007Ssam #include "../netinet/ip_var.h" 4724007Ssam #include "../netinet/if_ether.h" 4825855Ssam #endif 4925855Ssam #ifdef NS 5025855Ssam #include "../netns/ns.h" 5125855Ssam #include "../netns/ns_if.h" 5225855Ssam #endif 5324007Ssam 5430229Ssam #include "../machine/cpu.h" 5530229Ssam #include "../machine/pte.h" 5630229Ssam 5724007Ssam #include "../tahoe/mtpr.h" 5824007Ssam #include "../tahoeif/if_acereg.h" 5925694Ssam #include "../tahoevba/vbavar.h" 6024007Ssam 6124007Ssam int aceprobe(), aceattach(), acerint(), acecint(); 6224007Ssam struct vba_device *aceinfo[NACE]; 6325983Ssam long acestd[] = { 0 }; 6424007Ssam struct vba_driver acedriver = 6525927Ssam { aceprobe, 0, aceattach, 0, acestd, "ace", aceinfo, "v/eiu", 0 }; 6624007Ssam 6724007Ssam int aceinit(), aceoutput(), aceioctl(), acereset(); 6824007Ssam struct mbuf *aceget(); 6924007Ssam 7024007Ssam /* 7124007Ssam * Ethernet software status per interface. 7224007Ssam * 7324007Ssam * Each interface is referenced by a network interface structure, 7424007Ssam * is_if, which the routing code uses to locate the interface. 7524007Ssam * This structure contains the output queue for the interface, its address, ... 7624007Ssam */ 7724007Ssam struct ace_softc { 7824007Ssam struct arpcom is_ac; /* Ethernet common part */ 7924007Ssam #define is_if is_ac.ac_if /* network-visible interface */ 8024007Ssam #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ 8124007Ssam short is_flags; 8224007Ssam #define ACEF_OACTIVE 0x1 /* output is active */ 8324007Ssam #define ACEF_RCVPENDING 0x2 /* start rcv in acecint */ 8424007Ssam short is_promiscuous; /* true is enabled */ 8524007Ssam short is_segboundry; /* first TX Seg in dpm */ 8624007Ssam short is_eictr; /* Rx segment tracking ctr */ 8724007Ssam short is_eoctr; /* Tx segment tracking ctr */ 8824007Ssam short is_txnext; /* Next available Tx segment */ 8924007Ssam short is_currnd; /* current random backoff */ 9024007Ssam struct ace_stats is_stats; /* holds board statistics */ 9124007Ssam short is_xcnt; /* count xmitted segments to be acked 9224007Ssam by the controller */ 9325855Ssam long is_ivec; /* autoconfig interrupt vector base */ 9425927Ssam struct pte *is_map; /* pte map for dual ported memory */ 9525927Ssam caddr_t is_dpm; /* address of mapped memory */ 9624007Ssam } ace_softc[NACE]; 9724007Ssam extern struct ifnet loif; 9824007Ssam 9925855Ssam aceprobe(reg, vi) 10024007Ssam caddr_t reg; 10125855Ssam struct vba_device *vi; 10224007Ssam { 10325855Ssam register br, cvec; /* must be r12, r11 */ 10425855Ssam struct acedevice *ap = (struct acedevice *)reg; 10525855Ssam struct ace_softc *is = &ace_softc[vi->ui_unit]; 10624007Ssam 10724007Ssam #ifdef lint 10830295Ssam br = 0; cvec = br; br = cvec; 10924007Ssam acerint(0); acecint(0); 11024007Ssam #endif 11124007Ssam if (badaddr(reg, 2)) 11225855Ssam return (0); 11325855Ssam movow(&ap->csr, CSR_RESET); 11424007Ssam DELAY(10000); 11525855Ssam #ifdef notdef 11625855Ssam /* 11725855Ssam * Select two spaces for the interrupts aligned to an 11825855Ssam * eight vector boundary and fitting in 8 bits (as 11925855Ssam * required by the controller) -- YECH. The controller 12025855Ssam * will be notified later at initialization time. 12125855Ssam */ 12225855Ssam if ((vi->ui_hd->vh_lastiv -= 2) > 0xff) 12325855Ssam vi->ui_hd->vh_lastiv = 0x200; 12425855Ssam is->is_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x7; 12525855Ssam #else 12625855Ssam is->is_ivec = 0x90+vi->ui_unit*8; 12725855Ssam #endif 12825855Ssam br = 0x14, cvec = is->is_ivec; /* XXX */ 12925855Ssam return (sizeof (*ap)); 13024007Ssam } 13124007Ssam 13224007Ssam /* 13324007Ssam * Interface exists: make available by filling in network interface 13424007Ssam * record. System will initialize the interface when it is ready 13524007Ssam * to accept packets. 13624007Ssam */ 13724007Ssam aceattach(ui) 13824007Ssam struct vba_device *ui; 13924007Ssam { 14024007Ssam register short unit = ui->ui_unit; 14124007Ssam register struct ace_softc *is = &ace_softc[unit]; 14224007Ssam register struct ifnet *ifp = &is->is_if; 14324007Ssam register struct acedevice *addr = (struct acedevice *)ui->ui_addr; 14424007Ssam register short *wp, i; 14524007Ssam 14624007Ssam ifp->if_unit = unit; 14724007Ssam ifp->if_name = "ace"; 14824007Ssam ifp->if_mtu = ETHERMTU; 14924007Ssam /* 15029408Ssam * Get station's addresses and set multicast hash table. 15124007Ssam */ 15229408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 15329408Ssam is->is_addr[i] = ~*wp++; 15429408Ssam printf("ace%d: hardware address %s\n", unit, 15529408Ssam ether_sprintf(is->is_addr)); 15624007Ssam is->is_promiscuous = 0; 15729408Ssam for (wp = (short *)addr->hash, i = 0; i < 8; i++) 15829408Ssam movow(wp++, ~0xf); 15925694Ssam movow(&addr->bcastena[0], ~0xffff); 16025694Ssam movow(&addr->bcastena[1], ~0xffff); 16125927Ssam /* 16225927Ssam * Allocate and map dual ported VERSAbus memory. 16325927Ssam */ 16431735Skarels if (vbmemalloc(32, (caddr_t)ui->ui_flags, 16531735Skarels &is->is_map, &is->is_dpm) == 0) { 16631735Skarels printf("ace%d: can't allocate VERSAbus memory map\n", unit); 16731735Skarels return; 16831735Skarels } 16925927Ssam 17024007Ssam ifp->if_init = aceinit; 17124007Ssam ifp->if_output = aceoutput; 17224007Ssam ifp->if_ioctl = aceioctl; 17324007Ssam ifp->if_reset = acereset; 17425694Ssam ifp->if_flags = IFF_BROADCAST; 17524007Ssam if_attach(ifp); 17624007Ssam } 17724007Ssam 17824007Ssam /* 17924007Ssam * Reset of interface after "system" reset. 18024007Ssam */ 18124007Ssam acereset(unit, vban) 18224007Ssam int unit, vban; 18324007Ssam { 18424007Ssam register struct vba_device *ui; 18524007Ssam 18624007Ssam if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 || 18724007Ssam ui->ui_vbanum != vban) 18824007Ssam return; 18924007Ssam printf(" ace%d", unit); 19024007Ssam aceinit(unit); 19124007Ssam } 19224007Ssam 19324007Ssam /* 19424007Ssam * Initialization of interface; clear recorded pending operations 19524007Ssam */ 19624007Ssam aceinit(unit) 19724007Ssam int unit; 19824007Ssam { 19924007Ssam register struct ace_softc *is = &ace_softc[unit]; 20024007Ssam register struct vba_device *ui = aceinfo[unit]; 20124007Ssam register struct acedevice *addr; 20224007Ssam register struct ifnet *ifp = &is->is_if; 20324007Ssam register short Csr; 20425694Ssam register int s; 20524007Ssam 20625694Ssam if (ifp->if_addrlist == (struct ifaddr *)0) 20724007Ssam return; 20824007Ssam if ((ifp->if_flags & IFF_RUNNING) == 0) { 20924007Ssam /* 21024007Ssam * Reset the controller, initialize the recieve buffers, 21124007Ssam * and turn the controller on again and set board online. 21224007Ssam */ 21324007Ssam addr = (struct acedevice *)ui->ui_addr; 21424007Ssam s = splimp(); 21525694Ssam movow(&addr->csr, CSR_RESET); 21624007Ssam DELAY(10000); 21724007Ssam 21824007Ssam /* 21925927Ssam * Clean up dpm since the controller might 22025927Ssam * jumble dpm after reset. 22124007Ssam */ 22225927Ssam acesetup(unit); 22325694Ssam movow(&addr->csr, CSR_GO); 22424007Ssam Csr = addr->csr; 22524007Ssam if (Csr & CSR_ACTIVE) { 22625855Ssam movow(&addr->ivct, is->is_ivec); 22724007Ssam Csr |= CSR_IENA | is->is_promiscuous; 22825694Ssam movow(&addr->csr, Csr); 22924007Ssam is->is_flags = 0; 23024007Ssam is->is_xcnt = 0; 23125694Ssam is->is_if.if_flags |= IFF_RUNNING; 23224007Ssam } 23324007Ssam splx(s); 23424007Ssam } 23525694Ssam if (is->is_if.if_snd.ifq_head) 23625927Ssam acestart(unit); 23724007Ssam } 23824007Ssam 23924007Ssam /* 24024007Ssam * Start output on interface. 24124007Ssam * Get another datagram to send off of the interface queue, 24224007Ssam * and map it to the interface before starting the output. 24324007Ssam */ 24425927Ssam acestart(unit) 24525927Ssam int unit; 24624007Ssam { 24724007Ssam register struct tx_segment *txs; 24825694Ssam register long len; 24925694Ssam register int s; 25024007Ssam register struct ace_softc *is = &ace_softc[unit]; 25124007Ssam struct mbuf *m; 25225694Ssam short retries; 25324007Ssam 25425927Ssam if (is->is_flags & ACEF_OACTIVE) 25525927Ssam return; 25625927Ssam is->is_flags |= ACEF_OACTIVE; 25724007Ssam again: 25824007Ssam txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11)); 25924007Ssam if (txs->tx_csr & TCS_TBFULL) { 26024007Ssam is->is_stats.tx_busy++; 26125927Ssam is->is_flags &= ~ACEF_OACTIVE; 26224007Ssam return; 26324007Ssam } 26425694Ssam s = splimp(); 26524007Ssam IF_DEQUEUE(&is->is_if.if_snd, m); 26625694Ssam splx(s); 26725927Ssam if (m == 0) { 26825927Ssam is->is_flags &= ~ACEF_OACTIVE; 26924007Ssam return; 27025927Ssam } 27124007Ssam len = aceput(unit, txs->tx_data, m); 27224007Ssam retries = txs->tx_csr & TCS_RTC; 27324007Ssam if (retries > 0) 27424007Ssam acebakoff(is, txs, retries); 27524007Ssam 27624007Ssam /* 27724007Ssam * Ensure minimum packet length. 27824007Ssam * This makes the safe assumtion that there are no virtual holes 27924007Ssam * after the data. 28024007Ssam * For security, it might be wise to zero out the added bytes, 28124007Ssam * but we're mainly interested in speed at the moment. 28224007Ssam */ 28324007Ssam if (len - sizeof (struct ether_header) < ETHERMIN) 28424007Ssam len = ETHERMIN + sizeof (struct ether_header); 28524007Ssam if (++is->is_txnext > SEG_MAX) 28624007Ssam is->is_txnext = is->is_segboundry; 28724007Ssam is->is_if.if_opackets++; 28824007Ssam is->is_xcnt++; 28924007Ssam len = (len & 0x7fff) | TCS_TBFULL; 29025694Ssam movow(txs, len); 29124007Ssam goto again; 29224007Ssam } 29324007Ssam 29424007Ssam /* 29524007Ssam * Transmit done interrupt. 29624007Ssam */ 29724007Ssam acecint(unit) 29824007Ssam int unit; 29924007Ssam { 30024007Ssam register struct ace_softc *is = &ace_softc[unit]; 30124007Ssam register struct tx_segment *txseg; 30225694Ssam short eostat; 30324007Ssam 30424007Ssam if (is->is_xcnt <= 0) { 30525855Ssam log(LOG_ERR, "ace%d: stray xmit interrupt, xcnt %d\n", 30624007Ssam unit, is->is_xcnt); 30724007Ssam is->is_xcnt = 0; 30825694Ssam if (is->is_if.if_snd.ifq_head) 30925927Ssam acestart(unit); 31024007Ssam return; 31124007Ssam } 31224007Ssam is->is_xcnt--; 31324007Ssam txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm); 31424007Ssam eostat = txseg->tx_csr; 31524007Ssam if ((eostat & TCS_TBFULL) == 0) { 31624007Ssam is->is_stats.tx_retries += eostat & TCS_RTC; 31724007Ssam if (eostat & TCS_RTFAIL) { 31824007Ssam is->is_stats.tx_discarded++; 31924007Ssam is->is_if.if_oerrors++; 32024007Ssam } else 32124007Ssam is->is_stats.tx_datagrams++; 32224007Ssam if (++is->is_eoctr >= 16) 32324007Ssam is->is_eoctr = is->is_segboundry; 32424007Ssam } 32525694Ssam if (is->is_if.if_snd.ifq_head) 32625927Ssam acestart(unit); 32724007Ssam } 32824007Ssam 32924007Ssam /* 33024007Ssam * Ethernet interface receiver interrupt. 33124007Ssam * If input error just drop packet. 33224007Ssam * Otherwise purge input buffered data path and examine 33324007Ssam * packet to determine type. If can't determine length 33424007Ssam * from type, then have to drop packet. Othewise decapsulate 33524007Ssam * packet based on type and pass to type specific higher-level 33624007Ssam * input routine. 33724007Ssam */ 33824007Ssam acerint(unit) 33924007Ssam int unit; 34024007Ssam { 34124007Ssam register struct ace_softc *is = &ace_softc[unit]; 34224007Ssam register struct ifqueue *inq; 34324007Ssam register struct ether_header *ace; 34424007Ssam register struct rx_segment *rxseg; 34524007Ssam int len, s, off, resid; 34624007Ssam struct mbuf *m; 34724007Ssam short eistat; 34824007Ssam 34925694Ssam if ((is->is_if.if_flags&IFF_RUNNING) == 0) 35025694Ssam return; 35124007Ssam again: 35224007Ssam rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm); 35324007Ssam eistat = rxseg->rx_csr; 35424007Ssam if ((eistat & RCS_RBFULL) == 0) 35524007Ssam return; 35624007Ssam is->is_if.if_ipackets++; 35724007Ssam if (++is->is_eictr >= is->is_segboundry) 35824007Ssam is->is_eictr = 0; 35924007Ssam len = eistat & RCS_RBC; 36024007Ssam if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) || 36124007Ssam len < ET_MINLEN || len > ET_MAXLEN+CRC_SIZE) { 36224007Ssam if (eistat & RCS_ROVRN) 36324007Ssam is->is_stats.rx_overruns++; 36424007Ssam if (eistat & RCS_RCRC) 36524007Ssam is->is_stats.rx_crc_errors++; 36624007Ssam if (eistat & RCS_RODD) 36724007Ssam is->is_stats.rx_align_errors++; 36824007Ssam if (len < ET_MINLEN) 36924007Ssam is->is_stats.rx_underruns++; 37024007Ssam if (len > ET_MAXLEN+CRC_SIZE) 37124007Ssam is->is_stats.rx_overruns++; 37224007Ssam is->is_if.if_ierrors++; 37324007Ssam rxseg->rx_csr = 0; 37424007Ssam return; 37524007Ssam } else 37624007Ssam is->is_stats.rx_datagrams++; 37724007Ssam ace = (struct ether_header *)rxseg->rx_data; 37824007Ssam len -= sizeof (struct ether_header); 37924007Ssam /* 38025694Ssam * Deal with trailer protocol: if type is trailer 38124007Ssam * get true type from first 16-bit word past data. 38224007Ssam * Remember that type was trailer by setting off. 38324007Ssam */ 38424007Ssam ace->ether_type = ntohs((u_short)ace->ether_type); 38524007Ssam #define acedataaddr(ace, off, type) \ 38624007Ssam ((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off)))) 38725694Ssam if (ace->ether_type >= ETHERTYPE_TRAIL && 38825694Ssam ace->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 38925694Ssam off = (ace->ether_type - ETHERTYPE_TRAIL) * 512; 39024007Ssam if (off >= ETHERMTU) 39124007Ssam goto setup; /* sanity */ 39224007Ssam ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *)); 39324007Ssam resid = ntohs(*(acedataaddr(ace, off+2, u_short *))); 39424007Ssam if (off + resid > len) 39524007Ssam goto setup; /* sanity */ 39624007Ssam len = off + resid; 39724007Ssam } else 39824007Ssam off = 0; 39924007Ssam if (len == 0) 40024007Ssam goto setup; 40124007Ssam 40224007Ssam /* 40324007Ssam * Pull packet off interface. Off is nonzero if packet 40424007Ssam * has trailing header; aceget will then force this header 40524007Ssam * information to be at the front, but we still have to drop 40624007Ssam * the type and length which are at the front of any trailer data. 40724007Ssam */ 40825855Ssam m = aceget((u_char *)rxseg->rx_data, len, off, &is->is_if); 40924007Ssam if (m == 0) 41024007Ssam goto setup; 41124007Ssam if (off) { 41225855Ssam struct ifnet *ifp; 41325855Ssam 41425855Ssam ifp = *(mtod(m, struct ifnet **)); 41524007Ssam m->m_off += 2 * sizeof (u_short); 41624007Ssam m->m_len -= 2 * sizeof (u_short); 41725855Ssam *(mtod(m, struct ifnet **)) = ifp; 41824007Ssam } 41924007Ssam switch (ace->ether_type) { 42024007Ssam 42124007Ssam #ifdef INET 42225694Ssam case ETHERTYPE_IP: 42324007Ssam schednetisr(NETISR_IP); 42424007Ssam inq = &ipintrq; 42524007Ssam break; 42625855Ssam #endif 42724007Ssam 42825694Ssam case ETHERTYPE_ARP: 42924007Ssam arpinput(&is->is_ac, m); 43024007Ssam goto setup; 43125694Ssam #ifdef NS 43225694Ssam case ETHERTYPE_NS: 43325694Ssam schednetisr(NETISR_NS); 43425694Ssam inq = &nsintrq; 43525694Ssam break; 43625694Ssam 43725694Ssam #endif 43824007Ssam default: 43924007Ssam m_freem(m); 44024007Ssam goto setup; 44124007Ssam } 44224007Ssam if (IF_QFULL(inq)) { 44324007Ssam IF_DROP(inq); 44424007Ssam m_freem(m); 44524007Ssam goto setup; 44624007Ssam } 44724007Ssam s = splimp(); 44824007Ssam IF_ENQUEUE(inq, m); 44924007Ssam splx(s); 45024007Ssam setup: 45124007Ssam rxseg->rx_csr = 0; 45224007Ssam goto again; 45324007Ssam } 45424007Ssam 45524007Ssam /* 45624007Ssam * Ethernet output routine. 45724007Ssam * Encapsulate a packet of type family for the local net. 45824007Ssam * Use trailer local net encapsulation if enough data in first 45924007Ssam * packet leaves a multiple of 512 bytes of data in remainder. 46024007Ssam */ 46124007Ssam aceoutput(ifp, m0, dst) 46224007Ssam struct ifnet *ifp; 46324007Ssam struct mbuf *m0; 46424007Ssam struct sockaddr *dst; 46524007Ssam { 46624007Ssam register struct ace_softc *is = &ace_softc[ifp->if_unit]; 46724007Ssam register struct mbuf *m = m0; 46824007Ssam register struct ether_header *ace; 46924007Ssam register int off; 47024007Ssam struct mbuf *mcopy = (struct mbuf *)0; 47125957Ssam int type, s, error, usetrailers; 47225694Ssam u_char edst[6]; 47324007Ssam struct in_addr idst; 47424007Ssam 47525855Ssam if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 47625855Ssam error = ENETDOWN; 47725855Ssam goto bad; 47825855Ssam } 47924007Ssam switch (dst->sa_family) { 48024007Ssam 48124007Ssam #ifdef INET 48224007Ssam case AF_INET: 48324007Ssam idst = ((struct sockaddr_in *)dst)->sin_addr; 48425957Ssam if (!arpresolve(&is->is_ac, m, &idst, edst, &usetrailers)) 48524007Ssam return (0); /* if not yet resolved */ 48625694Ssam if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr, 48725937Ssam sizeof (edst))) 48824007Ssam mcopy = m_copy(m, 0, (int)M_COPYALL); 48924007Ssam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 49025957Ssam if (usetrailers && off > 0 && (off & 0x1ff) == 0 && 49124007Ssam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 49225694Ssam type = ETHERTYPE_TRAIL + (off>>9); 49324007Ssam m->m_off -= 2 * sizeof (u_short); 49424007Ssam m->m_len += 2 * sizeof (u_short); 49525694Ssam *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); 49624007Ssam *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 49724007Ssam goto gottrailertype; 49824007Ssam } 49925694Ssam type = ETHERTYPE_IP; 50024007Ssam off = 0; 50124007Ssam goto gottype; 50224007Ssam #endif 50325694Ssam #ifdef NS 50425694Ssam case AF_NS: 50525694Ssam bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), 50625694Ssam (caddr_t)edst, sizeof (edst)); 50725694Ssam if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost,sizeof(edst))) 50825694Ssam mcopy = m_copy(m, 0, (int)M_COPYALL); 50925694Ssam else if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, 51025694Ssam sizeof(edst))) 51125694Ssam return(looutput(&loif, m, dst)); 51225694Ssam type = ETHERTYPE_NS; 51325694Ssam off = 0; 51425694Ssam goto gottype; 51525694Ssam #endif 51624007Ssam case AF_UNSPEC: 51724007Ssam ace = (struct ether_header *)dst->sa_data; 51825694Ssam bcopy((caddr_t)ace->ether_dhost, (caddr_t)edst, sizeof (edst)); 51924007Ssam type = ace->ether_type; 52024007Ssam goto gottype; 52124007Ssam 52224007Ssam default: 52325855Ssam log(LOG_ERR, "ace%d: can't handle af%d\n", 52424007Ssam ifp->if_unit, dst->sa_family); 52524007Ssam error = EAFNOSUPPORT; 52624007Ssam goto bad; 52724007Ssam } 52824007Ssam 52924007Ssam gottrailertype: 53024007Ssam /* 53124007Ssam * Packet to be sent as trailer: move first packet 53224007Ssam * (control information) to end of chain. 53324007Ssam */ 53424007Ssam while (m->m_next) 53524007Ssam m = m->m_next; 53624007Ssam m->m_next = m0; 53724007Ssam m = m0->m_next; 53824007Ssam m0->m_next = 0; 53924007Ssam m0 = m; 54024007Ssam 54124007Ssam gottype: 54224007Ssam /* 54324007Ssam * Add local net header. If no space in first mbuf, 54424007Ssam * allocate another. 54524007Ssam */ 54624007Ssam if (m->m_off > MMAXOFF || 54724007Ssam MMINOFF + sizeof (struct ether_header) > m->m_off) { 54824007Ssam m = m_get(M_DONTWAIT, MT_HEADER); 54924007Ssam if (m == 0) { 55024007Ssam error = ENOBUFS; 55124007Ssam goto bad; 55224007Ssam } 55324007Ssam m->m_next = m0; 55424007Ssam m->m_off = MMINOFF; 55524007Ssam m->m_len = sizeof (struct ether_header); 55624007Ssam } else { 55724007Ssam m->m_off -= sizeof (struct ether_header); 55824007Ssam m->m_len += sizeof (struct ether_header); 55924007Ssam } 56024007Ssam ace = mtod(m, struct ether_header *); 56125694Ssam bcopy((caddr_t)edst, (caddr_t)ace->ether_dhost, sizeof (edst)); 56225694Ssam bcopy((caddr_t)is->is_addr, (caddr_t)ace->ether_shost, 56325694Ssam sizeof (is->is_addr)); 56424007Ssam ace->ether_type = htons((u_short)type); 56524007Ssam 56624007Ssam /* 56724007Ssam * Queue message on interface, and start output if interface 56824007Ssam * not yet active. 56924007Ssam */ 57024007Ssam s = splimp(); 57124007Ssam if (IF_QFULL(&ifp->if_snd)) { 57224007Ssam IF_DROP(&ifp->if_snd); 57324007Ssam error = ENOBUFS; 57424007Ssam goto qfull; 57524007Ssam } 57624007Ssam IF_ENQUEUE(&ifp->if_snd, m); 57724007Ssam splx(s); 57825927Ssam acestart(ifp->if_unit); 57924007Ssam return (mcopy ? looutput(&loif, mcopy, dst) : 0); 58024007Ssam qfull: 58124007Ssam m0 = m; 58224007Ssam splx(s); 58324007Ssam bad: 58424007Ssam m_freem(m0); 58524007Ssam if (mcopy) 58624007Ssam m_freem(mcopy); 58724007Ssam return (error); 58824007Ssam } 58924007Ssam 59024007Ssam /* 59124007Ssam * Routine to copy from mbuf chain to transmit buffer on the VERSAbus 59224007Ssam * If packet size is less than the minimum legal size, 59324007Ssam * the buffer is expanded. We probably should zero out the extra 59424007Ssam * bytes for security, but that would slow things down. 59524007Ssam */ 59625694Ssam /*ARGSUSED*/ 59724007Ssam aceput(unit, txbuf, m) 59825855Ssam int unit; 59925694Ssam char *txbuf; 60024007Ssam struct mbuf *m; 60124007Ssam { 60225855Ssam register u_char *bp, *mcp; 60325855Ssam register short *s1, *s2; 60425694Ssam register u_int len; 60524007Ssam register struct mbuf *mp; 60625694Ssam int total; 60724007Ssam 60824007Ssam total = 0; 60925694Ssam bp = (u_char *)txbuf; 61025694Ssam for (mp = m; (mp); mp = mp->m_next) { 61124007Ssam len = mp->m_len; 61224007Ssam if (len == 0) 61324007Ssam continue; 61424007Ssam total += len; 61524007Ssam mcp = mtod(mp, u_char *); 61624007Ssam if (((int)mcp & 01) && ((int)bp & 01)) { 61724007Ssam /* source & destination at odd addresses */ 61825694Ssam movob(bp++, *mcp++); 61924007Ssam --len; 62024007Ssam } 62124007Ssam if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) { 62225694Ssam register u_int l; 62324007Ssam 62424007Ssam s1 = (short *)bp; 62524007Ssam s2 = (short *)mcp; 62624007Ssam l = len >> 1; /* count # of shorts */ 62725694Ssam while (l-- != 0) 62825694Ssam movow(s1++, *s2++); 62924007Ssam len &= 1; /* # remaining bytes */ 63024007Ssam bp = (u_char *)s1; 63124007Ssam mcp = (u_char *)s2; 63224007Ssam } 63325694Ssam while (len-- != 0) 63425694Ssam movob(bp++, *mcp++); 63524007Ssam } 63624007Ssam m_freem(m); 63724007Ssam return (total); 63824007Ssam } 63924007Ssam 64024007Ssam /* 64124007Ssam * Routine to copy from VERSAbus memory into mbufs. 64224007Ssam * 64324007Ssam * Warning: This makes the fairly safe assumption that 64424007Ssam * mbufs have even lengths. 64524007Ssam */ 64625694Ssam /*ARGSUSED*/ 64724007Ssam struct mbuf * 64825855Ssam aceget(rxbuf, totlen, off0, ifp) 64924007Ssam u_char *rxbuf; 65024007Ssam int totlen, off0; 65125855Ssam struct ifnet *ifp; 65224007Ssam { 65325855Ssam register u_char *cp, *mcp; 65424007Ssam register int tlen; 65524007Ssam register struct mbuf *m; 65624007Ssam struct mbuf *top = 0, **mp = ⊤ 65724007Ssam int len, off = off0; 65824007Ssam 65924007Ssam cp = rxbuf + sizeof (struct ether_header); 66024007Ssam while (totlen > 0) { 66124007Ssam MGET(m, M_DONTWAIT, MT_DATA); 66224007Ssam if (m == 0) 66324007Ssam goto bad; 66424007Ssam if (off) { 66524007Ssam len = totlen - off; 66624007Ssam cp = rxbuf + sizeof (struct ether_header) + off; 66724007Ssam } else 66824007Ssam len = totlen; 66925855Ssam if (ifp) 67025855Ssam len += sizeof(ifp); 67129563Ssam if (len >= NBPG) { 67229563Ssam MCLGET(m); 67329563Ssam if (m->m_len == CLBYTES) 67429563Ssam m->m_len = len = MIN(len, CLBYTES); 67529563Ssam else 67624007Ssam m->m_len = len = MIN(MLEN, len); 67724007Ssam } else { 67824007Ssam m->m_len = len = MIN(MLEN, len); 67924007Ssam m->m_off = MMINOFF; 68024007Ssam } 68124007Ssam mcp = mtod(m, u_char *); 68225855Ssam if (ifp) { 68325855Ssam /* 68425855Ssam * Prepend interface pointer to first mbuf. 68525855Ssam */ 68625855Ssam *(mtod(m, struct ifnet **)) = ifp; 68725855Ssam mcp += sizeof(ifp); 68825855Ssam len -= sizeof(ifp); 68925855Ssam ifp = (struct ifnet *)0; 69025855Ssam } 69124007Ssam /*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/ 69224007Ssam /*cp += len; mcp += len;*/ 69324007Ssam tlen = len; 69424007Ssam if (((int)mcp & 01) && ((int)cp & 01)) { 69524007Ssam /* source & destination at odd addresses */ 69624007Ssam *mcp++ = *cp++; 69724007Ssam --tlen; 69824007Ssam } 69924007Ssam if (tlen > 1 && (((int)mcp&01) == 0) && (((int)cp&01) == 0)) { 70024007Ssam register short *s1, *s2; 70124007Ssam register int l; 70224007Ssam 70324007Ssam s1 = (short *)mcp; 70424007Ssam s2 = (short *)cp; 70524007Ssam l = tlen >> 1; /* count # of shorts */ 70624007Ssam while (l-- > 0) /* copy shorts */ 70724007Ssam *s1++ = *s2++; 70824007Ssam tlen &= 1; /* # remaining bytes */ 70924007Ssam mcp = (u_char *)s1; 71024007Ssam cp = (u_char *)s2; 71124007Ssam } 71224007Ssam while (tlen-- > 0) 71324007Ssam *mcp++ = *cp++; 71424007Ssam *mp = m; 71524007Ssam mp = &m->m_next; 71624007Ssam if (off == 0) { 71724007Ssam totlen -= len; 71824007Ssam continue; 71924007Ssam } 72024007Ssam off += len; 72124007Ssam if (off == totlen) { 72224007Ssam cp = rxbuf + sizeof (struct ether_header); 72324007Ssam off = 0; 72424007Ssam totlen = off0; 72524007Ssam } 72624007Ssam } 72724007Ssam return (top); 72824007Ssam bad: 72924007Ssam m_freem(top); 73024007Ssam return (0); 73124007Ssam } 73224007Ssam 73329408Ssam /* backoff table masks */ 73429408Ssam short random_mask_tbl[16] = { 73529563Ssam 0x0040, 0x00c0, 0x01c0, 0x03c0, 0x07c0, 0x0fc0, 0x1fc0, 0x3fc0, 73629563Ssam 0x7fc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0 73729408Ssam }; 73829408Ssam 73924007Ssam acebakoff(is, txseg, retries) 74024007Ssam struct ace_softc *is; 74124007Ssam struct tx_segment *txseg; 74224007Ssam register int retries; 74324007Ssam { 74424007Ssam register short *pBakNum, random_num; 74524007Ssam short *pMask; 74624007Ssam 74724007Ssam pMask = &random_mask_tbl[0]; 74824007Ssam pBakNum = &txseg->tx_backoff[0]; 74924007Ssam while (--retries >= 0) { 75024007Ssam random_num = (is->is_currnd = (is->is_currnd * 18741)-13849); 75124007Ssam random_num &= *pMask++; 75229563Ssam *pBakNum++ = random_num ^ (short)(0xff00 | 0x00fc); 75324007Ssam } 75424007Ssam } 75524007Ssam 75624007Ssam /* 75724007Ssam * Process an ioctl request. 75824007Ssam */ 75924007Ssam aceioctl(ifp, cmd, data) 76024007Ssam register struct ifnet *ifp; 76124007Ssam int cmd; 76224007Ssam caddr_t data; 76324007Ssam { 76425694Ssam register struct ifaddr *ifa = (struct ifaddr *)data; 76525855Ssam struct acedevice *addr; 76625694Ssam int s = splimp(), error = 0; 76724007Ssam 76824007Ssam switch (cmd) { 76924007Ssam 77024007Ssam case SIOCSIFADDR: 77125694Ssam ifp->if_flags |= IFF_UP; 77225855Ssam switch (ifa->ifa_addr.sa_family) { 77325855Ssam #ifdef INET 77425855Ssam case AF_INET: 77525855Ssam aceinit(ifp->if_unit); /* before arpwhohas */ 77625855Ssam ((struct arpcom *)ifp)->ac_ipaddr = 77725855Ssam IA_SIN(ifa)->sin_addr; 77825855Ssam arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 77925855Ssam break; 78025855Ssam #endif 78125855Ssam #ifdef NS 78225855Ssam case AF_NS: { 78325937Ssam struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; 78425937Ssam struct ace_softc *is = &ace_softc[ifp->if_unit]; 78525855Ssam 78625855Ssam if (!ns_nullhost(*ina)) { 78725855Ssam ifp->if_flags &= ~IFF_RUNNING; 78825855Ssam addr = (struct acedevice *) 78925937Ssam aceinfo[ifp->if_unit]->ui_addr; 79025855Ssam movow(&addr->csr, CSR_RESET); 79125855Ssam DELAY(10000); 79225855Ssam /* set station address & copy addr to arp */ 79329408Ssam acesetaddr(ifp->if_unit, addr, 79425855Ssam ina->x_host.c_host); 79525855Ssam } else 79625937Ssam ina->x_host = *(union ns_host *)is->is_addr; 79725855Ssam aceinit(ifp->if_unit); 79825855Ssam break; 79925855Ssam } 80025855Ssam #endif 80125855Ssam default: 80225855Ssam aceinit(ifp->if_unit); 80325855Ssam break; 80425855Ssam } 80524007Ssam break; 80624007Ssam 80725855Ssam case SIOCSIFFLAGS: 80825855Ssam if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) { 80925855Ssam addr = (struct acedevice *) 81025855Ssam (aceinfo[ifp->if_unit]->ui_addr); 81125855Ssam movow(&addr->csr, CSR_RESET); 81225855Ssam ifp->if_flags &= ~IFF_RUNNING; 81325855Ssam } else if (ifp->if_flags&IFF_UP && 81425855Ssam (ifp->if_flags&IFF_RUNNING) == 0) 81525855Ssam aceinit(ifp->if_unit); 81624007Ssam break; 81724007Ssam 81824007Ssam default: 81924007Ssam error = EINVAL; 82024007Ssam } 82124007Ssam splx(s); 82224007Ssam return (error); 82324007Ssam } 82424007Ssam 82529408Ssam /* 82629408Ssam * Set the on-board station address, then read it back 82729408Ssam * to initialize the address used by ARP (among others). 82829408Ssam */ 82929408Ssam acesetaddr(unit, addr, station) 83029408Ssam short unit; 83129408Ssam struct acedevice *addr; 83230295Ssam u_char *station; 83329408Ssam { 83429408Ssam struct ace_softc *is = &ace_softc[unit]; 83529408Ssam register short *wp, i; 83629408Ssam 83729408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 83829408Ssam movow(wp++, ~*station++); 83929408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 84029408Ssam is->is_addr[i] = ~*wp++; 84129408Ssam printf("ace%d: hardware address %s\n", unit, 84229408Ssam ether_sprintf(is->is_addr)); 84329408Ssam } 84429408Ssam 84529408Ssam /* 84629408Ssam * Setup the device for use. Initialize dual-ported memory, 84729408Ssam * backoff parameters, and various other software state. 84829408Ssam */ 84925927Ssam acesetup(unit) 85024007Ssam int unit; 85124007Ssam { 85224007Ssam register struct ace_softc *is = &ace_softc[unit]; 85325927Ssam register char *pData1; 85425694Ssam register short i; 85525927Ssam struct acedevice *addr; 85624007Ssam 85725927Ssam bzero(is->is_dpm, 16384*2); 85824007Ssam is->is_currnd = 49123; 85925927Ssam addr = (struct acedevice *)aceinfo[unit]->ui_addr; 86024007Ssam is->is_segboundry = (addr->segb >> 11) & 0xf; 86125927Ssam pData1 = is->is_dpm + (is->is_segboundry << 11); 86224007Ssam for (i = SEG_MAX + 1 - is->is_segboundry; --i >= 0;) { 86324007Ssam acebakoff(is, (struct tx_segment *)pData1, 15); 86424007Ssam pData1 += sizeof (struct tx_segment); 86524007Ssam } 86624007Ssam is->is_eictr = 0; 86724007Ssam is->is_eoctr = is->is_txnext = is->is_segboundry; 86824007Ssam bzero((char *)&is->is_stats, sizeof (is->is_stats)); 86924007Ssam } 87024007Ssam #endif 871