134405Skarels /* 234405Skarels * Copyright (c) 1988 Regents of the University of California. 334405Skarels * All rights reserved. 434405Skarels * 535411Skarels * This code is derived from software contributed to Berkeley by 635411Skarels * Computer Consoles Inc. 735411Skarels * 834405Skarels * Redistribution and use in source and binary forms are permitted 934864Sbostic * provided that the above copyright notice and this paragraph are 1034864Sbostic * duplicated in all such forms and that any documentation, 1134864Sbostic * advertising materials, and other materials related to such 1234864Sbostic * distribution and use acknowledge that the software was developed 1334864Sbostic * by the University of California, Berkeley. The name of the 1434864Sbostic * University may not be used to endorse or promote products derived 1534864Sbostic * from this software without specific prior written permission. 1634864Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1734864Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1834864Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1934405Skarels * 20*37503Smckusick * @(#)if_ace.c 7.5 (Berkeley) 04/25/89 2134405Skarels */ 2224007Ssam 2324007Ssam /* 2424007Ssam * ACC VERSAbus Ethernet controller 2524007Ssam */ 2624007Ssam #include "ace.h" 2724007Ssam #if NACE > 0 2824007Ssam 2925694Ssam #include "param.h" 3025694Ssam #include "systm.h" 3135411Skarels #include "malloc.h" 3225694Ssam #include "mbuf.h" 3325694Ssam #include "buf.h" 3425694Ssam #include "protosw.h" 3525694Ssam #include "socket.h" 3625694Ssam #include "vmmac.h" 3725694Ssam #include "ioctl.h" 3825694Ssam #include "errno.h" 3925694Ssam #include "vmparam.h" 4025855Ssam #include "syslog.h" 4124007Ssam 4224007Ssam #include "../net/if.h" 4324007Ssam #include "../net/netisr.h" 4424007Ssam #include "../net/route.h" 4525855Ssam #ifdef INET 4624007Ssam #include "../netinet/in.h" 4724007Ssam #include "../netinet/in_systm.h" 4825694Ssam #include "../netinet/in_var.h" 4924007Ssam #include "../netinet/ip.h" 5024007Ssam #include "../netinet/ip_var.h" 5124007Ssam #include "../netinet/if_ether.h" 5225855Ssam #endif 5325855Ssam #ifdef NS 5425855Ssam #include "../netns/ns.h" 5525855Ssam #include "../netns/ns_if.h" 5625855Ssam #endif 5724007Ssam 58*37503Smckusick #include "machine/cpu.h" 59*37503Smckusick #include "machine/pte.h" 6030229Ssam 6124007Ssam #include "../tahoe/mtpr.h" 6224007Ssam #include "../tahoeif/if_acereg.h" 6325694Ssam #include "../tahoevba/vbavar.h" 6424007Ssam 6535411Skarels int aceprobe(), aceattach(), acerint(), acecint(), acestart(); 6624007Ssam struct vba_device *aceinfo[NACE]; 6725983Ssam long acestd[] = { 0 }; 6824007Ssam struct vba_driver acedriver = 6925927Ssam { aceprobe, 0, aceattach, 0, acestd, "ace", aceinfo, "v/eiu", 0 }; 7024007Ssam 7124007Ssam int aceinit(), aceoutput(), aceioctl(), acereset(); 7224007Ssam struct mbuf *aceget(); 7324007Ssam 7424007Ssam /* 7524007Ssam * Ethernet software status per interface. 7624007Ssam * 7724007Ssam * Each interface is referenced by a network interface structure, 7824007Ssam * is_if, which the routing code uses to locate the interface. 7924007Ssam * This structure contains the output queue for the interface, its address, ... 8024007Ssam */ 8124007Ssam struct ace_softc { 8224007Ssam struct arpcom is_ac; /* Ethernet common part */ 8324007Ssam #define is_if is_ac.ac_if /* network-visible interface */ 8424007Ssam #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ 8524007Ssam short is_flags; 8624007Ssam #define ACEF_OACTIVE 0x1 /* output is active */ 8724007Ssam #define ACEF_RCVPENDING 0x2 /* start rcv in acecint */ 8824007Ssam short is_promiscuous; /* true is enabled */ 8924007Ssam short is_segboundry; /* first TX Seg in dpm */ 9024007Ssam short is_eictr; /* Rx segment tracking ctr */ 9124007Ssam short is_eoctr; /* Tx segment tracking ctr */ 9224007Ssam short is_txnext; /* Next available Tx segment */ 9324007Ssam short is_currnd; /* current random backoff */ 9424007Ssam struct ace_stats is_stats; /* holds board statistics */ 9524007Ssam short is_xcnt; /* count xmitted segments to be acked 9624007Ssam by the controller */ 9725855Ssam long is_ivec; /* autoconfig interrupt vector base */ 9825927Ssam struct pte *is_map; /* pte map for dual ported memory */ 9925927Ssam caddr_t is_dpm; /* address of mapped memory */ 10024007Ssam } ace_softc[NACE]; 10124007Ssam extern struct ifnet loif; 10224007Ssam 10325855Ssam aceprobe(reg, vi) 10424007Ssam caddr_t reg; 10525855Ssam struct vba_device *vi; 10624007Ssam { 10725855Ssam register br, cvec; /* must be r12, r11 */ 10825855Ssam struct acedevice *ap = (struct acedevice *)reg; 10925855Ssam struct ace_softc *is = &ace_softc[vi->ui_unit]; 11024007Ssam 11124007Ssam #ifdef lint 11230295Ssam br = 0; cvec = br; br = cvec; 11324007Ssam acerint(0); acecint(0); 11424007Ssam #endif 11524007Ssam if (badaddr(reg, 2)) 11625855Ssam return (0); 11725855Ssam movow(&ap->csr, CSR_RESET); 11824007Ssam DELAY(10000); 11925855Ssam #ifdef notdef 12025855Ssam /* 12125855Ssam * Select two spaces for the interrupts aligned to an 12225855Ssam * eight vector boundary and fitting in 8 bits (as 12325855Ssam * required by the controller) -- YECH. The controller 12425855Ssam * will be notified later at initialization time. 12525855Ssam */ 12625855Ssam if ((vi->ui_hd->vh_lastiv -= 2) > 0xff) 12725855Ssam vi->ui_hd->vh_lastiv = 0x200; 12825855Ssam is->is_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x7; 12925855Ssam #else 13025855Ssam is->is_ivec = 0x90+vi->ui_unit*8; 13125855Ssam #endif 13225855Ssam br = 0x14, cvec = is->is_ivec; /* XXX */ 13325855Ssam return (sizeof (*ap)); 13424007Ssam } 13524007Ssam 13624007Ssam /* 13724007Ssam * Interface exists: make available by filling in network interface 13824007Ssam * record. System will initialize the interface when it is ready 13924007Ssam * to accept packets. 14024007Ssam */ 14124007Ssam aceattach(ui) 14224007Ssam struct vba_device *ui; 14324007Ssam { 14424007Ssam register short unit = ui->ui_unit; 14524007Ssam register struct ace_softc *is = &ace_softc[unit]; 14624007Ssam register struct ifnet *ifp = &is->is_if; 14724007Ssam register struct acedevice *addr = (struct acedevice *)ui->ui_addr; 14824007Ssam register short *wp, i; 14924007Ssam 15024007Ssam ifp->if_unit = unit; 15124007Ssam ifp->if_name = "ace"; 15224007Ssam ifp->if_mtu = ETHERMTU; 15324007Ssam /* 15429408Ssam * Get station's addresses and set multicast hash table. 15524007Ssam */ 15629408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 15729408Ssam is->is_addr[i] = ~*wp++; 15829408Ssam printf("ace%d: hardware address %s\n", unit, 15929408Ssam ether_sprintf(is->is_addr)); 16024007Ssam is->is_promiscuous = 0; 16129408Ssam for (wp = (short *)addr->hash, i = 0; i < 8; i++) 16229408Ssam movow(wp++, ~0xf); 16325694Ssam movow(&addr->bcastena[0], ~0xffff); 16425694Ssam movow(&addr->bcastena[1], ~0xffff); 16525927Ssam /* 16625927Ssam * Allocate and map dual ported VERSAbus memory. 16725927Ssam */ 16831735Skarels if (vbmemalloc(32, (caddr_t)ui->ui_flags, 16931735Skarels &is->is_map, &is->is_dpm) == 0) { 17031735Skarels printf("ace%d: can't allocate VERSAbus memory map\n", unit); 17131735Skarels return; 17231735Skarels } 17325927Ssam 17424007Ssam ifp->if_init = aceinit; 17537474Ssklower ifp->if_output = ether_output; 17635411Skarels ifp->if_start = acestart; 17724007Ssam ifp->if_ioctl = aceioctl; 17824007Ssam ifp->if_reset = acereset; 17935411Skarels ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; 18024007Ssam if_attach(ifp); 18124007Ssam } 18224007Ssam 18324007Ssam /* 18424007Ssam * Reset of interface after "system" reset. 18524007Ssam */ 18624007Ssam acereset(unit, vban) 18724007Ssam int unit, vban; 18824007Ssam { 18924007Ssam register struct vba_device *ui; 19024007Ssam 19124007Ssam if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 || 19224007Ssam ui->ui_vbanum != vban) 19324007Ssam return; 19424007Ssam printf(" ace%d", unit); 19524007Ssam aceinit(unit); 19624007Ssam } 19724007Ssam 19824007Ssam /* 19924007Ssam * Initialization of interface; clear recorded pending operations 20024007Ssam */ 20124007Ssam aceinit(unit) 20224007Ssam int unit; 20324007Ssam { 20424007Ssam register struct ace_softc *is = &ace_softc[unit]; 20524007Ssam register struct vba_device *ui = aceinfo[unit]; 20624007Ssam register struct acedevice *addr; 20724007Ssam register short Csr; 20825694Ssam register int s; 20924007Ssam 21035411Skarels if (is->is_if.if_addrlist == (struct ifaddr *)0) 21124007Ssam return; 21235411Skarels if ((is->is_if.if_flags & IFF_RUNNING) == 0) { 21324007Ssam /* 21424007Ssam * Reset the controller, initialize the recieve buffers, 21524007Ssam * and turn the controller on again and set board online. 21624007Ssam */ 21724007Ssam addr = (struct acedevice *)ui->ui_addr; 21824007Ssam s = splimp(); 21925694Ssam movow(&addr->csr, CSR_RESET); 22024007Ssam DELAY(10000); 22124007Ssam 22224007Ssam /* 22325927Ssam * Clean up dpm since the controller might 22425927Ssam * jumble dpm after reset. 22524007Ssam */ 22625927Ssam acesetup(unit); 22725694Ssam movow(&addr->csr, CSR_GO); 22824007Ssam Csr = addr->csr; 22924007Ssam if (Csr & CSR_ACTIVE) { 23025855Ssam movow(&addr->ivct, is->is_ivec); 23124007Ssam Csr |= CSR_IENA | is->is_promiscuous; 23225694Ssam movow(&addr->csr, Csr); 23324007Ssam is->is_flags = 0; 23424007Ssam is->is_xcnt = 0; 23525694Ssam is->is_if.if_flags |= IFF_RUNNING; 23624007Ssam } 23724007Ssam splx(s); 23824007Ssam } 23925694Ssam if (is->is_if.if_snd.ifq_head) 24035411Skarels acestart(&is->is_if); 24124007Ssam } 24224007Ssam 24324007Ssam /* 24424007Ssam * Start output on interface. 24524007Ssam * Get another datagram to send off of the interface queue, 24624007Ssam * and map it to the interface before starting the output. 24724007Ssam */ 24835411Skarels acestart(ifp) 24935411Skarels register struct ifnet *ifp; 25024007Ssam { 25124007Ssam register struct tx_segment *txs; 25225694Ssam register long len; 25325694Ssam register int s; 25424007Ssam struct mbuf *m; 25525694Ssam short retries; 25635411Skarels #define is ((struct ace_softc *)ifp) 25724007Ssam 25824007Ssam again: 25924007Ssam txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11)); 26024007Ssam if (txs->tx_csr & TCS_TBFULL) { 26124007Ssam is->is_stats.tx_busy++; 26235411Skarels ifp->if_flags |= IFF_OACTIVE; 26335411Skarels return (0); 26424007Ssam } 26525694Ssam s = splimp(); 26635411Skarels IF_DEQUEUE(&ifp->if_snd, m); 26725694Ssam splx(s); 26825927Ssam if (m == 0) { 26935411Skarels ifp->if_flags &= ~IFF_OACTIVE; 27035411Skarels return (0); 27125927Ssam } 27235411Skarels len = aceput(txs->tx_data, m); 27324007Ssam retries = txs->tx_csr & TCS_RTC; 27424007Ssam if (retries > 0) 27524007Ssam acebakoff(is, txs, retries); 27624007Ssam 27724007Ssam /* 27824007Ssam * Ensure minimum packet length. 27924007Ssam * This makes the safe assumtion that there are no virtual holes 28024007Ssam * after the data. 28124007Ssam * For security, it might be wise to zero out the added bytes, 28224007Ssam * but we're mainly interested in speed at the moment. 28324007Ssam */ 28424007Ssam if (len - sizeof (struct ether_header) < ETHERMIN) 28524007Ssam len = ETHERMIN + sizeof (struct ether_header); 28624007Ssam if (++is->is_txnext > SEG_MAX) 28724007Ssam is->is_txnext = is->is_segboundry; 28835411Skarels ifp->if_opackets++; 28924007Ssam is->is_xcnt++; 29024007Ssam len = (len & 0x7fff) | TCS_TBFULL; 29125694Ssam movow(txs, len); 29224007Ssam goto again; 29335411Skarels #undef is 29424007Ssam } 29524007Ssam 29624007Ssam /* 29724007Ssam * Transmit done interrupt. 29824007Ssam */ 29924007Ssam acecint(unit) 30024007Ssam int unit; 30124007Ssam { 30224007Ssam register struct ace_softc *is = &ace_softc[unit]; 30324007Ssam register struct tx_segment *txseg; 30425694Ssam short eostat; 30524007Ssam 30624007Ssam if (is->is_xcnt <= 0) { 30725855Ssam log(LOG_ERR, "ace%d: stray xmit interrupt, xcnt %d\n", 30824007Ssam unit, is->is_xcnt); 30924007Ssam is->is_xcnt = 0; 31025694Ssam if (is->is_if.if_snd.ifq_head) 31135411Skarels acestart(&is->is_if); 31224007Ssam return; 31324007Ssam } 31424007Ssam is->is_xcnt--; 31524007Ssam txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm); 31624007Ssam eostat = txseg->tx_csr; 31724007Ssam if ((eostat & TCS_TBFULL) == 0) { 31824007Ssam is->is_stats.tx_retries += eostat & TCS_RTC; 31924007Ssam if (eostat & TCS_RTFAIL) { 32024007Ssam is->is_stats.tx_discarded++; 32124007Ssam is->is_if.if_oerrors++; 32224007Ssam } else 32324007Ssam is->is_stats.tx_datagrams++; 32424007Ssam if (++is->is_eoctr >= 16) 32524007Ssam is->is_eoctr = is->is_segboundry; 32624007Ssam } 32725694Ssam if (is->is_if.if_snd.ifq_head) 32835411Skarels acestart(&is->is_if); 32924007Ssam } 33024007Ssam 33124007Ssam /* 33224007Ssam * Ethernet interface receiver interrupt. 33324007Ssam * If input error just drop packet. 33424007Ssam * Otherwise purge input buffered data path and examine 33524007Ssam * packet to determine type. If can't determine length 33624007Ssam * from type, then have to drop packet. Othewise decapsulate 33724007Ssam * packet based on type and pass to type specific higher-level 33824007Ssam * input routine. 33924007Ssam */ 34024007Ssam acerint(unit) 34124007Ssam int unit; 34224007Ssam { 34324007Ssam register struct ace_softc *is = &ace_softc[unit]; 34424007Ssam register struct ifqueue *inq; 34524007Ssam register struct ether_header *ace; 34624007Ssam register struct rx_segment *rxseg; 34724007Ssam int len, s, off, resid; 34824007Ssam struct mbuf *m; 34924007Ssam short eistat; 35024007Ssam 35125694Ssam if ((is->is_if.if_flags&IFF_RUNNING) == 0) 35225694Ssam return; 35324007Ssam again: 35424007Ssam rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm); 35524007Ssam eistat = rxseg->rx_csr; 35624007Ssam if ((eistat & RCS_RBFULL) == 0) 35724007Ssam return; 35824007Ssam is->is_if.if_ipackets++; 35924007Ssam if (++is->is_eictr >= is->is_segboundry) 36024007Ssam is->is_eictr = 0; 36124007Ssam len = eistat & RCS_RBC; 36224007Ssam if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) || 36324007Ssam len < ET_MINLEN || len > ET_MAXLEN+CRC_SIZE) { 36424007Ssam if (eistat & RCS_ROVRN) 36524007Ssam is->is_stats.rx_overruns++; 36624007Ssam if (eistat & RCS_RCRC) 36724007Ssam is->is_stats.rx_crc_errors++; 36824007Ssam if (eistat & RCS_RODD) 36924007Ssam is->is_stats.rx_align_errors++; 37024007Ssam if (len < ET_MINLEN) 37124007Ssam is->is_stats.rx_underruns++; 37224007Ssam if (len > ET_MAXLEN+CRC_SIZE) 37324007Ssam is->is_stats.rx_overruns++; 37424007Ssam is->is_if.if_ierrors++; 37524007Ssam rxseg->rx_csr = 0; 37624007Ssam return; 37724007Ssam } else 37824007Ssam is->is_stats.rx_datagrams++; 37924007Ssam ace = (struct ether_header *)rxseg->rx_data; 38024007Ssam len -= sizeof (struct ether_header); 38124007Ssam /* 38225694Ssam * Deal with trailer protocol: if type is trailer 38324007Ssam * get true type from first 16-bit word past data. 38424007Ssam * Remember that type was trailer by setting off. 38524007Ssam */ 38624007Ssam ace->ether_type = ntohs((u_short)ace->ether_type); 38724007Ssam #define acedataaddr(ace, off, type) \ 38824007Ssam ((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off)))) 38925694Ssam if (ace->ether_type >= ETHERTYPE_TRAIL && 39025694Ssam ace->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 39125694Ssam off = (ace->ether_type - ETHERTYPE_TRAIL) * 512; 39224007Ssam if (off >= ETHERMTU) 39324007Ssam goto setup; /* sanity */ 39424007Ssam ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *)); 39524007Ssam resid = ntohs(*(acedataaddr(ace, off+2, u_short *))); 39624007Ssam if (off + resid > len) 39724007Ssam goto setup; /* sanity */ 39824007Ssam len = off + resid; 39924007Ssam } else 40024007Ssam off = 0; 40124007Ssam if (len == 0) 40224007Ssam goto setup; 40324007Ssam 40424007Ssam /* 40524007Ssam * Pull packet off interface. Off is nonzero if packet 40624007Ssam * has trailing header; aceget will then force this header 40735411Skarels * information to be at the front. 40824007Ssam */ 40925855Ssam m = aceget((u_char *)rxseg->rx_data, len, off, &is->is_if); 41024007Ssam if (m == 0) 41124007Ssam goto setup; 41224007Ssam switch (ace->ether_type) { 41324007Ssam 41424007Ssam #ifdef INET 41525694Ssam case ETHERTYPE_IP: 41624007Ssam schednetisr(NETISR_IP); 41724007Ssam inq = &ipintrq; 41824007Ssam break; 41925855Ssam #endif 42024007Ssam 42125694Ssam case ETHERTYPE_ARP: 42224007Ssam arpinput(&is->is_ac, m); 42324007Ssam goto setup; 42425694Ssam #ifdef NS 42525694Ssam case ETHERTYPE_NS: 42625694Ssam schednetisr(NETISR_NS); 42725694Ssam inq = &nsintrq; 42825694Ssam break; 42925694Ssam 43025694Ssam #endif 43124007Ssam default: 43224007Ssam m_freem(m); 43324007Ssam goto setup; 43424007Ssam } 43524007Ssam if (IF_QFULL(inq)) { 43624007Ssam IF_DROP(inq); 43724007Ssam m_freem(m); 43824007Ssam goto setup; 43924007Ssam } 44024007Ssam s = splimp(); 44124007Ssam IF_ENQUEUE(inq, m); 44224007Ssam splx(s); 44324007Ssam setup: 44424007Ssam rxseg->rx_csr = 0; 44524007Ssam goto again; 44624007Ssam } 44724007Ssam 44824007Ssam /* 44924007Ssam * Routine to copy from mbuf chain to transmit buffer on the VERSAbus 45024007Ssam * If packet size is less than the minimum legal size, 45124007Ssam * the buffer is expanded. We probably should zero out the extra 45224007Ssam * bytes for security, but that would slow things down. 45324007Ssam */ 45435411Skarels aceput(txbuf, m) 45525694Ssam char *txbuf; 45624007Ssam struct mbuf *m; 45737474Ssklower #ifdef notdef 45824007Ssam { 45925855Ssam register u_char *bp, *mcp; 46025855Ssam register short *s1, *s2; 46125694Ssam register u_int len; 46224007Ssam register struct mbuf *mp; 46325694Ssam int total; 46424007Ssam 46535411Skarels total = mp->m_pkthdr.len; 46625694Ssam bp = (u_char *)txbuf; 46737474Ssklower for (mp = m; mp; mp = mp->m_next) { 46824007Ssam len = mp->m_len; 46924007Ssam if (len == 0) 47024007Ssam continue; 47124007Ssam mcp = mtod(mp, u_char *); 47224007Ssam if (((int)mcp & 01) && ((int)bp & 01)) { 47324007Ssam /* source & destination at odd addresses */ 47425694Ssam movob(bp++, *mcp++); 47524007Ssam --len; 47624007Ssam } 47724007Ssam if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) { 47837474Ssklower int l = len & 1; 47924007Ssam 48024007Ssam s1 = (short *)bp; 48124007Ssam s2 = (short *)mcp; 48235411Skarels len >>= 1; /* count # of shorts */ 48335411Skarels while (len-- != 0) 48425694Ssam movow(s1++, *s2++); 48537474Ssklower len = l; /* # remaining bytes */ 48624007Ssam bp = (u_char *)s1; 48724007Ssam mcp = (u_char *)s2; 48824007Ssam } 48925694Ssam while (len-- != 0) 49025694Ssam movob(bp++, *mcp++); 49124007Ssam } 49224007Ssam m_freem(m); 49324007Ssam return (total); 49424007Ssam } 49537474Ssklower #else 49637474Ssklower { 49737474Ssklower register u_char *bp, *mcp; 49837474Ssklower register short *s1, *s2; 49937474Ssklower register u_int len; 50037474Ssklower register struct mbuf *mp; 50137474Ssklower int total; 50224007Ssam 50337474Ssklower total = 0; 50437474Ssklower bp = (u_char *)txbuf; 50537474Ssklower for (mp = m; (mp); mp = mp->m_next) { 50637474Ssklower len = mp->m_len; 50737474Ssklower if (len == 0) 50837474Ssklower continue; 50937474Ssklower total += len; 51037474Ssklower mcp = mtod(mp, u_char *); 51137474Ssklower if (((int)mcp & 01) && ((int)bp & 01)) { 51237474Ssklower /* source & destination at odd addresses */ 51337474Ssklower movob(bp++, *mcp++); 51437474Ssklower --len; 51537474Ssklower } 51637474Ssklower if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) { 51737474Ssklower register u_int l; 51837474Ssklower 51937474Ssklower s1 = (short *)bp; 52037474Ssklower s2 = (short *)mcp; 52137474Ssklower l = len >> 1; /* count # of shorts */ 52237474Ssklower while (l-- != 0) 52337474Ssklower movow(s1++, *s2++); 52437474Ssklower len &= 1; /* # remaining bytes */ 52537474Ssklower bp = (u_char *)s1; 52637474Ssklower mcp = (u_char *)s2; 52737474Ssklower } 52837474Ssklower while (len-- != 0) 52937474Ssklower movob(bp++, *mcp++); 53037474Ssklower } 53137474Ssklower m_freem(m); 53237474Ssklower return (total); 53337474Ssklower } 53437474Ssklower #endif 53537474Ssklower 53624007Ssam /* 53724007Ssam * Routine to copy from VERSAbus memory into mbufs. 53824007Ssam * 53924007Ssam * Warning: This makes the fairly safe assumption that 54024007Ssam * mbufs have even lengths. 54124007Ssam */ 54224007Ssam struct mbuf * 54335411Skarels aceget(rxbuf, totlen, off, ifp) 54424007Ssam u_char *rxbuf; 54535411Skarels int totlen, off; 54625855Ssam struct ifnet *ifp; 54724007Ssam { 54825855Ssam register u_char *cp, *mcp; 54935411Skarels register struct mbuf *m; 55024007Ssam register int tlen; 55124007Ssam struct mbuf *top = 0, **mp = ⊤ 55235411Skarels int len; 55335411Skarels u_char *packet_end; 55424007Ssam 55535411Skarels rxbuf += sizeof (struct ether_header); 55635411Skarels cp = rxbuf; 55735411Skarels packet_end = cp + totlen; 55835411Skarels if (off) { 55935411Skarels off += 2 * sizeof(u_short); 56035411Skarels totlen -= 2 *sizeof(u_short); 56135411Skarels cp = rxbuf + off; 56235411Skarels } 56335411Skarels 56435411Skarels MGETHDR(m, M_DONTWAIT, MT_DATA); 56535411Skarels if (m == 0) 56635411Skarels return (0); 56735411Skarels m->m_pkthdr.rcvif = ifp; 56835411Skarels m->m_pkthdr.len = totlen; 56935411Skarels m->m_len = MHLEN; 57035411Skarels 57124007Ssam while (totlen > 0) { 57235411Skarels if (top) { 57335411Skarels MGET(m, M_DONTWAIT, MT_DATA); 57435411Skarels if (m == 0) { 57535411Skarels m_freem(top); 57635411Skarels return (0); 57735411Skarels } 57835411Skarels m->m_len = MLEN; 57935411Skarels } 58035411Skarels len = min(totlen, (packet_end - cp)); 58135411Skarels if (len >= MINCLSIZE) { 58235411Skarels MCLGET(m, M_DONTWAIT); 58335411Skarels if (m->m_flags & M_EXT) 58435411Skarels m->m_len = len = min(len, MCLBYTES); 58529563Ssam else 58635411Skarels len = m->m_len; 58724007Ssam } else { 58825855Ssam /* 58935411Skarels * Place initial small packet/header at end of mbuf. 59025855Ssam */ 59135411Skarels if (len < m->m_len) { 59235411Skarels if (top == 0 && len + max_linkhdr <= m->m_len) 59335411Skarels m->m_data += max_linkhdr; 59435411Skarels m->m_len = len; 59535411Skarels } else 59635411Skarels len = m->m_len; 59725855Ssam } 59835411Skarels mcp = mtod(m, u_char *); 59924007Ssam /*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/ 60024007Ssam /*cp += len; mcp += len;*/ 60124007Ssam tlen = len; 60224007Ssam if (((int)mcp & 01) && ((int)cp & 01)) { 60324007Ssam /* source & destination at odd addresses */ 60424007Ssam *mcp++ = *cp++; 60524007Ssam --tlen; 60624007Ssam } 60724007Ssam if (tlen > 1 && (((int)mcp&01) == 0) && (((int)cp&01) == 0)) { 60824007Ssam register short *s1, *s2; 60924007Ssam register int l; 61024007Ssam 61124007Ssam s1 = (short *)mcp; 61224007Ssam s2 = (short *)cp; 61324007Ssam l = tlen >> 1; /* count # of shorts */ 61424007Ssam while (l-- > 0) /* copy shorts */ 61524007Ssam *s1++ = *s2++; 61624007Ssam tlen &= 1; /* # remaining bytes */ 61724007Ssam mcp = (u_char *)s1; 61824007Ssam cp = (u_char *)s2; 61924007Ssam } 62024007Ssam while (tlen-- > 0) 62124007Ssam *mcp++ = *cp++; 62224007Ssam *mp = m; 62324007Ssam mp = &m->m_next; 62435411Skarels totlen -= len; 62535411Skarels if (cp == packet_end) 62635411Skarels cp = rxbuf; 62724007Ssam } 62824007Ssam return (top); 62924007Ssam } 63024007Ssam 63129408Ssam /* backoff table masks */ 63229408Ssam short random_mask_tbl[16] = { 63329563Ssam 0x0040, 0x00c0, 0x01c0, 0x03c0, 0x07c0, 0x0fc0, 0x1fc0, 0x3fc0, 63429563Ssam 0x7fc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0, 0xffc0 63529408Ssam }; 63629408Ssam 63724007Ssam acebakoff(is, txseg, retries) 63824007Ssam struct ace_softc *is; 63924007Ssam struct tx_segment *txseg; 64024007Ssam register int retries; 64124007Ssam { 64224007Ssam register short *pBakNum, random_num; 64324007Ssam short *pMask; 64424007Ssam 64524007Ssam pMask = &random_mask_tbl[0]; 64624007Ssam pBakNum = &txseg->tx_backoff[0]; 64724007Ssam while (--retries >= 0) { 64824007Ssam random_num = (is->is_currnd = (is->is_currnd * 18741)-13849); 64924007Ssam random_num &= *pMask++; 65029563Ssam *pBakNum++ = random_num ^ (short)(0xff00 | 0x00fc); 65124007Ssam } 65224007Ssam } 65324007Ssam 65424007Ssam /* 65524007Ssam * Process an ioctl request. 65624007Ssam */ 65724007Ssam aceioctl(ifp, cmd, data) 65824007Ssam register struct ifnet *ifp; 65924007Ssam int cmd; 66024007Ssam caddr_t data; 66124007Ssam { 66225694Ssam register struct ifaddr *ifa = (struct ifaddr *)data; 66325855Ssam struct acedevice *addr; 66425694Ssam int s = splimp(), error = 0; 66524007Ssam 66624007Ssam switch (cmd) { 66724007Ssam 66824007Ssam case SIOCSIFADDR: 66925694Ssam ifp->if_flags |= IFF_UP; 67037474Ssklower switch (ifa->ifa_addr->sa_family) { 67125855Ssam #ifdef INET 67225855Ssam case AF_INET: 67325855Ssam aceinit(ifp->if_unit); /* before arpwhohas */ 67425855Ssam ((struct arpcom *)ifp)->ac_ipaddr = 67525855Ssam IA_SIN(ifa)->sin_addr; 67625855Ssam arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 67725855Ssam break; 67825855Ssam #endif 67925855Ssam #ifdef NS 68025855Ssam case AF_NS: { 68125937Ssam struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; 68225937Ssam struct ace_softc *is = &ace_softc[ifp->if_unit]; 68325855Ssam 68425855Ssam if (!ns_nullhost(*ina)) { 68525855Ssam ifp->if_flags &= ~IFF_RUNNING; 68625855Ssam addr = (struct acedevice *) 68725937Ssam aceinfo[ifp->if_unit]->ui_addr; 68825855Ssam movow(&addr->csr, CSR_RESET); 68925855Ssam DELAY(10000); 69025855Ssam /* set station address & copy addr to arp */ 69129408Ssam acesetaddr(ifp->if_unit, addr, 69225855Ssam ina->x_host.c_host); 69325855Ssam } else 69425937Ssam ina->x_host = *(union ns_host *)is->is_addr; 69525855Ssam aceinit(ifp->if_unit); 69625855Ssam break; 69725855Ssam } 69825855Ssam #endif 69925855Ssam default: 70025855Ssam aceinit(ifp->if_unit); 70125855Ssam break; 70225855Ssam } 70324007Ssam break; 70424007Ssam 70525855Ssam case SIOCSIFFLAGS: 70625855Ssam if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) { 70725855Ssam addr = (struct acedevice *) 70825855Ssam (aceinfo[ifp->if_unit]->ui_addr); 70925855Ssam movow(&addr->csr, CSR_RESET); 71025855Ssam ifp->if_flags &= ~IFF_RUNNING; 71125855Ssam } else if (ifp->if_flags&IFF_UP && 71225855Ssam (ifp->if_flags&IFF_RUNNING) == 0) 71325855Ssam aceinit(ifp->if_unit); 71424007Ssam break; 71524007Ssam 71624007Ssam default: 71724007Ssam error = EINVAL; 71824007Ssam } 71924007Ssam splx(s); 72024007Ssam return (error); 72124007Ssam } 72224007Ssam 72329408Ssam /* 72429408Ssam * Set the on-board station address, then read it back 72529408Ssam * to initialize the address used by ARP (among others). 72629408Ssam */ 72729408Ssam acesetaddr(unit, addr, station) 72829408Ssam short unit; 72929408Ssam struct acedevice *addr; 73030295Ssam u_char *station; 73129408Ssam { 73229408Ssam struct ace_softc *is = &ace_softc[unit]; 73329408Ssam register short *wp, i; 73429408Ssam 73529408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 73629408Ssam movow(wp++, ~*station++); 73729408Ssam for (wp = (short *)addr->station, i = 0; i < 6; i++) 73829408Ssam is->is_addr[i] = ~*wp++; 73929408Ssam printf("ace%d: hardware address %s\n", unit, 74029408Ssam ether_sprintf(is->is_addr)); 74129408Ssam } 74229408Ssam 74329408Ssam /* 74429408Ssam * Setup the device for use. Initialize dual-ported memory, 74529408Ssam * backoff parameters, and various other software state. 74629408Ssam */ 74725927Ssam acesetup(unit) 74824007Ssam int unit; 74924007Ssam { 75024007Ssam register struct ace_softc *is = &ace_softc[unit]; 75125927Ssam register char *pData1; 75225694Ssam register short i; 75325927Ssam struct acedevice *addr; 75424007Ssam 75525927Ssam bzero(is->is_dpm, 16384*2); 75624007Ssam is->is_currnd = 49123; 75725927Ssam addr = (struct acedevice *)aceinfo[unit]->ui_addr; 75824007Ssam is->is_segboundry = (addr->segb >> 11) & 0xf; 75925927Ssam pData1 = is->is_dpm + (is->is_segboundry << 11); 76024007Ssam for (i = SEG_MAX + 1 - is->is_segboundry; --i >= 0;) { 76124007Ssam acebakoff(is, (struct tx_segment *)pData1, 15); 76224007Ssam pData1 += sizeof (struct tx_segment); 76324007Ssam } 76424007Ssam is->is_eictr = 0; 76524007Ssam is->is_eoctr = is->is_txnext = is->is_segboundry; 76624007Ssam bzero((char *)&is->is_stats, sizeof (is->is_stats)); 76724007Ssam } 76824007Ssam #endif 769