if_lan91cxx.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,486 行 · 第 1/4 页
C
1,486 行
//==========================================================================
//
// dev/if_lan91cxx.c
//
// Ethernet device driver for SMSC LAN91CXX compatible controllers
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2003 Nick Garnett
// Copyright (C) 2004 Andrew Lunn
// Copyright (C) 2004 eCosCentric Ltd.
//
// 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.
//
// -------------------------------------------
//####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, based on lan900 (for LAN91C110) driver by jskov
// jskov, based on CS8900 driver by Gary Thomas
// Contributors: gthomas, jskov, hmt, jco@ict.es, nickg
// Date: 2001-01-22
// Purpose:
// Description: hardware driver for LAN91CXX "LAN9000" ethernet
// Notes: Pointer register is not saved/restored on receive interrupts.
// The pointer is shared by both receive/transmit code.
// But the net stack manages atomicity for you here.
//
// The controller has an autorelease mode that allows TX packets
// to be freed automatically on successful transmission - but
// that is not used since we're only sending one packet at a
// time anyway.
// We may want to pingpong in future for throughput reasons.
//
// <jco@ict.es> Added support for PCMCIA mode and shifted
// address buses.
//
//####DESCRIPTIONEND####
//
//==========================================================================
// Based on LAN91C110 and LAN91C96
#include <pkgconf/system.h>
#include <pkgconf/devs_eth_smsc_lan91cxx.h>
#include <pkgconf/io_eth_drivers.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_diag.h>
#include <cyg/infra/cyg_ass.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 <cyg/kernel/kapi.h>
#include <net/if.h> /* Needed for struct ifnet */
#endif
#ifdef CYGPKG_INFRA_DEBUG
// Then we log, OOI, the number of times we get a bad packet number
// from the tx done fifo.
int lan91cxx_txfifo_good = 0;
int lan91cxx_txfifo_bad = 0;
#endif
// Set to perms of:
// 0 disables all debug output
// 1 for process debug output
// 2 for added data IO output: get_reg, put_reg
// 4 for packet allocation/free output
// 8 for only startup status, so we can tell we're installed OK
#define DEBUG 0
#if DEBUG
#if defined(CYGPKG_REDBOOT)
static void db_printf( char *fmt, ... )
{
extern int start_console(void);
extern void end_console(int);
va_list a;
int old_console;
va_start( a, fmt );
old_console = start_console();
diag_vprintf( fmt, a );
end_console(old_console);
va_end( a );
}
#else
#if 0
static void db_printf( char *fmt, ... )
{
va_list a;
int old_console = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
va_start( a, fmt );
CYGACC_CALL_IF_SET_CONSOLE_COMM( 0 );
diag_vprintf( fmt, a );
CYGACC_CALL_IF_SET_CONSOLE_COMM(old_console);
va_end( a );
}
#else
#define db_printf diag_printf
#endif
#endif
#else
#if 0
static void db_printf( char *fmt, ... )
{
va_list a;
int old_console = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
va_start( a, fmt );
CYGACC_CALL_IF_SET_CONSOLE_COMM( 0 );
diag_vprintf( fmt, a );
CYGACC_CALL_IF_SET_CONSOLE_COMM(old_console);
va_end( a );
}
#else
#define db_printf( fmt, ... )
#endif
#endif
#if DEBUG & 1
#define DEBUG_FUNCTION() do { db_printf("%s\n", __FUNCTION__); } while (0)
#else
#define DEBUG_FUNCTION() do {} while(0)
#endif
#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD)
#define KEEP_STATISTICS
#endif
#ifdef KEEP_STATISTICS
#define INCR_STAT( _x_ ) (cpd->stats. _x_ ++)
#else
#define INCR_STAT( _x_ ) CYG_EMPTY_STATEMENT
#endif
#include "smsc_lan91cxx.h"
#ifdef LAN91CXX_IS_LAN91C111
static void lan91cxx_write_phy(struct eth_drv_sc *sc, cyg_uint8 phyaddr,
cyg_uint8 phyreg, cyg_uint16 value);
static cyg_uint16 lan91cxx_read_phy(struct eth_drv_sc *sc, cyg_uint8 phyaddr,
cyg_uint8 phyreg);
#endif
static void lan91cxx_poll(struct eth_drv_sc *sc);
#ifdef LAN91CXX_IS_LAN91C111
// Revision A of the LAN91C111 has a bug in which it does not set the
// ODD bit in the status word and control byte of received packets. We
// work around this by assuming the bit is always set and tacking the
// extra odd byte onto the packet anyway. Higher protocol levels never
// believe the packet size reported by the driver and always use the
// values in the protocol headers. So this workaround is quite safe.
// In theory nobody should be using the RevA part now, but it appears
// that some people still have some in their parts bins.
#define LAN91CXX_RX_STATUS_IS_ODD(__cpd,__stat) \
(((__cpd)->c111_reva)?1:((__stat) & LAN91CXX_RX_STATUS_ODDFRM))
#define LAN91CXX_CONTROLBYTE_IS_ODD(__cpd,__val) \
(((__cpd)->c111_reva)?1:((__val) & LAN91CXX_CONTROLBYTE_ODD))
#else
#define LAN91CXX_RX_STATUS_IS_ODD(__cpd,__stat) ((__stat) & LAN91CXX_RX_STATUS_ODDFRM)
#define LAN91CXX_CONTROLBYTE_IS_ODD(__cpd,__val) ((__val) & LAN91CXX_CONTROLBYTE_ODD)
#endif
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
static cyg_interrupt lan91cxx_interrupt;
static cyg_handle_t lan91cxx_interrupt_handle;
// This ISR is called when the ethernet interrupt occurs
static int lan91cxx_isr(cyg_vector_t vector, cyg_addrword_t data)
/* , HAL_SavedRegisters *regs */
{
struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
// DEBUG_FUNCTION();
INCR_STAT( interrupts );
cyg_drv_interrupt_mask(cpd->interrupt);
cyg_drv_interrupt_acknowledge(cpd->interrupt);
return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR); // Run the DSR
}
#endif
// The deliver function (ex-DSR) handles the ethernet [logical] processing
static void
lan91cxx_deliver(struct eth_drv_sc *sc)
{
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
// DEBUG_FUNCTION();
// Service the interrupt:
lan91cxx_poll(sc);
// Allow interrupts to happen again
cyg_drv_interrupt_unmask(cpd->interrupt);
}
static int
lan91cxx_int_vector(struct eth_drv_sc *sc)
{
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
return (cpd->interrupt);
}
static bool
smsc_lan91cxx_init(struct cyg_netdevtab_entry *tab)
{
struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
struct lan91cxx_priv_data *cpd =
(struct lan91cxx_priv_data *)sc->driver_private;
unsigned short val;
int i;
#if CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE
unsigned char ecor, ecsr;
#endif
cyg_bool esa_configured = false;
DEBUG_FUNCTION();
cpd->txbusy = cpd->within_send = 0;
#ifdef CYGNUM_DEVS_ETH_SMSC_LAN91CXX_SHIFT_ADDR
cpd->addrsh = CYGNUM_DEVS_ETH_SMSC_LAN91CXX_SHIFT_ADDR;
#else
cpd->addrsh = 0;
#endif
#if CYGINT_DEVS_ETH_SMSC_LAN91CXX_PCMCIA_MODE
// If the chip is configured in PCMCIA mode, the internal
// registers mapped in the attribute memory should be
// initialized (i.e. to enable the I/O map)
ecor = get_att(sc, LAN91CXX_ECOR);
// pulse SRESET on ECOR
ecor |= LAN91CXX_ECOR_RESET;
put_att(sc, LAN91CXX_ECOR, ecor);
CYGACC_CALL_IF_DELAY_US(1);
ecor &= ~LAN91CXX_ECOR_RESET;
put_att(sc, LAN91CXX_ECOR, ecor);
// then, enable I/O map
ecor |= LAN91CXX_ECOR_ENABLE;
put_att(sc, LAN91CXX_ECOR, ecor);
// verify the register contents
if (ecor != get_att(sc, LAN91CXX_ECOR))
db_printf("LAN91CXX - Cannot access PCMCIA attribute registers\n");
ecsr = get_att(sc, LAN91CXX_ECSR);
#ifdef CYGSEM_DEVS_ETH_SMSC_LAN91CXX_8_BIT
#error "91CXX 8-bit mode not yet supported."
ecsr |= LAN91CXX_ECSR_IOIS8;
#else
ecsr &= ~LAN91CXX_ECSR_IOIS8;
#endif
put_att(sc, LAN91CXX_ECSR, ecsr);
#endif
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
// Initialize environment, setup interrupt handler
cyg_drv_interrupt_create(cpd->interrupt,
CYGNUM_DEVS_ETH_SMSC_LAN91CXX_INT_PRIO,
(cyg_addrword_t)sc, // Data item passed to interrupt handler
(cyg_ISR_t *)lan91cxx_isr,
(cyg_DSR_t *)eth_drv_dsr, // The logical driver DSR
&lan91cxx_interrupt_handle,
&lan91cxx_interrupt);
cyg_drv_interrupt_attach(lan91cxx_interrupt_handle);
#endif // !CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
cyg_drv_interrupt_acknowledge(cpd->interrupt);
cyg_drv_interrupt_unmask(cpd->interrupt);
// probe chip by reading the signature in BS register
val = get_banksel(sc);
#if DEBUG & 9
db_printf("LAN91CXX - supposed BankReg @ %x = %04x\n",
cpd->base+LAN91CXX_BS, val );
#endif
if ((0xff00 & val) != 0x3300) {
CYG_FAIL("No 91Cxx signature" );
diag_printf("smsc_lan91cxx_init: No 91Cxx signature found\n");
return false;
}
val = get_reg(sc, LAN91CXX_REVISION);
#if DEBUG & 9
db_printf("LAN91CXX - type: %01x, rev: %01x\n",
(val>>4)&0xf, val & 0xf);
#endif
#ifdef LAN91CXX_IS_LAN91C111
// Set RevA flag for LAN91C111 so we can cope with the odd-bit bug.
cpd->c111_reva = (val == 0x3390);
#endif
// The controller may provide a function used to set up the ESA
if (cpd->config_enaddr)
(*cpd->config_enaddr)(cpd);
// Reset chip
put_reg(sc, LAN91CXX_RCR, LAN91CXX_RCR_SOFT_RST);
put_reg(sc, LAN91CXX_RCR, 0);
val = get_reg(sc, LAN91CXX_EPH_STATUS);
#ifndef LAN91CXX_IS_LAN91C111
// LINK_OK on 91C111 is just a general purpose input and may not
// have anything to do with the link.
if (!(val & LAN91CXX_STATUS_LINK_OK)) {
db_printf("no link\n");
return false; // Link not connected
}
#endif
#if DEBUG & 9
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?