if_upd985xx.c

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

C
1,622
字号
        bp->attr =
            ETH_BUF_LAST |
            ETH_BUF_D_L_DATA |
            ETH_BUF_OWN_CPU |
            (ETH_BUF_SIZE & 0);
    }
}

// ------------------------------------------------------------------------
//
//  Function : TxDone          (Called from delivery thread)
//
// This returns Tx's from the Tx Machine to the stack (ie. reports
// completion) - allowing for missed interrupts, and so on.
// ------------------------------------------------------------------------

STATIC void TxDone(struct eth_upd985xx* p_eth_upd985xx)
{
    struct cyg_netdevtab_entry *ndp;
    struct eth_drv_sc *sc;

    ndp = (struct cyg_netdevtab_entry *)(p_eth_upd985xx->ndp);
    sc = (struct eth_drv_sc *)(ndp->device_instance);

    CHECK_NDP_SC_LINK();

    if ( p_eth_upd985xx->tx_busy ) {
        cyg_uint32 ss;

        ss = INL( ETH_TXSR ); // Get tx status
        if ( ss & (ETH_TXSR_CSE |
                   ETH_TXSR_TUDR |
                   ETH_TXSR_TGNT |
                   ETH_TXSR_LCOL |
                   ETH_TXSR_ECOL |
                   ETH_TXSR_TEDFR |
                   ETH_TXSR_TDFR |
                   ETH_TXSR_TBRO |
                   ETH_TXSR_TMUL |
                   ETH_TXSR_TDONE ) ) {
            // Then it finished; somehow...
            cyg_uint32 key = p_eth_upd985xx->tx_keys[ 0 ];
            
            // Turn off the transmitter (before the callback to the stack).
            OUTL( ETH_TXCR, 0 );

#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E8
            // Must take action after certain types of tx failure:
            if ( ss & (ETH_TXSR_TUDR |
                       ETH_TXSR_LCOL |
                       ETH_TXSR_ECOL) ) {
                p_eth_upd985xx->count_bad_tx_completion++;
                CYG_ASSERT ( p_eth_upd985xx->active, "Device not active!" );
                eth_upd985xx_stop( sc );
                eth_upd985xx_start( sc, NULL, 0 );
                key = 0; // Important!  Stop above already fed it back.
            }
#endif

#ifdef DEBUG_TRAFFIC
            os_printf("TxDone %d %x: KEY %x\n",
                      p_eth_upd985xx->index, (int)p_eth_upd985xx, key );
#endif
            // Finished, ready for the next one
            p_eth_upd985xx->tx_keys[ 0 ] = 0;
            p_eth_upd985xx->tx_busy = 0;
            // Then tell the stack we are done:
            if (key) {
                (sc->funs->eth_drv->tx_done)( sc, key,
                       0 == (p_eth_upd985xx->intrs & ETH_ISR_TABR) );
            }
        }
    }
}


// ------------------------------------------------------------------------
//
//  Function : eth_upd985xx_can_send
//
// ------------------------------------------------------------------------

STATIC int 
eth_upd985xx_can_send(struct eth_drv_sc *sc)
{
    struct eth_upd985xx *p_eth_upd985xx;

    p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private;

    // Guard possible external entry points
    if ( ! p_eth_upd985xx->active )
        return 0;

    return ! p_eth_upd985xx->tx_busy;
}

// ------------------------------------------------------------------------
//
//  Function : eth_upd985xx_send
//
// ------------------------------------------------------------------------

STATIC void 
eth_upd985xx_send(struct eth_drv_sc *sc,
            struct eth_drv_sg *sg_list, int sg_len, int total_len,
            unsigned long key)
{
    struct eth_upd985xx *p_eth_upd985xx;
    struct eth_drv_sg *last_sg;
    volatile struct bufdesc *bp;
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E3
    struct eth_drv_sg local_sg[2];
#endif

    p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private;

#ifdef DEBUG_TRAFFIC
    os_printf("Tx %d %x: %d sg's, %d bytes, KEY %x\n",
              p_eth_upd985xx->index, (int)p_eth_upd985xx, sg_len, total_len, key );
#endif

    if ( ! p_eth_upd985xx->active )
        return;                         // device inactive, no return
    
    CYG_ASSERT( ! p_eth_upd985xx->tx_busy, "Can't send when busy!" );

    p_eth_upd985xx->tx_busy++;

    p_eth_upd985xx->tx_keys[0] = key;
    bp = &p_eth_upd985xx->txring[0]; // Current free tx
    CYG_ASSERT( 0 < sg_len, "sg_len underflow" );
    CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow" );

#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E3
    // We must copy any Tx that is more than two SGs into just one buffer.
    if ( sg_len > 2 ) {
        cyg_uint8 *from_p, *to_p;
        to_p = &tx_databuf[0]; // normal cached address
        if ( sizeof( tx_databuf ) < total_len )
            total_len = sizeof( tx_databuf );
        for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) {
            int l;

            from_p = (cyg_uint8 *)(sg_list->buf); // normal cached address
            l = sg_list->len;
           
            if ( l > total_len )
                l = total_len;

            memcpy( to_p, from_p, l ); // All in cached memory
            to_p += l;
            total_len -= l;

            if ( 0 > total_len ) 
                break; // Should exit via sg_last normally
        }

        // Set up SGs describing the single tx buffer
        total_len = to_p - &tx_databuf[0];
        local_sg[0].buf = (CYG_ADDRESS)&tx_databuf[0];
        local_sg[0].len = (CYG_ADDRWORD)total_len;
        local_sg[1].buf = (CYG_ADDRESS)0;
        local_sg[1].len = (CYG_ADDRWORD)0;

        // And make the subsequent code use it.
        sg_len = 1;
        sg_list = &local_sg[0];
    }
#endif

    for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) {
        cyg_uint8 *from_p;
        int l;
            
        from_p = (cyg_uint8 *)(sg_list->buf); // normal cached address
        l = sg_list->len;

        if ( l > total_len )
            l = total_len;

        // Ensure the mbuf contents really is in RAM where DMA can see it.
        // (Must round to cache lines apparantly for 4120)
        HAL_DCACHE_STORE( ((CYG_ADDRESS)from_p) &~(HAL_DCACHE_LINE_SIZE-1),
                          l + HAL_DCACHE_LINE_SIZE );
        
        bp->ptr = VIRT_TO_BUS( from_p ); // uncached real RAM address
        bp->attr &=~(ETH_BUF_LAST | ETH_BUF_SIZE);
        bp->attr |= ETH_BUF_SIZE & l;
        bp->attr |= ETH_BUF_D_L_DATA;

        total_len -= l;
        bp++;

        if ( 0 > total_len ) 
            break; // Should exit via sg_last normally
    }

    CYG_ASSERT( bp > &p_eth_upd985xx->txring[0], "bp underflow" );
    CYG_ASSERT( bp < &p_eth_upd985xx->txring[
        NUM_ELEMENTS(p_eth_upd985xx->tx_bufdesc)
        ], "bp underflow" );

    bp--;
    bp->attr |= ETH_BUF_LAST;

    // Make the rest be null links
    for ( bp++; bp <
              &p_eth_upd985xx->txring[NUM_ELEMENTS(p_eth_upd985xx->tx_bufdesc)];
          bp++ ) {
        bp->attr = ETH_BUF_D_L_LINK;
        bp->ptr = NULL;
    }

    CYG_ASSERT( 0 == total_len, "length mismatch in tx" );
    CYG_ASSERT( last_sg == sg_list, "sg count mismatch in tx" );

    // And start off the tx system

    // Point the hardware at the list of buffers
    OUTL( ETH_TXDPR, (cyg_uint32)p_eth_upd985xx->txring );
    // and start the tx.

    // Fault E4 - use only 8 for DTBS, not the previously recommended 16.
    // Tag: CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E4
    // but no config opt is provided.

    // Fault E7: ETH_TXCR_AFCE must not be used.
    // Tag: CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E7
    // but no config opt is provided.

    OUTL( ETH_TXCR, ETH_TXCR_TXE | ETH_TXCR_DTBS_8 /* | ETH_TXCR_AFCE */ );
}

#ifdef CYGPKG_NET
// ------------------------------------------------------------------------
//
//  Function : eth_upd985xx_configure
//
//  Return : 0 = It worked.
//           non0 = It failed.
// ------------------------------------------------------------------------

STATIC int
eth_upd985xx_configure(struct eth_upd985xx* p_eth_upd985xx, int promisc, int oversized)
{
    int ss;

    // We implement permission of oversize packets by changing LMAX (rather
    // than enabling HUGEN in ETH_MACC1) because we rely on only one
    // reception per rx descriptor.  General oversize packets could eat
    // many rx descriptors and we would become ...confused.

    // Sanity check the numbers we're about to use.
    CYG_ASSERT( sizeof( rx_databuf[0] ) >= MAX_OVERSIZE_PACKET_SIZE,
                "Oversize packet would overflow rx buffer" );
    CYG_ASSERT( sizeof( rx_databuf[0] ) >= MAX_ETHERNET_PACKET_SIZE,
                "Ethernet packet would overflow rx buffer" );
    if ( oversized )
        OUTL( ETH_LMAX, MAX_OVERSIZE_PACKET_SIZE );
    else
        OUTL( ETH_LMAX, MAX_ETHERNET_PACKET_SIZE );

#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2
    ss = promisc ? 1 : 0; // avoid unused var warning
    p_eth_upd985xx->promisc = ss;
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2_E2ONLY
    // Then we must also set the mode in the chip
    ss = INL( ETH_AFR );
    if ( promisc )
        ss |= ETH_AFR_PRO;
    else
        ss &=~ETH_AFR_PRO;
    OUTL( ETH_AFR, ss );
#endif // CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2_E2ONLY
#else
    ss = INL( ETH_AFR );
    if ( promisc )
        ss |= ETH_AFR_PRO;
    else
        ss &=~ETH_AFR_PRO;
    OUTL( ETH_AFR, ss );
#endif
    return 0; // OK
}
#endif

// ------------------------------------------------------------------------
//
//  Function : eth_upd985xx_ioctl
//
// ------------------------------------------------------------------------
STATIC int 
eth_upd985xx_ioctl(struct eth_drv_sc *sc, unsigned long key,
                   void *data, int data_length)
{
    struct eth_upd985xx *p_eth_upd985xx;

    p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private;

#ifdef DEBUG_IOCTL
    db_printf( "eth_upd985xx_ioctl: device eth%d at %x; key is 0x%x, data at %x[%d]\n",
               p_eth_upd985xx->index, p_eth_upd985xx, key, data, data_length );
#endif

    // DO NOT guard possible external entry points - want to be able eg. to
    // set a mac address of a down interface before bringing it up!

    switch ( key ) {

#ifdef ETH_DRV_SET_MAC_ADDRESS
    case ETH_DRV_SET_MAC_ADDRESS:
        if ( 6 != data_length )
            return -2;
        return eth_set_mac_address( p_eth_upd985xx, data );
#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;
        int i;

        // 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, "NEC uPD985xx on-chip ethernet (CANDY)" );
        // CYG_ASSERT( 48 > strlen(p->description), "Description too long" );

        i = eth_upd985xx_status( p_eth_upd985xx );

        if ( !( i & PHY_STATUS_LINK) ) {
            p->operational = 2;         // LINK DOWN
            p->duplex = 1;              // UNKNOWN
            p->speed = 0;
        }
        else {
            p->operational = 3;            // LINK UP
            p->duplex = (i & PHY_STATUS_FDX) ? 3 : 2; // 2 = SIMPLEX, 3 = DUPLEX
            p->speed = ((i & PHY_STATUS_100MBPS) ? 100 : 10) * 1000000;
        }

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

        // Those commented out are not available on this chip.
        p->tx_good              = INL( ETH_TPCT   )   ;
        p->tx_max_collisions    = INL( ETH_TXCL   )   ;
        p->tx_late_collisions   = INL( ETH_TLCL   )   ;
        //p->tx_underrun          = INL(    )   ;
        p->tx_carrier_loss      = INL( ETH_TCSE   )   ;
        p->tx_deferred          = INL( ETH_TDFR   ) +
                                  INL( ETH_TXDF   )    ;
        //p->tx_sqetesterrors     = INL(    )   ;
        p->tx_single_collisions = INL( ETH_TSCL   )   ;
        p->tx_mult_collisions   = INL( ETH_TMCL   )   ;
        p->tx_total_collisions  = INL( ETH_TSCL   ) +
                                  INL( ETH_TMCL   ) +
                                  INL( ETH_TLCL   ) +
                                  INL( ETH_TXCL   )   ;
        p->rx_good              = INL( ETH_RPKT   )   ;
        p->rx_crc_errors        = INL( ETH_RFCS   )   ;
        p->rx_align_errors      = INL( ETH_RALN   )   ;
        p->rx_resource_errors   = p_eth_upd985xx->count_rx_resource;
        //p->rx_overrun_errors    = INL(    )   ;
        //p->rx_collisions        = INL(    )   ;
        p->rx_short_frames      = INL( ETH_RUND   )   ;
        p->rx_too_long_frames   = INL( ETH_ROVR   )   ;
        p->rx_symbol_errors     = INL( ETH_RXUO   )   ;

        p->interrupts           = p_eth_upd985xx->count_interrupts;
        p->rx_count             = INL( ETH_RBYT   )   ;
        p->rx_deliver           = INL( ETH_RPKT   )   ;
        p->rx_resource          = p_eth_upd985xx->count_rx_resource;
        p->rx_restart           = p_eth_upd985xx->count_rx_resource +
                                  p_eth_upd985xx->count_rx_restart;
        p->tx_count             = INL( ETH_TBYT   )   ;
        p->tx_complete          = INL( ETH_TPCT   )   ;
        p->tx_dropped           = INL( ETH_TNCL   )   ;
        
        p->tx_queue_len = 1;
        
        return 0; // OK
    }
#endif

    default:
        break;
    }
    return -1;
}

// ------------------------------------------------------------------------

// EOF if_upd985xx.c

⌨️ 快捷键说明

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