📄 if_fe.c
字号:
* Add in total number of collisions on last * transmission. We also clear "collision occurred" flag * here. * * 86960 has a design flaw on collision count on multiple * packet transmission. When we send two or more packets * with one start command (that's what we do when the * transmission queue is clauded), 86960 informs us number * of collisions occured on the last packet on the * transmission only. Number of collisions on previous * packets are lost. I have told that the fact is clearly * stated in the Fujitsu document. * * I considered not to mind it seriously. Collision * count is not so important, anyway. Any comments? FIXME. */ if ( INB( sc->addr, FE_DLCR0 ) & FE_D0_COLLID ) { /* Clear collision flag. */ OUTB( sc->addr, FE_DLCR0, FE_D0_COLLID ); /* Extract collision count from 86960. */ col = INB( sc->addr, FE_DLCR4 ); col = ( col & FE_D4_COL ) >> FE_D4_COL_SHIFT; if ( col == 0 ) { /* * Status register indicates collisions, * while the collision count is zero. * This can happen after multiple packet * transmission, indicating that one or more * previous packet(s) had been collided. * * Since the accurate number of collisions * has been lost, we just guess it as 1; * Am I too optimistic? FIXME. */ col = 1; } sc->sc_if.if_collisions += col;#if FE_DEBUG >= 3 log( LOG_INFO, "fe%d: %d collision(s) (%d)\n", sc->sc_unit, col, sc->txb_sched );#endif } /* * Update total number of successfully * transmitted packets. */ sc->sc_if.if_opackets += sc->txb_sched; sc->txb_sched = 0; /* * The transmitter is no more active. * Reset output active flag and watchdog timer. */ sc->sc_if.if_flags &= ~IFF_OACTIVE; sc->sc_if.if_timer = 0; /* * If more data is ready to transmit in the buffer, start * transmitting them. Otherwise keep transmitter idle, * even if more data is queued. This gives receive * process a slight priority. */ if ( sc->txb_count > 0 ) fe_xmit( sc ); }}/* * Ethernet interface receiver interrupt. */static voidfe_rint ( struct fe_softc * sc, u_char rstat ){ u_short len; u_short status; int i; /* * Update statistics if this interrupt is caused by an error. */ if ( (rstat & ( FE_D1_OVRFLO | FE_D1_BUSERR ) ) || ( !(sc->proto_dlcr5 & FE_D5_BADPKT) && (rstat & ( FE_D1_CRCERR | FE_D1_ALGERR ) ) ) ) {#if FE_DEBUG >= 2 log( LOG_WARNING, "fe%d: receive error: %s%s%s%s(%02x)\n", sc->sc_unit, rstat & FE_D1_OVRFLO ? "OVR " : "", rstat & FE_D1_BUSERR ? "BUS " : "", rstat & FE_D1_CRCERR ? "CRC " : "", rstat & FE_D1_ALGERR ? "ALG " : "", rstat );#endif sc->sc_if.if_ierrors++; } /* * MB86960 has a flag indicating "receive queue empty." * We just loop cheking the flag to pull out all received * packets. * * We limit the number of iterrations to avoid inifnit-loop. * It can be caused by a very slow CPU (some broken * peripheral may insert incredible number of wait cycles) * or, worse, by a broken MB86960 chip. */ for ( i = 0; i < FE_MAX_RECV_COUNT; i++ ) { /* Stop the iterration if 86960 indicates no packets. */ if ( INB( sc->addr, FE_DLCR5 ) & FE_D5_BUFEMP ) break; /* * Extract A receive status byte. * As our 86960 is in 16 bit bus access mode, we have to * use inw() to get the status byte. The significant * value is returned in lower 8 bits. */ status = INW( sc->addr, FE_BMPR8 ); /* * Extract the packet length. * It is a sum of a header (14 bytes) and a payload. * CRC has been stripped off by the 86960. */ len = INW( sc->addr, FE_BMPR8 );#if FE_DEBUG >= 1 log( LOG_INFO, "fe%d: receive status = %04x len = %04x\n", sc->sc_unit, status, len );#endif status &= 0xff; /* * If there was an error, update statistics and drop * the packet, unless the interface is in promiscuous * mode. */ if ( ( status & 0xF0 ) != 0x20 ) {#if FE_DEBUG >= 2 if ( status & (FE_D1_CRCERR | FE_D1_ALGERR | FE_D1_SRTPKT)) { log( LOG_WARNING, "fe%d: receive packet error: %s%s%s(%02x), %u bytes\n", sc->sc_unit, status & FE_D1_CRCERR ? "CRC " : "", status & FE_D1_ALGERR ? "ALG " : "", status & FE_D1_SRTPKT ? "LEN " : "", status, len ); }#endif if ( !( sc->sc_if.if_flags & IFF_PROMISC ) ) { sc->sc_if.if_ierrors++; fe_droppacket(sc, len); continue; } } /* * MB86965 checks the packet length and drop big packet * before passing it to us. There are no chance we can * get [crufty] packets. Hence, if the length exceeds * the specified limit, it means some serious failure, * such as out-of-sync on receive buffer management. * * Is this statement true? FIXME. */ if ( len > ETHER_MAX_LEN || len < ETHER_HDR_SIZE ) {#if FE_DEBUG >= 2 log( LOG_WARNING, "fe%d: received a %s packet? (%u bytes)\n", sc->sc_unit, len < ETHER_HDR_SIZE ? "partial" : "big", len );#endif sc->sc_if.if_ierrors++; fe_droppacket( sc, len ); continue; } /* * Check for a short (RUNT) packet. We *do* check * but do nothing other than print a message. * Short packets are illegal, but does nothing bad * if it carries data for upper layer. */#if FE_DEBUG >= 2 if ( len < ETHER_MIN_LEN ) { log( LOG_WARNING, "fe%d: received a short packet? (%u bytes)\n", sc->sc_unit, len ); }#endif /* * Go get a packet. */ if ( fe_get_packet( sc, len ) < 0 ) { /* Skip a packet, updating statistics. */#if FE_DEBUG >= 2 log( LOG_WARNING, "fe%d: not enough mbuf;" " a packet (%u bytes) dropped\n", sc->sc_unit, len );#endif sc->sc_if.if_ierrors++; /* * We stop receiving packets, even if there are * more in the buffer. We hope we can get more * mbuf next time. */ return; } /* Successfully received a packet. Update stat. */ sc->sc_if.if_ipackets++; }}/* * Ethernet interface interrupt processor */voidfeintr ( int unit ){ struct fe_softc *sc = &fe_softc[unit]; u_char tstat, rstat; u_char dlcr0, dlcr1, dlcr5; /* * Loop until there are no more new interrupt conditions. */ for (;;) { /* * Get interrupt conditions, masking unneeded flags. */ dlcr0 = INB( sc->addr, FE_DLCR0 ); dlcr1 = INB( sc->addr, FE_DLCR1 ); tstat = dlcr0 & FE_TMASK; rstat = dlcr1 & FE_RMASK;#if FE_DEBUG >= 1 && 0 dlcr5 = INB( sc->addr, FE_DLCR5 ); if (rstat == 0 && (dlcr5 & FE_D5_BUFEMP) == 0) { log (LOG_DEBUG, "Kicking receiver\n"); rstat = FE_D1_PKTRDY; }#endif if ( tstat == 0 && rstat == 0 ) break;#if FE_DEBUG >= 4 fe_dump( LOG_INFO, sc, "intr()" );#endif /* * Reset the conditions we are acknowledging. */ OUTB( sc->addr, FE_DLCR0, dlcr0 ); OUTB( sc->addr, FE_DLCR1, dlcr1 ); /* * Handle transmitter interrupts. Handle these first because * the receiver will reset the board under some conditions. */ if ( tstat ) { fe_tint( sc, dlcr0 ); } /* * Handle receiver interrupts */ if ( rstat ) { fe_rint( sc, dlcr1 ); }#ifdef MULTICAST /* * Update the multicast address filter if it is * needed and possible. We do it now, because * we can make sure the transmission buffer is empty, * and there is a good chance that the receive queue * is empty. It will minimize the possibility of * packet lossage. */ if ( sc->filter_change && sc->txb_count == 0 && sc->txb_sched == 0 ) { fe_loadmar(sc); sc->sc_if.if_flags &= ~IFF_OACTIVE; }#endif /* * If it looks like the transmitter can take more data, * attempt to start output on the interface. This is done * after handling the receiver interrupt to give the * receive operation priority. * * BTW, I'm not sure in what case the OACTIVE is on at * this point. Is the following test redundant? * * No. This routine polls for both transmitter and * receiver interrupts. 86960 can raise a receiver * interrupt when the transmission buffer is full. */ if ( ( sc->sc_if.if_flags & IFF_OACTIVE ) == 0 ) { fe_start( &sc->sc_if ); } }}/* * Process an ioctl request. This code needs some work - it looks * pretty ugly. */intfe_ioctl ( struct ifnet *ifp, int command, caddr_t data ){ struct fe_softc *sc = IFNET2SOFTC( ifp ); int s, error = 0;#if FE_DEBUG >= 3 if (command != SIOCPOLL) log( LOG_INFO, "fe%d: ioctl(%x)\n", sc->sc_unit, command );#endif s = splimp(); switch (command) { case SIOCPOLL: if (ifp->if_flags & IFF_RUNNING) feintr (ifp->if_unit); break; case SIOCSIFADDR: { struct ifaddr * ifa = ( struct ifaddr * )data; sc->sc_if.if_flags |= IFF_UP; switch (ifa->ifa_addr->sa_family) {#ifdef INET case AF_INET: fe_init( sc->sc_unit ); /* before arpwhohas */ arp_ifinit( &sc->arpcom, ifa ); break;#endif#ifdef NS /* * XXX - This code is probably wrong */ case AF_NS: { register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); if (ns_nullhost(*ina)) ina->x_host = *(union ns_host *) (sc->sc_enaddr); else { bcopy((caddr_t) ina->x_host.c_host, (caddr_t) sc->sc_enaddr, sizeof(sc->sc_enaddr)); } /* * Set new address */ fe_init(sc->sc_unit); break; }#endif default: fe_init( sc->sc_unit ); break; } break; }#ifdef SIOCGIFADDR case SIOCGIFADDR: { struct ifreq * ifr = ( struct ifreq * )data; struct sockaddr * sa = ( struct sockaddr * )&ifr->ifr_data; bcopy((caddr_t)sc->sc_enaddr, (caddr_t)sa->sa_data, ETHER_ADDR_LEN); break; }#endif#ifdef SIOCGIFPHYSADDR case SIOCGIFPHYSADDR: { struct ifreq * ifr = ( struct ifreq * )data; bcopy((caddr_t)sc->sc_enaddr, (caddr_t)&ifr->ifr_data, ETHER_ADDR_LEN); break; }#endif#ifdef SIOCSIFPHYSADDR case SIOCSIFPHYSADDR: { /* * Set the physical (Ehternet) address of the interface. * When and by whom is this command used? FIXME. */ struct ifreq * ifr = ( struct ifreq * )data; bcopy((caddr_t)&ifr->ifr_data, (caddr_t)sc->sc_enaddr, ETHER_ADDR_LEN);#ifdef PROM if_newaddr(ifp, IFT_ETHER, (caddr_t)((struct arpcom *)ifp)->ac_enaddr);#else fe_setlinkaddr( sc );#endif break; }#endif#ifdef SIOCSIFFLAGS case SIOCSIFFLAGS: { /* * Switch interface state between "running" and * "stopped", reflecting the UP flag. */ if ( sc->sc_if.if_flags & IFF_UP ) { if ( ( sc->sc_if.if_flags & IFF_RUNNING ) == 0 ) { fe_init( sc->sc_unit ); } } else { if ( ( sc->sc_if.if_flags & IFF_RUNNING ) != 0 ) { fe_stop( sc->sc_unit ); } } /* * Promiscuous and/or multicast flags may have changed, * so reprogram the multicast filter and/or receive mode. */ fe_setmode( sc );#if FE_DEBUG >= 1 /* "ifconfig fe0 debug" to print register dump. */ if ( sc->sc_if.if_flags & IFF_DEBUG ) { fe_dump( LOG_DEBUG, sc, "SIOCSIFFLAGS(DEBUG)" ); }#endif break; }#endif#ifdef SIOCADDMULTI case SIOCADDMULTI: case SIOCDELMULTI: { /* * Update out multicast list. */ struct ifreq * ifr = ( struct ifreq * )data; error = ( command == SIOCADDMULTI ) ? ether_addmulti( ifr, &sc->arpcom ) : ether_delmulti( ifr, &sc->arpcom ); if ( error == ENETRESET ) { /* * Multicast list has changed; set the hardware filter * accordingly. */ fe_setmode( sc ); error = 0; } break; }#endif#ifdef SIOCSIFMTU case SIOC
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -