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, ð_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 *)ð_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 + -
显示快捷键?