if_upd985xx.c

来自「eCos操作系统源码」· C语言 代码 · 共 1,622 行 · 第 1/4 页

C
1,622
字号
//==========================================================================////      if_upd985xx.c////	Ethernet drivers//	NEC UPD985XX device ethernet specific support////==========================================================================//####ECOSGPLCOPYRIGHTBEGIN####// -------------------------------------------// This file is part of eCos, the Embedded Configurable Operating System.// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.//// eCos is free software; you can redistribute it and/or modify it under// the terms of the GNU General Public License as published by the Free// Software Foundation; either version 2 or (at your option) any later version.//// eCos is distributed in the hope that it will be useful, but WITHOUT ANY// WARRANTY; without even the implied warranty of MERCHANTABILITY or// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License// for more details.//// You should have received a copy of the GNU General Public License along// with eCos; if not, write to the Free Software Foundation, Inc.,// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.//// As a special exception, if other files instantiate templates or use macros// or inline functions from this file, or you compile this file and link it// with other works to produce a work based on this file, this file does not// by itself cause the resulting work to be covered by the GNU General Public// License. However the source code for this file must still be made available// in accordance with section (3) of the GNU General Public License.//// This exception does not invalidate any other reasons why a work based on// this file might be covered by the GNU General Public License.//// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.// at http://sources.redhat.com/ecos/ecos-license/// -------------------------------------------//####ECOSGPLCOPYRIGHTEND####//####BSDCOPYRIGHTBEGIN####//// -------------------------------------------//// Portions of this software may have been derived from OpenBSD or other sources,// and are covered by the appropriate copyright disclaimers included herein.//// -------------------------------------------////####BSDCOPYRIGHTEND####//==========================================================================//#####DESCRIPTIONBEGIN####//// Author(s):    hmt, gthomas// Contributors:// Date:         2001-06-28// Purpose:      // Description:  hardware driver for uPD985xx ethernet devices//              ////####DESCRIPTIONEND####////==========================================================================#include <pkgconf/system.h>#include <pkgconf/devs_eth_mips_upd985xx.h>#include <pkgconf/io_eth_drivers.h>#include <cyg/infra/cyg_type.h>#include <cyg/infra/cyg_ass.h>#include <cyg/hal/hal_arch.h>#include <cyg/hal/hal_intr.h>#include <cyg/hal/hal_cache.h>#include <cyg/infra/diag.h>#include <cyg/hal/drv_api.h>#include <cyg/io/eth/netdev.h>#include <cyg/io/eth/eth_drv.h>#ifdef CYGPKG_NET#include <pkgconf/net.h>#include <net/if.h>  /* Needed for struct ifnet */#else#include <cyg/hal/hal_if.h>#endif#include <cyg/devs/eth/upd985xx_eth.h>#include <cyg/io/eth/eth_drv_stats.h>// ------------------------------------------------------------------------#ifdef CYGDBG_DEVS_ETH_MIPS_UPD985XX_CHATTER#define nDEBUG_TRAFFIC	// This one prints stuff as packets come and go#define nDEBUG_IOCTL	// ioctl() call printing#define DEBUG      	// Startup printing mainly#endif#define os_printf diag_printf#define db_printf diag_printf#define STATIC static// ------------------------------------------------------------------------// I/O access macros as inlines for later changes to >1 device?//// (If we need to do this, then these macros would *assume* the//  presence of a valid p_eth_upd985xx just like we always have)static inline void OUTL( volatile cyg_uint32 *io_address, cyg_uint32 value ){   *io_address = value;   }static inline cyg_uint32 INL( volatile cyg_uint32 *io_address ){   return *io_address;    }// These map cachable addresses to uncachable ones and vice versa.// This is all fixed on MIPS.  8-9xxxxxxx uncachable, A-Bxxxxxxx cachable.#define VIRT_TO_BUS( _x_ ) virt_to_bus((cyg_uint32)(_x_))static inline cyg_uint8 *virt_to_bus(cyg_uint32 p_memory){    return (cyg_uint8 *)(0xa0000000u + (p_memory  & ~0xe0000000u));}#define BUS_TO_VIRT( _x_ ) bus_to_virt((cyg_uint32)(_x_))static inline cyg_uint8 *bus_to_virt(cyg_uint32 p_memory){    return (cyg_uint8 *)(0x80000000u + (p_memory  & ~0xe0000000u));}// ------------------------------------------------------------------------#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_S1#define FLUSH_WRITES() CYG_MACRO_START          \    (void) INL( ETH_RXSR );                     \CYG_MACRO_END#else#define FLUSH_WRITES() CYG_EMPTY_STATEMENT#endif// ------------------------------------------------------------------------////                      DEVICES AND PACKET QUEUES//// ------------------------------------------------------------------------// 128 bytes extra for VLAN packets should be enough; AFAICT usually the// encapsulation is only 4 or 10 bytes extra.#define MAX_ETHERNET_PACKET_SIZE  1536  // Ethernet Rx packet size#define MAX_OVERSIZE_PACKET_SIZE  1664  // VLAN Rx packet size#define MAX_RX_PACKET_SIZE        MAX_OVERSIZE_PACKET_SIZE#define NUM_RXBUFS (8)// This one is the hardware definition.struct bufdesc {    volatile cyg_uint32 attr;    cyg_uint8 *ptr;};// Rx databuffer.STATIC cyg_uint8 rx_databuf[ NUM_RXBUFS ] [ MAX_RX_PACKET_SIZE ];#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E3// Tx databufferSTATIC cyg_uint8 tx_databuf[ MAX_RX_PACKET_SIZE ];#endifstruct eth_upd985xx {    cyg_uint8 active, index, tx_busy, mac_addr_ok;    cyg_uint8 vector; // interrupt numbers are small    cyg_uint8 phy_status; // from PHY_STATUS_ flags below    cyg_uint8 hardwired_esa;#ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2    cyg_uint8 promisc;#endif    cyg_uint8 mac_address[6];    cyg_handle_t   interrupt_handle;    cyg_interrupt  interrupt_object;    struct cyg_netdevtab_entry *ndp;    // these shall hold uncached addresses of the structures following...    volatile struct bufdesc *txring;    volatile struct bufdesc *rxring_active;    volatile struct bufdesc *rxring_next;    int rxring_active_index;    int rxring_next_index;    cyg_uint32 intrs;    cyg_uint32 tx_keys[1];    // -----------------------------------------------------------------    // Statistics counters    cyg_uint32 count_rx_resource;    cyg_uint32 count_rx_restart;    cyg_uint32 count_interrupts;    cyg_uint32 count_bad_isr_restarts;    cyg_uint32 count_bad_tx_completion;    // -----------------------------------------------------------------    // DO NOT ACCESS THESE DIRECTLY - THE DEVICE HAS TO SEE THEM UNCACHED    // Initially, enough for one whole transmission to be described in one go,    // plus a null link on the end.    struct bufdesc tx_bufdesc[ MAX_ETH_DRV_SG + 2 ];    // Pending rx buffers, of full size.    struct bufdesc rx_bufdesc[ NUM_RXBUFS+1 ];    // -----------------------------------------------------------------};struct eth_upd985xx eth_upd985xx[CYGNUM_DEVS_ETH_MIPS_UPD985XX_DEV_COUNT] = {    {        index: 0,        vector: CYGNUM_HAL_INTERRUPT_ETHER, #ifdef CYGOPT_DEVS_ETH_MIPS_UPD985XX_HARDWARE_BUGS_E1E2        promisc: 0,#endif#ifdef CYGSEM_DEVS_ETH_UPD985XX_ETH0_SET_ESA        hardwired_esa: 1,        mac_address: CYGDAT_DEVS_ETH_UPD985XX_ETH0_ESA,        mac_addr_ok: 1,#else        hardwired_esa: 0,        mac_addr_ok: 0,#endif    }};// eth0ETH_DRV_SC(upd985xx_sc0,           &eth_upd985xx[0],            // Driver specific data           CYGDAT_DEVS_ETH_UPD985XX_ETH0_NAME, // name for this interface           eth_upd985xx_start,           eth_upd985xx_stop,           eth_upd985xx_ioctl,           eth_upd985xx_can_send,           eth_upd985xx_send,           eth_upd985xx_recv,           eth_upd985xx_deliver,           eth_upd985xx_poll,           eth_upd985xx_int_vector    );NETDEVTAB_ENTRY(upd985xx_netdev0,                 "upd985xx-" CYGDAT_DEVS_ETH_UPD985XX_ETH0_NAME,                upd985xx_eth_upd985xx_init,                 &upd985xx_sc0);// This is in a macro so that if more devices arrive it can easily be changed#define CHECK_NDP_SC_LINK() CYG_MACRO_START                              \    CYG_ASSERT( ((void *)ndp == (void *)&upd985xx_netdev0), "Bad ndp" ); \    CYG_ASSERT( ((void *)sc == (void *)&upd985xx_sc0), "Bad sc" );       \    CYG_ASSERT( (void *)p_eth_upd985xx == sc->driver_private,            \                "sc pointer bad" );                                      \    CYG_ASSERT( (void *)p_eth_upd985xx == (void *)&eth_upd985xx[0],      \                "bad p_eth_upd985x" );                                   \CYG_MACRO_END#define NUM_ELEMENTS( _x_ ) (sizeof( (_x_) ) / sizeof( (_x_[0]) ) )// ------------------------------------------------------------------------////                       FUNCTION PROTOTYPES//// ------------------------------------------------------------------------STATIC void InitRxRing(struct eth_upd985xx* p_eth_upd985xx);STATIC void NextRxRing(struct eth_upd985xx* p_eth_upd985xx);STATIC void InitTxRing(struct eth_upd985xx* p_eth_upd985xx);STATIC void ResetTxRing(struct eth_upd985xx* p_eth_upd985xx);#ifdef CYGPKG_NETSTATIC int eth_upd985xx_configure(struct eth_upd985xx* p_eth_upd985xx,                                  int promisc, int oversized);#endifSTATIC void PacketRxReady(struct eth_upd985xx* p_eth_upd985xx);STATIC void TxDone(struct eth_upd985xx* p_eth_upd985xx);#define PHY_STATUS_LINK    (1)#define PHY_STATUS_FDX     (2)#define PHY_STATUS_100MBPS (4)STATIC int eth_upd985xx_status( struct eth_upd985xx *p_eth_upd985xx );STATIC int eth_set_mac_address( struct eth_upd985xx *p_eth_upd985xx, void *data );// ------------------------------------------------------------------------////                       MII ACCESS TO PHY DEVICE//// ------------------------------------------------------------------------STATIC cyg_bool mii_read( cyg_uint32 reg, cyg_uint32 *pvalue,                          struct eth_upd985xx *p_eth_upd985xx ){    int i = 1000;    // wait a bit for it to be idle    while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY)                  & INL(ETH_MIND)) )        if ( --i < 0 )            return false;    // Tell it the register address and PHY address    OUTL( ETH_MADR, ETH_MADR_PHY_DEVICE_PHYS_ADDRESS | reg );    OUTL( ETH_MCMD, ETH_MCMD_RSTAT ); // "do a read"    // wait for the read to complete    while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY)                  & INL(ETH_MIND)) )        if ( --i < 0 )            return false;    // so get the data    *pvalue = INL( ETH_MRDD );    return true;}#if 0STATIC cyg_bool mii_write( cyg_uint32 reg, cyg_uint32 value,                          struct eth_upd985xx *p_eth_upd985xx ){    int i = 1000;    // wait a bit for it to be idle    while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY)                  & INL(ETH_MIND)) )        if ( --i < 0 )            return false;    // Tell it the register address and PHY address    OUTL( ETH_MADR, ETH_MADR_PHY_DEVICE_PHYS_ADDRESS | reg );    // And write the data:    OUTL( ETH_MWTD, value );    // wait a bit for it to be idle    while ( 0 != ((ETH_MIND_NVALID | ETH_MIND_SCANA | ETH_MIND_BUSY)                  & INL(ETH_MIND)) )        if ( --i < 0 )            return false;    return true;}#endif// ------------------------------------------------------------------------////                       INTERRUPT HANDLERS//// ------------------------------------------------------------------------STATIC cyg_uint32 eth_isr(cyg_vector_t vector, cyg_addrword_t data){    cyg_drv_interrupt_mask( vector );    return CYG_ISR_CALL_DSR;        // schedule DSR}// ------------------------------------------------------------------------// This is a callback from the higher level thread in consequence of the DSRSTATIC voideth_upd985xx_deliver(struct eth_drv_sc *sc){    register int intrs;    struct eth_upd985xx *p_eth_upd985xx;    p_eth_upd985xx = (struct eth_upd985xx *)sc->driver_private;    p_eth_upd985xx->count_interrupts++;    intrs = INL( ETH_ISR ); // Read-clear    // Acknowledge once at the start anyway to prevent an interrupt loop in    // case of a transient - interrupts latch in the interrupt controller    // as well as in the ethernet device.    cyg_drv_interrupt_acknowledge(p_eth_upd985xx->vector);#ifdef DEBUG_TRAFFIC    os_printf("\n[[[[[[[ Deliver intrs = %x\n", intrs );#endif    // Guard possible external entry points    if ( ! p_eth_upd985xx->active )        return; // without unmasking the interrupt    while ( intrs ) {        if ( 0xffff0000 & intrs ) {            // Then something very bad has happened            p_eth_upd985xx->count_bad_isr_restarts++;            CYG_ASSERT ( p_eth_upd985xx->active, "Device not active!" );            eth_upd985xx_stop( sc );            eth_upd985xx_start( sc, NULL, 0 );            intrs = INL( ETH_ISR ); // Read-clear        }        p_eth_upd985xx->intrs = intrs;        if ( ( ETH_ISR_XMTDN | ETH_ISR_TABR ) & intrs ) {            // Scan for completed Txen and inform the stack            TxDone(p_eth_upd985xx);        }        if ( ( ETH_ISR_RCVDN | ETH_ISR_RBDRS | ETH_ISR_RBDRU ) & intrs ) {            // Pass any rx data up the stack            PacketRxReady(p_eth_upd985xx);        }        // Now we have made the interrupt causes go away, acknowledge and        // *then* read the ISR again.  That way the race can result in a        // spurious interrupt rather than a lost interrupt.        cyg_drv_interrupt_acknowledge(p_eth_upd985xx->vector);        intrs = INL( ETH_ISR ); // Read-clear#ifdef DEBUG_TRAFFIC        if ( intrs )            os_printf("------- Again intrs = %x\n", intrs );#endif    }#ifdef DEBUG_TRAFFIC    os_printf("]]]]]]]] Done intrs = %x\n\n", intrs );#endif

⌨️ 快捷键说明

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