/* @(#)if_qe.c 1.3 (2.11BSD) 1997/2/16 */ /**************************************************************** * * * Licensed from Digital Equipment Corporation * * Copyright (c) * * Digital Equipment Corporation * * Maynard, Massachusetts * * 1985, 1986 * * All rights reserved. * * * * The Information in this software is subject to change * * without notice and should not be construed as a commitment * * by Digital Equipment Corporation. Digital makes no * * representations about the suitability of this software for * * any purpose. It is supplied "As Is" without expressed or * * implied warranty. * * * * If the Regents of the University of California or its * * licensees modify the software in a manner creating * * derivative copyright rights, appropriate copyright * * legends may be placed on the derivative work in addition * * to that set forth above. * * * ****************************************************************/ /* --------------------------------------------------------------------- * Modification History * * 16-Nov-90 -- sms@wlv.imsd.contel.com * Ported from 4.3BSD to 2.11BSD as a replacement for the previous * 2.10BSD deqna driver which was flakey. 'trailers' are completely * removed from this version - they are a bad idea and never really * worked. Advantage is taken of this being a Qbus driver - memory * is allocated from the system and physical addresses computed. * * 15-Apr-86 -- afd * Rename "unused_multi" to "qunused_multi" for extending Generic * kernel to MicroVAXen. * * 18-mar-86 -- jaw br/cvec changed to NOT use registers. * * 12 March 86 -- Jeff Chase * Modified to handle the new MCLGET macro * Changed if_qe_data.c to use more receive buffers * Added a flag to poke with adb to log qe_restarts on console * * 19 Oct 85 -- rjl * Changed the watch dog timer from 30 seconds to 3. VMS is using * less than 1 second in their's. Also turned the printf into an * mprintf. * * 09/16/85 -- Larry Cohen * Add 43bsd alpha tape changes for subnet routing * * 1 Aug 85 -- rjl * Panic on a non-existent memory interrupt and the case where a packet * was chained. The first should never happen because non-existant * memory interrupts cause a bus reset. The second should never happen * because we hang 2k input buffers on the device. * * 1 Aug 85 -- rich * Fixed the broadcast loopback code to handle Clusters without * wedging the system. * * 27 Feb. 85 -- ejf * Return default hardware address on ioctl request. * * 12 Feb. 85 -- ejf * Added internal extended loopback capability. * * 27 Dec. 84 -- rjl * Fixed bug that caused every other transmit descriptor to be used * instead of every descriptor. * * 21 Dec. 84 -- rjl * Added watchdog timer to mask hardware bug that causes device lockup. * * 18 Dec. 84 -- rjl * Reworked driver to use q-bus mapping routines. MicroVAX-I now does * copying instead of m-buf shuffleing. * A number of deficencies in the hardware/firmware were compensated * for. See comments in qestart and qerint. * * 14 Nov. 84 -- jf * Added usage counts for multicast addresses. * Updated general protocol support to allow access to the Ethernet * header. * * 04 Oct. 84 -- jf * Added support for new ioctls to add and delete multicast addresses * and set the physical address. * Add support for general protocols. * * 14 Aug. 84 -- rjl * Integrated Shannon changes. (allow arp above 1024 and ? ) * * 13 Feb. 84 -- rjl * * Initial version of driver. derived from IL driver. * --------------------------------------------------------------------- */ #include "qe.h" #if NQE > 0 /* * Digital Q-BUS to NI Adapter */ #include "param.h" #include "pdp/seg.h" #include "pdp/psl.h" #include "map.h" #include "systm.h" #include "mbuf.h" #include "buf.h" #include "protosw.h" #include "socket.h" #include "ioctl.h" #include "errno.h" #include "syslog.h" #include "time.h" #include "kernel.h" #include "../net/if.h" #include "../net/netisr.h" #include "../net/route.h" #ifdef INET #include "domain.h" #include "../netinet/in.h" #include "../netinet/in_systm.h" #include "../netinet/in_var.h" #include "../netinet/ip.h" #include "../netinet/if_ether.h" #endif #ifdef NS #include "../netns/ns.h" #include "../netns/ns_if.h" #endif #include "if_qereg.h" #include "if_uba.h" #include "../pdpuba/ubavar.h" #define NRCV 13 /* Receive descriptors (was 25) */ #define NXMT 5 /* Transmit descriptors */ #define NTOT (NXMT + NRCV) #define MINDATA 60 /* * 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, ... */ struct qe_softc { struct arpcom is_ac; /* Ethernet common part */ #define is_if is_ac.ac_if /* network-visible interface */ #define is_addr is_ac.ac_enaddr /* hardware Ethernet address */ struct ifuba qe_ifr[NRCV]; /* for receive buffers; */ struct ifuba qe_ifw[NXMT]; /* for xmit buffers; */ int qe_flags; /* software state */ #define QEF_RUNNING 0x01 #define QEF_SETADDR 0x02 long setupaddr; /* physaddr info for setup pkts */ long rringaddr; /* physaddr info for rings */ long tringaddr; /* "" */ struct qe_ring rring[NRCV+1]; /* Receive ring descriptors */ struct qe_ring tring[NXMT+1]; /* Transmit ring descriptors */ u_char setup_pkt[16][8]; /* Setup packet */ u_char rindex; /* Receive index */ u_char tindex; /* Transmit index */ int otindex; /* Old transmit index */ int qe_intvec; /* Interrupt vector */ struct qedevice *addr; /* device addr */ u_char setupqueued; /* setup packet queued */ u_char nxmit; /* Transmits in progress */ int timeout; /* watchdog */ int qe_restarts; /* timeouts */ } qe_softc[NQE]; struct uba_device *qeinfo[NQE]; int qeattach(), qeintr(), qewatch(), qeinit(),qeoutput(),qeioctl(); extern struct ifnet loif; u_short qestd[] = { 0 }; struct uba_driver qedriver = { 0, 0, qeattach, 0, qestd, "qe", qeinfo }; #define QE_TIMEO (15) #define QEUNIT(x) minor(x) int watchrun = 0; /* watchdog running */ /* * The deqna shouldn't receive more than ETHERMTU + sizeof(struct ether_header) * but will actually take in up to 2048 bytes. To guard against the receiver * chaining buffers (which we aren't prepared to handle) we allocate 2kb * size buffers. */ #define MAXPACKETSIZE 2048 /* Should really be ETHERMTU */ /* * The C compiler's propensity for prepending '_'s to names is the reason * for the routine below. We need the "handler" address (the code which * sets up the interrupt stack frame) in order to initialize the vector. */ static int qefoo() { asm("mov $qeintr, r0"); /* return value is in r0 */ } /* * Interface exists: make available by filling in network interface * record. System will initialize the interface when it is ready * to accept packets. */ qeattach(ui) struct uba_device *ui; { register struct qe_softc *sc = &qe_softc[ui->ui_unit]; register struct ifnet *ifp = &sc->is_if; struct qedevice *addr = (struct qedevice *)ui->ui_addr; register int i; int islqa = 0; extern int nextiv(); ifp->if_unit = ui->ui_unit; ifp->if_name = "qe"; ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST; /* * Read the address from the prom and save it. */ for ( i=0 ; i<6 ; i++ ) sc->setup_pkt[i][1] = sc->is_addr[i] = addr->qe_sta_addr[i] & 0xff; /* * Determine if this is a DEQNA or a DELQA... */ addr->qe_vector |= QE_VEC_ID; if (addr->qe_vector & QE_VEC_ID) islqa = 1; addr->qe_vector &= ~QE_VEC_ID; /* * Allocate a floating vector and initialize it with the address of * the interrupt handler and PSW (supervisor mode, priority 4, unit * number in the low bits. */ i = SKcall(nextiv, 0); sc->qe_intvec = i; mtkd(i, qefoo()); mtkd(i+2, PSL_CURSUP | PSL_BR4 | ifp->if_unit); /* * map the communications area onto the device */ sc->rringaddr = startnet + (long)sc->rring; sc->tringaddr = startnet + (long)sc->tring; sc->setupaddr = startnet + (long)sc->setup_pkt; /* * init buffers and maps */ if (qbaini(sc->qe_ifr, NRCV) == 0) sc->is_if.if_flags &= ~IFF_UP; if (qbaini(sc->qe_ifw, NXMT) == 0) sc->is_if.if_flags &= ~IFF_UP; ifp->if_init = qeinit; ifp->if_output = qeoutput; ifp->if_ioctl = qeioctl; ifp->if_reset = 0; if_attach(ifp); printf("qe%d: DEC DE%sA addr %s\n",ifp->if_unit, islqa ? "LQ": "QN", ether_sprintf(&sc->is_addr)); } /* * Initialization of interface. */ qeinit(unit) int unit; { register struct qe_softc *sc = &qe_softc[unit]; register struct uba_device *ui = qeinfo[unit]; register struct qedevice *addr = (struct qedevice *)ui->ui_addr; register struct ifnet *ifp = &sc->is_if; register i; int s; /* address not known */ if (ifp->if_addrlist == (struct ifaddr *)0) return; /* * Init the buffer descriptors and indexes for each of the lists and * loop them back to form a ring. */ for (i = 0; i < NRCV; i++) { qeinitdesc(&sc->rring[i], sc->qe_ifr[i].ifu_r.ifrw_info, MAXPACKETSIZE); sc->rring[i].qe_flag = sc->rring[i].qe_status1 = QE_NOTYET; sc->rring[i].qe_valid = 1; } qeinitdesc(&sc->rring[i], (long)NULL, 0); sc->rring[i].qe_addr_lo = loint(sc->rringaddr); sc->rring[i].qe_addr_hi = hiint(sc->rringaddr); sc->rring[i].qe_chain = 1; sc->rring[i].qe_flag = sc->rring[i].qe_status1 = QE_NOTYET; sc->rring[i].qe_valid = 1; for( i = 0 ; i <= NXMT ; i++ ) qeinitdesc(&sc->tring[i], (long)NULL, 0); i--; sc->tring[i].qe_addr_lo = loint(sc->tringaddr); sc->tring[i].qe_addr_hi = hiint(sc->tringaddr); sc->tring[i].qe_chain = 1; sc->tring[i].qe_flag = sc->tring[i].qe_status1 = QE_NOTYET; sc->tring[i].qe_valid = 1; sc->nxmit = sc->otindex = sc->tindex = sc->rindex = 0; /* * Take the interface out of reset, program the vector, * enable interrupts, and tell the world we are up. */ s = splimp(); addr->qe_vector = sc->qe_intvec; sc->addr = addr; addr->qe_csr = QE_RCV_ENABLE | QE_INT_ENABLE | QE_XMIT_INT | QE_RCV_INT | QE_ILOOP; addr->qe_rcvlist_lo = loint(sc->rringaddr); addr->qe_rcvlist_hi = hiint(sc->rringaddr); ifp->if_flags |= IFF_UP | IFF_RUNNING; sc->qe_flags |= QEF_RUNNING; qesetup(sc); qestart(unit); splx(s); } /* * Start output on interface. * */ qestart(dev) dev_t dev; { int unit = QEUNIT(dev); struct uba_device *ui = qeinfo[unit]; register struct qe_softc *sc = &qe_softc[unit]; register struct qedevice *addr; register struct qe_ring *rp; register index; struct mbuf *m; int len, s; long buf_addr; s = splimp(); addr = (struct qedevice *)ui->ui_addr; /* * The deqna doesn't look at anything but the valid bit * to determine if it should transmit this packet. If you have * a ring and fill it the device will loop indefinately on the * packet and continue to flood the net with packets until you * break the ring. For this reason we never queue more than n-1 * packets in the transmit ring. * * The microcoders should have obeyed their own defination of the * flag and status words, but instead we have to compensate. */ for( index = sc->tindex; sc->tring[index].qe_valid == 0 && sc->nxmit < (NXMT-1) ; sc->tindex = index = ++index % NXMT){ rp = &sc->tring[index]; if( sc->setupqueued ) { buf_addr = sc->setupaddr; len = 128; rp->qe_setup = 1; sc->setupqueued = 0; } else { IF_DEQUEUE(&sc->is_if.if_snd, m); if( m == 0 ){ splx(s); return; } buf_addr = sc->qe_ifw[index].ifu_w.ifrw_info; len = if_wubaput(&sc->qe_ifw[index], m); } /* * Does buffer end on odd byte ? */ if (len < MINDATA) len = MINDATA; if (len & 1) { len++; rp->qe_odd_end = 1; } rp->qe_buf_len = -(len/2); rp->qe_flag = rp->qe_status1 = QE_NOTYET; rp->qe_addr_lo = loint(buf_addr); rp->qe_addr_hi = hiint(buf_addr); rp->qe_eomsg = 1; rp->qe_flag = rp->qe_status1 = QE_NOTYET; rp->qe_valid = 1; sc->nxmit++; /* * If the watchdog time isn't running kick it. */ sc->timeout=1; if (watchrun == 0) { watchrun++; TIMEOUT(qewatch, (caddr_t)0, QE_TIMEO); } /* * See if the xmit list is invalid. */ if( addr->qe_csr & QE_XL_INVALID ) { buf_addr = sc->tringaddr + (index * sizeof (struct qe_ring)); addr->qe_xmtlist_lo = loint(buf_addr); addr->qe_xmtlist_hi = hiint(buf_addr); } } splx(s); } /* * Ethernet interface interrupt processor */ qeintr(unit) int unit; { register struct qe_softc *sc = &qe_softc[unit]; struct qedevice *addr = (struct qedevice *)qeinfo[unit]->ui_addr; int s, csr; long buf_addr; s = splimp(); csr = addr->qe_csr; addr->qe_csr = QE_RCV_ENABLE | QE_INT_ENABLE | QE_XMIT_INT | QE_RCV_INT | QE_ILOOP; if (csr & QE_RCV_INT) qerint(unit); if (csr & QE_XMIT_INT) qetint(unit); if (csr & QE_NEX_MEM_INT) panic("qe: Non existant memory interrupt"); if( addr->qe_csr & QE_RL_INVALID && sc->rring[sc->rindex].qe_status1 == QE_NOTYET ) { buf_addr = sc->rringaddr + (sc->rindex * sizeof(struct qe_ring)); addr->qe_rcvlist_lo = loint(buf_addr); addr->qe_rcvlist_hi = hiint(buf_addr); } splx(s); } /* * Ethernet interface transmit interrupt. */ qetint(unit) int unit; { register struct qe_softc *sc = &qe_softc[unit]; register struct qe_ring *rp; int status1, setupflag; short len; while (sc->otindex != sc->tindex && sc->tring[sc->otindex].qe_status1 != QE_NOTYET && sc->nxmit > 0) { /* * Save the status words from the descriptor so that it can * be released. */ rp = &sc->tring[sc->otindex]; status1 = rp->qe_status1; setupflag = rp->qe_setup; len = (-rp->qe_buf_len) * 2; if (rp->qe_odd_end) len++; /* * Init the buffer descriptor */ bzero((caddr_t)rp, sizeof(struct qe_ring)); if (--sc->nxmit == 0) sc->timeout = 0; if (!setupflag) { /* * Do some statistics. */ sc->is_if.if_opackets++; sc->is_if.if_collisions += (status1 & QE_CCNT) >> 4; if (status1 & QE_ERROR) sc->is_if.if_oerrors++; } sc->otindex = ++sc->otindex % NXMT; } qestart(unit); } /* * Ethernet interface receiver interrupt. * 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. */ qerint(unit) int unit; { register struct qe_softc *sc = &qe_softc[unit]; register struct qe_ring *rp; int len, status1, status2; long bufaddr; /* * Traverse the receive ring looking for packets to pass back. * The search is complete when we find a descriptor not in use. * * As in the transmit case the deqna doesn't honor it's own protocols * so there exists the possibility that the device can beat us around * the ring. The proper way to guard against this is to insure that * there is always at least one invalid descriptor. We chose instead * to make the ring large enough to minimize the problem. With a ring * size of 4 we haven't been able to see the problem. To be safe we * increased that to 5. * */ for( ; sc->rring[sc->rindex].qe_status1 != QE_NOTYET ; sc->rindex = ++sc->rindex % NRCV ){ rp = &sc->rring[sc->rindex]; status1 = rp->qe_status1; status2 = rp->qe_status2; bzero((caddr_t)rp, sizeof(struct qe_ring)); if ((status1 & QE_MASK) == QE_MASK) panic("qe: chained packet"); len = ((status1 & QE_RBL_HI) | (status2 & QE_RBL_LO)) + 60; sc->is_if.if_ipackets++; if (status1 & QE_ERROR) sc->is_if.if_ierrors++; else { /* * We don't process setup packets. */ if (!(status1 & QE_ESETUP)) qeread(sc, &sc->qe_ifr[sc->rindex], len - sizeof(struct ether_header)); } /* * Return the buffer to the ring */ bufaddr = sc->qe_ifr[sc->rindex].ifu_r.ifrw_info; rp->qe_buf_len = -((MAXPACKETSIZE)/2); rp->qe_addr_lo = loint(bufaddr); rp->qe_addr_hi = hiint(bufaddr); rp->qe_flag = rp->qe_status1 = QE_NOTYET; rp->qe_valid = 1; } } /* * Ethernet output routine. * Encapsulate a packet of type family for the local net. */ qeoutput(ifp, m0, dst) struct ifnet *ifp; struct mbuf *m0; struct sockaddr *dst; { int type, s, error; u_char edst[6]; struct in_addr idst; register struct qe_softc *is = &qe_softc[ifp->if_unit]; register struct mbuf *m = m0; register struct ether_header *eh; int usetrailers; struct mbuf *mcopy = (struct mbuf *)0; 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(&is->is_ac, m, &idst, edst, &usetrailers)) return (0); /* if not yet resolved */ if (!bcmp(edst, etherbroadcastaddr,sizeof (edst))) mcopy = m_copy(m, 0, (int)M_COPYALL); type = ETHERTYPE_IP; goto gottype; #endif #ifdef NS case AF_NS: type = ETHERTYPE_NS; bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host), (caddr_t)edst, sizeof (edst)); if (!bcmp(edst, &ns_broadcast, sizeof (edst))) return(looutput(&loif, m, dst)); goto gottype; #endif case AF_UNSPEC: eh = (struct ether_header *)dst->sa_data; bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst)); type = eh->ether_type; goto gottype; default: printf("qe%d: can't handle af%d\n", ifp->if_unit, dst->sa_family); error = EAFNOSUPPORT; goto bad; } gottype: /* * 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); 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 *); eh->ether_type = htons((u_short)type); bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst)); bcopy((caddr_t)is->is_addr, (caddr_t)eh->ether_shost, sizeof (is->is_addr)); /* * 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); splx(s); m_freem(m); if (mcopy) m_freem(mcopy); return (ENOBUFS); } IF_ENQUEUE(&ifp->if_snd, m); qestart(ifp->if_unit); splx(s); return(mcopy ? looutput(&loif, mcopy, dst) : 0); bad: m_freem(m0); if (mcopy) m_freem(mcopy); return(error); } /* * Process an ioctl request. */ qeioctl(ifp, cmd, data) register struct ifnet *ifp; int cmd; caddr_t data; { struct qe_softc *sc = &qe_softc[ifp->if_unit]; struct ifaddr *ifa = (struct ifaddr *)data; int s = splimp(), error = 0; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; qeinit(ifp->if_unit); switch(ifa->ifa_addr.sa_family) { #ifdef INET case AF_INET: ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); break; #endif #ifdef NS case AF_NS: { register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *)(sc->is_addr); else qe_setaddr(ina->x_host.c_host, ifp->if_unit); break; } #endif } break; case SIOCSIFFLAGS: if ((ifp->if_flags & IFF_UP) == 0 && sc->qe_flags & QEF_RUNNING) { ((struct qedevice *) (qeinfo[ifp->if_unit]->ui_addr))->qe_csr = QE_RESET; sc->qe_flags &= ~QEF_RUNNING; } else if (ifp->if_flags & IFF_UP && (sc->qe_flags & QEF_RUNNING) == 0) qerestart(sc); break; default: error = EINVAL; } splx(s); return (error); } /* * set ethernet address for unit */ qe_setaddr(physaddr, unit) u_char *physaddr; int unit; { register struct qe_softc *sc = &qe_softc[unit]; register int i; for (i = 0; i < 6; i++) sc->setup_pkt[i][1] = sc->is_addr[i] = physaddr[i]; sc->qe_flags |= QEF_SETADDR; if (sc->is_if.if_flags & IFF_RUNNING) qesetup(sc); qeinit(unit); } /* * Initialize a ring descriptor with mbuf allocation side effects */ qeinitdesc(rp, addr, len) register struct qe_ring *rp; long addr; /* physical address */ int len; { /* * clear the entire descriptor */ bzero((caddr_t)rp, sizeof(struct qe_ring)); if( len ) { rp->qe_buf_len = -(len/2); rp->qe_addr_lo = loint(addr); rp->qe_addr_hi = hiint(addr); } } /* * Build a setup packet - the physical address will already be present * in first column. */ qesetup( sc ) struct qe_softc *sc; { register i, j; /* * Copy the target address to the rest of the entries in this row. */ for ( j = 0; j < 6 ; j++ ) for ( i = 2 ; i < 8 ; i++ ) sc->setup_pkt[j][i] = sc->setup_pkt[j][1]; /* * Duplicate the first half. */ bcopy((caddr_t)sc->setup_pkt[0], (caddr_t)sc->setup_pkt[8], 64); /* * Fill in the broadcast address. */ for ( i = 0; i < 6 ; i++ ) sc->setup_pkt[i][2] = 0xff; sc->setupqueued++; } /* * Pass a packet to the higher levels. */ qeread(sc, ifuba, len) register struct qe_softc *sc; struct ifuba *ifuba; int len; { struct ether_header *eh; struct mbuf *m; struct ifqueue *inq; register int type; segm seg5; /* * Count trailers as errors and drop the packet. * SEG5 is mapped out briefly to peek at the packet type, swap the * bytes and then SEG5 is restored. */ saveseg5(seg5); mapseg5(ifuba->ifu_r.ifrw_click, 077406); /* 8k r/w for 1 word */ eh = (struct ether_header *)SEG5; eh->ether_type = ntohs((u_short)eh->ether_type); type = eh->ether_type; restorseg5(seg5); if (len == 0 || type >= ETHERTYPE_TRAIL && type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { sc->is_if.if_ierrors++; return; } /* * Pull packet off interface. */ m = if_rubaget(ifuba, len, 0, &sc->is_if); if (m == 0) return; switch (type) { #ifdef INET case ETHERTYPE_IP: schednetisr(NETISR_IP); inq = &ipintrq; break; case ETHERTYPE_ARP: arpinput(&sc->is_ac, m); return; #endif #ifdef NS case ETHERTYPE_NS: schednetisr(NETISR_NS); inq = &nsintrq; break; #endif default: m_freem(m); return; } if (IF_QFULL(inq)) { IF_DROP(inq); m_freem(m); return; } IF_ENQUEUE(inq, m); } /* * Watchdog timer routine. There is a condition in the hardware that * causes the board to lock up under heavy load. This routine detects * the hang up and restarts the device. */ qewatch() { register struct qe_softc *sc; register int i; int inprogress=0; for (i = 0; i < NQE; i++) { sc = &qe_softc[i]; if (sc->timeout) if (++sc->timeout > 3 ) { printf("qerestart: restarted qe%d %d\n", i, ++sc->qe_restarts); qerestart(sc); } else inprogress++; } if (inprogress) { TIMEOUT(qewatch, (caddr_t)0, QE_TIMEO); watchrun++; } else watchrun=0; } /* * Restart for board lockup problem. */ qerestart(sc) register struct qe_softc *sc; { register struct ifnet *ifp = &sc->is_if; register struct qedevice *addr = sc->addr; register struct qe_ring *rp; register i; addr->qe_csr = QE_RESET; addr->qe_csr &= ~QE_RESET; sc->timeout = 0; qesetup( sc ); for (i = 0, rp = sc->tring; i < NXMT; rp++, i++) { rp->qe_flag = rp->qe_status1 = QE_NOTYET; rp->qe_valid = 0; } sc->nxmit = sc->otindex = sc->tindex = sc->rindex = 0; addr->qe_csr = QE_RCV_ENABLE | QE_INT_ENABLE | QE_XMIT_INT | QE_RCV_INT | QE_ILOOP; addr->qe_rcvlist_lo = loint(sc->rringaddr); addr->qe_rcvlist_hi = hiint(sc->rringaddr); sc->qe_flags |= QEF_RUNNING; qestart(ifp->if_unit); } qbaini(ifuba, num) struct ifuba *ifuba; int num; { register int i; register memaddr click; for (i = 0; i < num; i++) { click = m_ioget(MAXPACKETSIZE); if (click == 0) { click = MALLOC(coremap, btoc(MAXPACKETSIZE)); if (click == 0) { printf("qe: can't get dma memory\n"); return(0); } } ifuba[i].ifu_hlen = sizeof (struct ether_header); ifuba[i].ifu_w.ifrw_click = ifuba[i].ifu_r.ifrw_click = click; ifuba[i].ifu_w.ifrw_info = ifuba[i].ifu_r.ifrw_info = ctob((long)click); } return(1); } #endif