if_lan91cxx.c

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

C
1,486
字号
    default:
        break;
    }
    return 1;
}

//
// This routine is called to see if it is possible to send another packet.
// It will return non-zero if a transmit is possible, zero otherwise.
//
static int
lan91cxx_can_send(struct eth_drv_sc *sc)
{
    struct lan91cxx_priv_data *cpd =
        (struct lan91cxx_priv_data *)sc->driver_private;
    int tcr;

//    DEBUG_FUNCTION();

#ifndef LAN91CXX_IS_LAN91C111
    // LINK_OK on 91C111 is just a general purpose input and may not
    // have anything to do with the link.
    if ((get_reg(sc, LAN91CXX_EPH_STATUS) & LAN91CXX_STATUS_LINK_OK) == 0) {
	db_printf("no link\n");
        return false;  // Link not connected
    }
#endif

    CYG_ASSERT( cpd->within_send < 10, "can_send: Excess send recursions" );
    cpd->within_send++;

    tcr = get_reg(sc, LAN91CXX_TCR);
    if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) {
#if DEBUG & 1
        db_printf("%s: ENGINE RESTART: tcr 0x%04x\n", __FUNCTION__, tcr );
#endif
        // Complete any outstanding activity:
        if ( cpd->txbusy ) {
            cpd->txbusy = 0;
#if DEBUG & 9
            db_printf("LAN91CXX - can_send, cleaning up pending TX\n" );
#endif
            (sc->funs->eth_drv->tx_done)(sc, cpd->txkey, 0);
        }
        tcr |= LAN91CXX_TCR_TXENA;
        put_reg(sc, LAN91CXX_TCR, tcr);
    }

    // This helps unstick deadly embraces.
    lan91cxx_poll( sc ); // Deal with any outstanding rx state
    cpd->within_send--;

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

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

    cyg_uint8 *sdata;
    cyg_uint16 data = 0;
    int dpos = 0;
    unsigned short ints, control;
    cyg_uint16 packet, status;

    DEBUG_FUNCTION();

    INCR_STAT( tx_count );

#if DEBUG & 1
    ints = get_reg(sc, LAN91CXX_INTERRUPT);
    db_printf("%s:START: ints: %04x\n", __FUNCTION__, ints);
#endif
    
    // Worry about the TX engine stopping.
    tcr = get_reg(sc, LAN91CXX_TCR);
    if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) {
#if DEBUG & 1
        db_printf("%s: ENGINE RESTART: tcr 0x%04x\n", __FUNCTION__, tcr );
#endif
        tcr |= LAN91CXX_TCR_TXENA;
        put_reg(sc, LAN91CXX_TCR, tcr);
    }

    // This helps unstick deadly embraces.
    CYG_ASSERT( cpd->within_send < 10, "send: Excess send recursions" );
    cpd->within_send++;
    lan91cxx_poll( sc ); // Deal with any outstanding rx state
    cpd->within_send--;

    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" );

    // Alloc new TX packet
    do {
        put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_alloc_for_tx
#ifndef LAN91CXX_IS_LAN91C111
		| ((plen >> 8) & 0x07)
#endif
	    );

        i = 1024 * 1024;
        do {
            status = get_reg(sc, LAN91CXX_INTERRUPT);
        } while (0 == (status & LAN91CXX_INTERRUPT_ALLOC_INT) && (--i > 0) );
        if ( i )
            packet = get_reg(sc, LAN91CXX_PNR);
        else
            packet = 0xffff;
#if DEBUG & 1
        db_printf("%s: allocated packet %04x\n", __FUNCTION__, packet);
#endif
        packet = packet >> 8;
        if (packet & 0x80) {
            // Hm.. Isn't this a dead end?
#if DEBUG & 1
            db_printf("%s: Allocation failed! Retrying...\n", __FUNCTION__ );
#endif
            // Not if we can make progress with what's filling memory.
            lan91cxx_poll( sc ); // Deal with any outstanding state
            continue;
        }
    } while (0);

#if DEBUG & 4
    db_printf("#####Tx packet allocated 0x%04x (previous 0x%04x)\n",
                packet, cpd->txpacket);
#endif
    cpd->txpacket = packet;

    put_reg(sc, LAN91CXX_PNR, packet);
    // Note: Check FIFO state here before continuing?
    put_reg(sc, LAN91CXX_POINTER, LAN91CXX_POINTER_AUTO_INCR | 0x0000);
    // Pointer is now set, and the proper bank is selected for
    // data writes.

    // Prepare header:
    put_data(sc, CYG_CPU_TO_LE16(0));        // reserve space for status word
    // packet length (includes status, byte-count and control shorts)
    put_data(sc, CYG_CPU_TO_LE16(0x7FE & (plen + 6)) ); // Always even, always < 15xx(dec)

    for (i = 0;  i < sg_len;  i++) {
        sdata = (cyg_uint8 *)sg_list[i].buf;
        len = sg_list[i].len;
        while( len > 0 )
        {
            data |= *sdata<<((dpos&1)*8);
            dpos++, len--, sdata++;
            if( (dpos & 1) == 0 )
            {
                put_data(sc, CYG_CPU_TO_LE16(data));
                data = 0;
            }
        }
    }

    // Lay down the control short unconditionally at the end.
    // (or it might use random memory contents)
    control = 0;
    if( 1 & plen ) {
        // Need to set ODD flag and insert the data
        control = data;
        control |= LAN91CXX_CONTROLBYTE_ODD;
    }
    control |= LAN91CXX_CONTROLBYTE_CRC; // Just in case...
    put_data(sc, CYG_CPU_TO_LE16(control));
    
    // Enqueue the packet
    put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_enq_packet);

    // Ack TX empty int and unmask it.
    ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00;
    put_reg(sc, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_SET_ACK);
    put_reg(sc, LAN91CXX_INTERRUPT, ints | LAN91CXX_INTERRUPT_TX_SET_M);

#if DEBUG & 1
    ints = get_reg(sc, LAN91CXX_INTERRUPT);
    db_printf("%s:END: ints at TX: %04x\n", __FUNCTION__, ints);
#endif
}

static void
lan91cxx_TxEvent(struct eth_drv_sc *sc, int stat)
{
    unsigned short packet, ints, tcr;
    struct lan91cxx_priv_data *cpd =
        (struct lan91cxx_priv_data *)sc->driver_private;
    int success = 1;

    DEBUG_FUNCTION();

    INCR_STAT( tx_complete );

    // Ack and mask TX interrupt set
    ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00;
    ints |= LAN91CXX_INTERRUPT_TX_SET_ACK;
    ints &= ~LAN91CXX_INTERRUPT_TX_SET_M;
    put_reg(sc, LAN91CXX_INTERRUPT, ints);

    // Get number of completed packet and read the status word
    packet = get_reg(sc, LAN91CXX_FIFO_PORTS);
#if DEBUG & 1
    db_printf("%s:START: fifo %04x ints %04x\n", __FUNCTION__, packet, ints);
#endif

#ifdef KEEP_STATISTICS
    {
        unsigned short reg;

        reg = get_reg( sc, LAN91CXX_EPH_STATUS );
        
        // Covering each bit in turn...
        if ( reg & LAN91CXX_STATUS_TX_UNRN   ) INCR_STAT( tx_underrun );
        //if ( reg & LAN91CXX_STATUS_LINK_OK ) INCR_STAT(  );
        //if ( reg & LAN91CXX_STATUS_CTR_ROL ) INCR_STAT(  );
        //if ( reg & LAN91CXX_STATUS_EXC_DEF ) INCR_STAT(  );
        if ( reg & LAN91CXX_STATUS_LOST_CARR ) INCR_STAT( tx_carrier_loss );
        if ( reg & LAN91CXX_STATUS_LATCOL    ) INCR_STAT( tx_late_collisions );
        //if ( reg & LAN91CXX_STATUS_WAKEUP  ) INCR_STAT(  );
        if ( reg & LAN91CXX_STATUS_TX_DEFR   ) INCR_STAT( tx_deferred );
        //if ( reg & LAN91CXX_STATUS_LTX_BRD ) INCR_STAT(  );
        if ( reg & LAN91CXX_STATUS_SQET      ) INCR_STAT( tx_sqetesterrors );
        if ( reg & LAN91CXX_STATUS_16COL     ) INCR_STAT( tx_max_collisions );
        //if ( reg & LAN91CXX_STATUS_LTX_MULT) INCR_STAT(  );
        if ( reg & LAN91CXX_STATUS_MUL_COL   ) INCR_STAT( tx_mult_collisions );
        if ( reg & LAN91CXX_STATUS_SNGL_COL  ) INCR_STAT( tx_single_collisions );
        if ( reg & LAN91CXX_STATUS_TX_SUC    ) INCR_STAT( tx_good );

        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;

        // We do not need to look in the Counter Register (LAN91CXX_COUNTER)
        // because it just mimics the info we already have above.
    }
#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.
    tcr = get_reg(sc, LAN91CXX_TCR);
    if ( 0 == (LAN91CXX_TCR_TXENA & tcr) ) {
#if DEBUG & 1
        db_printf("%s: ENGINE RESTART: tcr 0x%04x eph 0x%04x ints 0x%04x\n", __FUNCTION__, tcr, get_reg(sc, LAN91CXX_EPH_STATUS), ints);
#endif
        tcr |= LAN91CXX_TCR_TXENA;
        put_reg(sc, LAN91CXX_TCR, tcr);
        success = 0; // And treat this as an error...
    }

    packet &= 0xff;

    // It certainly appears that occasionally the tx fifo tells lies; we
    // get the wrong packet number.  Freeing the one we allocated seems to
    // give correct operation.
#ifdef CYGPKG_INFRA_DEBUG
    // Then we log, OOI, the number of times we get a bad packet number
    // from the tx done fifo.
    if (cpd->txpacket != packet )
        lan91cxx_txfifo_bad++;
    else
        lan91cxx_txfifo_good++;
#endif
#if DEBUG & 4
    db_printf("#####Tx packet freed 0x%04x (expected 0x%04x)\n", packet, cpd->txpacket );
#endif
    // and then free the packet
    put_reg(sc, LAN91CXX_PNR, cpd->txpacket);
    put_reg(sc, LAN91CXX_MMU_COMMAND, LAN91CXX_MMU_rel_packet);

    // Ack the TX int which is supposed to clear the packet from the TX
    // completion queue.
    ints = get_reg(sc, LAN91CXX_INTERRUPT) & 0xff00;
    ints |= LAN91CXX_INTERRUPT_TX_FIFO_ACK;
    put_reg(sc, LAN91CXX_INTERRUPT, ints);

#if DEBUG & 1
    // Hm... The free doesn't seem to have the desired effect?!?
    ints = get_reg(sc, LAN91CXX_INTERRUPT);
    packet = get_reg(sc, LAN91CXX_FIFO_PORTS);
    db_printf("%s:END: fifo %04x ints %04x\n", __FUNCTION__, packet, ints);
#endif

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

void get_data_init(struct eth_drv_sc *sc)
{
    struct lan91cxx_priv_data *cpd =
        (struct lan91cxx_priv_data *)sc->driver_private;

    cpd->data_buf = 0xa5a5a5a5;
    cpd->data_pos = sizeof(rxd_t);
}

cyg_uint8 get_data_byte(struct eth_drv_sc *sc)
{
    cyg_uint8 c;
    struct lan91cxx_priv_data *cpd =
        (struct lan91cxx_priv_data *)sc->driver_private;

    if( cpd->data_pos == sizeof(rxd_t) )
    {
        cpd->data_buf = get_data(sc);
        cpd->data_pos = 0;
    }

    c = (cpd->data_buf>>(cpd->data_pos*8))&0xFF;
    cpd->data_pos++;
    
    return c;

}

cyg_uint16 get_data_short(struct eth_drv_sc *sc)
{
    cyg_uint16 val;

    val = get_data_byte(sc);
    val |= get_data_byte(sc)<<8;
    
    return CYG_LE16_TO_CPU(val);
}

//
// 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
// 'lan91cxx_recv' will be called to actually fetch it from the hardware.
//
static void
lan91cxx_RxEvent(struct eth_drv_sc *sc)
{
    struct lan91cxx_priv_data *cpd = 
        (struct lan91cxx_priv_data *)sc->driver_private;
    unsigned short stat, len;

    DEBUG_FUNCTION();

    stat = get_reg(sc, LAN91CXX_FIFO_PORTS);
#if DEBUG & 1
    db_printf("RxEvent - FIFOs: 0x%04x\n", stat);
#endif
    if ( 0x8000 & stat ) {
        // Then the Rx FIFO is empty
#if DEBUG & 4
        db_printf("#####RxEvent with empty fifo\n");
#endif
        return;
    }

    INCR_STAT( rx_count );

#if DEBUG & 4
    db_printf("#####Rx packet allocated 0x%04x (previous 0x%04x)\n",

⌨️ 快捷键说明

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