This is the mail archive of the
ecos-patches@sources.redhat.com
mailing list for the eCos project.
RealTek 8139 modifications
- From: Chris Nimmers <chris at nimseus dot com>
- To: ecos-patches at sources dot redhat dot com
- Date: Wed, 6 Aug 2003 13:50:58 +0200
- Subject: RealTek 8139 modifications
Using Erics RTL 8139 driver (revision 1, 01 Aug) the attached
files if_8139.h and if_8139.c has been changed to work in an
i386 environment with interrupts. The original worked fine in
an Redboot setup on a i386 as it uses polling instead of
interrupts.
Any crucial modifications are indicated by
"//////****** Chg ->" as beginning and
"//////****** Chg <-" as ending.
Some changes may not be necessary to get the driver running
and I will return when I have had a chance to test each
individual modification.
Testing has been made on an Advantech PCM-5820.
Best regards,
Chris Nimmers
#ifndef CYGONCE_DEVS_ETH_REALTEK_8139_INFO_H
#define CYGONCE_DEVS_ETH_REALTEK_8139_INFO_H
/*==========================================================================
//
// 8139_info.h
//
//
//==========================================================================
//####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####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): Eric Doenges
// Contributors:
// Date: 2003-07-09
// Description:
//
//####DESCRIPTIONEND####
*/
#include <pkgconf/devs_eth_rltk_8139.h>
/*
* Used to define all vendor/device ID combinations we know about to find
* the chip.
*/
typedef struct {
cyg_uint16 vendor_id;
cyg_uint16 device_id;
void *data;
} pci_identifier_t;
#define PCI_ANY_ID (~0U)
/*
* Valid receive buffer sizes are 8k+16, 16k+16, 32k+16, or 64k+16.
* For the last case, WRAP mode should not be enabled. Since we do not
* currently want to support unwrapped mode, do not use 64k+16 at this
* point. The buffer length is set via the configuration mechanism.
*/
#if (CYGNUM_DEVS_ETH_RLTK_8139_RX_BUF_LEN_IDX < 0) |\
(CYGNUM_DEVS_ETH_RLTK_8139_RX_BUF_LEN_IDX > 2)
#error "The receive ring size index must be in the range of 0 to 2"
#endif
#define RX_BUF_LEN (8192 << (CYGNUM_DEVS_ETH_RLTK_8139_RX_BUF_LEN_IDX))
#define RX_BUF_PAD 16
#define RX_BUF_WRAP_PAD 2048 /* spare padding to handle lack of packet wrap */
#define RX_BUF_TOT_LEN (RX_BUF_LEN + RX_BUF_PAD + RX_BUF_WRAP_PAD)
/* Number of Tx descriptor registers. */
#define NUM_TX_DESC 4
/*
* Max supported ethernet frame size. The 8139 cannot transmit packets more
* than 1792 bytes long. Also, since transmit buffers must be 32-bit
* aligned, MAX_ETH_FRAME_SIZE should always be a multiple of 4.
*/
#define MIN_ETH_FRAME_SIZE 60 /* without FCS */
#define MAX_ETH_FRAME_SIZE 1536 /* is this with/without FCS ? */
/* Size of the Tx buffers. */
#define TX_BUF_SIZE MAX_ETH_FRAME_SIZE
#define TX_BUF_TOT_LEN (TX_BUF_SIZE * NUM_TX_DESC)
/* Rx buffer level before first PCI transfer ('5' is 512 bytes) */
#define RX_FIFO_THRESH 5
/*
* Maximum PCI rx and tx bursts. This value ranges from '0' (16 bytes)
* to '7' (unlimited), with MXDMA = 2^(4 + 'value').
*/
#define RX_DMA_BURST 6
#define TX_DMA_BURST 6
/*
* Device driver private data
*/
typedef struct {
/* Device number. Used for actually finding the device */
cyg_uint32 device_num;
/* Receive buffer ring area */
cyg_uint8 *rx_ring;
/* Transmit buffer area */
cyg_uint8 *tx_buffer;
/* PCI device ID */
cyg_pci_device_id pci_device_id;
/* Address for memory mapped I/O */
cyg_uint32 base_address;
/* Our current MAC address */
unsigned char mac[6];
/* tx FIFO threshold. */
cyg_uint8 tx_threshold;
/* This is the first free descriptor. */
int tx_free_desc;
/* This is the number of currently free descriptors */
int tx_num_free_desc;
/* Keys to match _send calls with the tx_done callback */
unsigned long tx_desc_key[NUM_TX_DESC];
/*
* This is used to (temporarily) store the address of the current
* received packet. We save it here to avoid having to calculate it
* several times.
*/
cyg_uint8 *rx_current;
/* Interrupt handling stuff */
cyg_vector_t vector;
cyg_handle_t interrupt_handle;
cyg_interrupt interrupt;
//////****** Chg ->
// Network Device Pointer
void *ndp;
//////****** Chg <-
} Rltk8139_t;
/*
* Register offsets and bit definitions. These use the names in the 8139
* data sheet, not those found e.g. in the Linux driver for the 8139.
*/
enum {
IDR0 = 0x0, /* mac address, seemingly in big-endian order */
IDR1 = 0x1,
IDR2 = 0x2,
IDR3 = 0x3,
IDR4 = 0x4,
IDR5 = 0x5,
MAR0 = 0x8, /* multicast registers (0-7) */
MAR1 = 0x9,
MAR2 = 0xA,
MAR3 = 0xB,
MAR4 = 0xC,
MAR5 = 0xD,
MAR6 = 0xE,
MAR7 = 0xF,
TSD0 = 0x10, /* L, transmit status of descriptor 0 */
TSD1 = 0x14,
TSD2 = 0x18,
TSD3 = 0x1C,
TSAD0 = 0x20, /* L, transmit start address of descriptor 0 */
TSAD1 = 0x24,
TSAD2 = 0x28,
TSAD3 = 0x2C,
RBSTART = 0x30, /* L, receive buffer start address */
ERBCR = 0x34, /* W, early receive byte count register */
ERSR = 0x36, /* B, early receive status register */
CR = 0x37, /* B, command register */
CAPR = 0x38, /* W, current address of packet read */
CBR = 0x3A, /* W, current buffer address */
IMR = 0x3C, /* W, interrupt mask register */
ISR = 0x3E, /* W, interrupt status register */
TCR = 0x40, /* L, transmit configuration register */
RCR = 0x44, /* L, receive configuration register */
TCTR = 0x48, /* L, timer count register */
MPC = 0x4C, /* L, missed packet counter */
CR9346 = 0x50, /* B, 93C46 (serial eeprom) command register */
CONFIG0 = 0x51, /* B, configuration register 0 */
CONFIG1 = 0x52, /* B, configuration register 1 */
TIMERINT= 0x54, /* L, timer interrupt register */
MSR = 0x58, /* B, media status register */
CONFIG3 = 0x59, /* B, configuration register 0 */
CONFIG4 = 0x5A, /* B, configuration register 1 */
MULINT = 0x5C, /* W, multiple interrupt select */
RERID = 0x5E, /* B, PCI revision ID; should be 0x10 */
TSAD = 0x60, /* W, transmit status of all descriptors */
BMCR = 0x62, /* W, basic mode control register */
BMSR = 0x64, /* W, basic mode status register */
ANAR = 0x66, /* W, auto-negotiation advertisement register */
ANLPAR = 0x68, /* W, auto-negotiation link partner register */
ANER = 0x6A, /* W, auto-negotiation expansion register */
DIS = 0x6C, /* W, disconnect counter */
FCSC = 0x6E, /* W, false carrier sense counter */
NWAYTR = 0x70, /* W, N-way test register */
REC = 0x72, /* W, RX_ER counter */
CSCR = 0x74, /* W, CS configuration register */
PHY1_PARM = 0x78, /* L, PHY parameter 1 */
TW_PARM = 0x7C, /* L, twister parameter */
PHY2_PARM = 0x80, /* B, PHY parameter 2 */
CRC0 = 0x84, /* B, power management CRC register for wakeup frame 0 */
CRC1 = 0x85, /* B, power management CRC register for wakeup frame 1 */
CRC2 = 0x86, /* B, power management CRC register for wakeup frame 2 */
CRC3 = 0x87, /* B, power management CRC register for wakeup frame 3 */
CRC4 = 0x88, /* B, power management CRC register for wakeup frame 4 */
CRC5 = 0x89, /* B, power management CRC register for wakeup frame 5 */
CRC6 = 0x8A, /* B, power management CRC register for wakeup frame 6 */
CRC7 = 0x8B, /* B, power management CRC register for wakeup frame 7 */
WAKEUP0 = 0x8C, /* Q, power management wakeup frame 0 (64 bits) */
WAKEUP1 = 0x94, /* Q, power management wakeup frame 1 (64 bits) */
WAKEUP2 = 0x9C, /* Q, power management wakeup frame 2 (64 bits) */
WAKEUP3 = 0xA4, /* Q, power management wakeup frame 3 (64 bits) */
WAKEUP4 = 0xAC, /* Q, power management wakeup frame 4 (64 bits) */
WAKEUP5 = 0xB4, /* Q, power management wakeup frame 5 (64 bits) */
WAKEUP6 = 0xBC, /* Q, power management wakeup frame 6 (64 bits) */
WAKEUP7 = 0xC4, /* Q, power management wakeup frame 7 (64 bits) */
LSBCRC0 = 0xCC, /* B, LSB of mask byte of wakeup frame 0 offset 12 to 75 */
LSBCRC1 = 0xCD, /* B, LSB of mask byte of wakeup frame 1 offset 12 to 75 */
LSBCRC2 = 0xCE, /* B, LSB of mask byte of wakeup frame 2 offset 12 to 75 */
LSBCRC3 = 0xCF, /* B, LSB of mask byte of wakeup frame 3 offset 12 to 75 */
LSBCRC4 = 0xD0, /* B, LSB of mask byte of wakeup frame 4 offset 12 to 75 */
LSBCRC5 = 0xD1, /* B, LSB of mask byte of wakeup frame 5 offset 12 to 75 */
LSBCRC6 = 0xD2, /* B, LSB of mask byte of wakeup frame 6 offset 12 to 75 */
LSBCRC7 = 0xD3, /* B, LSB of mask byte of wakeup frame 7 offset 12 to 75 */
FLASH = 0xD4, /* L, flash memory read/write register */
CONFIG5 = 0xD8, /* B, configuration register #5 */
FER = 0xF0, /* L, function event register (CardBus only) */
FEMR = 0xF4, /* L, function event mask register (CardBus only) */
FPSR = 0xF8, /* L, function present state register (CardBus only) */
FFER = 0xFC /* L, function force event register (CardBus only) */
};
/* Receive status register in Rx packet header */
enum {
MAR = (1<<15), /* multicast address received */
PAM = (1<<14), /* physical address matched */
BAR = (1<<13), /* broadcast address received */
ISE = (1<<5), /* invalid symbol error */
RUNT = (1<<4), /* runt packet (<64 bytes) received */
LONG = (1<<3), /* long packet (>4K bytes) received */
CRC = (1<<2), /* CRC error */
FAE = (1<<1), /* frame alignment error */
ROK = (1<<0) /* receive OK */
};
/* Transmit status register */
enum {
CRS = (1<<31), /* carrier sense lost */
TABT = (1<<30), /* transmit abort */
OWC = (1<<29), /* out of window collision */
CDH = (1<<28), /* CD heart beat */
NCC_SHIFT = 24,
NCC = (0xF<<24), /* number of collision count */
ERTXTH_SHIFT = 16,
ERTXTH = (0x1F<<16), /* early tx threshold, in multiples of 32 bytes */
TOK = (1<<15), /* transmission OK */
TUN = (1<<14), /* transmit FIFO underrun */
OWN = (1<<13), /* own */
SIZE = 0xFFF /* descriptor size */
};
/* Command register */
enum {
RST = (1<<4), /* reset */
RE = (1<<3), /* receiver enable */
TE = (1<<2), /* transmitter enable */
BUFE = (1<<0) /* buffer empty */
};
/* Transmit configuration register */
enum {
CLRABT = (1<<0) /* clear abort */
};
/* Receive configuration register */
enum {
ERTH_SHIFT = 24, /* Early Rx threshold bits (4) */
MULERINT = (1<<17), /* multiple early interrupt */
RER8 = (1<<16), /* ? */
RXFTH_SHIFT= 13, /* Rx FIFO threshold */
RBLEN_SHIFT= 11, /* Rx buffer length */
MXDMA_SHIFT= 8, /* max DMA burst size per Rx DMA burst */
WRAP = (1<<7), /* WRAP mode */
SEL9356 = (1<<6), /* EEPROM select */
AER = (1<<5), /* accept error packets */
AR = (1<<4), /* accept runt packets */
AB = (1<<3), /* accept broadcast packets */
AM = (1<<2), /* accept multicast packets */
APM = (1<<1), /* accept physical match packets (our MAC) */
AAP = (1<<0), /* accept physical address packets (any MAC) */
};
/* TSAD (transmit status of all descriptors */
enum {
TOK3 = (1<<15),
TOK2 = (1<<14),
TOK1 = (1<<13),
TOK0 = (1<<12),
TUN3 = (1<<11),
TUN2 = (1<<10),
TUN1 = (1<<9),
TUN0 = (1<<8),
TABT3 = (1<<7),
TABT2 = (1<<6),
TABT1 = (1<<5),
TABT0 = (1<<4),
OWN3 = (1<<3),
OWN2 = (1<<2),
OWN1 = (1<<1),
OWN0 = (1<<0)
};
/* Interrupt mask/status register */
enum {
IR_SERR = (1<<15), /* system error interrupt */
IR_TIMEOUT = (1<<14), /* time out interrupt */
IR_LENCHG = (1<<13), /* cable length change interrupt */
IR_FOVW = (1<<6), /* Rx FIFO overflow */
IR_FUN = (1<<5), /* Packet underrun or link change interrupt */
IR_RXOVW = (1<<4), /* Rx buffer overflow */
IR_TER = (1<<3), /* transmit error interrupt */
IR_TOK = (1<<2), /* transmit OK interrupt */
IR_RER = (1<<1), /* receive error interrupt */
IR_ROK = (1<<0) /* receive OK interrupt */
};
/* Packet header bits */
enum {
HDR_MAR = (1<<15), /* multicast address received */
HDR_PAM = (1<<14), /* physical address matched */
HDR_BAR = (1<<13), /* broadcast address matched */
HDR_ISE = (1<<5), /* invalid symbol error */
HDR_RUNT = (1<<4), /* runt packet received (packet < 64 bytes) */
HDR_LONG = (1<<3), /* long packet (>4k) */
HDR_CRC = (1<<2), /* CRC error */
HDR_FAE = (1<<1), /* frame alignment error */
HDR_ROK = (1<<0) /* receive OK */
};
/*
* Define some options to use
*/
#define TXCFG ((0x3 << 24) | (TX_DMA_BURST << MXDMA_SHIFT))
#define RXCFG ((RX_FIFO_THRESH << RXFTH_SHIFT) |\
(RX_BUF_LEN_IDX << RBLEN_SHIFT) |\
(RX_DMA_BURST << MXDMA_SHIFT) |\
WRAP | AB | APM)
#endif /* ifndef CYGONCE_DEVS_ETH_REALTEK_8139_INFO_H */
//==========================================================================
//
// if_8139.c
//
// RealTek 8139 ethernet driver
//
//==========================================================================
//####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): Eric Doenges
// Contributors:
// Date: 2003-07-09
// Purpose:
// Description: hardware driver for RealTek 8139 ethernet
// Notes: This is a very basic driver that will send and receive
// packets and not much else. A lot of features of the 8139
// are not supported (power management, MII interface,
// access to the serial eeprom, 'twister tuning', etc.).
//
// Many of the configuration options (like media type and
// speed)the 8139 has are taken directly from the serial
// eeprom and are not currently configurable from this driver.
//
// Since the official data sheet for this chip is a bit
// vague, I had to look at the Linux and OpenBSD drivers to
// understand the basic workings of the chip; however, I have
// not copied any code from those drivers to avoid tainting
// eCos' license.
// FIXME:
//
//####DESCRIPTIONEND####
//
//==========================================================================
#include <pkgconf/system.h>
#ifdef CYGPKG_IO_ETH_DRIVERS
#include <pkgconf/io_eth_drivers.h>
#endif
#include <pkgconf/devs_eth_rltk_8139.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/hal/hal_arch.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>
#include <string.h> /* for memset */
#ifdef CYGPKG_IO_PCI
#include <cyg/io/pci.h>
#else
#error "This driver requires the PCI package (CYGPKG_IO_PCI)"
#endif
#include <cyg/io/pci.h>
/* Required for CYGARC_PHYSICAL_ADDRESS */
#include CYGHWR_MEMORY_LAYOUT_H
/* Check if we should be dumping debug information or not */
#if CYGDBG_DEVS_ETH_RLTK_8139_CHATTER
#define DEBUG_RLTK8139_DRIVER
#endif
#include <cyg/devs/eth/if_8139.h>
/* Which interrupts we will handle */
#define RLTK8139_IRQ (IR_SERR|IR_FOVW|IR_RXOVW|IR_TER|IR_TOK|IR_RER|IR_ROK)
/* Allow platform-specific configuration of the driver */
//#ifndef CYGDAT_DEVS_ETH_RLTK_8139_INL
//#error "CYGDAT_DEVS_ETH_RLTK_8139_INL not defined"
//#else
//#include CYGDAT_DEVS_ETH_RLTK_8139_INL
//#endif
#include <cyg/io/devs_eth_i386_pc_rltk8139.inl>
/* Local driver function declarations */
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
static cyg_uint32 rltk8139_isr(cyg_vector_t vector, cyg_addrword_t data);
#endif
static void rltk8139_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);
#ifdef ETH_DRV_SET_MC_LIST
static cyg_uint32 ether_crc(cyg_uint8 *data, int length);
static void rltk8139_set_multicast_list(Rltk8139_t *rltk8139_info,
struct eth_drv_mc_list *mc_list);
#endif
static void rltk8139_reset(struct eth_drv_sc *sc);
static bool rltk8139_init(struct cyg_netdevtab_entry *tab);
static void rltk8139_start(struct eth_drv_sc *sc, unsigned char *enaddr,
int flags);
static void rltk8139_stop(struct eth_drv_sc *sc);
static int rltk8139_control(struct eth_drv_sc *sc, unsigned long key,
void *data, int data_length);
static int rltk8139_can_send(struct eth_drv_sc *sc);
static void rltk8139_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list,
int sg_len, int total_len, unsigned long key);
static void rltk8139_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list,
int sg_len);
static void rltk8139_deliver(struct eth_drv_sc *sc);
static void rltk8139_poll(struct eth_drv_sc *sc);
static int rltk8139_int_vector(struct eth_drv_sc *sc);
/*
* Define inline functions to access the card. This will also handle
* endianess issues in one place. This code was lifted from the eCos
* i82559 driver.
*/
#if (CYG_BYTEORDER == CYG_MSBFIRST)
#define HAL_CTOLE32(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) >> 24) & 0xff))
#define HAL_LE32TOC(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) >> 24) & 0xff))
#define HAL_CTOLE16(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
#define HAL_LE16TOC(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
static inline void
OUTB(cyg_uint8 value,
cyg_uint32 io_address)
{
HAL_WRITE_UINT8( io_address, value);
}
static inline void
OUTW(cyg_uint16 value, cyg_uint32 io_address)
{
HAL_WRITE_UINT16(io_address,
(((value & 0xff) << 8) | ((value & 0xff00) >> 8)) );
}
static inline void
OUTL(cyg_uint32 value, cyg_uint32 io_address)
{
HAL_WRITE_UINT32(io_address,
((((value) & 0xff) << 24) | (((value) & 0xff00) << 8)
| (((value) & 0xff0000) >> 8)
| (((value) >> 24) & 0xff)) );
}
static inline cyg_uint8
INB(cyg_uint32 io_address)
{
cyg_uint8 d;
HAL_READ_UINT8(io_address, d);
return d;
}
static inline cyg_uint16
INW(cyg_uint32 io_address)
{
cyg_uint16 d;
HAL_READ_UINT16( io_address, d );
return (((d & 0xff) << 8) | ((d & 0xff00) >> 8));
}
static inline cyg_uint32
INL(cyg_uint32 io_address)
{
cyg_uint32 d;
HAL_READ_UINT32(io_address, d);
return ((((d) & 0xff) << 24) | (((d) & 0xff00) << 8)
| (((d) & 0xff0000) >> 8) | (((d) >> 24) & 0xff));
}
#else
// Maintaining the same styleee as above...
#define HAL_CTOLE32(x) ((((x))))
#define HAL_LE32TOC(x) ((((x))))
#define HAL_CTOLE16(x) ((((x))))
#define HAL_LE16TOC(x) ((((x))))
static inline void OUTB(cyg_uint8 value, cyg_uint32 io_address)
{ HAL_WRITE_UINT8( io_address, value ); }
static inline void OUTW(cyg_uint16 value, cyg_uint32 io_address)
{ HAL_WRITE_UINT16( io_address, value ); }
static inline void OUTL(cyg_uint32 value, cyg_uint32 io_address)
{ HAL_WRITE_UINT32( io_address, value ); }
static inline cyg_uint8 INB(cyg_uint32 io_address)
{ cyg_uint8 _t_; HAL_READ_UINT8( io_address, _t_ ); return _t_; }
static inline cyg_uint16 INW(cyg_uint32 io_address)
{ cyg_uint16 _t_; HAL_READ_UINT16( io_address, _t_ ); return _t_; }
static inline cyg_uint32 INL(cyg_uint32 io_address)
{ cyg_uint32 _t_; HAL_READ_UINT32( io_address, _t_ ); return _t_; }
#endif // byteorder
/*
* Table of all known PCI device/vendor ID combinations known for the
* RealTek 8139. Add them as you get to know them.
*/
#define CYGNUM_DEVS_ETH_RLTK_8139_KNOWN_ALIASES 1
static pci_identifier_t
known_8139_aliases[CYGNUM_DEVS_ETH_RLTK_8139_KNOWN_ALIASES] = {
{ 0x10ec, 0x8139, NULL } /* This is the offical RealTek vendor/device code */
};
/*
* The code below will search all PCI busses for the Nth 8139 device.
* We might want to move this into the .inl file so that it can be adapted
* to different platforms.
*/
static cyg_bool
rltk8139_find_match_func(cyg_uint16 vendor_id, cyg_uint16 device_id,
cyg_uint32 class_id, void *p)
{
int i;
for (i = 0; i < CYGNUM_DEVS_ETH_RLTK_8139_KNOWN_ALIASES; ++i) {
if (((known_8139_aliases[i].vendor_id == PCI_ANY_ID) ||
(known_8139_aliases[i].vendor_id == vendor_id)) &&
((known_8139_aliases[i].device_id == PCI_ANY_ID) ||
(known_8139_aliases[i].device_id == device_id)))
return true;
}
return false;
}
/*
* Find the Nth 8139 device on all attached PCI busses and do some initial
* PCI-type initialization. Also setup the interrupt for use in eCos.
*/
static bool
rltk8139_find(int n_th, struct eth_drv_sc *sc)
{
Rltk8139_t *rltk8139_info;
cyg_pci_device_id pci_device_id;
cyg_pci_device pci_device_info;
cyg_uint16 command;
int found = -1;
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_find(%d, %s)\n", n_th, sc->dev_name);
#endif
/* Go through all PCI devices until we find the Nth 8139 chip */
do {
pci_device_id = CYG_PCI_NULL_DEVID;
if (!cyg_pci_find_matching(&rltk8139_find_match_func, NULL,
&pci_device_id))
return false;
else
found += 1;
} while (found != n_th);
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("Found %dth RealTek 8139 device on bus %d, devfn 0x%02x:\n",
n_th, CYG_PCI_DEV_GET_BUS(pci_device_id),
CYG_PCI_DEV_GET_DEVFN(pci_device_id));
#endif
/* Save device ID in driver private data in case we ever need it again */
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
rltk8139_info->pci_device_id = pci_device_id;
/* Now that we have found the device, we can extract some data about it */
cyg_pci_get_device_info(pci_device_id, &pci_device_info);
/* Get the assigned interrupt and set up ISR and DSR for it. */
if (cyg_pci_translate_interrupt(&pci_device_info, &rltk8139_info->vector)) {
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf(" Wired to HAL interrupt vector %d\n", rltk8139_info->vector);
#endif
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
/*
* Note that we use the generic eth_drv_dsr routine instead of
* our own.
*/
//////****** Chg ->
//cyg_drv_interrupt_create(rltk8139_info->vector, 0,
// (CYG_ADDRWORD)sc,
// rltk8139_isr,
// eth_drv_dsr,
// &rltk8139_info->interrupt_handle,
// &rltk8139_info->interrupt);
cyg_drv_interrupt_create(rltk8139_info->vector, 0,
(CYG_ADDRWORD)rltk8139_info,
rltk8139_isr,
rltk8139_dsr,
&rltk8139_info->interrupt_handle,
&rltk8139_info->interrupt);
//////****** Chg ->
cyg_drv_interrupt_attach(rltk8139_info->interrupt_handle);
#endif
}
else {
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf(" Does not generate interrupts.\n");
#endif
}
/*
* Enable memory mapped and port based I/O and busmastering. We currently
* only support IO space accesses; memory mapping is enabled so that bit
* DVRLOAD in CONFIG1 is cleared automatically.
*
* NOTE: it seems that for some HALs (in my case, Equator's BSP HAL), the
* device is already activated at this point, even though eCos'
* documentation suggests it shouldn't be. At least in my case,
* this is not a problem.
*/
cyg_pci_read_config_uint16(pci_device_info.devid, CYG_PCI_CFG_COMMAND,
&command);
command |= (CYG_PCI_CFG_COMMAND_IO | CYG_PCI_CFG_COMMAND_MEMORY
| CYG_PCI_CFG_COMMAND_MASTER);
cyg_pci_write_config_uint16(pci_device_info.devid, CYG_PCI_CFG_COMMAND,
command);
/*
* This is the base address used to talk to the device. The 8139's IOAR
* and MEMAR registers are BAR0 and BAR1, respectively.
*/
rltk8139_info->base_address = pci_device_info.base_map[0];
/*
* Read the MAC address. The RealTek data sheet seems to claim this should
* only be read in 4 byte accesses, but the code below works just fine.
*/
for (found = 0; found < 6; ++found)
rltk8139_info->mac[found] = INB(rltk8139_info->base_address + IDR0 + found);
//////****** Chg ->
// This is the indicator for "uses an interrupt"
// Code below lifted from the eCos Intel 82559 driver
if (rltk8139_info->interrupt_handle != 0) {
cyg_drv_interrupt_acknowledge(rltk8139_info->vector);
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
cyg_drv_interrupt_unmask(rltk8139_info->vector);
#endif // !CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
}
//////****** Chg <-
return true;
}
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
/*
* Interrupt service routine. We do not clear the interrupt status bits
* (since this should really only be done after handling whatever caused
* the interrupt, and that is done in the '_deliver' routine), but instead
* clear the interrupt mask.
*
* We do not simply disable the interrupt vector the 8139 is using because
* that might be shared with other devices we have no reason to disable
* interrupts from at this point.
*/
static cyg_uint32
rltk8139_isr(cyg_vector_t vector, cyg_addrword_t data)
{
Rltk8139_t *rltk8139_info;
cyg_uint16 isr;
//////****** Chg ->
//rltk8139_info = (Rltk8139_t *)(((struct eth_drv_sc *)data)->driver_private);
rltk8139_info = (Rltk8139_t *)data;
//////****** Chg <-
//////****** Chg ->
/*
* First, check if the interrupt is really for us in case we are
* sharing interrupts with another device. The RealTek data sheet claims
* that reading the interrupt state register will clear all it's bits;
* this is incorrect, so we can do this without any problems.
*/
//if (!(isr = INW(rltk8139_info->base_address + ISR)))
// return 0;
//////****** Chg <-
//////****** Chg ->
/* Clear the interrupt mask to stop the current request. */
//OUTW(0, rltk8139_info->base_address + IMR);
// Mask and acknowldge current interrupt
cyg_drv_interrupt_mask(rltk8139_info->vector);
cyg_drv_interrupt_acknowledge(rltk8139_info->vector);
//////****** Chg <-
return (CYG_ISR_HANDLED | CYG_ISR_CALL_DSR);
}
#endif /* ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE */
//////****** Chg ->
// Code lifted from eCOS AMD PCNet driver
static void
rltk8139_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
// This conditioning out is necessary because of explicit calls to this
// DSR - which would not ever be called in the case of a polled mode
// usage ie. in RedBoot.
#ifdef CYGPKG_IO_ETH_DRIVERS_NET
Rltk8139_t *rltk8139_info = (Rltk8139_t *)data;
struct cyg_netdevtab_entry *ndp = (struct cyg_netdevtab_entry *)(rltk8139_info->ndp);
struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance);
// but here, it must be a *sc:
eth_drv_dsr( vector, count, (cyg_addrword_t)sc );
#else
# ifndef CYGPKG_REDBOOT
# error Empty RealTek 8139 ethernet DSR is compiled. Is this what you want?
# endif
#endif
}
//////****** Chg <-
/*
* Reset the chip. This function is not exported to higher level software.
*/
static void
rltk8139_reset(struct eth_drv_sc *sc)
{
rltk8139_stop(sc);
rltk8139_start(sc, NULL, 0);
}
#ifdef ETH_DRV_SET_MC_LIST
/*
* I assume (hope !) that this is identical to Ethernet's CRC algorithm
* specified in IEEE 802.3. It does seem to calculate the same CRC that
* the 8139 itself does, so I think it is correct.
* Note that while Ethernet's polynomial is usually specified as 04C11DB7,
* we must use EDB88320 because we shift the bits to the left, not the right.
* (See ftp://ftp.rocksoft.com/papers/crc_v3.txt for a good description of
* CRC algorithms).
*/
static cyg_uint32
ether_crc(cyg_uint8 *data, int length)
{
int bit;
cyg_uint32 crc = 0xFFFFFFFFU;
while (length-- > 0) {
crc ^= *data++;
for (bit = 0; bit < 8; ++bit) {
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320U;
else
crc = (crc >> 1);
}
}
return crc ^ 0xFFFFFFFFU;
}
/*
* Set up multicast filtering. The way I understand existing driver code
* (Linux and OpenBSD), the 8139 calculates the ethernet CRC of
* incoming addresses and uses the top 6 bits as an index into a hash
* table. If the corresponding bit is set in MAR0..7, the address is
* accepted.
*/
static void
rltk8139_set_multicast_list(Rltk8139_t *rltk8139_info,
struct eth_drv_mc_list *mc_list)
{
cyg_uint32 mar[2], hash;
int i;
/* If 'mc_list' is NULL, allow all multicast packets. */
if (!mc_list) {
mar[0] = 0xFFFFFFFF;
mar[1] = 0xFFFFFFFF;
}
else {
mar[0] = 0;
mar[1] = 0;
for (i = 0; i < mc_list->len; ++i) {
hash = ether_crc(&mc_list->addrs[i][0], ETHER_ADDR_LEN) >> 26;
mar[hash >> 5] |= (1 << (hash & 0x1F));
}
}
/* Program the new filter values */
OUTL(mar[0], rltk8139_info->base_address + MAR0);
OUTL(mar[1], rltk8139_info->base_address + MAR4);
}
#endif /* ifdef ETH_DRV_SET_MC_LIST */
/*
* Initialize the network interface. Since the chips is reset by calling
* _stop() and _start(), any code that will never need to be executed more
* than once after system startup should go here.
*/
static bool
rltk8139_init(struct cyg_netdevtab_entry *tab)
{
struct eth_drv_sc *sc;
Rltk8139_t *rltk8139_info;
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_init()\n");
#endif
sc = (struct eth_drv_sc *)(tab->device_instance);
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
/*
* Initialize the eCos PCI library. According to the documentation, it
* is safe to call this function multiple times, so we call it just to
* be sure it has been done.
*/
cyg_pci_init();
/*
* Scan the PCI bus for the specified chip. The '_find' function will also
* do some basic PCI initialization.
*/
if (!rltk8139_find(rltk8139_info->device_num, sc)) {
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("Could not find RealTek 8139 chip #%d.\n",
rltk8139_info->device_num);
#endif
return false;
}
//////****** Chg ->
// Store the net dev pointer
rltk8139_info->ndp = (void *)tab;
//////****** Chg <-
/*
* The initial tx threshold is set here to prevent it from being reset
* with every _start().
*/
rltk8139_info->tx_threshold = 3;
/* Initialize upper level driver */
(sc->funs->eth_drv->init)(sc, rltk8139_info->mac);
return true;
}
/*
* (Re)Start the chip, initializing data structures and enabling the
* transmitter and receiver. Currently, 'flags' is unused by eCos.
*/
static void
rltk8139_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
Rltk8139_t *rltk8139_info;
int i;
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_start(%s)\n", sc->dev_name);
#endif
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
/*
* Reset the chip. Existing driver code implies that this may take up
* to 10ms; since I don't know under what exact circumstances this code may
* be called I busy wait here regardless.
*/
OUTB(RST, rltk8139_info->base_address + CR);
while (INB(rltk8139_info->base_address + CR) & RST);
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("8139 was successfully reset.\n");
#endif
/*
* Clear the key storage area. These keys are used by the eCos networking
* support code to keep track of individual packets.
*/
for (i = 0; i < NUM_TX_DESC; ++i)
rltk8139_info->tx_desc_key[i] = 0;
/* Initialize transmission buffer control */
rltk8139_info->tx_free_desc = 0;
rltk8139_info->tx_num_free_desc = NUM_TX_DESC;
/*
* Set the requested MAC address if enaddr != NULL. This is a special
* feature of my '_start' function since it's used to reset the chip after
* errors as well.
*/
if (enaddr != NULL) {
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("Sets the MAC address to: ");
#endif
for (i = 0; i < 6; ++i) {
rltk8139_info->mac[i] = enaddr[i];
OUTB(enaddr[i], rltk8139_info->base_address + IDR0 + i);
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("%02x:",enaddr[i]);
#endif
}
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("\n");
#endif
}
/*
* Now setup the transmission and reception buffers. These could be done
* in _init() and kept around, but putting them here fits better logically.
* Note the use of CYGARC_PHYSICAL_ADDRESS for platforms on which virtual
* and physical addresses do not match.
*/
OUTL(CYGARC_PHYSICAL_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
+ 0 * TX_BUF_SIZE)),
rltk8139_info->base_address + TSAD0);
OUTL(CYGARC_PHYSICAL_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
+ 1 * TX_BUF_SIZE)),
rltk8139_info->base_address + TSAD1);
OUTL(CYGARC_PHYSICAL_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
+ 2 * TX_BUF_SIZE)),
rltk8139_info->base_address + TSAD2);
OUTL(CYGARC_PHYSICAL_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
+ 3 * TX_BUF_SIZE)),
rltk8139_info->base_address + TSAD3);
OUTL(CYGARC_PHYSICAL_ADDRESS((cyg_uint32)rltk8139_info->rx_ring),
rltk8139_info->base_address + RBSTART);
/*
* Enable the transmitter and receiver, then clear the missed packet
* counter.
*/
OUTB(INB(rltk8139_info->base_address + CR) | (TE|RE),
rltk8139_info->base_address + CR);
OUTL(0, rltk8139_info->base_address + MPC);
/*
* It seems the receiver and transmitter configuration can only
* be set after the transmitter/receiver have been enabled.
*/
OUTL(TXCFG, rltk8139_info->base_address + TCR);
OUTL(RXCFG | AM, rltk8139_info->base_address + RCR);
/*
* Enable the transmitter and receiver again. I'm not sure why this is
* necessary; the Linux driver does it so we do it here just to be on
* the safe side.
*/
OUTB(INB(rltk8139_info->base_address + CR) | (TE|RE),
rltk8139_info->base_address + CR);
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
/*
* If this driver was not compiled in stand alone (without interrupts)
* mode, enable interrupts.
*/
OUTW(RLTK8139_IRQ, rltk8139_info->base_address + IMR);
#endif
}
/*
* Stop the chip, disabling the transmitter and receiver.
*/
static void
rltk8139_stop(struct eth_drv_sc *sc)
{
Rltk8139_t *rltk8139_info;
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_stop(%s)\n", sc->dev_name);
#endif
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
/* Disable receiver and transmitter */
OUTB(INB(rltk8139_info->base_address + CR) & ~(TE|RE),
rltk8139_info->base_address + CR);
/* Mask all interrupts */
OUTW(0, rltk8139_info->base_address + IMR);
}
/*
* 8139 control function. Unlike a 'real' ioctl function, this function is
* not required to tell the caller why a request failed, only that it did
* (see the eCos documentation).
*/
static int
rltk8139_control(struct eth_drv_sc *sc, unsigned long key, void *data,
int data_length)
{
int i;
Rltk8139_t *rltk8139_info;
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_control(%08x, %lx)\n", sc, key);
#endif
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
switch (key) {
#ifdef ETH_DRV_SET_MAC_ADDRESS
case ETH_DRV_SET_MAC_ADDRESS:
if ( 6 != data_length )
return 1;
/* Set the mac address */
for (i = 0; i < 6; ++i) {
rltk8139_info->mac[i] = *(((cyg_uint8 *)data) + i);
OUTB(rltk8139_info->mac[i], rltk8139_info->base_address + IDR0 + i);
}
return 0;
#endif
#ifdef ETH_DRV_GET_MAC_ADDRESS
case ETH_DRV_GET_MAC_ADDRESS:
if (6 != data_length)
return 1;
memcpy(data, rltk8139_info->mac, 6);
return 0;
#endif
#ifdef ETH_DRV_GET_IF_STATS_UD
case ETH_DRV_GET_IF_STATS_UD: // UD == UPDATE
//ETH_STATS_INIT( sc ); // so UPDATE the statistics structure
#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)
break;
#endif
#ifdef ETH_DRV_SET_MC_LIST
case ETH_DRV_SET_MC_LIST:
/*
* Program the 8139's multicast filter register. If the eth_drv_mc_list
* contains at least one element, set the accept multicast bit in the
* receive config register.
*/
rltk8139_set_multicast_list(rltk8139_info, (struct eth_drv_mc_list *)data);
if (((struct eth_drv_mc_list *)data)->len > 0)
OUTL(INL(rltk8139_info->base_address + RCR) | AM,
rltk8139_info->base_address + RCR);
else
OUTL(INL(rltk8139_info->base_address + RCR) & ~AM,
rltk8139_info->base_address + RCR);
return 0;
#endif // ETH_DRV_SET_MC_LIST
#ifdef ETH_DRV_SET_MC_ALL
case ETH_DRV_SET_MC_ALL:
/*
* Set the accept multicast bit in the receive config register and
* program the multicast filter to accept all addresses.
*/
rltk8139_set_multicast_list(rltk8139_info, NULL);
OUTL(INL(rltk8139_info->base_address + RCR) | AM,
rltk8139_info->base_address + RCR);
return 0;
#endif // ETH_DRV_SET_MC_ALL
default:
return 1;
}
return 1;
}
/*
* Check if a new packet can be sent.
*/
static int
rltk8139_can_send(struct eth_drv_sc *sc)
{
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_can_send(%s)\n",sc->dev_name);
#endif
return ((Rltk8139_t *)(sc->driver_private))->tx_num_free_desc;
}
/*
* Send a packet over the wire.
*/
static void
rltk8139_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
{
Rltk8139_t *rltk8139_info;
cyg_uint8 *tx_buffer;
struct eth_drv_sg *last_sg;
int desc;
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_send(%s, %08x, %d, %d, %08lx)\n",
sc->dev_name, sg_list, sg_len, total_len, key);
#endif
CYG_ASSERT(total_len <= TX_BUF_SIZE, "packet too long");
/*
* Get the next free descriptor to send. We lock out all interrupts
* and scheduling because we really, really do not want to be interrupted
* at this point.
*
* IMPORTANT NOTE: the RealTek data sheet does not really make this clear,
* but when they talk about a 'ring' of transmit descriptors, they
* _really_ mean it, i.e. you _must_ use descriptor #1 after descriptor
* #0 even if transmission of descriptor #0 has already completed.
*/
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
cyg_interrupt_disable();
#endif
/*
* Sanity check to see if '_send' was called even though there is no free
* descriptor. This is probably unnecessary.
*/
if (rltk8139_info->tx_num_free_desc == 0) {
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
cyg_interrupt_enable();
#endif
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("No descriptor free but send called regardless ?\n");
#endif
return;
}
/*
* Get the free descriptor and advance the descriptor counter modulo
* TX_NUM_DESC. We assume that TX_NUM_DESC will always be a power of 2.
*/
desc = rltk8139_info->tx_free_desc;
rltk8139_info->tx_free_desc = (rltk8139_info->tx_free_desc + 1)
& (NUM_TX_DESC - 1);
/* Decrement the number of free descriptors */
rltk8139_info->tx_num_free_desc -= 1;
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
/* Reenable interrupts at this point */
cyg_interrupt_enable();
#endif
/*
* Determine the buffer memory to use and tell the hardware about it.
* Since we use fixed buffer addresses, we do not need to set up TSADx.
* Memorize the key so we can call the tx_done callback correctly.
*
* While it would be possible to set TSADx to the packet directly if
* it is stored in a linear memory area with 32 bit alignment, it seems
* this happens so seldomly that it's simply not worth the extra
* runtime check.
*/
tx_buffer = rltk8139_info->tx_buffer + TX_BUF_SIZE * desc;
rltk8139_info->tx_desc_key[desc] = key;
/*
* Copy the data to the designated position. Note that unlike the eCos
* Intel 82559 driver, we simply assume that all the scatter/gather list
* elements' lengths will add up to total_len exactly, and don't check
* to make sure.
*/
for (last_sg = &sg_list[sg_len]; sg_list < last_sg; ++sg_list) {
memcpy(tx_buffer, (void *)(sg_list->buf), sg_list->len);
tx_buffer += sg_list->len;
}
/*
* Make sure the packet has the minimum ethernet packet size, padding
* with zeros if necessary.
*/
if (total_len < MIN_ETH_FRAME_SIZE) {
memset(tx_buffer, 0, MIN_ETH_FRAME_SIZE - total_len);
total_len = MIN_ETH_FRAME_SIZE;
}
/*
* Now setup the correct transmit descriptor to actually send the data.
* The early TX threshold is incremented by the driver until we reach a
* size that prevents transmit underruns. (An earlier attempt to calculate
* this parameter from the packet size didn't work).
*/
OUTL((rltk8139_info->tx_threshold << ERTXTH_SHIFT) | (total_len & SIZE),
rltk8139_info->base_address + TSD0 + (desc<<2));
}
/*
* This routine actually retrieves data from the receive ring by
* copying it into the specified scatter/gather buffers. Again,
* we assume the scatter/gather list is OK and don't check against
* total length.
*/
static void
rltk8139_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list,
int sg_len)
{
Rltk8139_t *rltk8139_info;
struct eth_drv_sg *last_sg;
cyg_uint8 *rx_buffer;
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_recv(%s,%d)\n",sc->dev_name,sg_len);
#endif
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
rx_buffer = rltk8139_info->rx_current;
for (last_sg = &sg_list[sg_len]; sg_list < last_sg; ++sg_list) {
memcpy((void *)(sg_list->buf), rx_buffer, sg_list->len);
rx_buffer += sg_list->len;
}
}
/*
* This function does all the heavy lifting associated with interrupts.
*/
static void
rltk8139_deliver(struct eth_drv_sc *sc)
{
Rltk8139_t *rltk8139_info;
cyg_uint16 status, pci_status;
cyg_uint32 tsd;
int desc;
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
/*
* The RealTek data sheet claims that reading the ISR will clear
* it. This is incorrect; to clear a bit in the ISR, a '1' must be
* written to the ISR instead. We immediately clear the interrupt
* bits at this point.
*/
status = INW(rltk8139_info->base_address + ISR);
OUTW(status, rltk8139_info->base_address + ISR);
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_deliver(%s): %04x\n", sc->dev_name, status);
#endif
/*
* Check for a PCI error. This seems like a very serious error to
* me, so we will reset the chip and hope for the best.
*/
if (status & IR_SERR) {
cyg_pci_read_config_uint16(rltk8139_info->pci_device_id, 0x6, &pci_status);
cyg_pci_write_config_uint16(rltk8139_info->pci_device_id, 0x6, pci_status);
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("%s: PCI error: %04x\n", sc->dev_name, pci_status);
#endif
rltk8139_reset(sc);
return;
}
/* Check for transmission complete (with errors or not) */
if ((status & IR_TER) || (status & IR_TOK)) {
/*
* Figure out which descriptors' status must be checked. We lock out
* interrupts while manipulating the descriptor list because we do not
* want to be interrupted at this point.
*/
while (1) {
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
cyg_interrupt_disable();
#endif
/* Check if all descriptors are ready, in which case we are done. */
if (rltk8139_info->tx_num_free_desc >= NUM_TX_DESC) {
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
cyg_interrupt_enable();
#endif
break;
}
desc = (rltk8139_info->tx_free_desc
- (NUM_TX_DESC - rltk8139_info->tx_num_free_desc))
& (NUM_TX_DESC - 1);
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
cyg_interrupt_enable();
#endif
/* Get the current status of the descriptor */
tsd = INL(rltk8139_info->base_address + TSD0 + (desc<<2));
/*
* If a transmit FIFO underrun occurred, increment the threshold
* value.
*/
if ((tsd & TUN) && (rltk8139_info->tx_threshold < 64))
rltk8139_info->tx_threshold += 1;
/*
* Check if a transmission completed OK. RealTek's data sheet implies
* that a successful transmission that experiences underrun will only
* set TUN. This is not true; TOK is set for all successful
* transmissions.
*/
if (tsd & TOK) {
(sc->funs->eth_drv->tx_done)(sc, rltk8139_info->tx_desc_key[desc], 0);
}
else if (tsd & TABT) {
/*
* Set the CLRABT bit in TCR. Since I haven't encountered any
* transmission aborts so far, I don't really know if this code
* will work or not.
*/
OUTL(INL(rltk8139_info->base_address + TCR) & CLRABT,
rltk8139_info->base_address + TCR);
(sc->funs->eth_drv->tx_done)(sc, rltk8139_info->tx_desc_key[desc], -1);
}
else {
/*
* This descriptor is not ready. Since the descriptors are used
* in a ring, this means that no more descriptors are ready.
*/
break;
}
/*
* Clear the saved key value. This is not really necessary, since
* the key value is never used to determine if a descriptor is
* valid or not. However, clearing it is a tidier IMO.
*/
rltk8139_info->tx_desc_key[desc] = 0;
/*
* Increment the free descriptor count and go through the loop again
* to see if more descriptors are ready.
*/
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
cyg_interrupt_disable();
#endif
rltk8139_info->tx_num_free_desc += 1;
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
cyg_interrupt_enable();
#endif
}
}
if (status & IR_ROK) {
/*
* Received a frame. Note that '_deliver' does not actually copy any
* data; it just determines how many bytes are available and tells
* the generic ethernet driver about it, which then calls into
* the '_recv' function to copy the data.
*/
cyg_uint16 rx_pos;
cyg_uint32 header;
int length;
/*
* CAPR contains the index into the receive buffer. It is controlled
* completely in software. For some reason, CAPR points 16 bytes
* before the actual start of the packet.
*/
rx_pos = (INW(rltk8139_info->base_address + CAPR) + 16) % RX_BUF_LEN;
/*
* Loop until the rx buffer is empty. I have no idea how the 8139
* determines if the buffer still contains a packet; it may check
* that CAPR points 16 bytes before CBR.
*/
while (!(INB(rltk8139_info->base_address + CR) & BUFE)) {
/*
* The 8139 prepends each packet with a 32 bit packet header that
* contains a 16 bit length and 16 bit status field, in little-endian
* byte order.
*/
header = HAL_LE32TOC(*((cyg_uint32 *)&rltk8139_info->rx_ring[rx_pos]));
/*
* If the 8139 is still copying data for this packet into the
* receive ring, it will set packet length to 0xfff0. This shouldn't
* ever happen because we do not use early receive.
*/
if ((header >> 16) == 0xFFF0)
break;
/*
* Since the 8139 appends the ethernet CRC to every packet, we
* must subtract 4 from the length to get the true packet length.
*/
length = (header >> 16) - 4;
/*
* Check if the packet was received correctly. The OpenBSD driver
* resets the chip if this is not the case; we attempt to salvage
* following packets by doing nothing.
*/
if (!(header & HDR_ROK)) {
}
else {
/*
* Packet was received OK. Determine from where to start copying
* bytes. This is saved in the driver private area so rlt8139_recv
* doesn't have to redetermine this information. Then, inform
* the generic ethernet driver about the packet.
*/
rltk8139_info->rx_current = rltk8139_info->rx_ring + rx_pos + 4;
/* Tell eCos about the packet */
(sc->funs->eth_drv->recv)(sc, length);
}
/*
* Update CAPR. CAPR must be aligned to a 32 bit boundary, and should
* point 16 bytes before it's actual position.
*/
rx_pos = ((rx_pos + length + 8 + 3) & ~3) % RX_BUF_LEN;
OUTW((rx_pos - 16) % RX_BUF_LEN, rltk8139_info->base_address + CAPR);
}
}
if (status & IR_RXOVW) {
/*
* In case of a receive buffer overflow, the RealTek data sheet claims we
* should update CAPR and then write a '1'?to ROK in ISR. However, none of
* the other 8139 drivers I have looked at do this, so we will just reset
* the chip instead.
*/
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("receive buffer overflow\n");
#endif
rltk8139_reset(sc);
return;
}
if (status & IR_FOVW) {
/*
* Rx FIFO overflow. According to RealTek's data sheet, this is cleared
* by writing a '1' to RXOVW. Again, none of the 8139 drivers I have
* seen actually do this, so we reset the chip instead.
*/
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("receive FIFO overflow\n");
#endif
rltk8139_reset(sc);
return;
}
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
//////****** Chg ->
/* Finally, reenable interrupts */
//OUTW(RLTK8139_IRQ, rltk8139_info->base_address + IMR);
cyg_drv_interrupt_unmask(rltk8139_info->vector);
//////****** Chg <-
#endif
}
/*
* '_poll' does the same thing as '_deliver'. It is called periodically when
* the ethernet driver is operated in non-interrupt mode, for instance by
* RedBoot.
*/
static void
rltk8139_poll(struct eth_drv_sc *sc)
{
Rltk8139_t *rltk8139_info;
cyg_uint16 isr;
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_poll(%s)\n", sc->dev_name);
#endif
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
/*
* Get the current interrupt status. If anything changed, call
* _deliver.
*/
if (isr = INW(rltk8139_info->base_address + ISR))
rltk8139_deliver(sc);
}
/*
* Return the interrupt vector used by this device.
*/
static int
rltk8139_int_vector(struct eth_drv_sc *sc)
{
return ((Rltk8139_t *)(sc->driver_private))->vector;
}