/* if_il.c 4.1 82/05/21 */ #include "il.h" #include "imp.h" #include "loop.h" /* * Interlan Ethernet Communications Controller interface */ #include "../h/param.h" #include "../h/systm.h" #include "../h/mbuf.h" #include "../h/pte.h" #include "../h/buf.h" #include "../h/protosw.h" #include "../h/socket.h" #include "../h/ubareg.h" #include "../h/ubavar.h" #include "../h/ilreg.h" #include "../h/cpu.h" #include "../h/mtpr.h" #include "../h/vmmac.h" #include "../net/in.h" #include "../net/in_systm.h" #include "../net/if.h" #include "../net/if_il.h" #include "../net/if_uba.h" #include "../net/ip.h" #include "../net/ip_var.h" #include "../net/pup.h" #include "../net/route.h" #include #define ILMTU 1500 int ilprobe(), ilattach(), ilrint(), ilcint(); struct uba_device *ilinfo[NIL]; u_short ilstd[] = { 0 }; struct uba_driver ildriver = { ilprobe, 0, ilattach, 0, ilstd, "il", ilinfo }; u_char il_ectop[3] = { 0x02, 0x60, 0x8c }; #define ILUNIT(x) minor(x) int ilinit(),iloutput(),ilreset(); /* * Ethernet software status per interface. * * Each interface is referenced by a network interface structure, * is_if, which the routing code uses to locate the interface. * This structure contains the output queue for the interface, its address, ... * We also have, for each interface, a UBA interface structure, which * contains information about the UNIBUS resources held by the interface: * map registers, buffered data paths, etc. Information is cached in this * structure for use by the if_uba.c routines in running the interface * efficiently. */ struct il_softc { struct ifnet is_if; /* network-visible interface */ struct ifuba is_ifuba; /* UNIBUS resources */ short is_oactive; /* is output active? */ short is_startrcv; /* hang receive next chance */ u_char is_enaddr[6]; /* board's ethernet address */ } il_softc[NIL]; /* * Do an OFFLINE command. This will cause an interrupt for the * autoconfigure stuff. */ ilprobe(reg) caddr_t reg; { register int br, cvec; /* r11, r10 value-result */ register struct ildevice *addr = (struct ildevice *)reg; register i; COUNT(ILPROBE); #ifdef lint br = 0; cvec = br; br = cvec; ilrint(0); ilcint(0); #endif addr->il_csr = ILC_OFFLINE|IL_CIE; DELAY(100000); i = addr->il_csr; /* Clear CDONE */ if (cvec > 0 && cvec != 0x200) cvec -= 4; return (1); } struct il_stat ilbuf; /* * Interface exists: make available by filling in network interface * record. System will initialize the interface when it is ready * to accept packets. A STATUS command is done to get the ethernet * address and other interesting data. */ ilattach(ui) struct uba_device *ui; { register struct il_softc *is = &il_softc[ui->ui_unit]; register struct sockaddr_in *sin; register struct ildevice *addr = (struct ildevice *)ui->ui_addr; register int i; int s; int ubaddr; COUNT(ILATTACH); is->is_if.if_unit = ui->ui_unit; is->is_if.if_name = "il"; is->is_if.if_mtu = ILMTU; is->is_if.if_net = ui->ui_flags & 0xff; /* * Reset the board */ s = splimp(); addr->il_csr = ((ubaddr>>2)&0xc000)|ILC_RESET; while (!(addr->il_csr & IL_CDONE)) /* Busy wait */; if (addr->il_csr & IL_STATUS) printf("il%d: %s\n", ui->ui_unit, ildiag[addr->il_csr & IL_STATUS]); splx(s); /* * Map the status buffer to the Unibus, do the status command, * and unmap the buffer. */ ubaddr = uballoc(ui->ui_ubanum, &ilbuf, sizeof(ilbuf), 0); s = splimp(); addr->il_bar = ubaddr & 0xffff; addr->il_bcr = sizeof(ilbuf); addr->il_csr = ((ubaddr>>2)&0xc000)|ILC_STAT; while (!(addr->il_csr & IL_CDONE)) /* Busy wait */; if (addr->il_csr & IL_STATUS) printf("il%d: %s\n", ui->ui_unit, ilerrs[addr->il_csr & IL_STATUS]); splx(s); ubarelse(ui->ui_ubanum, &ubaddr); /* * Fill in the Ethernet address from the status buffer */ for (i=0; i<6; i++) is->is_enaddr[i] = ilbuf.ils_addr[i]; printf("il%d: addr=%x:%x:%x:%x:%x:%x module=%s firmware=%s\n", ui->ui_unit, is->is_enaddr[0]&0xff, is->is_enaddr[1]&0xff, is->is_enaddr[2]&0xff, is->is_enaddr[3]&0xff, is->is_enaddr[4]&0xff, is->is_enaddr[5]&0xff, ilbuf.ils_module, ilbuf.ils_firmware); is->is_if.if_host[0] = ((is->is_enaddr[3]&0xff)<<16) | 0x800000 | ((is->is_enaddr[4]&0xff)<<8) | (is->is_enaddr[5]&0xff); sin = (struct sockaddr_in *)&is->is_if.if_addr; sin->sin_family = AF_INET; sin->sin_addr = if_makeaddr(is->is_if.if_net, is->is_if.if_host[0]); sin = (struct sockaddr_in *)&is->is_if.if_broadaddr; sin->sin_family = AF_INET; sin->sin_addr = if_makeaddr(is->is_if.if_net, 0); is->is_if.if_flags = IFF_BROADCAST; is->is_if.if_init = ilinit; is->is_if.if_output = iloutput; is->is_if.if_ubareset = ilreset; is->is_ifuba.ifu_flags = UBA_CANTWAIT; if_attach(&is->is_if); #if NIMP == 0 if (ui->ui_flags &~ 0xff) illhinit((ui->ui_flags &~ 0xff) | 0x0a); #endif } /* * Reset of interface after UNIBUS reset. * If interface is on specified uba, reset its state. */ ilreset(unit, uban) int unit, uban; { register struct uba_device *ui; COUNT(ILRESET); if (unit >= NIL || (ui = ilinfo[unit]) == 0 || ui->ui_alive == 0 || ui->ui_ubanum != uban) return; printf(" il%d", unit); ilinit(unit); } /* * Initialization of interface; clear recorded pending * operations, and reinitialize UNIBUS usage. */ ilinit(unit) int unit; { register struct il_softc *is = &il_softc[unit]; register struct uba_device *ui = ilinfo[unit]; register struct ildevice *addr; register i; int s; if (if_ubainit(&is->is_ifuba, ui->ui_ubanum, sizeof (struct il_rheader), (int)btoc(ILMTU)) == 0) { printf("il%d: can't initialize\n", unit); is->is_if.if_flags &= ~IFF_UP; return; } addr = (struct ildevice *)ui->ui_addr; /* * Set board online. * Hang receive buffer and start any pending * writes by faking a transmit complete. * Receive bcr is not a muliple of 4 so buffer * chaining can't happen. */ s = splimp(); addr->il_csr = ILC_ONLINE; while (!(addr->il_csr & IL_CDONE)) /* Busy wait */; addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; addr->il_bcr = sizeof(struct il_rheader) + ILMTU + 6; addr->il_csr = ((is->is_ifuba.ifu_r.ifrw_info>>2)&0xc000)| ILC_RCV|IL_RIE; while (!(addr->il_csr & IL_CDONE)) /* Busy wait */; is->is_startrcv = 0; is->is_oactive = 1; is->is_if.if_flags |= IFF_UP; ilcint(unit); splx(s); if_rtinit(&is->is_if, RTF_DIRECT|RTF_UP); } /* * Start output on interface. * Get another datagram to send off of the interface queue, * and map it to the interface before starting the output. */ ilstart(dev) dev_t dev; { int unit = ILUNIT(dev); struct uba_device *ui = ilinfo[unit]; register struct il_softc *is = &il_softc[unit]; register struct ildevice *addr; register len; struct mbuf *m; int dest; COUNT(ILSTART); /* * Dequeue another request and copy it into the buffer. * If no more requests, just return. */ IF_DEQUEUE(&is->is_if.if_snd, m); if (m == 0) return; len = if_wubaput(&is->is_ifuba, m); /* * Flush BDP, then start the output. */ if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_w.ifrw_bdp); addr = (struct ildevice *)ui->ui_addr; addr->il_bar = is->is_ifuba.ifu_w.ifrw_info & 0xffff; addr->il_bcr = len; addr->il_csr = ((is->is_ifuba.ifu_w.ifrw_info>>2)&0xc000)| ILC_XMIT|IL_CIE|IL_RIE; is->is_oactive = 1; } /* * Command done interrupt. * This should only happen after a transmit command, * so it is equivalent to a transmit interrupt. * Start another output if more data to send. */ ilcint(unit) int unit; { register struct uba_device *ui = ilinfo[unit]; register struct il_softc *is = &il_softc[unit]; register struct ildevice *addr = (struct ildevice *)ui->ui_addr; register int err; COUNT(ILCINT); if (is->is_oactive == 0) { printf("il%d: strange xmit interrupt!\n", unit); return; } is->is_if.if_opackets++; is->is_oactive = 0; if (err = (addr->il_csr & IL_STATUS)){ is->is_if.if_oerrors++; printf("il%d: output error %d\n", unit, err); } /* * Hang receive buffer if it couldn't be done earlier (in ilrint). */ if (is->is_startrcv) { addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; addr->il_bcr = sizeof(struct il_rheader) + ILMTU + 6; addr->il_csr = ((is->is_ifuba.ifu_r.ifrw_info>>2)&0xc000)| ILC_RCV|IL_RIE; while (!(addr->il_csr & IL_CDONE)) /* Busy wait */; is->is_startrcv = 0; } if (is->is_ifuba.ifu_xtofree) { m_freem(is->is_ifuba.ifu_xtofree); is->is_ifuba.ifu_xtofree = 0; } if (is->is_if.if_snd.ifq_head == 0) { return; } ilstart(unit); } /* * Ethernet interface receiver interrupt. * If input error just drop packet. * Otherwise purge input buffered data path and examine * packet to determine type. If can't determine length * from type, then have to drop packet. Othewise decapsulate * packet based on type and pass to type specific higher-level * input routine. */ ilrint(unit) int unit; { register struct il_softc *is = &il_softc[unit]; struct ildevice *addr = (struct ildevice *)ilinfo[unit]->ui_addr; register struct il_rheader *il; struct mbuf *m; int len, off, resid; register struct ifqueue *inq; COUNT(ILRINT); is->is_if.if_ipackets++; if (is->is_ifuba.ifu_flags & UBA_NEEDBDP) UBAPURGE(is->is_ifuba.ifu_uba, is->is_ifuba.ifu_r.ifrw_bdp); il = (struct il_rheader *)(is->is_ifuba.ifu_r.ifrw_addr); len = il->ilr_length - sizeof(struct il_rheader); if (il->ilr_status&0x3 || len < 46 || len > ILMTU) { is->is_if.if_ierrors++; #ifdef notdef if (is->is_if.if_ierrors % 100 == 0) printf("il%d: += 100 input errors\n", unit); #endif printf("il%d: input error (status=%x, len=%d)\n", unit, il->ilr_status, len); goto setup; } /* * Deal with trailer protocol: if type is PUP trailer * get true type from first 16-bit word past data. * Remember that type was trailer by setting off. */ #define ildataaddr(il, off, type) ((type)(((caddr_t)((il)+1)+(off)))) if (il->ilr_type >= ILPUP_TRAIL && il->ilr_type < ILPUP_TRAIL+ILPUP_NTRAILER) { off = (il->ilr_type - ILPUP_TRAIL) * 512; if (off >= ILMTU) goto setup; /* sanity */ il->ilr_type = *ildataaddr(il, off, u_short *); resid = *(ildataaddr(il, off+2, u_short *)); if (off + resid > len) goto setup; /* sanity */ len = off + resid; } else off = 0; if (len == 0) goto setup; /* * Pull packet off interface. Off is nonzero if packet * has trailing header; ilget will then force this header * information to be at the front, but we still have to drop * the type and length which are at the front of any trailer data. */ m = if_rubaget(&is->is_ifuba, len, off); if (m == 0) goto setup; if (off) { m->m_off += 2 * sizeof (u_short); m->m_len -= 2 * sizeof (u_short); } switch (il->ilr_type) { #ifdef INET case ILPUP_IPTYPE: schednetisr(NETISR_IP); inq = &ipintrq; break; #endif default: m_freem(m); goto setup; } if (IF_QFULL(inq)) { IF_DROP(inq); m_freem(m); } else IF_ENQUEUE(inq, m); setup: /* * Reset for next packet if possible. * If waiting for transmit command completion, set flag * and wait until command completes. */ if (!is->is_oactive) { addr->il_bar = is->is_ifuba.ifu_r.ifrw_info & 0xffff; addr->il_bcr = sizeof(struct il_rheader) + ILMTU + 6; addr->il_csr = ((is->is_ifuba.ifu_r.ifrw_info>>2)&0xc000)| ILC_RCV|IL_RIE; while (!(addr->il_csr & IL_CDONE)) /* Busy wait */; } else is->is_startrcv = 1; } /* * Ethernet output routine. * Encapsulate a packet of type family for the local net. * Use trailer local net encapsulation if enough data in first * packet leaves a multiple of 512 bytes of data in remainder. */ iloutput(ifp, m0, dst) struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; { int type, dest, s, error; register struct il_softc *is = &il_softc[ifp->if_unit]; register struct mbuf *m = m0; register struct il_xheader *il; register int off; register int i; COUNT(ILOUTPUT); switch (dst->sa_family) { #ifdef INET case AF_INET: dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr; off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; if (off > 0 && (off & 0x1ff) == 0 && m->m_off >= MMINOFF + 2 * sizeof (u_short)) { type = ILPUP_TRAIL + (off>>9); m->m_off -= 2 * sizeof (u_short); m->m_len += 2 * sizeof (u_short); *mtod(m, u_short *) = ILPUP_IPTYPE; *(mtod(m, u_short *) + 1) = m->m_len; goto gottrailertype; } type = ILPUP_IPTYPE; off = 0; goto gottype; #endif default: printf("il%d: can't handle af%d\n", ifp->if_unit, dst->sa_family); error = EAFNOSUPPORT; goto bad; } gottrailertype: /* * Packet to be sent as trailer: move first packet * (control information) to end of chain. */ while (m->m_next) m = m->m_next; m->m_next = m0; m = m0->m_next; m0->m_next = 0; m0 = m; gottype: /* * Add local net header. If no space in first mbuf, * allocate another. */ if (m->m_off > MMAXOFF || MMINOFF + sizeof (struct il_xheader) > m->m_off) { m = m_get(M_DONTWAIT); if (m == 0) { error = ENOBUFS; goto bad; } m->m_next = m0; m->m_off = MMINOFF; m->m_len = sizeof (struct il_xheader); } else { m->m_off -= sizeof (struct il_xheader); m->m_len += sizeof (struct il_xheader); } il = mtod(m, struct il_xheader *); if ((dest &~ 0xff) == 0) for (i=0; i<6; i++) il->ilx_dhost[i] = 0xff; else { if (dest & 0x8000) { il->ilx_dhost[0] = is->is_enaddr[0]; il->ilx_dhost[1] = is->is_enaddr[1]; il->ilx_dhost[2] = is->is_enaddr[2]; } else { il->ilx_dhost[0] = il_ectop[0]; il->ilx_dhost[1] = il_ectop[1]; il->ilx_dhost[2] = il_ectop[2]; } il->ilx_dhost[3] = (dest>>8) & 0x7f; il->ilx_dhost[4] = (dest>>16) & 0xff; il->ilx_dhost[5] = (dest>>24) & 0xff; } il->ilx_type = type; /* * Queue message on interface, and start output if interface * not yet active. */ s = splimp(); if (IF_QFULL(&ifp->if_snd)) { IF_DROP(&ifp->if_snd); error = ENOBUFS; goto qfull; } IF_ENQUEUE(&ifp->if_snd, m); if (is->is_oactive == 0) ilstart(ifp->if_unit); splx(s); return (0); qfull: m0 = m; splx(s); bad: m_freem(m0); return(error); } #if NIMP == 0 && NIL > 0 /* * Logical host interface driver. * Allows host to appear as an ARPAnet * logical host. Must also have routing * table entry set up to forward packets * to appropriate gateway on localnet. */ struct ifnet illhif; int illhoutput(); /* * Called by localnet interface to allow logical * host interface to "attach". Nothing should ever * be sent locally to this interface, it's purpose * is simply to establish the host's arpanet address. */ illhinit(addr) int addr; { register struct ifnet *ifp = &illhif; register struct sockaddr_in *sin; COUNT(ILLHINIT); ifp->if_name = "lh"; ifp->if_mtu = ILMTU; sin = (struct sockaddr_in *)&ifp->if_addr; sin->sin_family = AF_INET; sin->sin_addr.s_addr = addr; ifp->if_net = sin->sin_addr.s_net; ifp->if_flags = IFF_UP; ifp->if_output = illhoutput; /* should never be used */ if_attach(ifp); } illhoutput(ifp, m0, dst) struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; { COUNT(ILLHOUTPUT); ifp->if_oerrors++; m_freem(m0); return (0); } #endif