1*11574Ssam /* if_ec.c 4.33 83/03/15 */ 26520Sfeldman 36520Sfeldman #include "ec.h" 46520Sfeldman 56520Sfeldman /* 66520Sfeldman * 3Com Ethernet Controller interface 76520Sfeldman */ 89795Ssam #include "../machine/pte.h" 96520Sfeldman 106520Sfeldman #include "../h/param.h" 116520Sfeldman #include "../h/systm.h" 126520Sfeldman #include "../h/mbuf.h" 136520Sfeldman #include "../h/buf.h" 146520Sfeldman #include "../h/protosw.h" 156520Sfeldman #include "../h/socket.h" 166520Sfeldman #include "../h/vmmac.h" 178461Sroot #include <errno.h> 188461Sroot 198461Sroot #include "../net/if.h" 208461Sroot #include "../net/netisr.h" 218461Sroot #include "../net/route.h" 228417Swnj #include "../netinet/in.h" 238417Swnj #include "../netinet/in_systm.h" 248417Swnj #include "../netinet/ip.h" 258417Swnj #include "../netinet/ip_var.h" 26*11574Ssam #include "../netinet/if_ether.h" 278417Swnj #include "../netpup/pup.h" 286520Sfeldman 298461Sroot #include "../vax/cpu.h" 308461Sroot #include "../vax/mtpr.h" 318461Sroot #include "../vaxif/if_ecreg.h" 328461Sroot #include "../vaxif/if_uba.h" 338461Sroot #include "../vaxuba/ubareg.h" 348461Sroot #include "../vaxuba/ubavar.h" 358461Sroot 367470Sfeldman #define ECMEM 0000000 376520Sfeldman 386520Sfeldman int ecprobe(), ecattach(), ecrint(), ecxint(), eccollide(); 396520Sfeldman struct uba_device *ecinfo[NEC]; 406520Sfeldman u_short ecstd[] = { 0 }; 416520Sfeldman struct uba_driver ecdriver = 426520Sfeldman { ecprobe, 0, ecattach, 0, ecstd, "ec", ecinfo }; 436520Sfeldman #define ECUNIT(x) minor(x) 446520Sfeldman 456520Sfeldman int ecinit(),ecoutput(),ecreset(); 468773Sroot struct mbuf *ecget(); 476520Sfeldman 486545Sfeldman extern struct ifnet loif; 496545Sfeldman 506520Sfeldman /* 516520Sfeldman * Ethernet software status per interface. 526520Sfeldman * 536520Sfeldman * Each interface is referenced by a network interface structure, 546520Sfeldman * es_if, which the routing code uses to locate the interface. 556520Sfeldman * This structure contains the output queue for the interface, its address, ... 566520Sfeldman * We also have, for each interface, a UBA interface structure, which 576520Sfeldman * contains information about the UNIBUS resources held by the interface: 586520Sfeldman * map registers, buffered data paths, etc. Information is cached in this 596520Sfeldman * structure for use by the if_uba.c routines in running the interface 606520Sfeldman * efficiently. 616520Sfeldman */ 626520Sfeldman struct ec_softc { 63*11574Ssam struct arpcom es_ac; /* common Ethernet structures */ 64*11574Ssam #define es_if es_ac.ac_if /* network-visible interface */ 65*11574Ssam #define es_addr es_ac.ac_enaddr /* hardware Ethernet address */ 666520Sfeldman struct ifuba es_ifuba; /* UNIBUS resources */ 676520Sfeldman short es_mask; /* mask for current output delay */ 686520Sfeldman short es_oactive; /* is output active? */ 698773Sroot u_char *es_buf[16]; /* virtual addresses of buffers */ 706520Sfeldman } ec_softc[NEC]; 716520Sfeldman 726520Sfeldman /* 736520Sfeldman * Do output DMA to determine interface presence and 746520Sfeldman * interrupt vector. DMA is too short to disturb other hosts. 756520Sfeldman */ 766520Sfeldman ecprobe(reg) 776520Sfeldman caddr_t reg; 786520Sfeldman { 796520Sfeldman register int br, cvec; /* r11, r10 value-result */ 806520Sfeldman register struct ecdevice *addr = (struct ecdevice *)reg; 817470Sfeldman register caddr_t ecbuf = (caddr_t) &umem[numuba][ECMEM]; 826520Sfeldman 836520Sfeldman #ifdef lint 846520Sfeldman br = 0; cvec = br; br = cvec; 856520Sfeldman ecrint(0); ecxint(0); eccollide(0); 866520Sfeldman #endif 876520Sfeldman /* 886637Sfeldman * Make sure memory is turned on 896637Sfeldman */ 906637Sfeldman addr->ec_rcr = EC_AROM; 916637Sfeldman /* 927470Sfeldman * Disable map registers for ec unibus space, 937470Sfeldman * but don't allocate yet. 947470Sfeldman */ 958773Sroot (void) ubamem(numuba, ECMEM, 32*2, 0); 967470Sfeldman /* 976520Sfeldman * Check for existence of buffers on Unibus. 986520Sfeldman */ 998773Sroot if (badaddr((caddr_t)ecbuf, 2)) { 1007470Sfeldman bad1: 1017470Sfeldman printf("ec: buffer mem not found\n"); 1027470Sfeldman bad2: 1038773Sroot (void) ubamem(numuba, 0, 0, 0); /* reenable map (780 only) */ 1047470Sfeldman addr->ec_rcr = EC_MDISAB; /* disable memory */ 1056520Sfeldman return (0); 1066520Sfeldman } 1077470Sfeldman #if VAX780 1087470Sfeldman if (cpu == VAX_780 && uba_hd[numuba].uh_uba->uba_sr) { 1097470Sfeldman uba_hd[numuba].uh_uba->uba_sr = uba_hd[numuba].uh_uba->uba_sr; 1107470Sfeldman goto bad1; 1117470Sfeldman } 1127470Sfeldman #endif 1136520Sfeldman 1146520Sfeldman /* 1156520Sfeldman * Tell the system that the board has memory here, so it won't 1166520Sfeldman * attempt to allocate the addresses later. 1176520Sfeldman */ 1187470Sfeldman if (ubamem(numuba, ECMEM, 32*2, 1) == 0) { 1197470Sfeldman printf("ecprobe: cannot reserve uba addresses\n"); 1207470Sfeldman goto bad2; 1217470Sfeldman } 1226520Sfeldman 1236520Sfeldman /* 1246520Sfeldman * Make a one byte packet in what should be buffer #0. 1256520Sfeldman * Submit it for sending. This whould cause an xmit interrupt. 1266520Sfeldman * The xmit interrupt vector is 8 bytes after the receive vector, 1276520Sfeldman * so adjust for this before returning. 1286520Sfeldman */ 1296520Sfeldman *(u_short *)ecbuf = (u_short) 03777; 1306520Sfeldman ecbuf[03777] = '\0'; 1316520Sfeldman addr->ec_xcr = EC_XINTEN|EC_XWBN; 1326520Sfeldman DELAY(100000); 1336520Sfeldman addr->ec_xcr = EC_XCLR; 1347216Ssam if (cvec > 0 && cvec != 0x200) { 1357470Sfeldman if (cvec & 04) { /* collision interrupt */ 1367470Sfeldman cvec -= 04; 1377470Sfeldman br += 1; /* rcv is collision + 1 */ 1387470Sfeldman } else { /* xmit interrupt */ 1397470Sfeldman cvec -= 010; 1407470Sfeldman br += 2; /* rcv is xmit + 2 */ 1417470Sfeldman } 1427216Ssam } 1436520Sfeldman return (1); 1446520Sfeldman } 1456520Sfeldman 1466520Sfeldman /* 1476520Sfeldman * Interface exists: make available by filling in network interface 1486520Sfeldman * record. System will initialize the interface when it is ready 1496520Sfeldman * to accept packets. 1506520Sfeldman */ 1516520Sfeldman ecattach(ui) 1526520Sfeldman struct uba_device *ui; 1536520Sfeldman { 1547216Ssam struct ec_softc *es = &ec_softc[ui->ui_unit]; 1557216Ssam register struct ifnet *ifp = &es->es_if; 1566520Sfeldman register struct ecdevice *addr = (struct ecdevice *)ui->ui_addr; 1577216Ssam struct sockaddr_in *sin; 1587216Ssam int i, j; 1597216Ssam u_char *cp; 1606520Sfeldman 1617216Ssam ifp->if_unit = ui->ui_unit; 1627216Ssam ifp->if_name = "ec"; 1639745Ssam ifp->if_mtu = ETHERMTU; 1646520Sfeldman 1656520Sfeldman /* 1667216Ssam * Read the ethernet address off the board, one nibble at a time. 1676520Sfeldman */ 1686520Sfeldman addr->ec_xcr = EC_UECLR; 1696520Sfeldman addr->ec_rcr = EC_AROM; 170*11574Ssam cp = es->es_addr; 1717216Ssam #define NEXTBIT addr->ec_rcr = EC_AROM|EC_ASTEP; addr->ec_rcr = EC_AROM 1726520Sfeldman for (i=0; i<6; i++) { 1736520Sfeldman *cp = 0; 1746520Sfeldman for (j=0; j<=4; j+=4) { 1756520Sfeldman *cp |= ((addr->ec_rcr >> 8) & 0xf) << j; 1767216Ssam NEXTBIT; NEXTBIT; NEXTBIT; NEXTBIT; 1776520Sfeldman } 1786520Sfeldman cp++; 1796520Sfeldman } 1807216Ssam #ifdef notdef 1816520Sfeldman printf("ec%d: addr=%x:%x:%x:%x:%x:%x\n", ui->ui_unit, 182*11574Ssam es->es_addr[0]&0xff, es->es_addr[1]&0xff, 183*11574Ssam es->es_addr[2]&0xff, es->es_addr[3]&0xff, 184*11574Ssam es->es_addr[4]&0xff, es->es_addr[5]&0xff); 1857216Ssam #endif 1866520Sfeldman sin = (struct sockaddr_in *)&es->es_if.if_addr; 1876520Sfeldman sin->sin_family = AF_INET; 188*11574Ssam if (ui->ui_flags) { 189*11574Ssam i = ((es->es_addr[3]&0xff)<<16) | 190*11574Ssam ((es->es_addr[4]&0xff)<<8) | 191*11574Ssam (es->es_addr[5]&0xff); 192*11574Ssam sin->sin_addr = if_makeaddr(ui->ui_flags, i); 193*11574Ssam } else 194*11574Ssam sin->sin_addr = arpmyaddr(); 1957216Ssam ifp->if_init = ecinit; 1967216Ssam ifp->if_output = ecoutput; 1978977Sroot ifp->if_reset = ecreset; 1986520Sfeldman for (i=0; i<16; i++) 1998773Sroot es->es_buf[i] = (u_char *)&umem[ui->ui_ubanum][ECMEM+2048*i]; 2007216Ssam if_attach(ifp); 2016520Sfeldman } 2026520Sfeldman 2036520Sfeldman /* 2046520Sfeldman * Reset of interface after UNIBUS reset. 2056520Sfeldman * If interface is on specified uba, reset its state. 2066520Sfeldman */ 2076520Sfeldman ecreset(unit, uban) 2086520Sfeldman int unit, uban; 2096520Sfeldman { 2106520Sfeldman register struct uba_device *ui; 2116520Sfeldman 2126520Sfeldman if (unit >= NEC || (ui = ecinfo[unit]) == 0 || ui->ui_alive == 0 || 2136520Sfeldman ui->ui_ubanum != uban) 2146520Sfeldman return; 2156520Sfeldman printf(" ec%d", unit); 2168773Sroot (void) ubamem(uban, ECMEM, 32*2, 0); /* mr disable (no alloc) */ 2176520Sfeldman ecinit(unit); 2186520Sfeldman } 2196520Sfeldman 2206520Sfeldman /* 2216520Sfeldman * Initialization of interface; clear recorded pending 2226520Sfeldman * operations, and reinitialize UNIBUS usage. 2236520Sfeldman */ 2246520Sfeldman ecinit(unit) 2256520Sfeldman int unit; 2266520Sfeldman { 2277216Ssam struct ec_softc *es = &ec_softc[unit]; 2287216Ssam struct ecdevice *addr; 2297216Ssam int i, s; 230*11574Ssam register struct ifnet *ifp = &es->es_if; 231*11574Ssam register struct sockaddr_in *sin, *sinb; 2326520Sfeldman 233*11574Ssam sin = (struct sockaddr_in *)&ifp->if_addr; 234*11574Ssam if (sin->sin_addr.s_addr == 0) /* if address still unknown */ 235*11574Ssam return; 236*11574Ssam ifp->if_net = in_netof(sin->sin_addr); 237*11574Ssam ifp->if_host[0] = in_lnaof(sin->sin_addr); 238*11574Ssam sinb = (struct sockaddr_in *)&ifp->if_broadaddr; 239*11574Ssam sinb->sin_family = AF_INET; 240*11574Ssam sinb->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY); 241*11574Ssam ifp->if_flags = IFF_BROADCAST; 242*11574Ssam 2436520Sfeldman /* 2447217Sfeldman * Hang receive buffers and start any pending writes. 2456637Sfeldman * Writing into the rcr also makes sure the memory 2466637Sfeldman * is turned on. 2476520Sfeldman */ 2487216Ssam addr = (struct ecdevice *)ecinfo[unit]->ui_addr; 2496520Sfeldman s = splimp(); 2506520Sfeldman for (i=ECRHBF; i>=ECRLBF; i--) 2516520Sfeldman addr->ec_rcr = EC_READ|i; 2527217Sfeldman es->es_oactive = 0; 2537217Sfeldman es->es_mask = ~0; 2546520Sfeldman es->es_if.if_flags |= IFF_UP; 2557217Sfeldman if (es->es_if.if_snd.ifq_head) 2567217Sfeldman ecstart(unit); 2576520Sfeldman splx(s); 2587150Swnj if_rtinit(&es->es_if, RTF_UP); 259*11574Ssam arpattach(&es->es_ac); 260*11574Ssam arpwhohas(&es->es_ac, &sin->sin_addr); 2616520Sfeldman } 2626520Sfeldman 2636520Sfeldman /* 2646520Sfeldman * Start or restart output on interface. 2656520Sfeldman * If interface is already active, then this is a retransmit 2666545Sfeldman * after a collision, and just restuff registers. 2676520Sfeldman * If interface is not already active, get another datagram 2686520Sfeldman * to send off of the interface queue, and map it to the interface 2696520Sfeldman * before starting the output. 2706520Sfeldman */ 2716520Sfeldman ecstart(dev) 2726520Sfeldman dev_t dev; 2736520Sfeldman { 2748773Sroot int unit = ECUNIT(dev); 2757216Ssam struct ec_softc *es = &ec_softc[unit]; 2767216Ssam struct ecdevice *addr; 2776520Sfeldman struct mbuf *m; 2786520Sfeldman 2796520Sfeldman if (es->es_oactive) 2806520Sfeldman goto restart; 2816520Sfeldman 2826520Sfeldman IF_DEQUEUE(&es->es_if.if_snd, m); 2836520Sfeldman if (m == 0) { 2846520Sfeldman es->es_oactive = 0; 2856520Sfeldman return; 2866520Sfeldman } 2876520Sfeldman ecput(es->es_buf[ECTBF], m); 2886520Sfeldman 2896520Sfeldman restart: 2907216Ssam addr = (struct ecdevice *)ecinfo[unit]->ui_addr; 2916520Sfeldman addr->ec_xcr = EC_WRITE|ECTBF; 2926520Sfeldman es->es_oactive = 1; 2936520Sfeldman } 2946520Sfeldman 2956520Sfeldman /* 2966520Sfeldman * Ethernet interface transmitter interrupt. 2976520Sfeldman * Start another output if more data to send. 2986520Sfeldman */ 2996520Sfeldman ecxint(unit) 3006520Sfeldman int unit; 3016520Sfeldman { 3026520Sfeldman register struct ec_softc *es = &ec_softc[unit]; 3037216Ssam register struct ecdevice *addr = 3047216Ssam (struct ecdevice *)ecinfo[unit]->ui_addr; 3056520Sfeldman 3066520Sfeldman if (es->es_oactive == 0) 3076520Sfeldman return; 3087216Ssam if ((addr->ec_xcr&EC_XDONE) == 0 || (addr->ec_xcr&EC_XBN) != ECTBF) { 3097216Ssam printf("ec%d: stray xmit interrupt, xcr=%b\n", unit, 3107216Ssam addr->ec_xcr, EC_XBITS); 3117216Ssam es->es_oactive = 0; 3127216Ssam addr->ec_xcr = EC_XCLR; 3137216Ssam return; 3147216Ssam } 3156520Sfeldman es->es_if.if_opackets++; 3166520Sfeldman es->es_oactive = 0; 3176520Sfeldman es->es_mask = ~0; 3186520Sfeldman addr->ec_xcr = EC_XCLR; 3197216Ssam if (es->es_if.if_snd.ifq_head) 3207216Ssam ecstart(unit); 3216520Sfeldman } 3226520Sfeldman 3236520Sfeldman /* 3246520Sfeldman * Collision on ethernet interface. Do exponential 3256520Sfeldman * backoff, and retransmit. If have backed off all 3266520Sfeldman * the way print warning diagnostic, and drop packet. 3276520Sfeldman */ 3286520Sfeldman eccollide(unit) 3296520Sfeldman int unit; 3306520Sfeldman { 3316520Sfeldman struct ec_softc *es = &ec_softc[unit]; 3326520Sfeldman 3336520Sfeldman es->es_if.if_collisions++; 3347216Ssam if (es->es_oactive) 3357216Ssam ecdocoll(unit); 3366520Sfeldman } 3376520Sfeldman 3386520Sfeldman ecdocoll(unit) 3396520Sfeldman int unit; 3406520Sfeldman { 3416520Sfeldman register struct ec_softc *es = &ec_softc[unit]; 3426545Sfeldman register struct ecdevice *addr = 3436545Sfeldman (struct ecdevice *)ecinfo[unit]->ui_addr; 3446545Sfeldman register i; 3456545Sfeldman int delay; 3466520Sfeldman 3476520Sfeldman /* 3486520Sfeldman * Es_mask is a 16 bit number with n low zero bits, with 3496520Sfeldman * n the number of backoffs. When es_mask is 0 we have 3506520Sfeldman * backed off 16 times, and give up. 3516520Sfeldman */ 3526520Sfeldman if (es->es_mask == 0) { 3536545Sfeldman es->es_if.if_oerrors++; 3546520Sfeldman printf("ec%d: send error\n", unit); 3556520Sfeldman /* 3566545Sfeldman * Reset interface, then requeue rcv buffers. 3576545Sfeldman * Some incoming packets may be lost, but that 3586545Sfeldman * can't be helped. 3596520Sfeldman */ 3606545Sfeldman addr->ec_xcr = EC_UECLR; 3616545Sfeldman for (i=ECRHBF; i>=ECRLBF; i--) 3626545Sfeldman addr->ec_rcr = EC_READ|i; 3636545Sfeldman /* 3646545Sfeldman * Reset and transmit next packet (if any). 3656545Sfeldman */ 3666545Sfeldman es->es_oactive = 0; 3676545Sfeldman es->es_mask = ~0; 3686545Sfeldman if (es->es_if.if_snd.ifq_head) 3696545Sfeldman ecstart(unit); 3706520Sfeldman return; 3716520Sfeldman } 3726520Sfeldman /* 3736545Sfeldman * Do exponential backoff. Compute delay based on low bits 3746545Sfeldman * of the interval timer. Then delay for that number of 3756545Sfeldman * slot times. A slot time is 51.2 microseconds (rounded to 51). 3766545Sfeldman * This does not take into account the time already used to 3776545Sfeldman * process the interrupt. 3786520Sfeldman */ 3796520Sfeldman es->es_mask <<= 1; 3806545Sfeldman delay = mfpr(ICR) &~ es->es_mask; 3816545Sfeldman DELAY(delay * 51); 3826520Sfeldman /* 3836545Sfeldman * Clear the controller's collision flag, thus enabling retransmit. 3846520Sfeldman */ 3857470Sfeldman addr->ec_xcr = EC_CLEAR; 3866520Sfeldman } 3876520Sfeldman 3886520Sfeldman /* 3896520Sfeldman * Ethernet interface receiver interrupt. 3906520Sfeldman * If input error just drop packet. 3916520Sfeldman * Otherwise purge input buffered data path and examine 3926520Sfeldman * packet to determine type. If can't determine length 3936520Sfeldman * from type, then have to drop packet. Othewise decapsulate 3946520Sfeldman * packet based on type and pass to type specific higher-level 3956520Sfeldman * input routine. 3966520Sfeldman */ 3976520Sfeldman ecrint(unit) 3986520Sfeldman int unit; 3996520Sfeldman { 4006520Sfeldman struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr; 4016520Sfeldman 4026520Sfeldman while (addr->ec_rcr & EC_RDONE) 4036520Sfeldman ecread(unit); 4046520Sfeldman } 4056520Sfeldman 4066520Sfeldman ecread(unit) 4076520Sfeldman int unit; 4086520Sfeldman { 4096520Sfeldman register struct ec_softc *es = &ec_softc[unit]; 4106520Sfeldman struct ecdevice *addr = (struct ecdevice *)ecinfo[unit]->ui_addr; 4119745Ssam register struct ether_header *ec; 4126520Sfeldman struct mbuf *m; 4138773Sroot int len, off, resid, ecoff, rbuf; 4146520Sfeldman register struct ifqueue *inq; 4158773Sroot u_char *ecbuf; 4166520Sfeldman 4176520Sfeldman es->es_if.if_ipackets++; 4188773Sroot rbuf = addr->ec_rcr & EC_RBN; 4198773Sroot if (rbuf < ECRLBF || rbuf > ECRHBF) 4206520Sfeldman panic("ecrint"); 4218773Sroot ecbuf = es->es_buf[rbuf]; 4226520Sfeldman ecoff = *(short *)ecbuf; 4236545Sfeldman if (ecoff <= ECRDOFF || ecoff > 2046) { 4246520Sfeldman es->es_if.if_ierrors++; 4256520Sfeldman #ifdef notdef 4266520Sfeldman if (es->es_if.if_ierrors % 100 == 0) 4276520Sfeldman printf("ec%d: += 100 input errors\n", unit); 4286520Sfeldman #endif 4296520Sfeldman goto setup; 4306520Sfeldman } 4316520Sfeldman 4326520Sfeldman /* 4336520Sfeldman * Get input data length. 4346520Sfeldman * Get pointer to ethernet header (in input buffer). 4356520Sfeldman * Deal with trailer protocol: if type is PUP trailer 4366520Sfeldman * get true type from first 16-bit word past data. 4376520Sfeldman * Remember that type was trailer by setting off. 4386520Sfeldman */ 4399745Ssam len = ecoff - ECRDOFF - sizeof (struct ether_header); 4409745Ssam ec = (struct ether_header *)(ecbuf + ECRDOFF); 4419745Ssam ec->ether_type = ntohs((u_short)ec->ether_type); 4426520Sfeldman #define ecdataaddr(ec, off, type) ((type)(((caddr_t)((ec)+1)+(off)))) 4439745Ssam if (ec->ether_type >= ETHERPUP_TRAIL && 4449745Ssam ec->ether_type < ETHERPUP_TRAIL+ETHERPUP_NTRAILER) { 4459745Ssam off = (ec->ether_type - ETHERPUP_TRAIL) * 512; 4469745Ssam if (off >= ETHERMTU) 4476520Sfeldman goto setup; /* sanity */ 4489745Ssam ec->ether_type = ntohs(*ecdataaddr(ec, off, u_short *)); 4499745Ssam resid = ntohs(*(ecdataaddr(ec, off+2, u_short *))); 4506520Sfeldman if (off + resid > len) 4516520Sfeldman goto setup; /* sanity */ 4526520Sfeldman len = off + resid; 4536520Sfeldman } else 4546520Sfeldman off = 0; 4556520Sfeldman if (len == 0) 4566520Sfeldman goto setup; 4576520Sfeldman 4586520Sfeldman /* 4596520Sfeldman * Pull packet off interface. Off is nonzero if packet 4606520Sfeldman * has trailing header; ecget will then force this header 4616520Sfeldman * information to be at the front, but we still have to drop 4626520Sfeldman * the type and length which are at the front of any trailer data. 4636520Sfeldman */ 4646520Sfeldman m = ecget(ecbuf, len, off); 4656520Sfeldman if (m == 0) 4666520Sfeldman goto setup; 4676520Sfeldman if (off) { 4686520Sfeldman m->m_off += 2 * sizeof (u_short); 4696520Sfeldman m->m_len -= 2 * sizeof (u_short); 4706520Sfeldman } 4719745Ssam switch (ec->ether_type) { 4726520Sfeldman 4736520Sfeldman #ifdef INET 4749745Ssam case ETHERPUP_IPTYPE: 4756520Sfeldman schednetisr(NETISR_IP); 4766520Sfeldman inq = &ipintrq; 4776520Sfeldman break; 478*11574Ssam 479*11574Ssam case ETHERPUP_ARPTYPE: 480*11574Ssam arpinput(&es->es_ac, m); 481*11574Ssam return; 4826520Sfeldman #endif 4836520Sfeldman default: 4846520Sfeldman m_freem(m); 4856520Sfeldman goto setup; 4866520Sfeldman } 4876520Sfeldman 4886520Sfeldman if (IF_QFULL(inq)) { 4896520Sfeldman IF_DROP(inq); 4906520Sfeldman m_freem(m); 4917216Ssam goto setup; 4927216Ssam } 4937216Ssam IF_ENQUEUE(inq, m); 4946520Sfeldman 4956520Sfeldman setup: 4966520Sfeldman /* 4976520Sfeldman * Reset for next packet. 4986520Sfeldman */ 4998773Sroot addr->ec_rcr = EC_READ|EC_RCLR|rbuf; 5006520Sfeldman } 5016520Sfeldman 5026520Sfeldman /* 5036520Sfeldman * Ethernet output routine. 5046520Sfeldman * Encapsulate a packet of type family for the local net. 5056520Sfeldman * Use trailer local net encapsulation if enough data in first 5066520Sfeldman * packet leaves a multiple of 512 bytes of data in remainder. 5076545Sfeldman * If destination is this address or broadcast, send packet to 5086545Sfeldman * loop device to kludge around the fact that 3com interfaces can't 5096545Sfeldman * talk to themselves. 5106520Sfeldman */ 5116520Sfeldman ecoutput(ifp, m0, dst) 5126520Sfeldman struct ifnet *ifp; 5136520Sfeldman struct mbuf *m0; 5146520Sfeldman struct sockaddr *dst; 5156520Sfeldman { 516*11574Ssam int type, s, error; 517*11574Ssam u_char edst[6]; 518*11574Ssam struct in_addr idst; 5196520Sfeldman register struct ec_softc *es = &ec_softc[ifp->if_unit]; 5206520Sfeldman register struct mbuf *m = m0; 5219745Ssam register struct ether_header *ec; 5227216Ssam register int off, i; 523*11574Ssam struct mbuf *mcopy = (struct mbuf *)0; 5246520Sfeldman 5256520Sfeldman switch (dst->sa_family) { 5266520Sfeldman 5276520Sfeldman #ifdef INET 5286520Sfeldman case AF_INET: 529*11574Ssam idst = ((struct sockaddr_in *)dst)->sin_addr; 530*11574Ssam if (!arpresolve(&es->es_ac, m, &idst, edst)) 531*11574Ssam return (0); /* if not yet resolved */ 532*11574Ssam if (in_lnaof(idst) == INADDR_ANY) 5338838Sroot mcopy = m_copy(m, 0, (int)M_COPYALL); 5346520Sfeldman off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; 5356520Sfeldman if (off > 0 && (off & 0x1ff) == 0 && 5366520Sfeldman m->m_off >= MMINOFF + 2 * sizeof (u_short)) { 5379745Ssam type = ETHERPUP_TRAIL + (off>>9); 5386520Sfeldman m->m_off -= 2 * sizeof (u_short); 5396520Sfeldman m->m_len += 2 * sizeof (u_short); 5409745Ssam *mtod(m, u_short *) = ntohs((u_short)ETHERPUP_IPTYPE); 5419745Ssam *(mtod(m, u_short *) + 1) = ntohs((u_short)m->m_len); 5426520Sfeldman goto gottrailertype; 5436520Sfeldman } 5449745Ssam type = ETHERPUP_IPTYPE; 5456520Sfeldman off = 0; 5466520Sfeldman goto gottype; 5476520Sfeldman #endif 5486520Sfeldman 549*11574Ssam case AF_UNSPEC: 550*11574Ssam ec = (struct ether_header *)dst->sa_data; 551*11574Ssam bcopy(ec->ether_dhost, edst, sizeof edst); 552*11574Ssam type = ec->ether_type; 553*11574Ssam goto gottype; 554*11574Ssam 5556520Sfeldman default: 5566520Sfeldman printf("ec%d: can't handle af%d\n", ifp->if_unit, 5576520Sfeldman dst->sa_family); 5586520Sfeldman error = EAFNOSUPPORT; 5596520Sfeldman goto bad; 5606520Sfeldman } 5616520Sfeldman 5626520Sfeldman gottrailertype: 5636520Sfeldman /* 5646520Sfeldman * Packet to be sent as trailer: move first packet 5656520Sfeldman * (control information) to end of chain. 5666520Sfeldman */ 5676520Sfeldman while (m->m_next) 5686520Sfeldman m = m->m_next; 5696520Sfeldman m->m_next = m0; 5706520Sfeldman m = m0->m_next; 5716520Sfeldman m0->m_next = 0; 5726520Sfeldman m0 = m; 5736520Sfeldman 5746520Sfeldman gottype: 5756520Sfeldman /* 5766520Sfeldman * Add local net header. If no space in first mbuf, 5776520Sfeldman * allocate another. 5786520Sfeldman */ 5796520Sfeldman if (m->m_off > MMAXOFF || 5809745Ssam MMINOFF + sizeof (struct ether_header) > m->m_off) { 5819648Ssam m = m_get(M_DONTWAIT, MT_HEADER); 5826520Sfeldman if (m == 0) { 5836520Sfeldman error = ENOBUFS; 5846520Sfeldman goto bad; 5856520Sfeldman } 5866520Sfeldman m->m_next = m0; 5876520Sfeldman m->m_off = MMINOFF; 5889745Ssam m->m_len = sizeof (struct ether_header); 5896520Sfeldman } else { 5909745Ssam m->m_off -= sizeof (struct ether_header); 5919745Ssam m->m_len += sizeof (struct ether_header); 5926520Sfeldman } 5939745Ssam ec = mtod(m, struct ether_header *); 594*11574Ssam bcopy(edst, ec->ether_dhost, sizeof (edst)); 5959745Ssam ec->ether_type = htons((u_short)type); 596*11574Ssam bcopy((caddr_t)es->es_addr, (caddr_t)ec->ether_shost, 6); 5976520Sfeldman 5986520Sfeldman /* 5996520Sfeldman * Queue message on interface, and start output if interface 6006520Sfeldman * not yet active. 6016520Sfeldman */ 6026520Sfeldman s = splimp(); 6036520Sfeldman if (IF_QFULL(&ifp->if_snd)) { 6046520Sfeldman IF_DROP(&ifp->if_snd); 6056520Sfeldman error = ENOBUFS; 6066520Sfeldman goto qfull; 6076520Sfeldman } 6086520Sfeldman IF_ENQUEUE(&ifp->if_snd, m); 6096520Sfeldman if (es->es_oactive == 0) 6106520Sfeldman ecstart(ifp->if_unit); 6116520Sfeldman splx(s); 6127216Ssam 6136545Sfeldman gotlocal: 6147216Ssam return(mcopy ? looutput(&loif, mcopy, dst) : 0); 6157216Ssam 6166520Sfeldman qfull: 6176520Sfeldman m0 = m; 6186520Sfeldman splx(s); 6196520Sfeldman bad: 6206520Sfeldman m_freem(m0); 6216520Sfeldman return(error); 6226520Sfeldman } 6236520Sfeldman 6246520Sfeldman /* 6259177Ssam * Routine to copy from mbuf chain to transmit 6267216Ssam * buffer in UNIBUS memory. 6279177Ssam * If packet size is less than the minimum legal size, 6289177Ssam * the buffer is expanded. We probably should zero out the extra 6299177Ssam * bytes for security, but that would slow things down. 6306520Sfeldman */ 6316520Sfeldman ecput(ecbuf, m) 6327216Ssam u_char *ecbuf; 6336520Sfeldman struct mbuf *m; 6346520Sfeldman { 6356520Sfeldman register struct mbuf *mp; 6367216Ssam register int off; 6377263Ssam u_char *bp; 6386520Sfeldman 6397216Ssam for (off = 2048, mp = m; mp; mp = mp->m_next) 6407216Ssam off -= mp->m_len; 6419745Ssam if (2048 - off < ETHERMIN + sizeof (struct ether_header)) 6429745Ssam off = 2048 - ETHERMIN - sizeof (struct ether_header); 6437216Ssam *(u_short *)ecbuf = off; 6447216Ssam bp = (u_char *)(ecbuf + off); 6457263Ssam for (mp = m; mp; mp = mp->m_next) { 6467263Ssam register unsigned len = mp->m_len; 6477263Ssam u_char *mcp; 6487216Ssam 6497216Ssam if (len == 0) 6507216Ssam continue; 6517216Ssam mcp = mtod(mp, u_char *); 6527216Ssam if ((unsigned)bp & 01) { 6537032Swnj *bp++ = *mcp++; 6547216Ssam len--; 6557032Swnj } 6567263Ssam if (off = (len >> 1)) { 6577263Ssam register u_short *to, *from; 6587263Ssam 6597263Ssam to = (u_short *)bp; 6607263Ssam from = (u_short *)mcp; 6617263Ssam do 6627263Ssam *to++ = *from++; 6637263Ssam while (--off > 0); 6647263Ssam bp = (u_char *)to, 6657263Ssam mcp = (u_char *)from; 6667032Swnj } 6677263Ssam if (len & 01) 6686520Sfeldman *bp++ = *mcp++; 6696520Sfeldman } 6707263Ssam m_freem(m); 6716520Sfeldman } 6726520Sfeldman 6736520Sfeldman /* 6746520Sfeldman * Routine to copy from UNIBUS memory into mbufs. 6756520Sfeldman * Similar in spirit to if_rubaget. 6767032Swnj * 6777032Swnj * Warning: This makes the fairly safe assumption that 6787032Swnj * mbufs have even lengths. 6796520Sfeldman */ 6806520Sfeldman struct mbuf * 6816520Sfeldman ecget(ecbuf, totlen, off0) 6827263Ssam u_char *ecbuf; 6836520Sfeldman int totlen, off0; 6846520Sfeldman { 6857263Ssam register struct mbuf *m; 6867263Ssam struct mbuf *top = 0, **mp = ⊤ 6877263Ssam register int off = off0, len; 6887263Ssam u_char *cp; 6896520Sfeldman 6909745Ssam cp = ecbuf + ECRDOFF + sizeof (struct ether_header); 6916520Sfeldman while (totlen > 0) { 6927263Ssam register int words; 6937263Ssam u_char *mcp; 6947263Ssam 6959648Ssam MGET(m, M_DONTWAIT, MT_DATA); 6966520Sfeldman if (m == 0) 6976520Sfeldman goto bad; 6986520Sfeldman if (off) { 6996520Sfeldman len = totlen - off; 7009745Ssam cp = ecbuf + ECRDOFF + 7019745Ssam sizeof (struct ether_header) + off; 7026520Sfeldman } else 7036520Sfeldman len = totlen; 7046520Sfeldman if (len >= CLBYTES) { 7056520Sfeldman struct mbuf *p; 7066520Sfeldman 7076520Sfeldman MCLGET(p, 1); 7086520Sfeldman if (p != 0) { 7096520Sfeldman m->m_len = len = CLBYTES; 7106520Sfeldman m->m_off = (int)p - (int)m; 7116520Sfeldman } else { 7126520Sfeldman m->m_len = len = MIN(MLEN, len); 7136520Sfeldman m->m_off = MMINOFF; 7146520Sfeldman } 7156520Sfeldman } else { 7166520Sfeldman m->m_len = len = MIN(MLEN, len); 7176520Sfeldman m->m_off = MMINOFF; 7186520Sfeldman } 7197263Ssam mcp = mtod(m, u_char *); 7207263Ssam if (words = (len >> 1)) { 7217263Ssam register u_short *to, *from; 7227263Ssam 7237263Ssam to = (u_short *)mcp; 7247263Ssam from = (u_short *)cp; 7257263Ssam do 7267263Ssam *to++ = *from++; 7277263Ssam while (--words > 0); 7287263Ssam mcp = (u_char *)to; 7297263Ssam cp = (u_char *)from; 7307032Swnj } 7317216Ssam if (len & 01) 7326520Sfeldman *mcp++ = *cp++; 7336520Sfeldman *mp = m; 7346520Sfeldman mp = &m->m_next; 7357263Ssam if (off == 0) { 7366520Sfeldman totlen -= len; 7377263Ssam continue; 7387263Ssam } 7397263Ssam off += len; 7407263Ssam if (off == totlen) { 7419745Ssam cp = ecbuf + ECRDOFF + sizeof (struct ether_header); 7427263Ssam off = 0; 7437263Ssam totlen = off0; 7447263Ssam } 7456520Sfeldman } 7466520Sfeldman return (top); 7476520Sfeldman bad: 7486520Sfeldman m_freem(top); 7496520Sfeldman return (0); 7506520Sfeldman } 751