/* * Copyright IBM Corporation 1987,1990 * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of IBM not be * used in advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR USE. * IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF * THIS SOFTWARE. * * @(#)if_un.eg 7.1 (Berkeley) %G% */ /* * Ungermann-Bass PC-NIC (Ethernet) Adapter (4.3 driver) */ #include "un.h" #if NUN > 0 #include "../machine/pte.h" #include "param.h" #include "systm.h" #include "mbuf.h" #include "buf.h" #include "protosw.h" #include "socket.h" #include "vmmac.h" #include "ioctl.h" #include "errno.h" #include "../net/if.h" #include "../net/netisr.h" #include "../net/route.h" #ifdef INET #include "../netinet/in.h" #include "../netinet/in_systm.h" #include "../netinet/in_var.h" #include "../netinet/ip.h" #include "../netinet/if_ether.h" #endif INET #ifdef NS #include "../netns/ns.h" #include "../netns/ns_if.h" #endif NS #ifdef ISO #include "../netargo/if_clnp.h" #include "../netargo/iso.h" #include "../netargo/iso_var.h" #include "../netargo/argo_debug.h" #endif ISO #include "../machine/io.h" #include "if_unreg.h" #ifdef IEEELLC #include "if_llc.h" #endif IEEELLC #include "../machineio/ioccvar.h" #include "../machine/debug.h" int unprobe(), unattach(); #ifdef AT caddr_t unstd[] = { (caddr_t) 0xa0000, (caddr_t) 0xa8000, (caddr_t) 0xb0000, (caddr_t) 0xb8000, 0 }; #else caddr_t unstd[] = { (caddr_t) 0xf4080000, (caddr_t) 0xf4088000, (caddr_t) 0xf4090000, (caddr_t) 0xf4098000, 0 }; #endif AT struct iocc_device *uninfo[NUN]; int unint(), uninit(), unioctl(), unoutput(), unreset(); struct iocc_driver undriver = { unprobe, 0, unattach, 0, unstd, "un", uninfo, 0, 0, unint, UN_EADDROFF }; struct mbuf *unget(); /* * Ethernet software status per adapter. */ struct un_softc { struct arpcom us_ac; /* generic network interface stuff */ #define us_if us_ac.ac_if /* ifnet struct */ #define us_addr us_ac.ac_enaddr /* hardware (i.e. ethernet) address */ short us_oactive; /* 1 => output active */ short us_nextpage; /* next receive buffer page */ short us_xbuf; /* in-use xmt buf (if output active) */ short us_xfull[2]; /* 1 => a full xmt buf */ short us_xstart[2]; /* start address used in unstart */ } un_softc[NUN]; #ifdef DEBUG char undebug = 0; #endif DEBUG #ifdef ATR #define move_window(window, addr) {\ int real_addr;\ int new_window;\ \ window = get_128_window();\ real_addr = 0xfffff & (int) addr;\ new_window = real_addr & 0xe0000;\ set_128_window(new_window);\ addr = (struct undevice *) (real_addr - new_window);\ } #define restore_window(window) set_128_window(window) #define bcopyin(from,to,len) bcopy((from)+pcif_128_fw,to,len) #define bcopyout(from,to,len) bcopy(from,(to)+pcif_128_fw,len) #endif ATR #ifdef IBMRTPC #define bcopyin bcopy #define bcopyout bcopy #endif IBMRTPC /* * unprobe - try to generate an interrupt (to see if the board is there) */ unprobe(p) register caddr_t p; { register struct undevice *addr = (struct undevice *) p; #ifdef ATR register int old_window; move_window(old_window, addr); #endif ATR (void) unzap(addr); UN_GLOBIENB(0); /* global interrrupt enable */ MM_OUT(&addr->un_csr, UN_GSFTINT); /* generate software interrupt */ PROBE_DELAY(100000); MM_OUT(&addr->un_csr, 0); #ifdef ATR restore_window(old_window); #endif ATR return(PROBE_OK); } /* * unattach - make the interface available to the network software * (if the auto-configuration software determines that the interface * exists). The system will initialize the interface when it is * ready to accept packets. */ unattach(iod) register struct iocc_device *iod; { register struct un_softc *us = &un_softc[iod->iod_unit]; register struct ifnet *ifp = &us->us_if; register struct undevice *addr = (struct undevice *) iod->iod_addr; register int i; #ifdef ATR register int old_window; move_window(old_window, addr); #endif ATR ifp->if_unit = iod->iod_unit; ifp->if_name = "un"; #ifdef IEEELLC ifp->if_mtu = ETHERMTU - 3; /* 3 bytes for UI LLC frame */ #else ifp->if_mtu = ETHERMTU; #endif IEEELCC /* * Read the ethernet address off the board. * Save it and also write it to the edlc chip. */ for (i = 0; i < ETH_ADDR_SIZE; i++){ us->us_addr[i] = MM_IN(&addr->un_eprom[UN_EADDROFF+i]); MM_OUT(&addr->un_edlc.nodeID[i], us->us_addr[i]); } printf("un%d: ethernet address ", ifp->if_unit); unprintethaddr(us->us_addr); printf("\n"); ifp->if_init = uninit; ifp->if_ioctl = unioctl; ifp->if_output = unoutput; ifp->if_reset = unreset; ifp->if_flags = IFF_BROADCAST; #ifdef ISO ifp->if_flags |= IFF_EAVESDROP; #endif ISO if_attach(ifp); DEBUGF(undebug, printf("un%d: attached\n", iod->iod_unit);) #ifdef ATR restore_window(old_window); #endif ATR } /* * unreset - reset interface */ unreset(unit) register unsigned int unit; { register struct iocc_device *iod; if (unit < NUN && (iod = uninfo[unit]) != 0 && iod->iod_alive != 0){ un_softc[unit].us_if.if_flags &= ~IFF_RUNNING; DEBUGF(undebug, printf("un%d: reset\n", unit);) uninit(unit); } } /* * uninit - initialize interface, enable packet reception, start any * pending writes */ uninit(unit) register int unit; { register struct un_softc *us = &un_softc[unit]; register struct ifnet *ifp = &us->us_if; register int s; register struct undevice *addr; register int i; if (ifp->if_addrlist == (struct ifaddr *) 0){ /* no address */ return; } if ((ifp->if_flags & IFF_RUNNING) == 0){ int old_window; addr = (struct undevice *) (uninfo[unit]->iod_addr); #ifdef ATR move_window(old_window, addr); #endif ATR s = splimp(); us->us_nextpage = unzap(addr); /* initialize hardware */ /* unzap returns next receive page to be used */ for (i = 0; i < ETH_ADDR_SIZE; i++){ MM_OUT(&addr->un_edlc.nodeID[i], us->us_addr[i]); } us->us_oactive = 0; /* output not active */ /* turn adapter on */ ifp->if_flags |= IFF_RUNNING; MM_OUT(&addr->un_csr, UN_PAVIENB); /* Allow packet available interrupts */ UN_GLOBIENB(us->us_nextpage); /* global interrrupt enable */ if (ifp->if_snd.ifq_head){ /* anything on send queue */ struct mbuf *m; IF_DEQUEUE(&ifp->if_snd, m); unput(us, addr, m, 0); unstart(us, addr, 0); if (ifp->if_snd.ifq_head){ IF_DEQUEUE(&ifp->if_snd, m); unput(us, addr, m, 1); } } splx(s); #ifdef ATR restore_window(old_window); #endif ATR } DEBUGF(undebug, printf("un%d: init'ed\n", unit);) } /* * unstart - start output from one of the adapter's 2 transmit buffers */ unstart(us, addr, xbuf) register struct un_softc *us; register struct undevice *addr; register int xbuf; { us->us_oactive = 1; us->us_xbuf = xbuf; UN_XMIT(addr, us->us_xstart[xbuf]); MM_OUT(&addr->un_csr, UN_IENABLE); /* enable transmit done interrupt */ } /* * unint - interrupt handler. find the cause of the interrupt and * dispatch an appropriate handler routine. */ unint(unit) register int unit; { register struct un_softc *us = &un_softc[unit]; register struct undevice *addr = (struct undevice *) uninfo[unit]->iod_addr; register char status; register int rc = 1; #ifdef ATR register int old_window; move_window(old_window, addr); #endif ATR UN_DISABLE(us->us_nextpage); while ((status = ~MM_IN(&addr->un_csr)) & UN_PAVINT){ DEBUGF(undebug & 0x2, printf("unint: unit = %d, csr = %b", unit, status & 0xff, UN_CSRBITS);) unrint(unit, us, addr); rc = 0; } if (status & UN_TXRINT){ DEBUGF(undebug & 0x2, printf("unint: unit = %d, csr = %b", unit, status & 0xff, UN_CSRBITS);) unxint(unit, us, addr); rc = 0; } UN_ENABLE(us->us_nextpage); #ifdef ATR restore_window(old_window); #endif ATR return(rc); } /* * unrint - interrupt handler for packet reception. * * log error if error bits are latched, examine packet to determine * type, if can't determine packet length from type, drop packet. * otherwise decapsulate packet based on type and pass to an appropriate * higher-level input routine. */ unrint(unit, us, addr) int unit; register struct un_softc *us; register struct undevice *addr; { register struct ether_header *eh; register struct mbuf *m; register int len; register int off; int resid; struct ifqueue *inq; char status = MM_IN(&addr->un_edlc.rstat); u_short type; u_short ungetushortatoff(); #ifdef IEEELLC struct ether_header ehbuf; #endif IEEELLC MM_OUT(&addr->un_edlc.rstat, status); /* clear status */ /* (the hardware xor's in the value of status setting rstat to 0) */ DEBUGF(undebug & 0x2, printf(" rstat = %b", status, RS_BITS);) /* * Latch errors. (Errors found correspond to packets * that were received prior to the current packet * since packet available interrupts are generated * for good packets only.) */ if (status & RS_ERR){ DEBUGF(undebug, printf("unrint: input error\n");) us->us_if.if_ierrors++; } us->us_if.if_ipackets++; /* * determine the length of the received packet. */ len = 0; off = us->us_nextpage; #define BUMP(page) if (++(page) == UN_NUMRBUFS) page = 0 while ((MM_IN(&addr->un_pram[us->us_nextpage]) & UN_LAST_PAGE) == 0){ len += UN_RBUFSIZE; BUMP(us->us_nextpage); } len += (MM_IN(&addr->un_pram[us->us_nextpage]) & UN_PAGE_LENGTH_MASK) + 1; BUMP(us->us_nextpage); #undef BUMP DEBUGF(undebug & 0x2, printf(" len = %d ", len);) if (len > UN_XBSIZE){ printf("un%d: huge packet!\n",unit); goto chuckit; } /* * Process the packet */ eh = (struct ether_header *) &addr->un_rcvbuf[off][0]; DEBUGF(undebug & 0x2, { char cbuf[6]; printf(" from = "); bcopyin(eh->ether_shost, cbuf, sizeof(cbuf)); unprintethaddr(cbuf); printf(" to = "); bcopyin(eh->ether_dhost, cbuf, sizeof(cbuf)); unprintethaddr(cbuf); printf(" "); } ) len -= sizeof(struct ether_header); type = ntohs((u_short) MM_INW(&eh->ether_type)); /* * The ETHERTYPE_NTRAILER packet types starting at ETHERTYPE_TRAIL * have (type - ETHERTYPE_TRAIL) * 512 bytes of data followed by * a type field and then a (variable length) header */ if (type >= ETHERTYPE_TRAIL && type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER){ off = (type - ETHERTYPE_TRAIL) * 512; if (off >= ETHERMTU){ goto chuckit; } type = ungetushortatoff(addr, eh, off); resid = ungetushortatoff(addr, eh, off + 2); if (off + resid > len){ goto chuckit; } len = off + resid; } else { off = 0; } if (len == 0){ goto chuckit; } #ifdef IEEELLC if (type <= ETHERMTU) { /* may need ether_header for XID, TEST LLC functions */ ehbuf = *eh; } #endif IEEELLC /* * pull packet off interface. if off is non-zero, the * packet has a trailing "header". unget will move this * header to the front, but we still have to remove the * type and length fields from the front of the data. */ m = unget(addr, (char *) eh, len, off, &us->us_if); /* * update the full page pointer and clear the packet available * flag if necessary. update the fpp here to free the on-board * receive pages as soon as possible. */ unupdatefpp(addr, us->us_nextpage); if (m != 0){ if (off){ #ifdef ISO /* * Move snpa header over by 4 bytes to skip * the trailer Type and Header length fields. */ struct snpa_hdr sh; bcopy(mtod(m, char *), (caddr_t)&sh, sizeof(struct snpa_hdr)); m->m_off += 2 * sizeof(u_short); m->m_len -= 2 * sizeof(u_short); bcopy((caddr_t)&sh, mtod(m, char *), sizeof(struct snpa_hdr)); #else ISO struct ifnet *ifp; /* * bcopy is used since word moves must be on 4 byte * boundaries on the RT PC */ bcopy(mtod(m, char *), (char *) &ifp, sizeof(ifp)); m->m_off += 2 * sizeof(u_short); m->m_len -= 2 * sizeof(u_short); bcopy((char *) &ifp, mtod(m, char *), sizeof(ifp)); #endif ISO } switch (type){ #ifdef INET case ETHERTYPE_IP: { int s; DEBUGF(undebug & 0x2, printf("ip packet\n");) schednetisr(NETISR_IP); s = splimp(); inq = &ipintrq; if (IF_QFULL(inq)){ DEBUGF(undebug & 0x2, printf(" qfull\n");) IF_DROP(inq); m_freem(m); } else { IF_ENQUEUE(inq, m); DEBUGF(undebug & 0x2, printf(" queued\n");) } splx(s); break; } case ETHERTYPE_ARP: DEBUGF(undebug & 0x2, printf("arp packet\n");) arpinput(&us->us_ac, m); /* arpinput frees m */ break; #endif INET #ifdef NS case ETHERTYPE_NS: DEBUGF(undebug & 0x2, printf("ns packet\n");) schednetisr(NETISR_NS); inq = &nsintrq; break; #endif NS #ifndef IEEELLC #ifdef ISO case ETHERTYPE_CLNP: /* should be CLNL */ DEBUGF(undebug & 0x2, printf("clnl packet\n");) /* IFF_EAVESDROP can not be turned off for Ethernet */ schednetisr(NETISR_CLNP); inq = &clnlintrq; if (IF_QFULL(inq)){ DEBUGF(undebug & 0x2, printf(" qfull\n");) IF_DROP(inq); m_freem(m); } else { IF_ENQUEUE(inq, m); DEBUGF(undebug & 0x2, printf(" queued\n");) } break; #endif ISO default: DEBUGF(undebug & 0x2, printf("unknown packet\n");) m_freem(m); break; #else default: { struct llc *l; caddr_t pkt_start; #ifdef ISO #define PREPENDED_SIZE sizeof(struct snpa_hdr) #else #define PREPENDED_SIZE sizeof(struct ifnet *) #endif ISO if (type > ETHERMTU) goto not802; /* * This assumes that the snpa header is in the same mbuf * as the llc header. Currently this is ok, but if * unget allocates a cluster, this will not be the case */ pkt_start = mtod(m, caddr_t); l = (struct llc *) (pkt_start + PREPENDED_SIZE); IFDEBUG(D_ETHER) printf("unrint: llc: length %d, control x%x:\n", type, l->llc_control); ENDDEBUG switch (l->llc_control) { case LLC_UI: /* LLC_UI_P forbidden in class 1 service */ #ifdef ISO if (l->llc_dsap == LLC_ISO_LSAP) { if ((IS_MULTICAST(ehbuf.ether_dhost)) && ((us->us_if.if_flags & IFF_EAVESDROP) == 0) && (!snpac_ownmulti(ehbuf.ether_dhost, 6))) { m_freem(m); return; } /* move struct snpa_header over the llc header */ clnp_ypocb(pkt_start, pkt_start + 3, PREPENDED_SIZE); m->m_off += 3; m->m_len -= 3; DEBUGF(undebug & 0x2, printf("clnp packet\n");) schednetisr(NETISR_CLNP); inq = &clnlintrq; if (IF_QFULL(inq)){ DEBUGF(undebug & 0x2, printf(" qfull\n");) IF_DROP(inq); m_freem(m); } else { IF_ENQUEUE(inq, m); DEBUGF(undebug & 0x2, printf(" queued\n");) } return; } else { IFDEBUG(D_ETHER) printf("unrint: unknown llc sap\n"); ENDDEBUG m_freem(m); return; } #endif ISO break; /* LLC_XID, LLC_XID_P, LLC_TEST, and LLC_TEST_P are untested */ case LLC_XID: case LLC_XID_P: /* control field is untouched for resp */ if(m->m_len < 6) goto not802; l->llc_fid = LLC_IEEE_basic_format; l->llc_class = LLC_CLASS1; l->llc_window = 0; l->llc_dsap = l->llc_ssap = 0; /* FALL THROUGH */ case LLC_TEST: case LLC_TEST_P: { struct ifnet *ifp = &us->us_if; struct sockaddr_iso siso; u_char c = l->llc_dsap; l->llc_dsap = l->llc_ssap; l->llc_ssap = c; /* Do not TEST or XID to multicasts */ if (IS_MULTICAST(ehbuf.ether_dhost)) { m_freem(m); break; } siso.siso_family = AF_ISO; bcopy(ehbuf.ether_shost, siso.siso_addr.sna_idi, 6); siso.siso_addr.isoa_afi = AFI_SNA; siso.siso_addr.isoa_len = 7; /* trim off prepended snpa_hdr or ifp */ m->m_off += PREPENDED_SIZE; m->m_len -= PREPENDED_SIZE; unoutput(ifp, m, &siso); return; } not802: default: DEBUGF(undebug & 0x2, printf("unknown packet\n");) m_freem(m); break; } } #endif IEEELLC } } return; chuckit: DEBUGF(undebug, printf("unrint: packet dropped\n");) unupdatefpp(addr, us->us_nextpage); } /* * unxint - interrupt handler for transmit ready */ unxint(unit, us, addr) register int unit; register struct un_softc *us; register struct undevice *addr; { register char status; register int next_buf; /* * collect stats on last packet */ status = MM_IN(&addr->un_edlc.xstat); MM_OUT(&addr->un_edlc.xstat, status); /* clear status bits */ DEBUGF(undebug & 0x2, printf(" unxint: xstat = %b\n", status & 0xff, XS_BITS);) if (status & XS_16CL){ us->us_if.if_collisions += 16; us->us_if.if_oerrors++; printf("un%d: ethernet jammed\n", unit); } else if (status & XS_SHRT){ us->us_if.if_oerrors++; printf( "un%d: ethernet not responding (is it connected?)\n", unit); } else { us->us_if.if_opackets++; us->us_if.if_collisions += UN_NCOLL(addr); } DEBUGF(undebug & 0x2, printf(" ipkt = %d ierr = %d okt = %d oerr = %d coll = %d\n", us->us_if.if_ipackets, us->us_if.if_ierrors, us->us_if.if_opackets, us->us_if.if_oerrors, us->us_if.if_collisions);) /* mark the current transmit buffer empty */ us->us_xfull[us->us_xbuf] = 0; /* switch to the other transmit buffer */ next_buf = 1 - us->us_xbuf; if (us->us_xfull[next_buf]){ /* if it's full */ unstart(us, addr, next_buf); /* start output from it */ if (us->us_if.if_snd.ifq_head){ /* if more on out queue */ struct mbuf *m; IF_DEQUEUE(&us->us_if.if_snd, m); /* fill empty buf */ unput(us, addr, m, 1 - next_buf); } } else { /* the other transmit buffer is empty */ us->us_oactive = 0; MM_OUT(&addr->un_csr, UN_PAVIENB); /* Turn off TxRIENB */ } } /* * unoutput - ethernet output routine. encapsulate a packet of type * family for the local net. use trailer local net encapsulation if * the number of bytes in the mbufs after the first is a multiple of * 512. */ unoutput(ifp, m0, dst) register struct ifnet *ifp; register struct mbuf *m0; register struct sockaddr *dst; { u_short type; int s; int error; char edst[ETH_ADDR_SIZE]; struct in_addr idst; register struct un_softc *us = &un_softc[ifp->if_unit]; register struct mbuf *m = m0; register struct ether_header *eh; int off; struct mbuf *m_get(); int usetrailers; if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)){ error = ENETDOWN; goto bad; } switch (dst->sa_family){ #ifdef INET case AF_INET: idst = ((struct sockaddr_in *)dst)->sin_addr; if (!arpresolve(&us->us_ac, m, &idst, edst, &usetrailers)){ /* not resolved */ return(0); } off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len; if (usetrailers && off > 0 && (off & 0x1ff) == 0 && m->m_off >= MMINOFF + 2 * sizeof(u_short)){ type = ETHERTYPE_TRAIL + (off>>9); m->m_off -= 2 * sizeof(u_short); m->m_len += 2 * sizeof(u_short); *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP); *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len); /* * Packet to be sent with 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; } else { type = ETHERTYPE_IP; } break; #endif INET #ifdef NS case AF_NS: bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), (caddr_t)edst, sizeof(edst)); type = ETHERTYPE_NS; off = 0; break; #endif NS #ifdef ISO case AF_ISO: { int ret; int len; struct iso_addr *dst_nsap = &((struct sockaddr_iso *)dst)->siso_addr; if ((ret = iso_tryloopback(m, dst)) >= 0) return (ret); else if (ret = iso_snparesolve(&us->us_ac.ac_if, dst_nsap, edst, &len)){ /* not resolved */ IFDEBUG(D_ETHER) printf("unoutput: clnp packet dropped\n"); ENDDEBUG m_freem(m); return(ret); } else if (len != 6) { printf("unoutput: snpa len is not 6 (%d)\n", len); m_freem(m); return(ENETUNREACH); } #ifndef IEEELLC type = ETHERTYPE_CLNP; #else /* check for enough space for LLC header */ { struct mbuf *llcm; char *cp; if (m->m_off >= MMAXOFF || m->m_off < MMINOFF + 3) { MGET(llcm, M_DONTWAIT, MT_DATA); if (llcm == NULL) { m_freem(m); return(0); } llcm->m_off = MMAXOFF - 3; llcm->m_len = 3; llcm->m_next = m; m = llcm; } else { m->m_off -= 3; m->m_len += 3; } type = m_datalen(m); cp = mtod(m, u_char *); cp[0] = cp[1] = LLC_ISO_LSAP; cp[2] = LLC_UI; off = 0; } #endif IEEELLC off = 0; IFDEBUG(D_ETHER) int i; printf("unoutput: sending pkt to: "); for (i=0; i<6; i++) printf("%x ", edst[i] & 0xff); #ifdef IEEELLC printf(" llc len %d", type); #endif IEEELLC printf("\n"); ENDDEBUG } break; #endif ISO case AF_UNSPEC: eh = (struct ether_header *)dst->sa_data; bcopy((char *)eh->ether_dhost, (caddr_t)edst, sizeof(edst)); type = eh->ether_type; break; default: printf("un%d: can't handle af%d\n", ifp->if_unit, dst->sa_family); error = EAFNOSUPPORT; goto bad; } /* * Add local net header. If no space in first mbuf, * allocate another. */ if (m->m_off > MMAXOFF || MMINOFF + sizeof(struct ether_header) > m->m_off){ m = m_get(M_DONTWAIT, MT_HEADER); /* * Note: m_get, m_freem etc. guard against concurrent * updates to the list of free mbufs. */ if (m == 0){ error = ENOBUFS; goto bad; } m->m_next = m0; m->m_off = MMINOFF; m->m_len = sizeof(struct ether_header); } else { m->m_off -= sizeof(struct ether_header); m->m_len += sizeof(struct ether_header); } eh = mtod(m, struct ether_header *); bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof(edst)); bcopy((caddr_t)us->us_addr, (caddr_t)eh->ether_shost, sizeof(eh->ether_shost)); bcopy((caddr_t)&type, (caddr_t)&eh->ether_type, sizeof(u_short)); /* * queue packet for transmission. if there is an empty * transmit buffer on the adapter, use it. */ s = splimp(); if (IF_QFULL(&ifp->if_snd)){ IF_DROP(&ifp->if_snd); error = ENOBUFS; goto qfull; } if (us->us_xfull[0] == 0 || us->us_xfull[1] == 0){ /* empty xmt buf */ struct undevice *addr = (struct undevice *) uninfo[ifp->if_unit]->iod_addr; int next_buf; #ifdef ATR int old_window; move_window(old_window, addr); #endif ATR if (us->us_xfull[0] == 0){ next_buf = 0; } else { next_buf = 1; } unput(us, addr, m, next_buf); if (us->us_oactive == 0){ unstart(us, addr, next_buf); } #ifdef ATR restore_window(old_window); #endif ATR } else { IF_ENQUEUE(&ifp->if_snd, m); } splx(s); return(0); qfull: m0 = m; splx(s); bad: m_freem(m0); return(error); } /* * unput - copy packet from an mbuf chain to one of the adapter's * transmit buffers. the packet is extended to the minimum legal * size if necessary. the extra bytes could be zeroed out to improve * security but are not to maximize performance. */ unput(us, addr, m, xbuf) struct un_softc *us; struct undevice *addr; register struct mbuf *m; register int xbuf; { register unsigned off; register struct mbuf *mp; register char *bp; /* * compute starting address in transmit buffer. packets must be * "end_aligned". */ for (off = UN_XBSIZE, mp = m; mp; mp = mp->m_next){ off -= mp->m_len; } if (UN_XBSIZE - off < ETHERMIN + sizeof(struct ether_header)){ /* packet too short => extend it */ off = UN_XBSIZE - ETHERMIN - sizeof(struct ether_header); } if (xbuf == 1){ /* use the second buffer */ off += UN_XBSIZE; /* the 2 buffers are adjacent */ } bp = ((char *)(addr->un_xmtbuf)) + off; for (mp = m; mp; mp = mp->m_next){ register unsigned len = mp->m_len; bcopyout(mtod(mp, char *), bp, len); bp += len; } /* save starting address so interrupt handler can find it */ us->us_xstart[xbuf] = off; /* start address to be passed to adapter */ us->us_xfull[xbuf] = 1; /* mark buffer full */ m_freem(m); } /* * unget - copy packet from adapter's receive buffers into a chain of mbufs * */ struct mbuf * unget(addr, unbuf, totlen, off0, ifp) struct undevice *addr; char *unbuf; register int totlen; int off0; struct ifnet *ifp; { register struct mbuf *m; struct mbuf *top = 0; register struct mbuf **mp = ⊤ register int off = off0; register int len; register char *cp; #ifdef ISO int copied_snpa = 0; #endif ISO cp = unbuf + sizeof(struct ether_header); while (totlen > 0){ char *mcp; MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) goto bad; if (off){ /* trailer exists */ len = totlen - off; cp = unbuf + sizeof(struct ether_header) + off; } else len = totlen; #ifdef ISO if (!copied_snpa) len += sizeof(struct snpa_hdr); #else ISO if (ifp) len += sizeof(ifp); #endif ISO if (len >= NBPG){ MCLGET(m); if (m->m_len == CLBYTES) m->m_len = len = MIN(len, CLBYTES); else m->m_len = len = MIN(MLEN, len); } else { m->m_len = len = MIN(MLEN, len); m->m_off = MMINOFF; } mcp = mtod(m, char *); #ifdef ISO if (!copied_snpa) { /* * Prepend snpa_hdr to first mbuf * The hardcoded 12 below refers to the length of the dhost * and shost fields. We recklessly assume * the order of dhost,shost in the snpa_hdr is the same * as the order in the ether_header. */ struct snpa_hdr *sh = (struct snpa_hdr *)mcp; struct ether_header *eh = (struct ether_header *)unbuf; bcopy((char *) &ifp, (caddr_t)&sh->snh_ifp, sizeof(ifp)); bcopy((caddr_t)eh, (caddr_t)sh->snh_dhost, 12); mcp += sizeof(struct snpa_hdr); len -= sizeof(struct snpa_hdr); copied_snpa = 1; } #else ISO if (ifp){ /* prepend ifp to first mbuf */ /* * bcopy is used since since word moves must * be on 4 byte boundaries on the RT PC */ bcopy((char *) &ifp, mcp, sizeof(ifp)); mcp += sizeof(ifp); len -= sizeof(ifp); ifp = (struct ifnet *) 0; } #endif ISO unbcopy(addr, cp, mcp, len); cp += len; *mp = m; mp = &m->m_next; if (off == 0){ totlen -= len; continue; } off += len; if (off == totlen){ cp = unbuf + sizeof(struct ether_header); off = 0; totlen = off0; } } return(top); bad: m_freem(top); return(0); } /* * ioctl - process an ioctl request. */ unioctl(ifp, cmd, data) register struct ifnet *ifp; register int cmd; register caddr_t data; { register struct ifaddr *ifa = (struct ifaddr *)data; register int s = splimp(); register int error = 0; switch (cmd){ case SIOCSIFADDR: ifp->if_flags |= IFF_UP; switch (ifa->ifa_addr.sa_family){ #ifdef INET case AF_INET: uninit(ifp->if_unit); /* before arpwhohas */ ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); break; #endif INET #ifdef NS case AF_NS: { struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); struct un_softc *us = &un_softc[ifp->if_unit]; if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *)(us->us_addr); else { ifp->if_flags &= ~IFF_RUNNING; bcopy((caddr_t) ina->x_host.c_host, (caddr_t) us->us_addr, sizeof(us->us_addr)); /* * the uninit will set the hardware address * since the IFF_RUNNING flag is off */ } uninit(ifp->if_unit); break; } #endif NS default: uninit(ifp->if_unit); break; } break; case SIOCSIFFLAGS: if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING){ #ifdef ATR int old_window; #endif ATR struct undevice *addr; addr = (struct undevice *) uninfo[ifp->if_unit]-> iod_addr; #ifdef ATR move_window(old_window, addr); #endif ATR (void) unzap((struct undevice *) addr); ifp->if_flags &= ~IFF_RUNNING; #ifdef ATR restore_window(old_window); #endif ATR } else if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0) uninit(ifp->if_unit); break; default: error = EINVAL; } splx(s); return(error); } /* * unzap - initialize adapter but don't enable it. * returns page number of next receive page to be used. */ unzap(addr) register struct undevice *addr; { register int next; MM_OUT(&addr->un_csr, 0); /* disable interrupts */ MM_OUT(&addr->un_edlc.reset, RESET_ON); /* set reset bit while init'ing */ MM_OUT(&addr->un_edlc.rstat, RS_CLEAR); MM_OUT(&addr->un_edlc.xstat, XS_CLEAR); MM_OUT(&addr->un_edlc.rmask, 0); MM_OUT(&addr->un_edlc.xmask, 0); MM_OUT(&addr->un_edlc.rmode, RM_NORMAL); /* * the next line puts the transmitter in loopback mode so * that a spurious packet is not sent when the reset bit is * cleared. */ MM_OUT(&addr->un_edlc.tmode, TM_NORMAL - TM_LBC); MM_OUT(&addr->un_edlc.reset, RESET_OFF); /* clear reset bit */ /* * clear the receive buffers. assign the value in the empty * page pointer to the full page pointer and clear the packet * available flag. */ next = MM_IN(&addr->un_fppepp) & UN_PAGE_MASK; /* clears the IKSYDK flag */ MM_OUT(&addr->un_fppepp, next); /* fpp = epp */ UN_CLRPAV(addr); /* clear the PAV flag */ MM_OUT(&addr->un_edlc.tmode, TM_NORMAL); /* put transmitter in normal mode */ DEBUGF(undebug & 0x2, printf("unzap: zzzzapped!\n");) return(next); } /* * unupdatefpp - update adapter's full page pointer and clear packet available * flag if appropriate */ unupdatefpp(addr, nextpage) register struct undevice *addr; register int nextpage; { if (nextpage == /* EPP */ (MM_IN(&addr->un_fppepp) & UN_PAGE_MASK)) UN_CLRPAV(addr); MM_OUT(&addr->un_fppepp, nextpage); /* set FPP */ } /* * unbcopy - similar to bcopy but can deal with packets that wrap * around from the high end of the adapter's receive buffer to the * low end */ unbcopy(addr, from, to, len) register struct undevice *addr; register char *from; register char *to; register int len; { register char *high_end = &addr->un_rcvbuf[UN_LASTRBUF][UN_RBUFSIZE]; register int n; if (from + len <= high_end){ bcopyin(from, to, len); } else if (from >= high_end){ from -= sizeof(addr->un_rcvbuf); bcopyin(from, to, len); } else { n = high_end - from; bcopyin(from, to, n); to += n; bcopyin((char *)addr->un_rcvbuf, to, len - n); } } /* * ungetushortatoff - return the u_short at offset in the received packet, * handling wrap-around in the receive buffer and conversion between network * and host formats as necessary. */ u_short ungetushortatoff(addr, eh, off) register struct undevice *addr; register struct ether_header *eh; register int off; { register char *high_end = &addr->un_rcvbuf[UN_LASTRBUF][UN_RBUFSIZE]; register char *p; p = (caddr_t)(eh + 1) + off; if (p >= high_end){ p -= sizeof(addr->un_rcvbuf); } return(ntohs((u_short) MM_INW(p))); } /* * unprintethaddr - print an ethernet address */ unprintethaddr(p) register char *p; { register int i; for (i = 0; i < ETH_ADDR_SIZE; i++){ if (i != 0) printf(":"); printf("%x", *p++); } } #endif NUN > 0