if_upd985xx.c

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

C
1,622
字号
#ifdef DEBUG
    os_printf("eth_upd985xx_stop %d flg %x\n", p_eth_upd985xx->index, *(int *)p_eth_upd985xx );
#endif

    p_eth_upd985xx->active = 0;         // stop people tormenting it

    if ( p_eth_upd985xx->tx_busy ) {
        // Then it is finshed now, by force:
        cyg_uint32 key = p_eth_upd985xx->tx_keys[ 0 ];
        // Turn off the transmitter (before the callback to the stack).
        OUTL( ETH_TXCR, 0 );
#ifdef DEBUG_TRAFFIC
        os_printf("Stop: tidying up TX, KEY %x\n", key );
#endif
        // Leave tx_busy true so no recursion can occur here.
        // Then tell the stack we are done:
        if ( key ) {
            (sc->funs->eth_drv->tx_done)( sc, key, 0 );
        }
    }
    p_eth_upd985xx->tx_keys[ 0 ] = 0;
    p_eth_upd985xx->tx_busy = p_eth_upd985xx->active = 0;

    eth_upd985xx_reset(p_eth_upd985xx);

    ResetTxRing( p_eth_upd985xx );
}


// ------------------------------------------------------------------------
//
//  Function : InitRxRing
//
// ------------------------------------------------------------------------
STATIC void InitRxRing(struct eth_upd985xx* p_eth_upd985xx)
{
    int i;
    struct bufdesc *bp;

    // first just blat the various flags and addresses: the first N
    // bufdescs point to data buffers, the last one is NULL.
    bp = (struct bufdesc *)VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[0] );
    // Record the initial active buffer:
    p_eth_upd985xx->rxring_active = bp;
    p_eth_upd985xx->rxring_active_index = 0;
    for ( i = 0; i < NUM_RXBUFS - 1; i++, bp++ ) {
        bp->ptr = VIRT_TO_BUS( &rx_databuf[i][0] );
        bp->attr = ( ETH_BUF_D_L_DATA | ETH_BUF_OWN_CPU
                     | (ETH_BUF_SIZE & sizeof( rx_databuf[0] )) );
    }
    CYG_ASSERT( i == NUM_RXBUFS-1, "Penultimate rx buffer index mismatch" );
    CYG_ASSERT( (cyg_uint8 *)bp == 
                VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[NUM_RXBUFS-1] ),
                "Penultimate rx buffer address mismatch" );

    // NULL out the penultimate one
    bp->ptr = NULL;
    bp->attr = 0;
    // And record it as next one to use
    p_eth_upd985xx->rxring_next = bp;
    p_eth_upd985xx->rxring_next_index = NUM_RXBUFS-1;

    // Step on to the extra entry at the end which makes a ring:
    bp++;
    CYG_ASSERT( (cyg_uint8 *)bp == 
                VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[NUM_RXBUFS] ),
                "Ultimate rx buffer address mismatch" );

    // Link the Ultimate back to the start
    bp->ptr = (cyg_uint8 *)p_eth_upd985xx->rxring_active; // Zeroth entry
    bp->attr = ETH_BUF_D_L_LINK;

    // All done.
}


// ------------------------------------------------------------------------
//
//  Function : NextRxRing
//
// ------------------------------------------------------------------------

STATIC void NextRxRing(struct eth_upd985xx* p_eth_upd985xx )
{
    volatile struct bufdesc *next, *dead;
    int iactive;
    int inext;

    iactive = p_eth_upd985xx->rxring_active_index;
    inext = p_eth_upd985xx->rxring_next_index;

    // Preconditions:
    CYG_ASSERT( 0 <=   inext && inext   < NUM_RXBUFS, "Bad inext" );
    CYG_ASSERT( 0 <= iactive && iactive < NUM_RXBUFS, "Bad iactive" );
    CYG_ASSERT( VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ inext ] )
                == (cyg_uint8 *)p_eth_upd985xx->rxring_next, "Next rx_bufdesc bad" );
    CYG_ASSERT( VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ iactive ] )
                == (cyg_uint8 *)p_eth_upd985xx->rxring_active, "Active rx_bufdesc bad" );
    CYG_ASSERT( ETH_BUF_D_L_LINK == p_eth_upd985xx->rxring_next->attr, "Next not a link" );
    CYG_ASSERT( ETH_BUF_D_L_DATA & p_eth_upd985xx->rxring_active->attr, "Active not data" );
    CYG_ASSERT( NULL == p_eth_upd985xx->rxring_next->ptr, "Next not NULL" );
    CYG_ASSERT( VIRT_TO_BUS( &rx_databuf[iactive][0] ) ==
                p_eth_upd985xx->rxring_active->ptr, "Active bad data pointer" );
    CYG_ASSERT( (iactive - 1 == inext) || (0 == iactive && NUM_RXBUFS - 1 == inext),
                "Chasing pointers mismatch" );

    // Select the new bufdesc to be active - ie. next to scan for reception:
    if ( ++iactive >= NUM_RXBUFS )
        iactive = 0;
    dead = p_eth_upd985xx->rxring_active; // the one that just died
    // Step ahead the new active buffer:
    p_eth_upd985xx->rxring_active = (volatile struct bufdesc *)
        VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ iactive ] );
    p_eth_upd985xx->rxring_active_index = iactive;

    // Blow away the currently active entry; we have dealt with it already
    // and it is needed for an end stop to the ring:
    dead->ptr = NULL;
    dead->attr = 0;
   
    // Select the next bufdesc to enliven
    next = p_eth_upd985xx->rxring_next;
    next->ptr = VIRT_TO_BUS( &rx_databuf[inext][0] );
    next->attr = ( ETH_BUF_D_L_DATA | ETH_BUF_OWN_CPU
                   | (ETH_BUF_SIZE & sizeof( rx_databuf[0] )) );

    // And update the external info to reflect this:
    if ( ++inext >= NUM_RXBUFS )
        inext = 0;
    next = (volatile struct bufdesc *)
        VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ inext ] );

    p_eth_upd985xx->rxring_next = next;
    p_eth_upd985xx->rxring_next_index = inext;

    // Postconditions:
    CYG_ASSERT( 0 <=   inext && inext   < NUM_RXBUFS, "Bad inext" );
    CYG_ASSERT( 0 <= iactive && iactive < NUM_RXBUFS, "Bad iactive" );
    CYG_ASSERT( VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ inext ] )
                == (cyg_uint8 *)p_eth_upd985xx->rxring_next, "Next rx_bufdesc bad" );
    CYG_ASSERT( VIRT_TO_BUS( &p_eth_upd985xx->rx_bufdesc[ iactive ] )
                == (cyg_uint8 *)p_eth_upd985xx->rxring_active, "Active rx_bufdesc bad" );
    CYG_ASSERT( ETH_BUF_D_L_LINK == p_eth_upd985xx->rxring_next->attr, "Next not a link" );
    CYG_ASSERT( ETH_BUF_D_L_DATA & p_eth_upd985xx->rxring_active->attr, "Active not data" );
    CYG_ASSERT( NULL == p_eth_upd985xx->rxring_next->ptr, "Next not NULL" );
    CYG_ASSERT( VIRT_TO_BUS( &rx_databuf[iactive][0] ) ==
                p_eth_upd985xx->rxring_active->ptr, "Active bad data pointer" );
    CYG_ASSERT( (iactive - 1 == inext) || (0 == iactive && NUM_RXBUFS - 1 == inext),
                "Chasing pointers mismatch" );
}

// ------------------------------------------------------------------------
//
//  Function : PacketRxReady        (Called from delivery thread)
//
// ------------------------------------------------------------------------
STATIC void PacketRxReady(struct eth_upd985xx* p_eth_upd985xx)
{
    struct cyg_netdevtab_entry *ndp;
    struct eth_drv_sc *sc;
    cyg_uint32 ss, length;
    cyg_bool reset_required = 0;

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

    CHECK_NDP_SC_LINK();

#ifdef DEBUG_TRAFFIC
    ss = INL( ETH_RXSR );
    os_printf("PacketRxReady: RXSR %x\n", ss );
#endif

    if ( ETH_ISR_RBDRU & p_eth_upd985xx->intrs )
        reset_required = 1; // Out of buffers
    if ( ! ETH_ISR_RCVDN & p_eth_upd985xx->intrs )
        reset_required = 1; // or if no reception completed, reset anyway

    // For all ready rx blocks...
    do {
        volatile struct bufdesc *bp;

        bp = p_eth_upd985xx->rxring_active; // Current rx candidate

        ss = bp->attr;
#ifdef DEBUG_TRAFFIC
        os_printf("PacketRxReady attr %x at %x\n", ss, bp );
#endif
        if ( ETH_BUF_OWN_CPU == (ETH_BUF_OWN & ss) ) {
            // Then the packet is untouched...
            break;
        }
#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2
        // Perform address recognition by hand, hardware is in promisc mode
        // (we have settable "software" promisc mode too of course)
        if ( ETH_BUF_OK & ss ) {
            cyg_uint8 *esa = (cyg_uint8 *)bp->ptr; // (this is a non-cachable address)
            int ok = 0;
            if ( p_eth_upd985xx->promisc )
                ok = 1; // accept the packet
            else
            if ( p_eth_upd985xx->mac_address[0] == esa[0] &&
                 p_eth_upd985xx->mac_address[1] == esa[1] &&
                 p_eth_upd985xx->mac_address[2] == esa[2] &&
                 p_eth_upd985xx->mac_address[3] == esa[3] &&
                 p_eth_upd985xx->mac_address[4] == esa[4] &&
                 p_eth_upd985xx->mac_address[5] == esa[5] )
                ok = 1; // Then they are equal - accept
            else
            if ( 0xff == esa[0] &&
                 0xff == esa[1] &&
                 0xff == esa[2] &&
                 0xff == esa[3] &&
                 0xff == esa[4] &&
                 0xff == esa[5] )
                ok = 1; // Then they are equal - accept

            if ( !ok )
                ss = 0; // Easiest way...
        }
#endif
        if ( ETH_BUF_OK & ss ) {
            length = ETH_BUF_SIZE & ss;
#ifdef DEBUG_TRAFFIC
            os_printf("PacketRxReady found a packet size %d attr %x\n", length, ss );
#endif
            // Asserts for the length in-range can fire, with good status
            // in the block, so be defensive here instead.  Belt and braces.
            if ( 63 < length && length <= MAX_RX_PACKET_SIZE ) {
                CYG_ASSERT( ETH_BUF_D_L_DATA == (ETH_BUF_D_L & ss), "Not data buffer" );
                CYG_ASSERT( length > 63, "Tiny packet" );
                CYG_ASSERT( length <= MAX_RX_PACKET_SIZE, "Too big packet" );
                CYG_ASSERT( ETH_BUF_LAST & ss, "Not last buffer" );
                (sc->funs->eth_drv->recv)( sc, length );
            } // Else drop it on the floor.
        }

        // Step along to the next buffer descriptor...
        NextRxRing( p_eth_upd985xx );
        if ( ! reset_required ) {
            // And tell the device it can have a biscuit:
            OUTL( ETH_RXPDR, ETH_RXPDR_AL | 1 );

            // Now, before moving on to the next packet, find out if receptions
            // had caught up with us before adding that new buffer:
            ss = INL( ETH_RXPDR );
            ss &= ETH_RXPDR_RNOD;
            ss >>= ETH_RXPDR_RNOD_SHIFT;
            if ( 1 >= ss ) {
                // Then it was zero before.  So the rx engine is stopped.
#ifdef DEBUG_TRAFFIC
                os_printf( "***ZERO rx buffers were left\n" );
#endif
                reset_required = 1;
            }
            // Otherwise we carry on as usual.
        }
    } while ( 1 );

    if ( reset_required ) {
        p_eth_upd985xx->count_rx_resource++;
        // Disable the wet string end of the receiver
        ss = INL( ETH_MACC1 );
        ss &=~ETH_MACC1_SRXEN;
        OUTL( ETH_MACC1, ss );
        // Disable the DMA engine
        OUTL( ETH_RXCR, 0 );
        // Reset the RxRing from scratch
        InitRxRing( p_eth_upd985xx );
        // Point the hardware at the list of buffers
        OUTL( ETH_RXDPR, (cyg_uint32)p_eth_upd985xx->rxring_active );
        // Tell it about the buffers via the rx descriptor count:
        ss = INL( ETH_RXPDR );
        ss &= ETH_RXPDR_RNOD;
        ss >>= ETH_RXPDR_RNOD_SHIFT;
        // This awful register *increments* by what you write, even if the
        // machinery is halted.  Vile filthy evil rubbish.
        OUTL( ETH_RXPDR, ETH_RXPDR_AL | ((NUM_RXBUFS-1) - ss)  );
        ss = INL( ETH_RXPDR );
        CYG_ASSERT( (ETH_RXPDR_AL | (NUM_RXBUFS-1)) == ss, "RXPDR not right" );
        // Start the rx.
        OUTL( ETH_RXCR, ETH_RXCR_RXE | ETH_RXCR_DRBS_16 );
        // Enable the wet string end of the receiver
        ss = INL( ETH_MACC1 );
        ss |= ETH_MACC1_SRXEN;
        OUTL( ETH_MACC1, ss );
        // All done.
#ifdef DEBUG_TRAFFIC
        os_printf( "***Rx Machine restarted\n" );
#endif
    }
}

// and the callback function

STATIC void
eth_upd985xx_recv( struct eth_drv_sc *sc,
                   struct eth_drv_sg *sg_list, int sg_len )
{
    struct eth_upd985xx *p_eth_upd985xx;
    int total_len;
    struct eth_drv_sg *last_sg;
    cyg_uint8 *from_p;
    volatile struct bufdesc *bp;

    p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private;
    
    // Guard possible external entry points
    if ( ! p_eth_upd985xx->active )
        return;

    bp = p_eth_upd985xx->rxring_active; // Current rx candidate

#ifdef DEBUG_TRAFFIC
    os_printf("Rx status %x\n", bp->attr );
#endif

    if ( 0 == (ETH_BUF_OK & bp->attr) )
        return;
        
    total_len = ETH_BUF_SIZE & bp->attr;
    
#ifdef DEBUG_TRAFFIC
    os_printf("Rx %d %x (status %x): %d sg's, %d bytes\n",
              p_eth_upd985xx->index, (int)p_eth_upd985xx,
              bp->attr,
              sg_len, total_len);
#endif

    // Copy the data to the network stack
    from_p = bp->ptr; // (this is a non-cachable address)

    // check we have memory to copy into; we would be called even if
    // caller was out of memory in order to maintain our state.
    if ( 0 == sg_len || 0 == sg_list )
        return; // caller was out of mbufs

    CYG_ASSERT( 0 < sg_len, "sg_len underflow" );
    CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow" );

    for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) {
        cyg_uint8 *to_p;
        int l;
            
        to_p = (cyg_uint8 *)(sg_list->buf);
        l = sg_list->len;

        CYG_ASSERT( 0 <= l, "sg length -ve" );

        if ( 0 >= l || 0 == to_p )
            return; // caller was out of mbufs

        if ( l > total_len )
            l = total_len;

        memcpy( to_p, from_p, l );
        from_p += l;
        total_len -= l;
    }

    CYG_ASSERT( 0 == total_len, "total_len mismatch in rx" );
    CYG_ASSERT( last_sg == sg_list, "sg count mismatch in rx" );
    CYG_ASSERT( bp->ptr < from_p, "from_p wild in rx" );
    CYG_ASSERT( bp->ptr + MAX_RX_PACKET_SIZE >= from_p,
                "from_p overflow in rx" );
}    


// ------------------------------------------------------------------------
//
//  Function : InitTxRing
//
// ------------------------------------------------------------------------
STATIC void InitTxRing(struct eth_upd985xx* p_eth_upd985xx)
{
    int i;
    volatile struct bufdesc *bp;
    
    p_eth_upd985xx->txring =
        (struct bufdesc *)VIRT_TO_BUS( &p_eth_upd985xx->tx_bufdesc[0] );

    bp = p_eth_upd985xx->txring;

    for ( i = 0; i < NUM_ELEMENTS( p_eth_upd985xx->tx_bufdesc ); i++, bp++ ) {
        bp->ptr = NULL;
        bp->attr = 0;
    }
    // Last one is a NULL link
    bp--;
    bp->ptr = NULL;
    bp->attr = ETH_BUF_D_L_LINK;

    ResetTxRing(p_eth_upd985xx);
}

// ------------------------------------------------------------------------
//
//  Function : ResetTxRing
//
// ------------------------------------------------------------------------
STATIC void ResetTxRing(struct eth_upd985xx* p_eth_upd985xx)
{
    int i;
    volatile struct bufdesc *bp;
    bp = p_eth_upd985xx->txring;
    for ( i = 0; i < NUM_ELEMENTS( p_eth_upd985xx->tx_bufdesc ) - 1; i++, bp++ ) {

⌨️ 快捷键说明

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