if_vx.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,090 行 · 第 1/2 页

C
1,090
字号
	if (vxstatus(sc)) {	    if (ifp->if_flags & IFF_DEBUG)	       printf("vx%d: adapter reset\n", ifp->if_unit);	    vxreset(sc);	}    }    goto startagain;}/* * XXX: The 3c509 card can get in a mode where both the fifo status bit *      FIFOS_RX_OVERRUN and the status bit ERR_INCOMPLETE are set *      We detect this situation and we reset the adapter. *      It happens at times when there is a lot of broadcast traffic *      on the cable (once in a blue moon). */static intvxstatus(sc)    struct vx_softc *sc;{    int fifost;    /*     * Check the FIFO status and act accordingly     */    GO_WINDOW(4);    fifost = inw(BASE + VX_W4_FIFO_DIAG);    GO_WINDOW(1);    if (fifost & FIFOS_RX_UNDERRUN) {	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)	    printf("vx%d: RX underrun\n", sc->unit);	vxreset(sc);	return 0;    }    if (fifost & FIFOS_RX_STATUS_OVERRUN) {	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)	    printf("vx%d: RX Status overrun\n", sc->unit);	return 1;    }    if (fifost & FIFOS_RX_OVERRUN) {	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)	    printf("vx%d: RX overrun\n", sc->unit);	return 1;    }    if (fifost & FIFOS_TX_OVERRUN) {	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)	    printf("vx%d: TX overrun\n", sc->unit);	vxreset(sc);	return 0;    }    return 0;}static void     vxtxstat(sc)    struct vx_softc *sc;{    int i;    /*    * We need to read+write TX_STATUS until we get a 0 status    * in order to turn off the interrupt flag.    */    while ((i = inb(BASE + VX_W1_TX_STATUS)) & TXS_COMPLETE) {	outb(BASE + VX_W1_TX_STATUS, 0x0);    if (i & TXS_JABBER) {	++sc->arpcom.ac_if.if_oerrors;	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)	    printf("vx%d: jabber (%x)\n", sc->unit, i);	vxreset(sc);    } else if (i & TXS_UNDERRUN) {	++sc->arpcom.ac_if.if_oerrors;	if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)	    printf("vx%d: fifo underrun (%x) @%d\n",		sc->unit, i, sc->tx_start_thresh);	if (sc->tx_succ_ok < 100)	    sc->tx_start_thresh = min(ETHER_MAX_LEN, sc->tx_start_thresh + 20);	sc->tx_succ_ok = 0;	vxreset(sc);    } else if (i & TXS_MAX_COLLISION) {	++sc->arpcom.ac_if.if_collisions;	outw(BASE + VX_COMMAND, TX_ENABLE);	sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;    } else	sc->tx_succ_ok = (sc->tx_succ_ok+1) & 127;    }}voidvxintr(voidsc)    void *voidsc;{    register short status;    struct vx_softc *sc = voidsc;    struct ifnet *ifp = &sc->arpcom.ac_if;    for (;;) {	outw(BASE + VX_COMMAND, C_INTR_LATCH);	status = inw(BASE + VX_STATUS);	if ((status & (S_TX_COMPLETE | S_TX_AVAIL |		S_RX_COMPLETE | S_CARD_FAILURE)) == 0)	    break;	/*	 * Acknowledge any interrupts.  It's important that we do this	 * first, since there would otherwise be a race condition.	 * Due to the i386 interrupt queueing, we may get spurious	 * interrupts occasionally.	 */	outw(BASE + VX_COMMAND, ACK_INTR | status);	if (status & S_RX_COMPLETE)	    vxread(sc);	if (status & S_TX_AVAIL) {	    ifp->if_timer = 0;	    sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;	    vxstart(&sc->arpcom.ac_if);	}	if (status & S_CARD_FAILURE) {	    printf("vx%d: adapter failure (%x)\n", sc->unit, status);	    ifp->if_timer = 0;	    vxreset(sc);	    return;	}	if (status & S_TX_COMPLETE) {	    ifp->if_timer = 0;	    vxtxstat(sc);	    vxstart(ifp);	}    }    /* no more interrupts */    return;}static voidvxread(sc)    struct vx_softc *sc;{    struct ifnet *ifp = &sc->arpcom.ac_if;    struct mbuf *m;    struct ether_header *eh;    u_int len;    len = inw(BASE + VX_W1_RX_STATUS);again:    if (ifp->if_flags & IFF_DEBUG) {	int err = len & ERR_MASK;	char *s = NULL;	if (len & ERR_INCOMPLETE)	    s = "incomplete packet";	else if (err == ERR_OVERRUN)	    s = "packet overrun";	else if (err == ERR_RUNT)	    s = "runt packet";	else if (err == ERR_ALIGNMENT)	    s = "bad alignment";	else if (err == ERR_CRC)	    s = "bad crc";	else if (err == ERR_OVERSIZE)	    s = "oversized packet";	else if (err == ERR_DRIBBLE)	    s = "dribble bits";	if (s)	printf("vx%d: %s\n", sc->unit, s);    }    if (len & ERR_INCOMPLETE)	return;    if (len & ERR_RX) {	++ifp->if_ierrors;	goto abort;    }    len &= RX_BYTES_MASK;	/* Lower 11 bits = RX bytes. */    /* Pull packet off interface. */    m = vxget(sc, len);    if (m == 0) {	ifp->if_ierrors++;	goto abort;    }    ++ifp->if_ipackets;    /* We assume the header fit entirely in one mbuf. */    eh = mtod(m, struct ether_header *);#if NBPFILTER > 0    /*     * Check if there's a BPF listener on this interface.     * If so, hand off the raw packet to BPF.     */    if (sc->arpcom.ac_if.if_bpf) {	bpf_mtap(&sc->arpcom.ac_if, m);    }#endif    /*     * XXX: Some cards seem to be in promiscous mode all the time.     * we need to make sure we only get our own stuff always.     * bleah!     */    if ((eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */	bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,	sizeof(eh->ether_dhost)) != 0) {	m_freem(m);	    return;    }    /* We assume the header fit entirely in one mbuf. */    m_adj(m, sizeof(struct ether_header));    ether_input(ifp, eh, m);    /*    * In periods of high traffic we can actually receive enough    * packets so that the fifo overrun bit will be set at this point,    * even though we just read a packet. In this case we    * are not going to receive any more interrupts. We check for    * this condition and read again until the fifo is not full.    * We could simplify this test by not using vxstatus(), but    * rechecking the RX_STATUS register directly. This test could    * result in unnecessary looping in cases where there is a new    * packet but the fifo is not full, but it will not fix the    * stuck behavior.    *    * Even with this improvement, we still get packet overrun errors    * which are hurting performance. Maybe when I get some more time    * I'll modify vxread() so that it can handle RX_EARLY interrupts.    */    if (vxstatus(sc)) {	len = inw(BASE + VX_W1_RX_STATUS);	/* Check if we are stuck and reset [see XXX comment] */	if (len & ERR_INCOMPLETE) {	    if (ifp->if_flags & IFF_DEBUG)		printf("vx%d: adapter reset\n", sc->unit);	    vxreset(sc);	    return;	}	goto again;    }    return;abort:    outw(BASE + VX_COMMAND, RX_DISCARD_TOP_PACK);}static struct mbuf *vxget(sc, totlen)    struct vx_softc *sc;    u_int totlen;{    struct ifnet *ifp = &sc->arpcom.ac_if;    struct mbuf *top, **mp, *m;    int len;    int sh;    m = sc->mb[sc->next_mb];    sc->mb[sc->next_mb] = 0;    if (m == 0) {        MGETHDR(m, M_DONTWAIT, MT_DATA);        if (m == 0)            return 0;    } else {        /* If the queue is no longer full, refill. */        if (sc->last_mb == sc->next_mb && sc->buffill_pending == 0) {	    sc->ch = timeout(vxmbuffill, sc, 1);	    sc->buffill_pending = 1;	}        /* Convert one of our saved mbuf's. */        sc->next_mb = (sc->next_mb + 1) % MAX_MBS;        m->m_data = m->m_pktdat;        m->m_flags = M_PKTHDR;    }    m->m_pkthdr.rcvif = ifp;    m->m_pkthdr.len = totlen;    len = MHLEN;    top = 0;    mp = &top;    /*     * We read the packet at splhigh() so that an interrupt from another     * device doesn't cause the card's buffer to overflow while we're     * reading it.  We may still lose packets at other times.     */    sh = splhigh();    /*     * Since we don't set allowLargePackets bit in MacControl register,     * we can assume that totlen <= 1500bytes.     * The while loop will be performed iff we have a packet with     * MLEN < m_len < MINCLSIZE.     */    while (totlen > 0) {        if (top) {            m = sc->mb[sc->next_mb];            sc->mb[sc->next_mb] = 0;            if (m == 0) {                MGET(m, M_DONTWAIT, MT_DATA);                if (m == 0) {                    splx(sh);                    m_freem(top);                    return 0;                }            } else {                sc->next_mb = (sc->next_mb + 1) % MAX_MBS;            }            len = MLEN;        }        if (totlen >= MINCLSIZE) {	    MCLGET(m, M_DONTWAIT);	    if (m->m_flags & M_EXT)		len = MCLBYTES;        }        len = min(totlen, len);        if (len > 3)            insl(BASE + VX_W1_RX_PIO_RD_1, mtod(m, u_int32_t *), len / 4);	if (len & 3) {	    insb(BASE + VX_W1_RX_PIO_RD_1, mtod(m, u_int8_t *) + (len & ~3),		len & 3);	}        m->m_len = len;        totlen -= len;        *mp = m;        mp = &m->m_next;    }    outw(BASE +VX_COMMAND, RX_DISCARD_TOP_PACK);    splx(sh);    return top;}static intvxioctl(ifp, cmd, data)    register struct ifnet *ifp;    u_long cmd;    caddr_t data;{    struct vx_softc *sc = vx_softc[ifp->if_unit];    struct ifreq *ifr = (struct ifreq *) data;    int s, error = 0;    s = splimp();    switch (cmd) {    case SIOCSIFADDR:    case SIOCGIFADDR:	ether_ioctl(ifp, cmd, data);	break;    case SIOCSIFFLAGS:	if ((ifp->if_flags & IFF_UP) == 0 &&	    (ifp->if_flags & IFF_RUNNING) != 0) {	    /*             * If interface is marked up and it is stopped, then             * start it.             */	    vxstop(sc);	    ifp->if_flags &= ~IFF_RUNNING;        } else if ((ifp->if_flags & IFF_UP) != 0 &&                   (ifp->if_flags & IFF_RUNNING) == 0) {            /*             * If interface is marked up and it is stopped, then             * start it.             */            vxinit(sc);        } else {            /*             * deal with flags changes:             * IFF_MULTICAST, IFF_PROMISC,             * IFF_LINK0, IFF_LINK1,             */            vxsetfilter(sc);            vxsetlink(sc);        }        break;    case SIOCSIFMTU:        /*         * Set the interface MTU.         */        if (ifr->ifr_mtu > ETHERMTU) {            error = EINVAL;        } else {            ifp->if_mtu = ifr->ifr_mtu;        }        break;    case SIOCADDMULTI:    case SIOCDELMULTI:	/*	 * Multicast list has changed; set the hardware filter	 * accordingly.	 */	vxreset(sc);	error = 0;        break;    default:        error = EINVAL;    }    splx(s);    return (error);}static voidvxreset(sc)    struct vx_softc *sc;{    int s;    s = splimp();    vxstop(sc);    vxinit(sc);    splx(s);}static voidvxwatchdog(ifp)    struct ifnet *ifp;{    struct vx_softc *sc = vx_softc[ifp->if_unit];    if (ifp->if_flags & IFF_DEBUG)	printf("vx%d: device timeout\n", ifp->if_unit);    ifp->if_flags &= ~IFF_OACTIVE;    vxstart(ifp);    vxintr(sc);}voidvxstop(sc)    struct vx_softc *sc;{    struct ifnet *ifp = &sc->arpcom.ac_if;    ifp->if_timer = 0;    outw(BASE + VX_COMMAND, RX_DISABLE);    outw(BASE + VX_COMMAND, RX_DISCARD_TOP_PACK);    VX_BUSY_WAIT;    outw(BASE + VX_COMMAND, TX_DISABLE);    outw(BASE + VX_COMMAND, STOP_TRANSCEIVER);    DELAY(800);    outw(BASE + VX_COMMAND, RX_RESET);    VX_BUSY_WAIT;    outw(BASE + VX_COMMAND, TX_RESET);    VX_BUSY_WAIT;    outw(BASE + VX_COMMAND, C_INTR_LATCH);    outw(BASE + VX_COMMAND, SET_RD_0_MASK);    outw(BASE + VX_COMMAND, SET_INTR_MASK);    outw(BASE + VX_COMMAND, SET_RX_FILTER);    vxmbufempty(sc);}intvxbusyeeprom(sc)    struct vx_softc *sc;{    int j, i = 100;    while (i--) {        j = inw(BASE + VX_W0_EEPROM_COMMAND);        if (j & EEPROM_BUSY)            DELAY(100);        else            break;    }    if (!i) {        printf("vx%d: eeprom failed to come ready\n", sc->unit);        return (1);    }    return (0);}static voidvxmbuffill(sp)    void *sp;{    struct vx_softc *sc = (struct vx_softc *) sp;    int s, i;    s = splimp();    i = sc->last_mb;    do {	if (sc->mb[i] == NULL)	    MGET(sc->mb[i], M_DONTWAIT, MT_DATA);	if (sc->mb[i] == NULL)	    break;	i = (i + 1) % MAX_MBS;    } while (i != sc->next_mb);    sc->last_mb = i;    /* If the queue was not filled, try again. */    if (sc->last_mb != sc->next_mb) {	sc->ch = timeout(vxmbuffill, sc, 1);	sc->buffill_pending = 1;    } else {	sc->buffill_pending = 0;    }    splx(s);}static voidvxmbufempty(sc)    struct vx_softc *sc;{    int s, i;    s = splimp();    for (i = 0; i < MAX_MBS; i++) {	if (sc->mb[i]) {	    m_freem(sc->mb[i]);	    sc->mb[i] = NULL;	}    }    sc->last_mb = sc->next_mb = 0;    if (sc->buffill_pending != 0)	untimeout(vxmbuffill, sc, sc->ch);    splx(s);}#endif				/* NVX > 0 */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?