📄 if_fe.c
字号:
/* Put the interface into known initial state. */ if ( sc->sc_if.if_flags & IFF_UP ) { fe_reset( unit ); } else { fe_stop( unit ); }}/* * Drop (skip) a packet from receive buffer in 86960 memory. */static INLINE voidfe_droppacket ( struct fe_softc * sc, int len ){ int n; /* read at least 8 bytes (or total remaining) from packet; leave at least 9 if we're going to tell chip to skip. */ n = (len > 15) ? 8 : len; while (n > 0) {#if FE_SYS_BUS_WIDTH == 16 INW( sc->addr, FE_BMPR8); n -= 2; len -= 2;#else INB( sc->addr, FE_BMPR8); n -= 1; len -= 1;#endif } /* if any is remaining then tell chip to skip it */ if (len > 0) { OUTB( sc->addr, FE_BMPR14, FE_B14_SKIP ); while (INB ( sc->addr, FE_BMPR14 ) & FE_B14_SKIP) continue; }}/* * Initialize device. */voidfe_init ( int unit ){ struct fe_softc *sc = &fe_softc[unit]; int i, s;#if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "init()" );#endif /* We need an address. */ if (sc->sc_if.if_addrlist == 0) {#if FE_DEBUG >= 1 log( LOG_ERR, "fe%d: init() without any address\n", sc->sc_unit );#endif return; }#if FE_DEBUG >= 1 /* * Make sure we have a valid station address. * The following test is applicable for any Ethernet interfaces. * It can be done in somewhere common to all of them. FIXME. */ if ( ( sc->sc_enaddr[ 0 ] & 0x01 ) != 0 || ( sc->sc_enaddr[ 0 ] == 0x00 && sc->sc_enaddr[ 1 ] == 0x00 && sc->sc_enaddr[ 2 ] == 0x00 ) ) { log( LOG_ERR, "fe%d: invalid station address (%s)\n", sc->sc_unit, ether_sprintf( sc->sc_enaddr ) ); return; }#endif /* Start initializing 86960. */ s = splimp(); /* Call a hook. */ if ( sc->init ) sc->init( sc );#if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "after init hook" );#endif /* * Make sure to disable the chip, also. * This may also help re-programming the chip after * hot insertion of PCMCIAs. */ OUTB( sc->addr, FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_DISABLE ); /* Power up the chip and select register bank for DLCRs. */ DELAY(200); OUTB( sc->addr, FE_DLCR7, sc->proto_dlcr7 | FE_D7_RBS_DLCR | FE_D7_POWER_UP ); DELAY(200); /* Feed the station address. */ outblk( sc->addr, FE_DLCR8, sc->sc_enaddr, ETHER_ADDR_LEN ); /* Clear multicast address filter to receive nothing. */ OUTB( sc->addr, FE_DLCR7, sc->proto_dlcr7 | FE_D7_RBS_MAR | FE_D7_POWER_UP ); outblk( sc->addr, FE_MAR8, fe_filter_nothing.data, FE_FILTER_LEN ); /* Select the BMPR bank for runtime register access. */ OUTB( sc->addr, FE_DLCR7, sc->proto_dlcr7 | FE_D7_RBS_BMPR | FE_D7_POWER_UP ); /* Initialize registers. */ OUTB( sc->addr, FE_DLCR0, 0xFF ); /* Clear all bits. */ OUTB( sc->addr, FE_DLCR1, 0xFF ); /* ditto. */ OUTB( sc->addr, FE_DLCR2, 0x00 ); OUTB( sc->addr, FE_DLCR3, 0x00 ); OUTB( sc->addr, FE_DLCR4, sc->proto_dlcr4 ); OUTB( sc->addr, FE_DLCR5, sc->proto_dlcr5 ); OUTB( sc->addr, FE_BMPR10, 0x00 ); OUTB( sc->addr, FE_BMPR11, FE_B11_CTRL_SKIP ); OUTB( sc->addr, FE_BMPR12, 0x00 );#ifndef FE_B13_INIT#define FE_B13_INIT (FE_B13_TPTYPE_UTP | FE_B13_PORT_AUTO)#endif OUTB( sc->addr, FE_BMPR13, FE_B13_INIT ); OUTB( sc->addr, FE_BMPR14, 0x00 ); OUTB( sc->addr, FE_BMPR14, 0x00 ); OUTB( sc->addr, FE_BMPR15, 0x00 );#if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "just before enabling DLC" );#endif /* Enable interrupts. */ OUTB( sc->addr, FE_DLCR2, FE_TMASK ); OUTB( sc->addr, FE_DLCR3, FE_RMASK ); /* Enable transmitter and receiver. */ DELAY(200); OUTB( sc->addr, FE_DLCR6, sc->proto_dlcr6 | FE_D6_DLC_ENABLE ); DELAY(200);#if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "just after enabling DLC" );#endif /* * Make sure to empty the receive buffer. * * This may be redundant, but *if* the receive buffer were full * at this point, the driver would hang. I have experienced * some strange hangups just after UP. I hope the following * code solve the problem. * * I have changed the order of hardware initialization. * I think the receive buffer cannot have any packets at this * point in this version. The following code *must* be * redundant now. FIXME. */ for ( i = 0; i < FE_MAX_RECV_COUNT; i++ ) { u_short status, len; if ( INB( sc->addr, FE_DLCR5 ) & FE_D5_BUFEMP ) break; status = INW( sc->addr, FE_BMPR8 ); len = INW( sc->addr, FE_BMPR8 ); fe_droppacket (sc, len); }#if FE_DEBUG >= 1 if ( i >= FE_MAX_RECV_COUNT ) { log( LOG_ERR, "fe%d: cannot empty receive buffer\n", sc->sc_unit ); }#endif#if FE_DEBUG >= 3 if ( i < FE_MAX_RECV_COUNT ) { log( LOG_INFO, "fe%d: receive buffer emptied (%d)\n", sc->sc_unit, i ); }#endif#if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "after ERB loop" );#endif /* Do we need this here? */ OUTB( sc->addr, FE_DLCR0, 0xFF ); /* Clear all bits. */ OUTB( sc->addr, FE_DLCR1, 0xFF ); /* ditto. */#if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "after FIXME" );#endif /* Set 'running' flag, because we are now running. */ sc->sc_if.if_flags |= IFF_RUNNING;#ifndef PROM /* Update device config status. */ sc->sc_dcstate = DC_BUSY;#endif /* * At this point, the interface is runnung properly, * except that it receives *no* packets. we then call * fe_setmode() to tell the chip what packets to be * received, based on the if_flags and multicast group * list. It completes the initialization process. */ fe_setmode( sc );#if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "after setmode" );#endif /* ...and attempt to start output queued packets. */ fe_start( &sc->sc_if );#if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, "init() done" );#endif (void) splx(s);}/* * This routine actually starts the transmission on the interface */static INLINE voidfe_xmit ( struct fe_softc * sc ){ /* * Set a timer just in case we never hear from the board again. * We use longer timeout for multiple packet transmission. * I'm not sure this timer value is appropriate. FIXME. */ sc->sc_if.if_timer = 1 + sc->txb_count;#if FE_DEBUG >= 1 log (LOG_DEBUG, "Switching transmit buffers\n");#endif /* Update txb variables. */ sc->txb_sched = sc->txb_count; sc->txb_count = 0; sc->txb_free = sc->txb_size;#if FE_DELAYED_PADDING /* Omit the postponed padding process. */ sc->txb_padding = 0;#endif /* Start transmitter, passing packets in TX buffer. */ OUTB( sc->addr, FE_BMPR10, sc->txb_sched | FE_B10_START );}/* * Start output on interface. * We make two assumptions here: * 1) that the current priority is set to splimp _before_ this code * is called *and* is returned to the appropriate priority after * return * 2) that the IFF_OACTIVE flag is checked before this code is called * (i.e. that the output part of the interface is idle) */voidfe_start ( struct ifnet *ifp ){ struct fe_softc *sc = IFNET2SOFTC( ifp ); struct mbuf *m;#if FE_DEBUG >= 1 /* Just a sanity check. */ if ( ( sc->txb_count == 0 ) != ( sc->txb_free == sc->txb_size ) ) { /* * Txb_count and txb_free co-works to manage the * transmission buffer. Txb_count keeps track of the * used potion of the buffer, while txb_free does unused * potion. So, as long as the driver runs properly, * txb_count is zero if and only if txb_free is same * as txb_size (which represents whole buffer.) */ log( LOG_ERR, "fe%d: inconsistent txb variables (%d, %d)\n", sc->sc_unit, sc->txb_count, sc->txb_free ); /* * So, what should I do, then? * * We now know txb_count and txb_free contradicts. We * cannot, however, tell which is wrong. More * over, we cannot peek 86960 transmission buffer or * reset the transmission buffer. (In fact, we can * reset the entire interface. I don't want to do it.) * * If txb_count is incorrect, leaving it as is will cause * sending of gabages after next interrupt. We have to * avoid it. Hence, we reset the txb_count here. If * txb_free was incorrect, resetting txb_count just loose * some packets. We can live with it. */ sc->txb_count = 0; }#endif#if FE_DEBUG >= 1 /* * First, see if there are buffered packets and an idle * transmitter - should never happen at this point. */ if ( ( sc->txb_count > 0 ) && ( sc->txb_sched == 0 ) ) { log( LOG_ERR, "fe%d: transmitter idle with %d buffered packets\n", sc->sc_unit, sc->txb_count ); fe_xmit( sc ); }#endif#ifdef MULTICAST /* * Stop accepting more transmission packets temporarily, when * a filter change request is delayed. Updating the MARs on * 86960 flushes the transmisstion buffer, so it is delayed * until all buffered transmission packets have been sent * out. */ if ( sc->filter_change ) { /* * Filter change requst is delayed only when the DLC is * working. DLC soon raise an interrupt after finishing * the work. */ goto indicate_active; }#endif for (;;) { /* * Get the next mbuf chain for a packet to send. */ IF_DEQUEUE( &sc->sc_if.if_snd, m ); if ( m == NULL ) { /* No more packets to send. */ goto indicate_inactive; } /* We need to use m->m_pkthdr.len, so require the header */ if ((m->m_flags & M_PKTHDR) == 0) panic("fe_start: mbuf not pkthdr"); /* * See if there is room to put another packet in the buffer. */ if ( sc->txb_free < max (m->m_pkthdr.len, ETHER_MIN_LEN) + FE_DATA_LEN_LEN ) { /* No room. */ IF_PREPEND( &sc->sc_if.if_snd, m ); goto indicate_active; }#if FE_SINGLE_TRANSMISSION if ( sc->txb_count > 0 ) { /* Just one packet per a transmission buffer. */ IF_PREPEND( &sc->sc_if.if_snd, m ); goto indicate_active; }#else if ( sc->txb_count >= FE_QUEUEING_MAX ) { /* Limit of packets per transmit buffer. */ IF_PREPEND( &sc->sc_if.if_snd, m ); goto indicate_active; }#endif /* * Copy the mbuf chain into the transmission buffer. * txb_* variables are updated as necessary. */ fe_write_mbufs( sc, m ); /* Start transmitter if it's idle. */ if ( sc->txb_sched == 0 ) fe_xmit( sc );#if 0 /* Turned of, since our interface is now duplex. */ /* * Tap off here if there is a bpf listener. */#if NBPFILTER > 0 if ( sc->bpf ) bpf_mtap( sc->bpf, m );#endif#endif m_freem( m ); } indicate_inactive: /* * We are using the !OACTIVE flag to indicate to * the outside world that we can accept an * additional packet rather than that the * transmitter is _actually_ active. Indeed, the * transmitter may be active, but if we haven't * filled all the buffers with data then we still * want to accept more. */ sc->sc_if.if_flags &= ~IFF_OACTIVE; return; indicate_active: /* * The transmitter is active, and there are no room for * more outgoing packets in the transmission buffer. */ sc->sc_if.if_flags |= IFF_OACTIVE; return;}/* * Transmission interrupt handler * The control flow of this function looks silly. FIXME. */static voidfe_tint ( struct fe_softc * sc, u_char tstat ){ int left; int col; /* * Handle "excessive collision" interrupt. */ if ( tstat & FE_D0_COLL16 ) { /* * Find how many packets (including this collided one) * are left unsent in transmission buffer. */ left = INB( sc->addr, FE_BMPR10 );#if FE_DEBUG >= 2 log( LOG_ERR, "fe%d: excessive collision (%d/%d)\n", sc->sc_unit, left, sc->txb_sched );#endif#if FE_DEBUG >= 3 fe_dump( LOG_INFO, sc, NULL );#endif /* * Update statistics. */ sc->sc_if.if_collisions += 16; sc->sc_if.if_oerrors++; sc->sc_if.if_opackets += sc->txb_sched - left; /* * Collision statistics has been updated. * Clear the collision flag on 86960 now to avoid confusion. */ OUTB( sc->addr, FE_DLCR0, FE_D0_COLLID ); /* * Restart transmitter, skipping the * collided packet. * * We *must* skip the packet to keep network running * properly. Excessive collision error is an * indication of the network overload. If we * tried sending the same packet after excessive * collision, the network would be filled with * out-of-time packets. Packets belonging * to reliable transport (such as TCP) are resent * by some upper layer. */ OUTB( sc->addr, FE_BMPR11, FE_B11_CTRL_SKIP | FE_B11_MODE1 ); sc->txb_sched = left - 1; } if ( tstat & ( FE_D0_BUSERR | FE_D0_JABBER | FE_D0_CRLOST ) ) {#if FE_DEBUG >= 2 log( LOG_WARNING, "fe%d: transmit error: %s%s%s(%02x)\n", sc->sc_unit, tstat & FE_D0_BUSERR ? "BUS " : "", tstat & FE_D0_JABBER ? "JAB " : "", tstat & FE_D0_CRLOST ? "CRL " : "", tstat );#endif sc->sc_if.if_oerrors++; } /* * Handle "transmission complete" interrupt. */ if ( tstat & FE_D0_TXDONE ) { /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -