if_etherc.c

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

C
1,128
字号
                if ((cyg_uint32)p == reg) {
                    if (_SU32(reg, ETHERC_TD_STAT) & ETHERC_TD_STAT_TACT) {
                        still_active = true;
                        break;
                    }
                }
            }
        }
#else
        if (_SU32(reg, ETHERC_TD_STAT) & ETHERC_TD_STAT_TACT)
            still_active = true;
#endif
    } while (still_active);

    db_printf("all done\n");
#endif
    cpd->active = 0;
}

static void
etherc_stop(struct eth_drv_sc *sc)
{
    struct etherc_priv_data *cpd =
        (struct etherc_priv_data *)sc->driver_private;

    DEBUG_FUNCTION();
    
    etherc_suspend(cpd);
}

//
// 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
etherc_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
    cyg_uint32 reg, prm_mode = 0;
    struct etherc_priv_data *cpd =
        (struct etherc_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) {
        etherc_suspend(cpd);
    }

#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.
        prm_mode = CYGARC_REG_ECMR_PRM;
    }
#endif

    // Unsuspend the device
    reg = get_reg(cpd, _REG_ECMR);
    reg |= CYGARC_REG_ECMR_TE | CYGARC_REG_ECMR_RE;
    reg &= ~CYGARC_REG_ECMR_PRM;
    reg |= prm_mode;
    put_reg(cpd, _REG_ECMR, reg);

    put_reg(cpd, _REG_EDRRR, CYGARC_REG_EDRRR_RR);

    cpd->active = 1;
}

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

    DEBUG_FUNCTION();

    // If device is already active, suspend it
    if (cpd->active) {
        etherc_suspend(cpd);
    }

    res = 0;                            // expect success
    switch (key) {
    case ETH_DRV_SET_MAC_ADDRESS:
#if 9 & DEBUG
        db_printf("ETHERC - 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];
        put_reg(cpd, _REG_MAHR,(cpd->esa[0] << 24) | (cpd->esa[1] << 16) | (cpd->esa[2] << 8) | cpd->esa[3]);
        put_reg(cpd, _REG_MALR, (cpd->esa[4] << 8) | cpd->esa[5]);
        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.
        cyg_uint32 reg;
        reg = get_reg(cpd, _REG_MAHR);
        cpd->esa[0] = (reg >> 24) & 0xff;
        cpd->esa[1] = (reg >> 16) & 0xff;
        cpd->esa[2] = (reg >> 08) & 0xff;
        cpd->esa[3] = (reg >> 00) & 0xff;
        reg = get_reg(cpd, _REG_MALR);
        cpd->esa[4] = (reg >> 08) & 0xff;
        cpd->esa[5] = (reg >> 00) & 0xff;
        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, "SH Etherc" );
        // CYG_ASSERT( 48 > strlen(p->description), "Description too long" );

        if (_MII_LINK_STATE(cpd, 1)) {
            // Link is up
            p->operational = 3;         // LINK UP
            if (_MII_DUPLEX_STATE(cpd, 1))
                p->duplex = 3;              // 3 = DUPLEX
            else
                p->duplex = 2;              // 2 = SIMPLEX
            if (_MII_SPEED_STATE(cpd, 1))
                p->speed = 100 * 1000000;
            else
                p->speed = 10 * 1000000;
        } else {
            // Link is down
            p->operational = 2;         // LINK DOWN
            p->duplex = 1;              // UNKNOWN
            p->speed = 0;
        }
#if FIXME
#ifdef KEEP_STATISTICS
        {
            struct sh_etherc_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;
    }

    // Restore controller state
    if (was_active) {
        // Unsuspend the device
        reg = get_reg(cpd, _REG_ECMR);
        reg |= CYGARC_REG_ECMR_RE | CYGARC_REG_ECMR_TE;
        put_reg(cpd, _REG_ECMR, reg);
    }

    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
etherc_can_send(struct eth_drv_sc *sc)
{
    struct etherc_priv_data *cpd =
        (struct etherc_priv_data *)sc->driver_private;

    DEBUG_FUNCTION();

    if (!_MII_LINK_STATE(cpd, 1))
        return 0;                       // Link not connected

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

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

    cyg_uint8* sdata = NULL;
    cyg_uint8 *d, *buf, *txd;

    DEBUG_FUNCTION();

    INCR_STAT( tx_count );

    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
            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*ETHERC_TD_SIZE;
    buf = cpd->tx_buffers + ring_entry*_BUF_SIZE;
    CYG_ASSERT(0 == (_SU32(txd, ETHERC_TD_STAT) & ETHERC_TD_STAT_TACT),
               "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 (plen < IEEE_8023_MIN_FRAME) {
#if DEBUG & 1
        db_printf("Packet too short (%d) - padding to %d bytes.\n", plen, IEEE_8023_MIN_FRAME);
#endif
        for (i = plen; i < IEEE_8023_MIN_FRAME; i++)
            *d++ = 0;
        plen = IEEE_8023_MIN_FRAME;
    }

    _SU16(txd, ETHERC_TD_TDL) = plen;
    _SU32(txd, ETHERC_TD_STAT) |= ETHERC_TD_STAT_TACT;

#if DEBUG & 1
    db_printf("Last TX: LEN %04x STAT %08x PTR %08x\n", 
              _SU16(txd, ETHERC_TD_TDL),
              _SU32(txd, ETHERC_TD_STAT),
              _SU32(txd, ETHERC_TD_TBA));
#endif

    // Set transmit demand
    put_reg(cpd, _REG_EDTRR, CYGARC_REG_EDTRR_TR);

#if DEBUG & 1
    {
        cyg_uint32 reg;
        reg = get_reg(cpd, _REG_EESR);
        db_printf("%s:END: EESR at TX: 0x%08x\n", __FUNCTION__, reg);
    }
#endif
}

static void
etherc_TxEvent(struct eth_drv_sc *sc, int stat)
{
     struct etherc_priv_data *cpd =
        (struct etherc_priv_data *)sc->driver_private;
    int success = 1;
    cyg_uint8 *txd;
    cyg_uint32 pkt_stat;

    DEBUG_FUNCTION();

    if (0 == cpd->tx_ring_owned) {
#if DEBUG & 1
        db_printf("%s: got TX completion when no outstanding packets\n", __FUNCTION__);
#endif
        // Ack the TX int
        put_reg(cpd, _REG_EESR, CYGARC_REG_EESR_TC);
        return;
    }
        

    INCR_STAT( tx_complete );

    txd = cpd->tx_ring + cpd->tx_ring_alloc*ETHERC_TD_SIZE;
    pkt_stat = _SU32(txd, ETHERC_TD_STAT);
    if (pkt_stat & ETHERC_TD_STAT_TACT) {
#if DEBUG & 1
        db_printf("%s: got TX completion when buffer is still owned\n", __FUNCTION__);
#endif
        // first dirty ring entry not freed - wtf?
    }

    if (pkt_stat & ETHERC_TD_STAT_TDFE) {
        // We had an error. Tell the stack.
        success = 0;
#if DEBUG & 1
        db_printf("%s: TX failure, retrying...\n", __FUNCTION__);

⌨️ 快捷键说明

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