if_lancepci.c

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

C
1,324
字号

    DEBUG_FUNCTION();
    if (!cpd->active)
        return;
    if (cpd->txbusyh) {
#if DEBUG & 9
        db_printf("Lancepci-stop:waiting for tx empty\n");
#endif
        b=100;
        while (cpd->txbusyh && b) {
	    CYGACC_CALL_IF_DELAY_US(200);
	    b--;
	 }
    }
    put_reg(sc, LANCE_CSR_CSCR, LANCE_CSR_CSCR_STOP);
    cpd->active = 0;
#if DEBUG & 9
    db_printf("Lancepci-stop:done\n");
#endif
}

//
// 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
lancepci_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
    cyg_uint16 reg;
    cyg_uint32 b;
    struct lancepci_priv_data *cpd =
        (struct lancepci_priv_data *)sc->driver_private;
#ifdef CYGPKG_NET
    struct ifnet *ifp = &sc->sc_arpcom.ac_if;
#endif
    DEBUG_FUNCTION();

    // If device is already active, stop it
#if DEBUG & 9
	db_printf("Lancepci-start:entered\n");
#endif
    if (cpd->active)
     {
        if (cpd->txbusyh) {
#if DEBUG & 9
	    db_printf("Lancepci-start:waiting for tx empty\n");
#endif
	    b=100;
     	    while (cpd->txbusyh && b) {
	        CYGACC_CALL_IF_DELAY_US(200);
		b--;
	    }
	}
    	put_reg(sc, LANCE_CSR_CSCR, LANCE_CSR_CSCR_STOP);
    	cpd->active = 0;
#if DEBUG & 9
    	db_printf("Lancepci-start:stopped\n");
#endif
    }
    CYGACC_CALL_IF_DELAY_US(200);

#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.
    	_SU16(cpd->init_table, LANCE_IB_MODE) = 0x0000|LANCE_CSR_MODE_PROM;
#if DEBUG & 9
	db_printf("Promisc MODE!");
#endif
    }
    else _SU16(cpd->init_table, LANCE_IB_MODE) = 0x0000;
#endif
    cpd->rx_ring_next = 0;
    cpd->tx_ring_free = cpd->tx_ring_alloc = cpd->tx_ring_owned = 0;
    // Init the chip again
    HAL_PCI_CPU_TO_BUS(cpd->init_table, (cyg_uint8 *)b);
    put_reg(sc, LANCE_CSR_IBA0, (b >>  0) & 0xffff);
    put_reg(sc, LANCE_CSR_IBA1, (b >> 16) & 0xffff);
    // Disable automatic TX polling (_send will force a poll), pad
    // XT frames to legal length, mask status interrupts.
    put_reg(sc, LANCE_CSR_TFC, (LANCE_CSR_TFC_TXDPOLL | LANCE_CSR_TFC_APAD_XMT
                                | LANCE_CSR_TFC_MFCOM | LANCE_CSR_TFC_RCVCCOM
                                | LANCE_CSR_TFC_TXSTRTM));
    // Recover after TX FIFO underflow
    put_reg(sc, LANCE_CSR_IM, LANCE_CSR_IM_DXSUFLO);
    // Initialize controller - load up init_table
    put_reg(sc, LANCE_CSR_CSCR, LANCE_CSR_CSCR_INIT);
    while (0 == (get_reg(sc, LANCE_CSR_CSCR) & LANCE_CSR_CSCR_IDON));
	reg=get_reg(sc,LANCE_CSR_CSCR);
    put_reg(sc, LANCE_CSR_CSCR, (reg|(LANCE_CSR_CSCR_IENA | LANCE_CSR_CSCR_STRT))&~LANCE_CSR_CSCR_INIT);
#if DEBUG & 9
	reg=get_reg(sc,LANCE_CSR_CSCR);
	db_printf("CSR after start = %4x\n",reg);
#endif
    cpd->active = 1; cpd->txbusy=0; cpd->txbusyh=0;
    // delay is necessary for Vmware to get a tick !!!
    CYGACC_CALL_IF_DELAY_US(50000);
}

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

    DEBUG_FUNCTION();

#if DEBUG & 9
	db_printf("Lancepci-control:entered\n");
#endif
    res = 0;                            // expect success
    switch (key) {
        case ETH_DRV_SET_MAC_ADDRESS:
#if 9 & DEBUG
            db_printf("PCNET - set ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
                esa[0], esa[1], esa[2], esa[3], esa[4], esa[5] );
#endif // DEBUG
            for ( i = 0; i < sizeof(cpd->esa);  i++ )
                cpd->esa[i] = esa[i];
            for (i = 0;  i < sizeof(cpd->esa);  i += 2) {
                reg = cpd->esa[i] | (cpd->esa[i+1] << 8);
                put_reg(sc, LANCE_CSR_PAR0+i/2, reg );
            }
            for (i = 0; i < 6; i++)	// in case of later restart
                _SU8(cpd->init_table, LANCE_IB_PADR0+i) = cpd->esa[i];
            break;
#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->esa);  i += 2) {
                cyg_uint16 z = get_reg(sc, LANCE_CSR_PAR0+i/2 );
                esa[i] =   (cyg_uint8)(0xff & z);
                esa[i+1] = (cyg_uint8)(0xff & (z >> 8));
            }
            break;
#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, "AMD LancePCI" );
            // CYG_ASSERT( 48 > strlen(p->description), "Description too long" );

            p->operational = 3;         // LINK UP
            p->duplex = 2;              // 2 = SIMPLEX
            p->speed = 10 * 1000000;

#if FIXME
#ifdef KEEP_STATISTICS
            {
                struct amd_lancepci_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
#endif // FIXME

            p->tx_queue_len = 1;
            break;
        }
#endif
        default:
            res = 1;
            break;
    }
#if DEBUG & 9
    db_printf("Lancepci-control:done\n");
#endif
    CYGACC_CALL_IF_DELAY_US(50000);	// let VMware get a tick
    return res;
}

//
// 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
lancepci_can_send(struct eth_drv_sc *sc)
{
    struct lancepci_priv_data *cpd =
        (struct lancepci_priv_data *)sc->driver_private;

    DEBUG_FUNCTION();

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

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

    cyg_uint8* sdata = NULL;
    cyg_uint8 *d, *buf, *txd;
    cyg_uint16 ints;
    cyg_uint32 b;

    DEBUG_FUNCTION();

    INCR_STAT( tx_count );

    cpd->txbusy = 1; cpd->txbusyh=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
            db_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*LANCE_TD_SIZE;
    buf = cpd->tx_buffers + ring_entry*_BUF_SIZE;
    CYG_ASSERT(0 == (_SU32(txd, LANCE_TD_PTR) & LANCE_TD_PTR_OWN),
               "TX descriptor not free");

#if DEBUG & 4
    db_printf("#####Tx descriptor 0x%08x buffer 0x%08x\n",
                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" );

#if DEBUG & 1
    db_printf("CSCR %04x\n", get_reg(sc, LANCE_CSR_CSCR));
#endif
    _SU16(txd, LANCE_TD_LEN) = (-plen);
    _SU16(txd, LANCE_TD_MISC) = 0;
    HAL_PCI_CPU_TO_BUS(buf, (cyg_uint8 *)b);
    _SU32(txd, LANCE_TD_PTR) = ((b & LANCE_TD_PTR_MASK)
                                | LANCE_TD_PTR_OWN | LANCE_TD_PTR_STP | LANCE_TD_PTR_ENP);

#if DEBUG & 1
    db_printf("Last TX: LEN %04x MISC %04x PTR %08x\n",
                _SU16(txd, LANCE_TD_LEN),
                _SU16(txd, LANCE_TD_MISC),
                _SU32(txd, LANCE_TD_PTR));
#endif

    // This delay seems to be necessary on some platforms
    // (Malta 5kc for example).
    // Why it is needed is not clear, but removing it or

⌨️ 快捷键说明

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