if_lan91cxx.c

来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,486 行 · 第 1/4 页

C
1,486
字号
    db_printf("LAN91CXX - status: %04x\n", val);
#endif

#if 0 < CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA
    // Use statically configured ESA from the private data
#if DEBUG & 9
    db_printf("LAN91CXX - static ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
                cpd->enaddr[0],
                cpd->enaddr[1],
                cpd->enaddr[2],
                cpd->enaddr[3],
                cpd->enaddr[4],
                cpd->enaddr[5] );
#endif // DEBUG
    // Set up hardware address
    for (i = 0;  i < sizeof(cpd->enaddr);  i += 2)
        put_reg(sc, LAN91CXX_IA01+i/2,
                cpd->enaddr[i] | (cpd->enaddr[i+1] << 8));
#else // not CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA
    // Find ESA - check possible sources in sequence and stop when
    // one provides the ESA:
    //   RedBoot option (via provide_esa)
    //   Compile-time configuration
    //   EEPROM

    if (NULL != cpd->provide_esa) {
        esa_configured = cpd->provide_esa(cpd);
# if DEBUG & 8
        if (esa_configured)
            db_printf("Got ESA from RedBoot option\n");
# endif
    }
    if (!esa_configured && cpd->hardwired_esa) {
        // ESA is already set in cpd->esa[]
        esa_configured = true;
# if DEBUG & 8
        db_printf("Got ESA from cpd\n");
# endif
    }
    if (esa_configured) {
        // Set up hardware address
        for (i = 0;  i < sizeof(cpd->enaddr);  i += 2)
            put_reg(sc, LAN91CXX_IA01+i/2,
                    cpd->enaddr[i] | (cpd->enaddr[i+1] << 8));
    } else {
        // Use the address from the serial EEPROM
        // Read out hardware address
        for (i = 0;  i < sizeof(cpd->enaddr);  i += 2) {
            unsigned short z = get_reg(sc, LAN91CXX_IA01+i/2 );
            cpd->enaddr[i] =   (unsigned char)(0xff & z);
            cpd->enaddr[i+1] = (unsigned char)(0xff & (z >> 8));
        }
        esa_configured = true;
# if DEBUG & 8
        db_printf("Got ESA from eeprom\n");
# endif
    }
#if DEBUG & 9
    db_printf("LAN91CXX - ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
                cpd->enaddr[0],
                cpd->enaddr[1],
                cpd->enaddr[2],
                cpd->enaddr[3],
                cpd->enaddr[4],
                cpd->enaddr[5] );
#endif // DEBUG
#endif // !CYGINT_DEVS_ETH_SMSC_LAN91CXX_STATIC_ESA

    // Initialize upper level driver
    (sc->funs->eth_drv->init)(sc, cpd->enaddr);
    return true;
}

static void
lan91cxx_stop(struct eth_drv_sc *sc)
{
    struct lan91cxx_priv_data *cpd =
        (struct lan91cxx_priv_data *)sc->driver_private;
    DEBUG_FUNCTION();

    CYG_ASSERT( cpd->within_send < 10, "stop: Excess send recursions" );
    cpd->within_send++;
    // Complete any outstanding activity:
    if ( cpd->txbusy ) {
        cpd->txbusy = 0;
#if DEBUG & 9
        db_printf("LAN91CXX - Stopping, cleaning up pending TX\n" );
#endif
        (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 0);
    }
    // Reset chip
    put_reg(sc, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST);
    put_reg(sc, LAN91CXX_RCR, 0);
    cpd->txbusy = cpd->within_send = 0;
}

//
// This function is called to "start up" the interface.  It may be called
// multiple times, even when the hardware is already running.  It will be
// called whenever something "hardware oriented" changes and should leave
// the hardware ready to send/receive packets.
//
static void
lan91cxx_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
    cyg_uint16 intr;
#ifdef LAN91CXX_IS_LAN91C111
    int delay;
#endif
#ifdef CYGPKG_NET
    struct ifnet *ifp = &sc->sc_arpcom.ac_if;
#endif
    DEBUG_FUNCTION();

#ifdef LAN91CXX_IS_LAN91C111
    // 91C111 Errata. Internal PHY comes up disabled. Must enable here.
    lan91cxx_write_phy(sc, 0, LAN91CXX_PHY_CTRL, LAN91CXX_PHY_CTRL_RST);
    CYGACC_CALL_IF_DELAY_US(500000);
    lan91cxx_write_phy(sc, 0, LAN91CXX_PHY_CTRL, LAN91CXX_PHY_CTRL_ANEG_EN |
		                                 LAN91CXX_PHY_CTRL_SPEED);

#ifdef LAN91CXX_FORCE_10MHZ
    lan91cxx_write_phy( sc, 0, LAN91CXX_PHY_AUTO_AD, 0x0061);
#endif

    // Start auto-negotiation
    put_reg(sc, LAN91CXX_RPCR,
	    LAN91CXX_RPCR_LEDA_RX | LAN91CXX_RPCR_LEDB_LINK | LAN91CXX_RPCR_ANEG);

    // wait for auto-negotiation to finish.
    // give it ~5 seconds before giving up (no cable?)
    delay = 50;
    while (!(lan91cxx_read_phy(sc, 0, LAN91CXX_PHY_STAT) & 0x20)) {
	if (--delay <= 0)
	    break;
	CYGACC_CALL_IF_DELAY_US(100000);
    }
#if DEBUG & 1
    if (delay <= 0)
	    diag_printf("auto-negotiation failed.\n");
#endif
#endif

    put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_reset_mmu);

    put_reg(sc, LAN91CXX_INTERRUPT, 0);   // disable interrupts
    intr = get_reg(sc, LAN91CXX_INTERRUPT);
    put_reg(sc, LAN91CXX_INTERRUPT, intr &      // ack old interrupts
            (LAN91CXX_INTERRUPT_TX_INT | LAN91CXX_INTERRUPT_TX_EMPTY_INT | 
            LAN91CXX_INTERRUPT_RX_OVRN_INT | LAN91CXX_INTERRUPT_ERCV_INT));
    put_reg(sc, LAN91CXX_RCR, 
#ifdef RCR_HAS_ABORT_ENB // 91C96 does not - page 46.
            LAN91CXX_RCR_ABORT_ENB |
#endif
            LAN91CXX_RCR_STRIP_CRC |
            LAN91CXX_RCR_RXEN | LAN91CXX_RCR_ALMUL);
    put_reg(sc, LAN91CXX_TCR, LAN91CXX_TCR_TXENA | LAN91CXX_TCR_PAD_EN);
    put_reg(sc, LAN91CXX_CONTROL, 0);
    put_reg(sc, LAN91CXX_INTERRUPT,       // enable interrupts
            LAN91CXX_INTERRUPT_RCV_INT_M);

#ifdef CYGPKG_NET
    if (( 0
#ifdef ETH_DRV_FLAGS_PROMISC_MODE
         != (flags & ETH_DRV_FLAGS_PROMISC_MODE)
#endif
        ) || (ifp->if_flags & IFF_PROMISC)
        ) {
        // Then we select promiscuous mode.
        unsigned short rcr;
        rcr = get_reg(sc, LAN91CXX_RCR );
        rcr |= LAN91CXX_RCR_PRMS;
        put_reg(sc, LAN91CXX_RCR, rcr );
    }
#endif
}

//
// This routine is called to perform special "control" opertions
//
static int
lan91cxx_control(struct eth_drv_sc *sc, unsigned long key,
               void *data, int data_length)
{
    unsigned char *esa = (unsigned char *)data;
    int i;
    unsigned short reg;
    struct lan91cxx_priv_data *cpd =
        (struct lan91cxx_priv_data *)sc->driver_private;

    DEBUG_FUNCTION();

    switch (key) {
    case ETH_DRV_SET_MAC_ADDRESS:
#if 9 & DEBUG
        db_printf("LAN91CXX - set ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
                esa[0],
                esa[1],
                esa[2],
                esa[3],
                esa[4],
                esa[5] );
#ifndef CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM
        db_printf("*** PERMANENT EEPROM WRITE NOT ENABLED ***\n");
#endif
#endif // DEBUG

#ifdef CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM
        // Only now can we command the chip to perform EEPROM writes:

        // select arbitrary writing to the EEPROM
        reg = get_reg(sc, LAN91CXX_CONTROL);
        reg |= LAN91CXX_CONTROL_EEPROM_SELECT;
        put_reg(sc, LAN91CXX_CONTROL, reg );

        for (i = 0;  i < sizeof(cpd->enaddr);  i += 2) {
            int j;
            // Set the address register
            put_reg(sc, LAN91CXX_POINTER, LAN91CXX_ESA_EEPROM_OFFSET + i/2);
            // Poke the data
            put_reg(sc, LAN91CXX_GENERAL, esa[i] | (esa[i+1] << 8));
            // Command the store
            reg = get_reg(sc, LAN91CXX_CONTROL);
            reg |= LAN91CXX_CONTROL_STORE;
            put_reg(sc, LAN91CXX_CONTROL, reg );
            // and poll for completion
            for ( j = 1024 * 1024; 0 < j ; j-- ) {
                reg = get_reg(sc, LAN91CXX_CONTROL);
                if ( 0 == (reg & LAN91CXX_CONTROL_EEPROM_BUSY) )
                    break;
            }
            CYG_ASSERT( 0 < j, "EEPROM write timout!" );
        }

        reg = get_reg(sc, LAN91CXX_CONTROL);
        CYG_ASSERT( 0 == (reg & LAN91CXX_CONTROL_EEPROM_BUSY),
                    "EEPROM still busy!" );
        // Clear the EEPROM selection bit
        reg &=~LAN91CXX_CONTROL_EEPROM_SELECT;
        put_reg(sc, LAN91CXX_CONTROL, reg );
        // and check it "took"
        reg = get_reg(sc, LAN91CXX_CONTROL);
        CYG_ASSERT( 0 == (reg & LAN91CXX_CONTROL_EEPROM_SELECT),
                    "EEPROM still selected!" );
        // and command a complete reload
        reg |= LAN91CXX_CONTROL_RELOAD;
        put_reg(sc, LAN91CXX_CONTROL, reg );
        for ( i = 1024 * 1024; 0 < i ; i-- ) {
            reg = get_reg(sc, LAN91CXX_CONTROL);
            if ( 0 == (reg & LAN91CXX_CONTROL_EEPROM_BUSY) )
                break;
        }
        CYG_ASSERT( 0 < i, "EEPROM reload timout!" );
        // Now extract the MAC address that is in the chip, and tell the
        // system about it.
        for (i = 0;  i < sizeof(cpd->enaddr);  i += 2) {
            unsigned short z = get_reg(sc, LAN91CXX_IA01+i/2 );
            cpd->enaddr[i] =   (unsigned char)(0xff & z);
            cpd->enaddr[i+1] = (unsigned char)(0xff & (z >> 8));
        }
#if DEBUG & 9
        db_printf("LAN91CXX - eeprom new ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
                    cpd->enaddr[0],
                    cpd->enaddr[1],
                    cpd->enaddr[2],
                    cpd->enaddr[3],
                    cpd->enaddr[4],
                    cpd->enaddr[5] );
#endif // DEBUG
        for (i = 0;  i < sizeof(cpd->enaddr);  i++ ) {
            CYG_ASSERT( esa[i] == cpd->enaddr[i], "ESA not written correctly" );
            if ( esa[i] != cpd->enaddr[i] )
                return 1; // the operation failed.
        }
#else // not CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM
        // Whatever, we can write the MAC address into the interface info,
        // and the chip registers no problem.
        for ( i = 0; i < sizeof(cpd->enaddr);  i++ )
            cpd->enaddr[i] = esa[i];
        for (i = 0;  i < sizeof(cpd->enaddr);  i += 2) {
            reg = cpd->enaddr[i] | (cpd->enaddr[i+1] << 8);
            put_reg(sc, LAN91CXX_IA01+i/2, reg );
        }
#endif // !CYGSEM_DEVS_ETH_SMSC_LAN91CXX_WRITE_EEPROM
        return 0;

#ifdef ETH_DRV_GET_MAC_ADDRESS
    case ETH_DRV_GET_MAC_ADDRESS:
        // Extract the MAC address that is in the chip, and tell the
        // system about it.
        for (i = 0;  i < sizeof(cpd->enaddr);  i += 2) {
            unsigned short z = get_reg(sc, LAN91CXX_IA01+i/2 );
            esa[i] =   (unsigned char)(0xff & z);
            esa[i+1] = (unsigned char)(0xff & (z >> 8));
        }
        return 0;
#endif

#ifdef ETH_DRV_GET_IF_STATS_UD
    case ETH_DRV_GET_IF_STATS_UD: // UD == UPDATE
#endif
        // drop through
#ifdef ETH_DRV_GET_IF_STATS
    case ETH_DRV_GET_IF_STATS:
#endif
#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD)
    {
        struct ether_drv_stats *p = (struct ether_drv_stats *)data;
        // Chipset entry is no longer supported; RFC1573.
        for ( i = 0; i < SNMP_CHIPSET_LEN; i++ )
            p->snmp_chipset[i] = 0;

        // This perhaps should be a config opt, so you can make up your own
        // description, or supply it from the instantiation.
        strcpy( p->description, "SMSC LAN91Cxx" );
        // CYG_ASSERT( 48 > strlen(p->description), "Description too long" );

        reg = get_reg(sc, LAN91CXX_EPH_STATUS);
        if ((reg & LAN91CXX_STATUS_LINK_OK) == 0) {
            p->operational = 2;         // LINK DOWN
            p->duplex = 1;              // UNKNOWN
            p->speed = 0;
        }
        else {
            p->operational = 3;         // LINK UP
            p->duplex = 2;              // 2 = SIMPLEX, 3 = DUPLEX
            p->speed = 10 * 1000000;    // it's only a 10Mbit device
        }

#ifdef KEEP_STATISTICS
        {
            struct smsc_lan91cxx_stats *ps = &(cpd->stats);

            // Admit to it...
            p->supports_dot3        = true;

            p->tx_good              = ps->tx_good             ;
            p->tx_max_collisions    = ps->tx_max_collisions   ;
            p->tx_late_collisions   = ps->tx_late_collisions  ;
            p->tx_underrun          = ps->tx_underrun         ;
            p->tx_carrier_loss      = ps->tx_carrier_loss     ;
            p->tx_deferred          = ps->tx_deferred         ;
            p->tx_sqetesterrors     = ps->tx_sqetesterrors    ;
            p->tx_single_collisions = ps->tx_single_collisions;
            p->tx_mult_collisions   = ps->tx_mult_collisions  ;
            p->tx_total_collisions  = ps->tx_total_collisions ;
            p->rx_good              = ps->rx_good             ;
            p->rx_crc_errors        = ps->rx_crc_errors       ;
            p->rx_align_errors      = ps->rx_align_errors     ;
            p->rx_resource_errors   = ps->rx_resource_errors  ;
            p->rx_overrun_errors    = ps->rx_overrun_errors   ;
            p->rx_collisions        = ps->rx_collisions       ;
            p->rx_short_frames      = ps->rx_short_frames     ;
            p->rx_too_long_frames   = ps->rx_too_long_frames  ;
            p->rx_symbol_errors     = ps->rx_symbol_errors    ;
        
            p->interrupts           = ps->interrupts          ;
            p->rx_count             = ps->rx_count            ;
            p->rx_deliver           = ps->rx_deliver          ;
            p->rx_resource          = ps->rx_resource         ;
            p->rx_restart           = ps->rx_restart          ;
            p->tx_count             = ps->tx_count            ;
            p->tx_complete          = ps->tx_complete         ;
            p->tx_dropped           = ps->tx_dropped          ;
        }
#endif // KEEP_STATISTICS

        p->tx_queue_len = 1;

        return 0; // OK
    }
#endif

⌨️ 快捷键说明

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