if_pcnet.c

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

C
1,329
字号
    cyg_uint8* p;
    cyg_uint8* d;
    cyg_uint8* init_table;
    int i;

    DEBUG_FUNCTION();


    if ( 0 == initialized++ ) {
        // then this is the first time ever:
        if ( ! pci_init_find_pcnet() ) {
#if DEBUG & 8
            db_printf( "pci_init_find_pcnet failed" );
#endif
            return false;
        }
    }

    // If this device is not present, exit
    if (0 == cpd->found)
        return 0;

    cpd->txbusy = 0;

#if DEBUG & 8
    db_printf("PCNet at base 0x%08x, EEPROM key 0x%04x\n",
                cpd->base, _SU16(cpd->base, PCNET_IO_ID));
#endif

#if 0
    // FIXME: Doesn't work with non-conforming EEPROMS
    if (PCNET_IO_ID_KEY != _SU16(cpd->base, PCNET_IO_ID) ) {
        db_printf("PCNet EPROM key not found\n");
        return false;
    }
#endif

#if DEBUG & 9
    db_printf("pcimem : %08x size: %08x\n", pcnet_heap_base, pcnet_heap_size);
#endif

    // Prepare ESA
    if (!cpd->hardwired_esa) {
        // Use the address from the serial EEPROM
        p = cpd->base + PCNET_IO_EEPROM;
        for (i = 0; i < 6; i++)
            cpd->esa[i] = *p++;
    }
#if DEBUG & 9
    db_printf("PCNET - %s ESA: %02x:%02x:%02x:%02x:%02x:%02x\n",
                (cpd->hardwired_esa) ? "static" : "eeprom",
                cpd->esa[0], cpd->esa[1], cpd->esa[2],
                cpd->esa[3], cpd->esa[4], cpd->esa[5] );
#endif

#ifdef CYGSEM_DEVS_ETH_AMD_PCNET_FORCE_10MBPS
    if (get_reg(sc,PCNET_ANR_AAR) & PCNET_ANR_AAR_100) {
        cyg_uint16 anr;
	int loop;

#if DEBUG & 9
	db_printf("%s: Forcing 10Mbps negotiation\n", __FUNCTION__);
#endif
	// adjust speed/duplex auto-negotiation mask to clear 100Mbps bits
	anr = get_reg(sc,PCNET_ANR_AAR);
	anr &= ~PCNET_ANR_AAR_100;
	put_reg(sc,PCNET_ANR_AAR,anr);
	// renegotiate
	anr = get_reg(sc,PCNET_ANR_PHYCTRL);
	anr |= PCNET_ANR_PHYCTRL_RENEGOTIATE;
	put_reg(sc,PCNET_ANR_PHYCTRL,anr);
	loop = 100000;
	while (loop>0 && !(get_reg(sc,PCNET_ANR_PHYSTAT) & PCNET_ANR_PHYSTAT_AUTONEG_COMP))
		loop--;
#if DEBUG & 9
	db_printf("ANR0: %04x\n",get_reg(sc,PCNET_ANR_PHYCTRL));
	db_printf("ANR1: %04x\n",get_reg(sc,PCNET_ANR_PHYSTAT));
	db_printf("ANR4: %04x\n",get_reg(sc,PCNET_ANR_AAR));
#endif
    }
#endif

    // Prepare RX and TX rings
    p = cpd->rx_ring = (cyg_uint8*) CYGARC_UNCACHED_ADDRESS((cyg_uint32)pciwindow_mem_alloc((1<<cpd->rx_ring_log_cnt)*PCNET_RD_SIZE));
    memset(cpd->rx_ring,0,(1<<cpd->rx_ring_log_cnt)*PCNET_RD_SIZE);

    d = cpd->rx_buffers = (cyg_uint8*) CYGARC_UNCACHED_ADDRESS((cyg_uint32)pciwindow_mem_alloc(_BUF_SIZE*cpd->rx_ring_cnt));
    memset(cpd->rx_buffers,0,_BUF_SIZE*cpd->rx_ring_cnt);

    for (i = 0; i < cpd->rx_ring_cnt; i++) {
        HAL_PCI_CPU_TO_BUS(d, b);
        _SU32(p, PCNET_RD_PTR) = (b & PCNET_RD_PTR_MASK) | PCNET_RD_PTR_OWN;
        _SU16(p, PCNET_RD_BLEN) = (-_BUF_SIZE);
        p += PCNET_RD_SIZE;
        d += _BUF_SIZE;
    }
    cpd->rx_ring_next = 0;

    p = cpd->tx_ring = (cyg_uint8*) CYGARC_UNCACHED_ADDRESS((cyg_uint32)pciwindow_mem_alloc((1<<cpd->tx_ring_log_cnt)*PCNET_TD_SIZE));
    memset(cpd->tx_ring,0,(1<<cpd->tx_ring_log_cnt)*PCNET_TD_SIZE);

    d = cpd->tx_buffers = (cyg_uint8*) CYGARC_UNCACHED_ADDRESS((cyg_uint32)pciwindow_mem_alloc(_BUF_SIZE*cpd->tx_ring_cnt));
    for (i = 0; i < cpd->tx_ring_cnt; i++) {
        HAL_PCI_CPU_TO_BUS(d, b);
        _SU32(p, PCNET_RD_PTR) = b & PCNET_TD_PTR_MASK;
        p += PCNET_TD_SIZE;
        d += _BUF_SIZE;
    }
    cpd->tx_ring_free = cpd->tx_ring_alloc = cpd->tx_ring_owned = 0;

    // Initialization table
    init_table = (cyg_uint8*)CYGARC_UNCACHED_ADDRESS((cyg_uint32)pciwindow_mem_alloc(PCNET_IB_SIZE));
    _SU16(init_table, PCNET_IB_MODE) = 0x0000;
    for (i = 0; i < 6; i++)
        _SU8(init_table, PCNET_IB_PADR0+i) = cpd->esa[i];
    for (i = 0; i < 8; i++)
        _SU8(init_table, PCNET_IB_LADRF0+i) = 0;
    
    HAL_PCI_CPU_TO_BUS(cpd->rx_ring, b);
    _SU32(init_table, PCNET_IB_RDRA) = ((b & PCNET_IB_RDRA_PTR_mask)
                                        | (cpd->rx_ring_log_cnt << PCNET_IB_RDRA_CNT_shift));
    HAL_PCI_CPU_TO_BUS(cpd->tx_ring, b);
    _SU32(init_table, PCNET_IB_TDRA) = ((b & PCNET_IB_TDRA_PTR_mask)
                                        | (cpd->tx_ring_log_cnt << PCNET_IB_TDRA_CNT_shift));

#if DEBUG & 9
    db_printf("Loading up PCNet controller from table at 0x%08x\n", init_table);
    db_printf(" Mode 0x%04x\n", _SU16(init_table, PCNET_IB_MODE));
    db_printf(" PADR %02x:%02x:%02x:%02x:%02x:%02x ",
                _SU8(init_table, PCNET_IB_PADR0+0), _SU8(init_table, PCNET_IB_PADR0+1),
                _SU8(init_table, PCNET_IB_PADR0+2), _SU8(init_table, PCNET_IB_PADR0+3),
                _SU8(init_table, PCNET_IB_PADR0+4), _SU8(init_table, PCNET_IB_PADR0+5));
    db_printf("LADR %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
                _SU8(init_table, PCNET_IB_LADRF0+0), _SU8(init_table, PCNET_IB_LADRF0+1),
                _SU8(init_table, PCNET_IB_LADRF0+2), _SU8(init_table, PCNET_IB_LADRF0+3),
                _SU8(init_table, PCNET_IB_LADRF0+4), _SU8(init_table, PCNET_IB_LADRF0+5),
                _SU8(init_table, PCNET_IB_LADRF0+5), _SU8(init_table, PCNET_IB_LADRF0+7));
    db_printf(" RX 0x%08x (len %d) TX 0x%08x (len %d)\n",
                _SU32(init_table, PCNET_IB_RDRA) & 0x1fffffff,
                (_SU32(init_table, PCNET_IB_RDRA) >> PCNET_IB_RDRA_CNT_shift) & 7,
                _SU32(init_table, PCNET_IB_TDRA) & 0x1fffffff,
                (_SU32(init_table, PCNET_IB_TDRA) >> PCNET_IB_TDRA_CNT_shift) & 7);
#endif

    // Reset chip
    HAL_PCI_IO_READ_UINT16(cpd->base+PCNET_IO_RESET, val);

    // Load up chip with buffers.
    // Note: There is a 16M limit on the addresses used by the driver
    // since the top 8 bits of the init_table address is appended to
    // all other addresses used by the controller.
    HAL_PCI_CPU_TO_BUS(init_table, b);
    put_reg(sc, PCNET_CSR_IBA0, (b >>  0) & 0xffff);
    put_reg(sc, PCNET_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, PCNET_CSR_TFC, (PCNET_CSR_TFC_TXDPOLL | PCNET_CSR_TFC_APAD_XMT
                                | PCNET_CSR_TFC_MFCOM | PCNET_CSR_TFC_RCVCCOM
                                | PCNET_CSR_TFC_TXSTRTM));
    // Recover after TX FIFO underflow
    put_reg(sc, PCNET_CSR_IM, PCNET_CSR_IM_DXSUFLO);
    // Initialize controller - load up init_table
    put_reg(sc, PCNET_CSR_CSCR, PCNET_CSR_CSCR_INIT);
    while (0 == (get_reg(sc, PCNET_CSR_CSCR) & PCNET_CSR_CSCR_IDON));

    // Stop controller
    put_reg(sc, PCNET_CSR_CSCR, PCNET_CSR_CSCR_STOP);

#if DEBUG & 9
    db_printf("PCNet controller state is now:\n");
    db_printf(" Mode 0x%04x  TFC 0x%04x\n", _SU16(init_table, PCNET_IB_MODE), get_reg(sc, PCNET_CSR_TFC));
    db_printf(" PADR %04x:%04x:%04x ",
                get_reg(sc, PCNET_CSR_PAR0),
                get_reg(sc, PCNET_CSR_PAR1),
                get_reg(sc, PCNET_CSR_PAR2));
    db_printf("LADR %04x:%04x:%04x:%04x\n",
                get_reg(sc, PCNET_CSR_LAR0),
                get_reg(sc, PCNET_CSR_LAR1),
                get_reg(sc, PCNET_CSR_LAR2),
                get_reg(sc, PCNET_CSR_LAR3));
    db_printf(" RX 0x%04x%04x (len 0x%04x) TX 0x%04x%04x (len 0x%04x)\n",
                get_reg(sc, PCNET_CSR_BARRU), get_reg(sc, PCNET_CSR_BARRL), 
                get_reg(sc, PCNET_CSR_RRLEN),
                get_reg(sc, PCNET_CSR_BATRU), get_reg(sc, PCNET_CSR_BATRL), 
                get_reg(sc, PCNET_CSR_TRLEN));

    val = get_reg(sc, PCNET_CSR_ID_LO);
    db_printf("PCnet ID 0x%04x (%s) ",
                val, 
                (0x5003 == val) ? "Am79C973" : (0x7003 == val) ? "Am79C975" : "Unknown");
    val = get_reg(sc, PCNET_CSR_ID_HI);
    db_printf("Part IDU 0x%03x Silicon rev %d\n",
                val & 0x0fff, (val >> 12) & 0xf);
#endif

    // and record the net dev pointer
    cpd->ndp = (void *)tab;

    // Start controller, but put it in suspended mode
    put_reg(sc, PCNET_CSR_CSCR, PCNET_CSR_CSCR_STOP);
    put_reg(sc, PCNET_CSR_CSCR, (PCNET_CSR_CSCR_IENA | PCNET_CSR_CSCR_STRT));
    i = 0;
    while (0 == (PCNET_CSR_CSCR_STRT & get_reg(sc, PCNET_CSR_CSCR))) {
        CYGACC_CALL_IF_DELAY_US(1000);
        put_reg(sc, PCNET_CSR_CSCR, (PCNET_CSR_CSCR_IENA | PCNET_CSR_CSCR_STRT));
        if (i++ == 1000) {
#if DEBUG & 9
            db_printf("Failed to start the controller\n");
#endif
            return false;
        }
    }

    val = get_reg(sc, PCNET_CSR_ECI);
    val |= PCNET_CSR_ECI_SPND;
    put_reg(sc, PCNET_CSR_ECI, val);
    // Wait for device to suspend
    do {
        val = get_reg(sc, PCNET_CSR_ECI);
    } while (0 == (val & PCNET_CSR_ECI_SPND));
    cpd->active = 0;

    // Initialize upper level driver
    (sc->funs->eth_drv->init)(sc, cpd->esa);

#if DEBUG & 9
    db_printf("Done\n");
#endif
    return true;
}

static void
pcnet_stop(struct eth_drv_sc *sc)
{
    cyg_uint16 reg;
    struct pcnet_priv_data *cpd =
        (struct pcnet_priv_data *)sc->driver_private;

    DEBUG_FUNCTION();
    
    reg = get_reg(sc, PCNET_CSR_ECI);
    reg |= PCNET_CSR_ECI_SPND;
    put_reg(sc, PCNET_CSR_ECI, reg);
    // Wait for device to suspend
    do {
        reg = get_reg(sc, PCNET_CSR_ECI);
    } while (0 == (reg & PCNET_CSR_ECI_SPND));
    cpd->active = 0;
}

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

    // If device is already active, suspend it
    if (cpd->active) {
        reg = get_reg(sc, PCNET_CSR_ECI);
        reg |= PCNET_CSR_ECI_SPND;
        put_reg(sc, PCNET_CSR_ECI, reg);
        // Wait for device to suspend
        do {
            reg = get_reg(sc, PCNET_CSR_ECI);
        } while (0 == (reg & PCNET_CSR_ECI_SPND));
        cpd->active = 0;
    }

#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.
        cyg_uint16 rcr;
        rcr = get_reg(sc, PCNET_CSR_MODE );
        rcr |= PCNET_CSR_MODE_PROM;
        put_reg(sc, PCNET_CSR_MODE, rcr );
    }
#endif

    // Unsuspend the device
    reg = get_reg(sc, PCNET_CSR_ECI);
    reg &= ~PCNET_CSR_ECI_SPND;
    put_reg(sc, PCNET_CSR_ECI, reg);
    cpd->active = 1;
}

//
// This routine is called to perform special "control" opertions
//
static int
pcnet_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 pcnet_priv_data *cpd =
        (struct pcnet_priv_data *)sc->driver_private;
    cyg_bool was_active = cpd->active;

    DEBUG_FUNCTION();

    // If device is already active, suspend it
    if (cpd->active) {
        reg = get_reg(sc, PCNET_CSR_ECI);
        reg |= PCNET_CSR_ECI_SPND;
        put_reg(sc, PCNET_CSR_ECI, reg);
        // Wait for device to suspend
        do {
            reg = get_reg(sc, PCNET_CSR_ECI);
        } while (0 == (reg & PCNET_CSR_ECI_SPND));
        cpd->active = 0;
    }

    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, PCNET_CSR_PAR0+i/2, reg );
        }
        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, PCNET_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)
    {
        cyg_uint16 anr;
        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 PCNet" );
        // CYG_ASSERT( 48 > strlen(p->description), "Description too long" );

        anr = get_reg(sc, PCNET_ANR_PHYSTAT);
        if ((anr & PCNET_ANR_PHYSTAT_LINK) == 0) {
            p->operational = 2;         // LINK DOWN
            p->duplex = 1;              // UNKNOWN
            p->speed = 0;
        }
        else {
            p->operational = 3;         // LINK UP
            anr = get_reg(sc, PCNET_ANR_PHYCTRL);

            if (anr & PCNET_ANR_PHYCTRL_DUPLEX)
                p->duplex = 3;              // 3 = DUPLEX
            else
                p->duplex = 2;              // 2 = SIMPLEX
            p->speed = (anr & PCNET_ANR_PHYCTRL_100MBPS) ? 100 * 1000000 : 10 * 1000000;
        }

#if FIXME
#ifdef KEEP_STATISTICS
        {
            struct amd_pcnet_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;
    }

⌨️ 快捷键说明

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