1*c5249c6dSsam /* if_ace.c 1.8 86/01/26 */ 2226df7bfSsam 3226df7bfSsam /* 4226df7bfSsam * ACC VERSAbus Ethernet controller 5226df7bfSsam */ 6226df7bfSsam #include "ace.h" 7226df7bfSsam #if NACE > 0 8226df7bfSsam 9226df7bfSsam #include "../machine/pte.h" 10226df7bfSsam 11c0fcc33dSsam #include "param.h" 12c0fcc33dSsam #include "systm.h" 13c0fcc33dSsam #include "mbuf.h" 14c0fcc33dSsam #include "buf.h" 15c0fcc33dSsam #include "protosw.h" 16c0fcc33dSsam #include "socket.h" 17c0fcc33dSsam #include "vmmac.h" 18c0fcc33dSsam #include "ioctl.h" 19c0fcc33dSsam #include "errno.h" 20c0fcc33dSsam #include "vmparam.h" 217a0e3b47Ssam #include "syslog.h" 22226df7bfSsam 23226df7bfSsam #include "../net/if.h" 24226df7bfSsam #include "../net/netisr.h" 25226df7bfSsam #include "../net/route.h" 267a0e3b47Ssam #ifdef INET 27226df7bfSsam #include "../netinet/in.h" 28226df7bfSsam #include "../netinet/in_systm.h" 29c0fcc33dSsam #include "../netinet/in_var.h" 30226df7bfSsam #include "../netinet/ip.h" 31226df7bfSsam #include "../netinet/ip_var.h" 32226df7bfSsam #include "../netinet/if_ether.h" 337a0e3b47Ssam #endif 347a0e3b47Ssam #ifdef NS 357a0e3b47Ssam #include "../netns/ns.h" 367a0e3b47Ssam #include "../netns/ns_if.h" 377a0e3b47Ssam #endif 38226df7bfSsam 39226df7bfSsam #include "../tahoe/mtpr.h" 40226df7bfSsam #include "../tahoeif/if_acereg.h" 41c0fcc33dSsam #include "../tahoevba/vbavar.h" 42226df7bfSsam 43226df7bfSsam /* station address */ 44226df7bfSsam char ace_station[6] = { ~0x8, ~0x0, ~0x3, ~0x0, ~0x0, ~0x1 }; 45226df7bfSsam /* multicast hash table initializer */ 46226df7bfSsam char ace_hash[8] = { ~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF }; 47226df7bfSsam /* backoff table masks */ 48226df7bfSsam short random_mask_tbl[16] = { 49226df7bfSsam 0x0040, 0x00C0, 0x01C0, 0x03C0, 0x07C0, 0x0FC0, 0x1FC0, 0x3FC0, 50226df7bfSsam 0x7FC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0 51226df7bfSsam }; 52226df7bfSsam 53226df7bfSsam int aceprobe(), aceattach(), acerint(), acecint(); 54226df7bfSsam struct vba_device *aceinfo[NACE]; 55*c5249c6dSsam long acestd[] = { 0 }; 56226df7bfSsam struct vba_driver acedriver = 57226df7bfSsam { aceprobe, 0, aceattach, 0, acestd, "ace", aceinfo, "v/eiu", 0 }; 58226df7bfSsam 59226df7bfSsam int aceinit(), aceoutput(), aceioctl(), acereset(); 60226df7bfSsam struct mbuf *aceget(); 61226df7bfSsam 62226df7bfSsam /* 63226df7bfSsam * Ethernet software status per interface. 64226df7bfSsam * 65226df7bfSsam * Each interface is referenced by a network interface structure, 66226df7bfSsam * is_if, which the routing code uses to locate the interface. 67226df7bfSsam * This structure contains the output queue for the interface, its address, ... 68226df7bfSsam */ 69226df7bfSsam struct ace_softc { 70226df7bfSsam struct arpcom is_ac; /* Ethernet common part */ 71226df7bfSsam #define is_if is_ac.ac_if /* network-visible interface */ 72226df7bfSsam #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ 73226df7bfSsam short is_flags; 74226df7bfSsam #define ACEF_OACTIVE 0x1 /* output is active */ 75226df7bfSsam #define ACEF_RCVPENDING 0x2 /* start rcv in acecint */ 76226df7bfSsam short is_promiscuous; /* true is enabled */ 77226df7bfSsam short is_segboundry; /* first TX Seg in dpm */ 78226df7bfSsam short is_eictr; /* Rx segment tracking ctr */ 79226df7bfSsam short is_eoctr; /* Tx segment tracking ctr */ 80226df7bfSsam short is_txnext; /* Next available Tx segment */ 81226df7bfSsam short is_currnd; /* current random backoff */ 82226df7bfSsam struct ace_stats is_stats; /* holds board statistics */ 83226df7bfSsam short is_xcnt; /* count xmitted segments to be acked 84226df7bfSsam by the controller */ 857a0e3b47Ssam long is_ivec; /* autoconfig interrupt vector base */ 86a29de3d6Ssam struct pte *is_map; /* pte map for dual ported memory */ 87a29de3d6Ssam caddr_t is_dpm; /* address of mapped memory */ 88226df7bfSsam } ace_softc[NACE]; 89226df7bfSsam extern struct ifnet loif; 90226df7bfSsam 917a0e3b47Ssam aceprobe(reg, vi) 92226df7bfSsam caddr_t reg; 937a0e3b47Ssam struct vba_device *vi; 94226df7bfSsam { 957a0e3b47Ssam register br, cvec; /* must be r12, r11 */ 967a0e3b47Ssam struct acedevice *ap = (struct acedevice *)reg; 977a0e3b47Ssam struct ace_softc *is = &ace_softc[vi->ui_unit]; 98226df7bfSsam 99226df7bfSsam #ifdef lint 100226df7bfSsam acerint(0); acecint(0); 101226df7bfSsam #endif 102226df7bfSsam if (badaddr(reg, 2)) 103226df7bfSsam return (0); 1047a0e3b47Ssam movow(&ap->csr, CSR_RESET); 105226df7bfSsam DELAY(10000); 1067a0e3b47Ssam #ifdef notdef 1077a0e3b47Ssam /* 1087a0e3b47Ssam * Select two spaces for the interrupts aligned to an 1097a0e3b47Ssam * eight vector boundary and fitting in 8 bits (as 1107a0e3b47Ssam * required by the controller) -- YECH. The controller 1117a0e3b47Ssam * will be notified later at initialization time. 1127a0e3b47Ssam */ 1137a0e3b47Ssam if ((vi->ui_hd->vh_lastiv -= 2) > 0xff) 1147a0e3b47Ssam vi->ui_hd->vh_lastiv = 0x200; 1157a0e3b47Ssam is->is_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x7; 1167a0e3b47Ssam #else 1177a0e3b47Ssam is->is_ivec = 0x90+vi->ui_unit*8; 1187a0e3b47Ssam #endif 1197a0e3b47Ssam br = 0x14, cvec = is->is_ivec; /* XXX */ 1207a0e3b47Ssam return (sizeof (*ap)); 121226df7bfSsam } 122226df7bfSsam 123226df7bfSsam /* 124226df7bfSsam * Interface exists: make available by filling in network interface 125226df7bfSsam * record. System will initialize the interface when it is ready 126226df7bfSsam * to accept packets. 127226df7bfSsam */ 128226df7bfSsam aceattach(ui) 129226df7bfSsam struct vba_device *ui; 130226df7bfSsam { 131226df7bfSsam register short unit = ui->ui_unit; 132226df7bfSsam register struct ace_softc *is = &ace_softc[unit]; 133226df7bfSsam register struct ifnet *ifp = &is->is_if; 134226df7bfSsam register struct acedevice *addr = (struct acedevice *)ui->ui_addr; 135226df7bfSsam register short *wp, i; 136226df7bfSsam 137226df7bfSsam ifp->if_unit = unit; 138226df7bfSsam ifp->if_name = "ace"; 139226df7bfSsam ifp->if_mtu = ETHERMTU; 140226df7bfSsam /* 141a29de3d6Ssam * Set station's addresses and multicast hash table. 142226df7bfSsam */ 143226df7bfSsam ace_station[5] = ~(unit + 1); 144226df7bfSsam acesetetaddr(unit, addr, ace_station); 145226df7bfSsam is->is_promiscuous = 0; 146226df7bfSsam wp = (short *)addr->hash; 147226df7bfSsam for (i = 0; i < 8; i++) 148c0fcc33dSsam movow(wp++, ace_hash[i]); 149c0fcc33dSsam movow(&addr->bcastena[0], ~0xffff); 150c0fcc33dSsam movow(&addr->bcastena[1], ~0xffff); 151a29de3d6Ssam /* 152a29de3d6Ssam * Allocate and map dual ported VERSAbus memory. 153a29de3d6Ssam */ 154a29de3d6Ssam vbmemalloc(32, (caddr_t)ui->ui_flags, &is->is_map, &is->is_dpm); 155a29de3d6Ssam 156226df7bfSsam ifp->if_init = aceinit; 157226df7bfSsam ifp->if_output = aceoutput; 158226df7bfSsam ifp->if_ioctl = aceioctl; 159226df7bfSsam ifp->if_reset = acereset; 160c0fcc33dSsam ifp->if_flags = IFF_BROADCAST; 161226df7bfSsam if_attach(ifp); 162226df7bfSsam } 163226df7bfSsam 164226df7bfSsam acesetetaddr(unit, addr, station_addr) 165226df7bfSsam short unit; 166226df7bfSsam struct acedevice *addr; 167226df7bfSsam char *station_addr; 168226df7bfSsam { 169226df7bfSsam register short *wp, i; 170226df7bfSsam register char *cp; 171226df7bfSsam struct ace_softc *is = &ace_softc[unit]; 172226df7bfSsam 173226df7bfSsam wp = (short *)addr->station; 174226df7bfSsam cp = station_addr; 175226df7bfSsam for (i = 0; i < 6; i++) 176c0fcc33dSsam movow(wp++, *cp++); 177226df7bfSsam wp = (short *)addr->station; 178c0fcc33dSsam cp = (char *)is->is_addr; 179226df7bfSsam for (i = 0; i < 6; i++) 180226df7bfSsam *cp++ = ~(*wp++); 181226df7bfSsam } 182226df7bfSsam 183226df7bfSsam /* 184226df7bfSsam * Reset of interface after "system" reset. 185226df7bfSsam */ 186226df7bfSsam acereset(unit, vban) 187226df7bfSsam int unit, vban; 188226df7bfSsam { 189226df7bfSsam register struct vba_device *ui; 190226df7bfSsam 191226df7bfSsam if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 || 192226df7bfSsam ui->ui_vbanum != vban) 193226df7bfSsam return; 194226df7bfSsam printf(" ace%d", unit); 195226df7bfSsam aceinit(unit); 196226df7bfSsam } 197226df7bfSsam 198226df7bfSsam /* 199226df7bfSsam * Initialization of interface; clear recorded pending operations 200226df7bfSsam */ 201226df7bfSsam aceinit(unit) 202226df7bfSsam int unit; 203226df7bfSsam { 204226df7bfSsam register struct ace_softc *is = &ace_softc[unit]; 205226df7bfSsam register struct vba_device *ui = aceinfo[unit]; 206226df7bfSsam register struct acedevice *addr; 207226df7bfSsam register struct ifnet *ifp = &is->is_if; 208226df7bfSsam register short Csr; 209c0fcc33dSsam register int s; 210226df7bfSsam 211c0fcc33dSsam if (ifp->if_addrlist == (struct ifaddr *)0) 212226df7bfSsam return; 213226df7bfSsam if ((ifp->if_flags & IFF_RUNNING) == 0) { 214226df7bfSsam /* 215226df7bfSsam * Reset the controller, initialize the recieve buffers, 216226df7bfSsam * and turn the controller on again and set board online. 217226df7bfSsam */ 218226df7bfSsam addr = (struct acedevice *)ui->ui_addr; 219226df7bfSsam s = splimp(); 220c0fcc33dSsam movow(&addr->csr, CSR_RESET); 221226df7bfSsam DELAY(10000); 222226df7bfSsam 223226df7bfSsam /* 224a29de3d6Ssam * Clean up dpm since the controller might 225a29de3d6Ssam * jumble dpm after reset. 226226df7bfSsam */ 227a29de3d6Ssam acesetup(unit); 228c0fcc33dSsam movow(&addr->csr, CSR_GO); 229226df7bfSsam Csr = addr->csr; 230226df7bfSsam if (Csr & CSR_ACTIVE) { 2317a0e3b47Ssam movow(&addr->ivct, is->is_ivec); 232226df7bfSsam Csr |= CSR_IENA | is->is_promiscuous; 233c0fcc33dSsam movow(&addr->csr, Csr); 234226df7bfSsam is->is_flags = 0; 235226df7bfSsam is->is_xcnt = 0; 236c0fcc33dSsam is->is_if.if_flags |= IFF_RUNNING; 237226df7bfSsam } 238226df7bfSsam splx(s); 239226df7bfSsam } 240c0fcc33dSsam if (is->is_if.if_snd.ifq_head) 241a29de3d6Ssam acestart(unit); 242226df7bfSsam } 243226df7bfSsam 244226df7bfSsam /* 245226df7bfSsam * Start output on interface. 246226df7bfSsam * Get another datagram to send off of the interface queue, 247226df7bfSsam * and map it to the interface before starting the output. 248226df7bfSsam * 249226df7bfSsam */ 250a29de3d6Ssam acestart(unit) 251a29de3d6Ssam int unit; 252226df7bfSsam { 253226df7bfSsam register struct tx_segment *txs; 254c0fcc33dSsam register long len; 255c0fcc33dSsam register int s; 256226df7bfSsam register struct ace_softc *is = &ace_softc[unit]; 257226df7bfSsam struct mbuf *m; 258c0fcc33dSsam short retries; 259226df7bfSsam 260a29de3d6Ssam if (is->is_flags & ACEF_OACTIVE) 261a29de3d6Ssam return; 262a29de3d6Ssam is->is_flags |= ACEF_OACTIVE; 263226df7bfSsam again: 264226df7bfSsam txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11)); 265226df7bfSsam if (txs->tx_csr & TCS_TBFULL) { 266226df7bfSsam is->is_stats.tx_busy++; 267a29de3d6Ssam is->is_flags &= ~ACEF_OACTIVE; 268226df7bfSsam return; 269226df7bfSsam } 270c0fcc33dSsam s = splimp(); 271226df7bfSsam IF_DEQUEUE(&is->is_if.if_snd, m); 272c0fcc33dSsam splx(s); 273a29de3d6Ssam if (m == 0) { 274a29de3d6Ssam is->is_flags &= ~ACEF_OACTIVE; 275226df7bfSsam return; 276a29de3d6Ssam } 277226df7bfSsam len = aceput(unit, txs->tx_data, m); 278226df7bfSsam retries = txs->tx_csr & TCS_RTC; 279226df7bfSsam if (retries > 0) 280226df7bfSsam acebakoff(is, txs, retries); 281226df7bfSsam 282226df7bfSsam /* 283226df7bfSsam * Ensure minimum packet length. 284226df7bfSsam * This makes the safe assumtion that there are no virtual holes 285226df7bfSsam * after the data. 286226df7bfSsam * For security, it might be wise to zero out the added bytes, 287226df7bfSsam * but we're mainly interested in speed at the moment. 288226df7bfSsam */ 289226df7bfSsam if (len - sizeof (struct ether_header) < ETHERMIN) 290226df7bfSsam len = ETHERMIN + sizeof (struct ether_header); 291226df7bfSsam if (++is->is_txnext > SEG_MAX) 292226df7bfSsam is->is_txnext = is->is_segboundry; 293226df7bfSsam is->is_if.if_opackets++; 294226df7bfSsam is->is_xcnt++; 295226df7bfSsam len = (len & 0x7fff) | TCS_TBFULL; 296c0fcc33dSsam movow(txs, len); 297226df7bfSsam goto again; 298226df7bfSsam } 299226df7bfSsam 300226df7bfSsam /* 301226df7bfSsam * Transmit done interrupt. 302226df7bfSsam */ 303226df7bfSsam acecint(unit) 304226df7bfSsam int unit; 305226df7bfSsam { 306226df7bfSsam register struct ace_softc *is = &ace_softc[unit]; 307226df7bfSsam register struct tx_segment *txseg; 308c0fcc33dSsam short eostat; 309226df7bfSsam 310226df7bfSsam if (is->is_xcnt <= 0) { 3117a0e3b47Ssam log(LOG_ERR, "ace%d: stray xmit interrupt, xcnt %d\n", 312226df7bfSsam unit, is->is_xcnt); 313226df7bfSsam is->is_xcnt = 0; 314c0fcc33dSsam if (is->is_if.if_snd.ifq_head) 315a29de3d6Ssam acestart(unit); 316226df7bfSsam return; 317226df7bfSsam } 318226df7bfSsam is->is_xcnt--; 319226df7bfSsam txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm); 320226df7bfSsam eostat = txseg->tx_csr; 321226df7bfSsam if ((eostat & TCS_TBFULL) == 0) { 322226df7bfSsam is->is_stats.tx_retries += eostat & TCS_RTC; 323226df7bfSsam if (eostat & TCS_RTFAIL) { 324226df7bfSsam is->is_stats.tx_discarded++; 325226df7bfSsam is->is_if.if_oerrors++; 326226df7bfSsam } else 327226df7bfSsam is->is_stats.tx_datagrams++; 328226df7bfSsam if (++is->is_eoctr >= 16) 329226df7bfSsam is->is_eoctr = is->is_segboundry; 330226df7bfSsam } 331c0fcc33dSsam if (is->is_if.if_snd.ifq_head) 332a29de3d6Ssam acestart(unit); 333226df7bfSsam } 334226df7bfSsam 335226df7bfSsam /* 336226df7bfSsam * Ethernet interface receiver interrupt. 337226df7bfSsam * If input error just drop packet. 338226df7bfSsam * Otherwise purge input buffered data path and examine 339226df7bfSsam * packet to determine type. If can't determine length 340226df7bfSsam * from type, then have to drop packet. Othewise decapsulate 341226df7bfSsam * packet based on type and pass to type specific higher-level 342226df7bfSsam * input routine. 343226df7bfSsam */ 344226df7bfSsam acerint(unit) 345226df7bfSsam int unit; 346226df7bfSsam { 347226df7bfSsam register struct ace_softc *is = &ace_softc[unit]; 348226df7bfSsam register struct ifqueue *inq; 349226df7bfSsam register struct ether_header *ace; 350226df7bfSsam register struct rx_segment *rxseg; 351226df7bfSsam int len, s, off, resid; 352226df7bfSsam struct mbuf *m; 353226df7bfSsam short eistat; 354226df7bfSsam 355c0fcc33dSsam if ((is->is_if.if_flags&IFF_RUNNING) == 0) 356c0fcc33dSsam return; 357226df7bfSsam again: 358226df7bfSsam rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm); 359226df7bfSsam eistat = rxseg->rx_csr; 360226df7bfSsam if ((eistat & RCS_RBFULL) == 0) 361226df7bfSsam return; 362226df7bfSsam is->is_if.if_ipackets++; 363226df7bfSsam if (++is->is_eictr >= is->is_segboundry) 364226df7bfSsam is->is_eictr = 0; 365226df7bfSsam len = eistat & RCS_RBC; 366226df7bfSsam if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) || 367226df7bfSsam len < ET_MINLEN || len > ET_MAXLEN+CRC_SIZE) { 368226df7bfSsam if (eistat & RCS_ROVRN) 369226df7bfSsam is->is_stats.rx_overruns++; 370226df7bfSsam if (eistat & RCS_RCRC) 371226df7bfSsam is->is_stats.rx_crc_errors++; 372226df7bfSsam if (eistat & RCS_RODD) 373226df7bfSsam is->is_stats.rx_align_errors++; 374226df7bfSsam if (len < ET_MINLEN) 375226df7bfSsam is->is_stats.rx_underruns++; 376226df7bfSsam if (len > ET_MAXLEN+CRC_SIZE) 377226df7bfSsam is->is_stats.rx_overruns++; 378226df7bfSsam is->is_if.if_ierrors++; 379226df7bfSsam rxseg->rx_csr = 0; 380226df7bfSsam return; 381226df7bfSsam } else 382226df7bfSsam is->is_stats.rx_datagrams++; 383226df7bfSsam ace = (struct ether_header *)rxseg->rx_data; 384226df7bfSsam len -= sizeof (struct ether_header); 385226df7bfSsam /* 386c0fcc33dSsam * Deal with trailer protocol: if type is trailer 387226df7bfSsam * get true type from first 16-bit word past data. 388226df7bfSsam * Remember that type was trailer by setting off. 389226df7bfSsam */ 390226df7bfSsam ace->ether_type = ntohs((u_short)ace->ether_type); 391226df7bfSsam #define acedataaddr(ace, off, type) \ 392226df7bfSsam ((type)(((caddr_t)(((char *)ace)+sizeof (struct ether_header))+(off)))) 393c0fcc33dSsam if (ace->ether_type >= ETHERTYPE_TRAIL && 394c0fcc33dSsam ace->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 395c0fcc33dSsam off = (ace->ether_type - ETHERTYPE_TRAIL) * 512; 396226df7bfSsam if (off >= ETHERMTU) 397226df7bfSsam goto setup; /* sanity */ 398226df7bfSsam ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *)); 399226df7bfSsam resid = ntohs(*(acedataaddr(ace, off+2, u_short *))); 400226df7bfSsam if (off + resid > len) 401226df7bfSsam goto setup; /* sanity */ 402226df7bfSsam len = off + resid; 403226df7bfSsam } else 404226df7bfSsam off = 0; 405226df7bfSsam if (len == 0) 406226df7bfSsam goto setup; 407226df7bfSsam 408226df7bfSsam /* 409226df7bfSsam * Pull packet off interface. Off is nonzero if packet 410226df7bfSsam * has trailing header; aceget will then force this header 411226df7bfSsam * information to be at the front, but we still have to drop 412226df7bfSsam * the type and length which are at the front of any trailer data. 413226df7bfSsam */ 4147a0e3b47Ssam m = aceget((u_char *)rxseg->rx_data, len, off, &is->is_if); 415226df7bfSsam if (m == 0) 416226df7bfSsam goto setup; 417226df7bfSsam if (off) { 4187a0e3b47Ssam struct ifnet *ifp; 4197a0e3b47Ssam 4207a0e3b47Ssam ifp = *(mtod(m, struct ifnet **)); 421226df7bfSsam m->m_off += 2 * sizeof (u_short); 422226df7bfSsam m->m_len -= 2 * sizeof (u_short); 4237a0e3b47Ssam *(mtod(m, struct ifnet **)) = ifp; 424226df7bfSsam } 425226df7bfSsam switch (ace->ether_type) { 426226df7bfSsam 427226df7bfSsam #ifdef INET 428c0fcc33dSsam case ETHERTYPE_IP: 429226df7bfSsam schednetisr(NETISR_IP); 430226df7bfSsam inq = &ipintrq; 431226df7bfSsam break; 4327a0e3b47Ssam #endif 433226df7bfSsam 434c0fcc33dSsam case ETHERTYPE_ARP: 435226df7bfSsam arpinput(&is->is_ac, m); 436226df7bfSsam goto setup; 437c0fcc33dSsam #ifdef NS 438c0fcc33dSsam case ETHERTYPE_NS: 439c0fcc33dSsam schednetisr(NETISR_NS); 440c0fcc33dSsam inq = &nsintrq; 441c0fcc33dSsam break; 442c0fcc33dSsam 443c0fcc33dSsam #endif 444226df7bfSsam default: 445226df7bfSsam m_freem(m); 446226df7bfSsam goto setup; 447226df7bfSsam } 448226df7bfSsam if (IF_QFULL(inq)) { 449226df7bfSsam IF_DROP(inq); 450226df7bfSsam m_freem(m); 451226df7bfSsam goto setup; 452226df7bfSsam } 453226df7bfSsam s = splimp(); 454226df7bfSsam IF_ENQUEUE(inq, m); 455226df7bfSsam splx(s); 456226df7bfSsam setup: 457226df7bfSsam rxseg->rx_csr = 0; 458226df7bfSsam goto again; 459226df7bfSsam } 460226df7bfSsam 461226df7bfSsam /* 462226df7bfSsam * Ethernet output routine. 463226df7bfSsam * Encapsulate a packet of type family for the local net. 464226df7bfSsam * Use trailer local net encapsulation if enough data in first 465226df7bfSsam * packet leaves a multiple of 512 bytes of data in remainder. 466226df7bfSsam */ 467226df7bfSsam aceoutput(ifp, m0, dst) 468226df7bfSsam struct ifnet *ifp; 469226df7bfSsam struct mbuf *m0; 470226df7bfSsam struct sockaddr *dst; 471226df7bfSsam { 472226df7bfSsam register struct ace_softc *is = &ace_softc[ifp->if_unit]; 473226df7bfSsam register struct mbuf *m = m0; 474226df7bfSsam register struct ether_header *ace; 475226df7bfSsam register int off; 476226df7bfSsam struct mbuf *mcopy = (struct mbuf *)0; 477f814bab2Ssam int type, s, error, usetrailers; 478c0fcc33dSsam u_char edst[6]; 479226df7bfSsam struct in_addr idst; 480226df7bfSsam 4817a0e3b47Ssam if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) { 4827a0e3b47Ssam error = ENETDOWN; 4837a0e3b47Ssam goto bad; 4847a0e3b47Ssam } 485226df7bfSsam switch (dst->sa_family) { 486226df7bfSsam 487226df7bfSsam #ifdef INET 488226df7bfSsam case AF_INET: 489226df7bfSsam idst = ((struct sockaddr_in *)dst)->sin_addr; 490f814bab2Ssam if (!arpresolve(&is->is_ac, m, &idst, edst, &usetrailers)) 491226df7bfSsam return (0); /* if not yet resolved */ 492c0fcc33dSsam if (!bcmp((caddr_t)edst, (caddr_t)etherbroadcastaddr, 493c0fcc33dSsam sizeof (edst))) 494226df7bfSsam mcopy = m_copy(m, 0, (int)M_COPYALL); 495226df7bfSsam off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 496226df7bfSsam /* need per host negotiation */ 497f814bab2Ssam if (usetrailers && off > 0 && (off & 0x1ff) == 0 && 498226df7bfSsam m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 499c0fcc33dSsam type = ETHERTYPE_TRAIL + (off>>9); 500226df7bfSsam m->m_off -= 2 * sizeof (u_short); 501226df7bfSsam m->m_len += 2 * sizeof (u_short); 502c0fcc33dSsam *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); 503226df7bfSsam *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); 504226df7bfSsam goto gottrailertype; 505226df7bfSsam } 506c0fcc33dSsam type = ETHERTYPE_IP; 507c0fcc33dSsam off = 0; 508c0fcc33dSsam goto gottype; 509c0fcc33dSsam #endif 510c0fcc33dSsam #ifdef NS 511c0fcc33dSsam case AF_NS: 512c0fcc33dSsam bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), 513c0fcc33dSsam (caddr_t)edst, sizeof (edst)); 514c0fcc33dSsam if (!bcmp((caddr_t)edst, (caddr_t)&ns_broadhost,sizeof(edst))) 515c0fcc33dSsam mcopy = m_copy(m, 0, (int)M_COPYALL); 516c0fcc33dSsam else if (!bcmp((caddr_t)edst, (caddr_t)&ns_thishost, 517c0fcc33dSsam sizeof(edst))) 518c0fcc33dSsam return(looutput(&loif, m, dst)); 519c0fcc33dSsam type = ETHERTYPE_NS; 520226df7bfSsam off = 0; 521226df7bfSsam goto gottype; 522226df7bfSsam #endif 523226df7bfSsam case AF_UNSPEC: 524226df7bfSsam ace = (struct ether_header *)dst->sa_data; 525c0fcc33dSsam bcopy((caddr_t)ace->ether_dhost, (caddr_t)edst, sizeof (edst)); 526226df7bfSsam type = ace->ether_type; 527226df7bfSsam goto gottype; 528226df7bfSsam 529226df7bfSsam default: 5307a0e3b47Ssam log(LOG_ERR, "ace%d: can't handle af%d\n", 531226df7bfSsam ifp->if_unit, dst->sa_family); 532226df7bfSsam error = EAFNOSUPPORT; 533226df7bfSsam goto bad; 534226df7bfSsam } 535226df7bfSsam 536226df7bfSsam gottrailertype: 537226df7bfSsam /* 538226df7bfSsam * Packet to be sent as trailer: move first packet 539226df7bfSsam * (control information) to end of chain. 540226df7bfSsam */ 541226df7bfSsam while (m->m_next) 542226df7bfSsam m = m->m_next; 543226df7bfSsam m->m_next = m0; 544226df7bfSsam m = m0->m_next; 545226df7bfSsam m0->m_next = 0; 546226df7bfSsam m0 = m; 547226df7bfSsam 548226df7bfSsam gottype: 549226df7bfSsam /* 550226df7bfSsam * Add local net header. If no space in first mbuf, 551226df7bfSsam * allocate another. 552226df7bfSsam */ 553226df7bfSsam if (m->m_off > MMAXOFF || 554226df7bfSsam MMINOFF + sizeof (struct ether_header) > m->m_off) { 555226df7bfSsam m = m_get(M_DONTWAIT, MT_HEADER); 556226df7bfSsam if (m == 0) { 557226df7bfSsam error = ENOBUFS; 558226df7bfSsam goto bad; 559226df7bfSsam } 560226df7bfSsam m->m_next = m0; 561226df7bfSsam m->m_off = MMINOFF; 562226df7bfSsam m->m_len = sizeof (struct ether_header); 563226df7bfSsam } else { 564226df7bfSsam m->m_off -= sizeof (struct ether_header); 565226df7bfSsam m->m_len += sizeof (struct ether_header); 566226df7bfSsam } 567226df7bfSsam ace = mtod(m, struct ether_header *); 568c0fcc33dSsam bcopy((caddr_t)edst, (caddr_t)ace->ether_dhost, sizeof (edst)); 569c0fcc33dSsam bcopy((caddr_t)is->is_addr, (caddr_t)ace->ether_shost, 570c0fcc33dSsam sizeof (is->is_addr)); 571226df7bfSsam ace->ether_type = htons((u_short)type); 572226df7bfSsam 573226df7bfSsam /* 574226df7bfSsam * Queue message on interface, and start output if interface 575226df7bfSsam * not yet active. 576226df7bfSsam */ 577226df7bfSsam s = splimp(); 578226df7bfSsam if (IF_QFULL(&ifp->if_snd)) { 579226df7bfSsam IF_DROP(&ifp->if_snd); 580226df7bfSsam error = ENOBUFS; 581226df7bfSsam goto qfull; 582226df7bfSsam } 583226df7bfSsam IF_ENQUEUE(&ifp->if_snd, m); 584226df7bfSsam splx(s); 585a29de3d6Ssam acestart(ifp->if_unit); 586226df7bfSsam return (mcopy ? looutput(&loif, mcopy, dst) : 0); 587226df7bfSsam qfull: 588226df7bfSsam m0 = m; 589226df7bfSsam splx(s); 590226df7bfSsam bad: 591226df7bfSsam m_freem(m0); 592226df7bfSsam if (mcopy) 593226df7bfSsam m_freem(mcopy); 594226df7bfSsam return (error); 595226df7bfSsam } 596226df7bfSsam 597226df7bfSsam /* 598226df7bfSsam * Routine to copy from mbuf chain to transmit buffer on the VERSAbus 599226df7bfSsam * If packet size is less than the minimum legal size, 600226df7bfSsam * the buffer is expanded. We probably should zero out the extra 601226df7bfSsam * bytes for security, but that would slow things down. 602226df7bfSsam */ 603c0fcc33dSsam /*ARGSUSED*/ 604226df7bfSsam aceput(unit, txbuf, m) 6057a0e3b47Ssam int unit; 606c0fcc33dSsam char *txbuf; 607226df7bfSsam struct mbuf *m; 608226df7bfSsam { 6097a0e3b47Ssam register u_char *bp, *mcp; 6107a0e3b47Ssam register short *s1, *s2; 611c0fcc33dSsam register u_int len; 612226df7bfSsam register struct mbuf *mp; 613c0fcc33dSsam int total; 614226df7bfSsam 615226df7bfSsam total = 0; 616c0fcc33dSsam bp = (u_char *)txbuf; 617226df7bfSsam for (mp = m; (mp); mp = mp->m_next) { 618226df7bfSsam len = mp->m_len; 619226df7bfSsam if (len == 0) 620226df7bfSsam continue; 621226df7bfSsam total += len; 622226df7bfSsam mcp = mtod(mp, u_char *); 623226df7bfSsam if (((int)mcp & 01) && ((int)bp & 01)) { 624226df7bfSsam /* source & destination at odd addresses */ 625c0fcc33dSsam movob(bp++, *mcp++); 626226df7bfSsam --len; 627226df7bfSsam } 628226df7bfSsam if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) { 629c0fcc33dSsam register u_int l; 630226df7bfSsam 631226df7bfSsam s1 = (short *)bp; 632226df7bfSsam s2 = (short *)mcp; 633226df7bfSsam l = len >> 1; /* count # of shorts */ 634c0fcc33dSsam while (l-- != 0) 635c0fcc33dSsam movow(s1++, *s2++); 636226df7bfSsam len &= 1; /* # remaining bytes */ 637226df7bfSsam bp = (u_char *)s1; 638226df7bfSsam mcp = (u_char *)s2; 639226df7bfSsam } 640c0fcc33dSsam while (len-- != 0) 641c0fcc33dSsam movob(bp++, *mcp++); 642226df7bfSsam } 643226df7bfSsam m_freem(m); 644226df7bfSsam return (total); 645226df7bfSsam } 646226df7bfSsam 647226df7bfSsam /* 648226df7bfSsam * Routine to copy from VERSAbus memory into mbufs. 649226df7bfSsam * 650226df7bfSsam * Warning: This makes the fairly safe assumption that 651226df7bfSsam * mbufs have even lengths. 652226df7bfSsam */ 653c0fcc33dSsam /*ARGSUSED*/ 654226df7bfSsam struct mbuf * 6557a0e3b47Ssam aceget(rxbuf, totlen, off0, ifp) 656226df7bfSsam u_char *rxbuf; 657226df7bfSsam int totlen, off0; 6587a0e3b47Ssam struct ifnet *ifp; 659226df7bfSsam { 6607a0e3b47Ssam register u_char *cp, *mcp; 661226df7bfSsam register int tlen; 662226df7bfSsam register struct mbuf *m; 663226df7bfSsam struct mbuf *top = 0, **mp = ⊤ 664226df7bfSsam int len, off = off0; 665226df7bfSsam 666226df7bfSsam cp = rxbuf + sizeof (struct ether_header); 667226df7bfSsam while (totlen > 0) { 668226df7bfSsam MGET(m, M_DONTWAIT, MT_DATA); 669226df7bfSsam if (m == 0) 670226df7bfSsam goto bad; 671226df7bfSsam if (off) { 672226df7bfSsam len = totlen - off; 673226df7bfSsam cp = rxbuf + sizeof (struct ether_header) + off; 674226df7bfSsam } else 675226df7bfSsam len = totlen; 6767a0e3b47Ssam if (ifp) 6777a0e3b47Ssam len += sizeof(ifp); 678226df7bfSsam if (len >= CLBYTES) { 679226df7bfSsam struct mbuf *p; 680226df7bfSsam 681226df7bfSsam MCLGET(p, 1); 682226df7bfSsam if (p != 0) { 683226df7bfSsam m->m_len = len = CLBYTES; 684226df7bfSsam m->m_off = (int)p - (int)m; 685226df7bfSsam } else { 686226df7bfSsam m->m_len = len = MIN(MLEN, len); 687226df7bfSsam m->m_off = MMINOFF; 688226df7bfSsam } 689226df7bfSsam } else { 690226df7bfSsam m->m_len = len = MIN(MLEN, len); 691226df7bfSsam m->m_off = MMINOFF; 692226df7bfSsam } 693226df7bfSsam mcp = mtod(m, u_char *); 6947a0e3b47Ssam if (ifp) { 6957a0e3b47Ssam /* 6967a0e3b47Ssam * Prepend interface pointer to first mbuf. 6977a0e3b47Ssam */ 6987a0e3b47Ssam *(mtod(m, struct ifnet **)) = ifp; 6997a0e3b47Ssam mcp += sizeof(ifp); 7007a0e3b47Ssam len -= sizeof(ifp); 7017a0e3b47Ssam ifp = (struct ifnet *)0; 7027a0e3b47Ssam } 703226df7bfSsam /*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/ 704226df7bfSsam /*cp += len; mcp += len;*/ 705226df7bfSsam tlen = len; 706226df7bfSsam if (((int)mcp & 01) && ((int)cp & 01)) { 707226df7bfSsam /* source & destination at odd addresses */ 708226df7bfSsam *mcp++ = *cp++; 709226df7bfSsam --tlen; 710226df7bfSsam } 711226df7bfSsam if (tlen > 1 && (((int)mcp&01) == 0) && (((int)cp&01) == 0)) { 712226df7bfSsam register short *s1, *s2; 713226df7bfSsam register int l; 714226df7bfSsam 715226df7bfSsam s1 = (short *)mcp; 716226df7bfSsam s2 = (short *)cp; 717226df7bfSsam l = tlen >> 1; /* count # of shorts */ 718226df7bfSsam while (l-- > 0) /* copy shorts */ 719226df7bfSsam *s1++ = *s2++; 720226df7bfSsam tlen &= 1; /* # remaining bytes */ 721226df7bfSsam mcp = (u_char *)s1; 722226df7bfSsam cp = (u_char *)s2; 723226df7bfSsam } 724226df7bfSsam while (tlen-- > 0) 725226df7bfSsam *mcp++ = *cp++; 726226df7bfSsam *mp = m; 727226df7bfSsam mp = &m->m_next; 728226df7bfSsam if (off == 0) { 729226df7bfSsam totlen -= len; 730226df7bfSsam continue; 731226df7bfSsam } 732226df7bfSsam off += len; 733226df7bfSsam if (off == totlen) { 734226df7bfSsam cp = rxbuf + sizeof (struct ether_header); 735226df7bfSsam off = 0; 736226df7bfSsam totlen = off0; 737226df7bfSsam } 738226df7bfSsam } 739226df7bfSsam return (top); 740226df7bfSsam bad: 741226df7bfSsam m_freem(top); 742226df7bfSsam return (0); 743226df7bfSsam } 744226df7bfSsam 745226df7bfSsam acebakoff(is, txseg, retries) 746226df7bfSsam struct ace_softc *is; 747226df7bfSsam struct tx_segment *txseg; 748226df7bfSsam register int retries; 749226df7bfSsam { 750226df7bfSsam register short *pBakNum, random_num; 751226df7bfSsam short *pMask; 752226df7bfSsam 753226df7bfSsam pMask = &random_mask_tbl[0]; 754226df7bfSsam pBakNum = &txseg->tx_backoff[0]; 755226df7bfSsam while (--retries >= 0) { 756226df7bfSsam random_num = (is->is_currnd = (is->is_currnd * 18741)-13849); 757226df7bfSsam random_num &= *pMask++; 758226df7bfSsam *pBakNum++ = random_num ^ (short)(0xFF00 | 0x00FC); 759226df7bfSsam } 760226df7bfSsam } 761226df7bfSsam 762226df7bfSsam /* 763226df7bfSsam * Process an ioctl request. 764226df7bfSsam */ 765226df7bfSsam aceioctl(ifp, cmd, data) 766226df7bfSsam register struct ifnet *ifp; 767226df7bfSsam int cmd; 768226df7bfSsam caddr_t data; 769226df7bfSsam { 770c0fcc33dSsam register struct ifaddr *ifa = (struct ifaddr *)data; 7717a0e3b47Ssam struct acedevice *addr; 772c0fcc33dSsam int s = splimp(), error = 0; 773226df7bfSsam 774226df7bfSsam switch (cmd) { 775226df7bfSsam 776226df7bfSsam case SIOCSIFADDR: 777c0fcc33dSsam ifp->if_flags |= IFF_UP; 7787a0e3b47Ssam switch (ifa->ifa_addr.sa_family) { 7797a0e3b47Ssam #ifdef INET 7807a0e3b47Ssam case AF_INET: 7817a0e3b47Ssam aceinit(ifp->if_unit); /* before arpwhohas */ 7827a0e3b47Ssam ((struct arpcom *)ifp)->ac_ipaddr = 7837a0e3b47Ssam IA_SIN(ifa)->sin_addr; 784c0fcc33dSsam arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 785226df7bfSsam break; 7867a0e3b47Ssam #endif 7877a0e3b47Ssam #ifdef NS 7887a0e3b47Ssam case AF_NS: { 789e79104c5Ssam struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; 790e79104c5Ssam struct ace_softc *is = &ace_softc[ifp->if_unit]; 791226df7bfSsam 7927a0e3b47Ssam if (!ns_nullhost(*ina)) { 7937a0e3b47Ssam ifp->if_flags &= ~IFF_RUNNING; 7947a0e3b47Ssam addr = (struct acedevice *) 795e79104c5Ssam aceinfo[ifp->if_unit]->ui_addr; 796c0fcc33dSsam movow(&addr->csr, CSR_RESET); 797226df7bfSsam DELAY(10000); 7987a0e3b47Ssam /* set station address & copy addr to arp */ 7997a0e3b47Ssam acesetetaddr(ifp->if_unit, addr, 8007a0e3b47Ssam ina->x_host.c_host); 8017a0e3b47Ssam } else 802e79104c5Ssam ina->x_host = *(union ns_host *)is->is_addr; 8037a0e3b47Ssam aceinit(ifp->if_unit); 804226df7bfSsam break; 805226df7bfSsam } 806226df7bfSsam #endif 8077a0e3b47Ssam default: 8087a0e3b47Ssam aceinit(ifp->if_unit); 8097a0e3b47Ssam break; 8107a0e3b47Ssam } 8117a0e3b47Ssam break; 8127a0e3b47Ssam 8137a0e3b47Ssam case SIOCSIFFLAGS: 8147a0e3b47Ssam if ((ifp->if_flags&IFF_UP) == 0 && ifp->if_flags&IFF_RUNNING) { 8157a0e3b47Ssam addr = (struct acedevice *) 8167a0e3b47Ssam (aceinfo[ifp->if_unit]->ui_addr); 8177a0e3b47Ssam movow(&addr->csr, CSR_RESET); 8187a0e3b47Ssam ifp->if_flags &= ~IFF_RUNNING; 8197a0e3b47Ssam } else if (ifp->if_flags&IFF_UP && 8207a0e3b47Ssam (ifp->if_flags&IFF_RUNNING) == 0) 8217a0e3b47Ssam aceinit(ifp->if_unit); 8227a0e3b47Ssam break; 823226df7bfSsam 824226df7bfSsam default: 825226df7bfSsam error = EINVAL; 826226df7bfSsam } 827226df7bfSsam splx(s); 828226df7bfSsam return (error); 829226df7bfSsam } 830226df7bfSsam 831a29de3d6Ssam acesetup(unit) 832226df7bfSsam int unit; 833226df7bfSsam { 834226df7bfSsam register struct ace_softc *is = &ace_softc[unit]; 835226df7bfSsam register char *pData1; 836a29de3d6Ssam register short i; 837a29de3d6Ssam struct acedevice *addr; 838226df7bfSsam 839a29de3d6Ssam bzero(is->is_dpm, 16384*2); 840226df7bfSsam is->is_currnd = 49123; 841a29de3d6Ssam addr = (struct acedevice *)aceinfo[unit]->ui_addr; 842226df7bfSsam is->is_segboundry = (addr->segb >> 11) & 0xf; 843a29de3d6Ssam pData1 = is->is_dpm + (is->is_segboundry << 11); 844226df7bfSsam for (i = SEG_MAX + 1 - is->is_segboundry; --i >= 0;) { 845226df7bfSsam acebakoff(is, (struct tx_segment *)pData1, 15); 846226df7bfSsam pData1 += sizeof (struct tx_segment); 847226df7bfSsam } 848226df7bfSsam is->is_eictr = 0; 849226df7bfSsam is->is_eoctr = is->is_txnext = is->is_segboundry; 850226df7bfSsam bzero((char *)&is->is_stats, sizeof (is->is_stats)); 851226df7bfSsam } 852226df7bfSsam #endif 853