if_rhine.c

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

C
1,320
字号

    // This MII read forces the MIISR to get updated
    (void) rhine_read_MII(cpd, cpd->phys_id, MII_BMSR);
    HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_MIISR, stat);
    if (stat & RHINE_MIISR_LNKFL) {
#if DEBUG & 1
        diag_printf("*** Link failure\n");
#endif
        return false;  // Link not connected
    }

    return (cpd->txbusy == 0);
}

//
// This routine is called to send data to the hardware.
static void 
rhine_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, 
            int total_len, unsigned long key)
{
    struct rhine_priv_data *cpd = 
        (struct rhine_priv_data *)sc->driver_private;
    int i, len, plen, ring_entry;

    cyg_uint8* sdata = NULL;
    cyg_uint8 *d, *buf, *txd;
    cyg_uint16 status;
    cyg_uint8 cr0;

    DEBUG_FUNCTION();

    INCR_STAT( tx_count );

    // Worry about the engine stopping.
    HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_CR0, status);
    if ( 0 == (RHINE_CR0_STRT & status) ) {
#if DEBUG & 1
        diag_printf("%s: ENGINE RESTART: status %04x\n", __FUNCTION__, status);
#endif
        status &= ~RHINE_CR0_STOP;
        status |= RHINE_CR0_STRT;
        HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_CR0, status);
    }

    cpd->txbusy = 1;
    cpd->txkey = key;

    // Find packet length
    plen = 0;
    for (i = 0;  i < sg_len;  i++)
        plen += sg_list[i].len;

    CYG_ASSERT( plen == total_len, "sg data length mismatch" );
    
    // Get next TX descriptor
    ring_entry = cpd->tx_ring_free;
    do {
        if (cpd->tx_ring_owned == cpd->tx_ring_cnt) {
            // Is this a dead end? Probably is.
#if DEBUG & 1
            diag_printf("%s: Allocation failed! Retrying...\n", __FUNCTION__ );
#endif
            continue;
        }

        cpd->tx_ring_free++;
        cpd->tx_ring_owned++;
        if (cpd->tx_ring_free == cpd->tx_ring_cnt)
            cpd->tx_ring_free = 0;
    } while (0);

    txd = cpd->tx_ring + ring_entry*RHINE_TD_SIZE;
    buf = cpd->tx_buffers + ring_entry*_BUF_SIZE;
    CYG_ASSERT(0 == (_SU32(txd, RHINE_TDES0) & RHINE_TDES0_OWN),
               "TX descriptor not free");

#if DEBUG & 4
    diag_printf("##Tx descriptor index %d TDES %08x buffer %08x\n", 
                ring_entry, txd, buf);
#endif

    // Put data into buffer
    d = buf;
    for (i = 0;  i < sg_len;  i++) {
        sdata = (cyg_uint8 *)sg_list[i].buf;
        len = sg_list[i].len;

        CYG_ASSERT( sdata, "No sg data pointer here" );
        while(len--)
            *d++ = *sdata++;
    }
    CYG_ASSERT( sdata, "No sg data pointer outside" );
    
    // Annoyingly the chip doesn't pad to minimal packet size, so do
    // that by steam
    if (plen < 60) {
        plen = 60;
#if DEBUG & 4
        diag_printf("Padded %d bytes packet to 60 bytes\n", plen);
#endif
    }        

    CYG_ASSERT( (plen & RHINE_TDES1_TLNG_mask) == plen, "packet too long");
    CYG_ASSERT( (plen & RHINE_TDES1_TLNG_mask) >= 60, "packet too short");
    _SU32(txd, RHINE_TDES1) &= ~RHINE_TDES1_TLNG_mask;
    _SU32(txd, RHINE_TDES1) |= plen;
    _SU32(txd, RHINE_TDES0) = RHINE_TDES0_OWN;

#if DEBUG & 1 // FIXME
    diag_printf("Before TX: Desc (@0x%08lx) %08x %08x %08x %08x\n Next (@0x%08lx) %08x %08x %08x %08x\n",
                (unsigned long) txd,
                _SU32(txd, RHINE_TDES0), _SU32(txd, RHINE_TDES1),
                _SU32(txd, RHINE_TDES2), _SU32(txd, RHINE_TDES3),
                (  (unsigned long)txd)+0x10,
                _SU32(txd, (0x10+RHINE_TDES0)), _SU32(txd, (0x10+RHINE_TDES1)),
                _SU32(txd,(0x10+ RHINE_TDES2)), _SU32(txd,(0x10+ RHINE_TDES3)));
#endif

    // Ack TX empty int
    HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_ISR, RHINE_ISR_PTX);
    // Set transmit demand
    HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_CR0, cr0);
    cr0 |= RHINE_CR0_TDMD;
    HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_CR0, cr0);

#if DEBUG & 1
    HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_ISR, status);
    diag_printf("%s:END: ints at TX: %04x\n", __FUNCTION__, status);
#endif

}

static void
rhine_TxEvent(struct eth_drv_sc *sc, int stat)
{
     struct rhine_priv_data *cpd =
        (struct rhine_priv_data *)sc->driver_private;
    int success = 1;
    cyg_uint8 *txd;
    cyg_uint8 status;

    DEBUG_FUNCTION();

    INCR_STAT( tx_complete );

    txd = cpd->tx_ring + cpd->tx_ring_alloc*RHINE_TD_SIZE;
#if DEBUG & 4
    diag_printf("##Tx packet %d freed %08x %08x!\n", cpd->tx_ring_alloc, txd, _SU32(txd, RHINE_TDES0) );
#endif
    if ((_SU32(txd, RHINE_TDES0) & RHINE_TDES0_OWN)) {
#if DEBUG & 1
        diag_printf("%s: got TX completion when buffer is still owned\n", __FUNCTION__);
#endif
        // first dirty ring entry not freed - wtf?
    }
    cpd->tx_ring_alloc++;
    if (cpd->tx_ring_alloc == cpd->tx_ring_cnt)
        cpd->tx_ring_alloc = 0;
    cpd->tx_ring_owned--;

#ifdef KEEP_STATISTICS
    {
        cyg_uint32 reg = _SU32(txd, RHINE_TDES0);
        int collisions;

        // Covering each bit in turn...
        if ( reg & RHINE_TDES0_TXOK ) INCR_STAT( tx_good );
        if ( reg & RHINE_TDES0_CRS ) INCR_STAT( tx_carrier_loss );
        if ( reg & RHINE_TDES0_OWC ) INCR_STAT( tx_late_collisions );
        if ( reg & RHINE_TDES0_ABT ) INCR_STAT( tx_max_collisions );

        if ( reg & RHINE_TDES0_DFR ) INCR_STAT( tx_deferred );

        collisions = ((reg & RHINE_TDES0_NCR_mask) >> RHINE_TDES0_NCR_shift);
        if (1 == collisions)
            INCR_STAT( tx_single_collisions );
        else if (1 < collisions)
            INCR_STAT( tx_mult_collisions );

        cpd->stats.tx_total_collisions = 
            cpd->stats.tx_late_collisions + 
            cpd->stats.tx_max_collisions + 
            cpd->stats.tx_mult_collisions + 
            cpd->stats.tx_single_collisions;
    }
#endif // KEEP_STATISTICS

    // We do not really care about Tx failure.  Ethernet is not a reliable
    // medium.  But we do care about the TX engine stopping.
    HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_CR0, status);
    if ( 0 == (RHINE_CR0_STRT & status) ) {
#if DEBUG & 1
        diag_printf("%s: ENGINE RESTART: status %04x\n", __FUNCTION__, status);
#endif
        status &= ~RHINE_CR0_STOP;
        status |= RHINE_CR0_STRT;
        HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_CR0, status);
        success = 0; // And treat this as an error...
    }

    if ( cpd->txbusy ) {
        cpd->txbusy = 0;
        (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, success);
    }

    // Ack TX interrupt set
    HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_ISR, RHINE_ISR_PTX);
}

//
// This function is called when a packet has been received.  Its job is
// to prepare to unload the packet from the hardware.  Once the length of
// the packet is known, the upper layer of the driver can be told.  When
// the upper layer is ready to unload the packet, the internal function
// 'rhine_recv' will be called to actually fetch it from the hardware.
//
static void
rhine_RxEvent(struct eth_drv_sc *sc)
{
    struct rhine_priv_data *cpd = 
        (struct rhine_priv_data *)sc->driver_private;
    cyg_uint8 *rxd;
    cyg_uint32 rstat;
    cyg_uint16 ints, len, mask;

    DEBUG_FUNCTION();

    HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_ISR, ints);
#if DEBUG & 1
    diag_printf("RxEvent - CSR: 0x%04x\n", ints);
#endif
    if ( 0 == (RHINE_ISR_PRX & ints) )
        // Then there's no RX event pending
        return;

    // Mask interrupt
    HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_IMR, mask);
    mask &= ~RHINE_IMR_PRX;
    HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_IMR, mask);

    while (1) {
        // Get state of next (supposedly) full ring entry
        cpd->rxpacket = cpd->rx_ring_next;
        rxd = cpd->rx_ring + cpd->rxpacket*RHINE_RD_SIZE;
        rstat = _SU32(rxd, RHINE_RDES0);

        // Keep going until we hit an entry that is owned by the
        // controller.
        if (rstat & RHINE_RDES0_OWN) {
#ifdef CYGDBG_USE_ASSERTS
            // Sanity check of queue
            int i;
            for (i = 0; i < cpd->rx_ring_cnt; i++) {
                rxd = cpd->rx_ring + i*RHINE_RD_SIZE;
                rstat = _SU32(rxd, RHINE_RDES0);

                if (!(rstat & RHINE_RDES0_OWN)) {
                    int i;
                    cyg_uint32 rstat;
                    cyg_uint8* rxd;

                    diag_printf("####Rx %s Inconsistent RX state - next was %d\n", 
                                __FUNCTION__, cpd->rx_ring_next);
                    for (i = 0; i < cpd->rx_ring_cnt; i++) {
                        rxd = cpd->rx_ring + i*RHINE_RD_SIZE;
                        rstat = _SU32(rxd, RHINE_RDES0);
                        diag_printf("#### %02d: 0x%08x\n", i, rstat);
                    }
                }
                break;
            }
#endif
            break;
        }

#if DEBUG & 4
        diag_printf("##Rx packet %d RDES %08x stat %08x\n", 
                    cpd->rxpacket, rxd, rstat);
#endif

        // Increment counts
        INCR_STAT( rx_count );
        cpd->rx_ring_next++;
        if (cpd->rx_ring_next == cpd->rx_ring_cnt) cpd->rx_ring_next = 0;

        len = (rstat & RHINE_RDES0_FLNG_mask) >> RHINE_RDES0_FLNG_shift;

#ifdef KEEP_STATISTICS
        if ( rstat & RHINE_RDES0_CRC ) INCR_STAT( rx_crc_errors );
        if ( rstat & RHINE_RDES0_FAE ) INCR_STAT( rx_align_errors );
        if ( rstat & RHINE_RDES0_LONG ) INCR_STAT( rx_too_long_frames );
#endif // KEEP_STATISTICS

        if (RHINE_RDES0_RXOK & rstat) {
            // It's OK
            INCR_STAT( rx_good );

#if DEBUG & 1
            diag_printf("RxEvent good rx - stat: 0x%08x, len: 0x%04x\n", rstat, len);
            {
                    unsigned char *buf = cpd->rx_buffers + cpd->rxpacket*_BUF_SIZE;

                    int i;
                    diag_printf("RDES: %08x %08x %08x %08x\n", 
                                _SU32(rxd, RHINE_RDES0), _SU32(rxd, RHINE_RDES1),
                                _SU32(rxd, RHINE_RDES2), _SU32(rxd, RHINE_RDES3));

                    diag_printf("Packet data at %p\n", buf);
                    for (i=0;i<len;i++) diag_printf("%02x ", buf[i]);
                    diag_printf("\n");
            }
#endif
            // Check for bogusly short packets; can happen in promisc
            // mode: Asserted against and checked by upper layer
            // driver.
#ifdef CYGPKG_NET
            if ( len > sizeof( struct ether_header ) )
                // then it is acceptable; offer the data to the network stack
#endif
                (sc->funs->eth_drv->recv)(sc, len);
        } else {
            // Not OK for one reason or another...
#if DEBUG & 1
            diag_printf("RxEvent - No RX bit: stat: 0x%08x, len: 0x%04x\n",
                        rstat, len);
#endif
        }

        // Free packet (clear all status flags, and set OWN)
        _SU32(rxd, RHINE_RDES0) = RHINE_RDES0_OWN;
    }

    // Ack RX int
    HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_ISR, RHINE_ISR_PRX);
    // And reenable the interrupt
    HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_IMR, mask);
    mask |= RHINE_IMR_PRX;
    HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_IMR, mask);
}

//
// This function is called as a result of the "eth_drv_recv()" call above.
// Its job is to actually fetch data for a packet from the hardware once
// memory buffers have been allocated for the packet.  Note that the buffers
// may come in pieces, using a scatter-gather list.  This allows for more
// efficient processing in the upper layers of the stack.
//
static void
rhine_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
    struct rhine_priv_data *cpd = 
        (struct rhine_priv_data *)sc->driver_private;
    int i, mlen=0, plen;
    cyg_uint8 *data, *rxd, *buf;

    DEBUG_FUNCTION();

    rxd = cpd->rx_ring + cpd->rxpacket*RHINE_RD_SIZE;
    buf = cpd->rx_buffers + cpd->rxpacket*_BUF_SIZE;

    INCR_STAT( rx_deliver );

    plen = (_SU32(rxd, RHINE_RDES0) & RHINE_RDES0_FLNG_mask) >> RHINE_RDES0_FLNG_shift;

    for (i = 0;  i < sg_len;  i++) {
        data = (cyg_uint8*)sg_list[i].buf;
        mlen = sg_list[i].len;

#if DEBUG & 1
        diag_printf("%s : mlen %x, plen %x\n", __FUNCTION__, mlen, plen);
#endif
        if (data) {
            while (mlen > 0) {
                *data++ = *buf++;
                mlen--;
                plen--;
            }
        }
    }
}

static void
rhine_poll(struct eth_drv_sc *sc)
{
    struct rhine_priv_data *cpd = 
        (struct rhine_priv_data *)sc->driver_private;
    cyg_uint16 event, mask;
    static volatile bool locked = false;

//    DEBUG_FUNCTION();

    while (1) {
        // Get the (unmasked) requests
        HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_ISR, event);
        HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_IMR, mask);

        event &= mask;

        if (0 == event)
            break;

        if (event & RHINE_ISR_PRX) {
            rhine_RxEvent(sc);
        }
        else if (event & RHINE_ISR_PTX) {
            rhine_TxEvent(sc, event);
        } 
        else if (event & RHINE_ISR_RU) {
#if DEBUG & 1
            int i;
            cyg_uint32 rstat;
            cyg_uint8* rxd;
            struct rhine_priv_data *cpd = 
                (struct rhine_priv_data *)sc->driver_private;

            diag_printf("%s: Ran out of RX buffers (%04x)\n", __FUNCTION__, event);
            for (i = 0; i < cpd->rx_ring_cnt; i++) {
                rxd = cpd->rx_ring + i*RHINE_RD_SIZE;
                
                rstat = _SU32(rxd, RHINE_RDES0);
                diag_printf(" %02d: 0x%08x\n", i, rstat);
            }
#endif
            HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_ISR, RHINE_ISR_RU);
        }
        else {
#if DEBUG & 1
            diag_printf("%s: Unknown interrupt: 0x%04x\n", __FUNCTION__, event);
#endif
            // Clear unhandled interrupts and hope for the best
            // This should never happen though, since we only enable
            // the sources we handle.
            HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_ISR, event);
        }
    }

    locked = false;
}
// EOF if_rhine.c

⌨️ 快捷键说明

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