URL
https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk
Subversion Repositories openrisc_me
Compare Revisions
- This comparison shows the changes necessary to convert path
/openrisc/trunk/rtos/ecos-2.0/packages/io/eth/v2_0/src
- from Rev 27 to Rev 174
- ↔ Reverse comparison
Rev 27 → Rev 174
/net/eth_drv.c
0,0 → 1,1042
//========================================================================== |
// |
// src/net/eth_drv.c |
// |
// Hardware independent ethernet driver |
// |
//========================================================================== |
//####ECOSGPLCOPYRIGHTBEGIN#### |
// ------------------------------------------- |
// This file is part of eCos, the Embedded Configurable Operating System. |
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. |
// Copyright (C) 2002 Gary Thomas |
// |
// 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): gthomas |
// Contributors: gthomas |
// Date: 2000-01-10 |
// Purpose: Hardware independent ethernet driver |
// Description: |
// |
// |
//####DESCRIPTIONEND#### |
// |
//========================================================================== |
|
// High-level ethernet driver |
|
#include <sys/param.h> |
#include <sys/errno.h> |
#include <sys/ioctl.h> |
#include <sys/mbuf.h> |
#include <sys/socket.h> |
|
#include <net/if.h> |
#include <net/if_dl.h> |
#include <net/if_types.h> |
#include <net/netisr.h> |
|
#ifdef INET |
#include <netinet/in.h> |
#include <netinet/in_systm.h> |
#include <netinet/in_var.h> |
#include <netinet/ip.h> |
#include <netinet/if_ether.h> |
#endif |
|
#if NBPFILTER > 0 |
#include <net/bpf.h> |
#include <net/bpfdesc.h> |
#endif |
|
#include <cyg/infra/cyg_ass.h> |
#include <cyg/hal/drv_api.h> |
#include <pkgconf/hal.h> |
#include <cyg/hal/hal_if.h> |
#include <pkgconf/io_eth_drivers.h> // module configury; SIMULATED_FAILURES |
#include <pkgconf/net.h> // CYGPKG_NET_FAST_THREAD_TICKLE_DEVS? |
|
#include <cyg/io/eth/eth_drv.h> |
#include <cyg/io/eth/netdev.h> |
|
#ifndef min |
#define min( _x_, _y_ ) ((_x_) < (_y_) ? (_x_) : (_y_)) |
#endif |
|
// ------------------------------------------------------------------------ |
#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES |
|
#define noLOG_RANDOM 32 // so you can tell this is really being random |
#ifdef LOG_RANDOM |
static struct { |
unsigned int *which; |
unsigned int random; |
unsigned int r100; |
} random_log[LOG_RANDOM]; |
|
static int random_index = 0; |
#endif |
|
static unsigned int |
randomize( unsigned int *p ) |
{ |
unsigned int r100; |
HAL_CLOCK_READ( &r100 ); |
r100 ^= *p; |
*p = (r100 * 1103515245) + 12345; |
r100 &= 127; |
if ( r100 >= 100 ) // spread the overflow around evenly |
r100 = 4 * (r100 - 100); |
if ( r100 >= 100 ) // and again - (125,126,127=>100,104,108) |
r100 = 12 * (r100 - 100); // =>(0,48,96) |
#ifdef LOG_RANDOM |
random_log[random_index].which = p; |
random_log[random_index].random = *p; |
random_log[random_index].r100 = r100; |
random_index++; |
random_index &= (LOG_RANDOM-1); |
#endif |
return r100; |
} |
|
#define SIMULATE_FAIL_SEND 1 |
#define SIMULATE_FAIL_RECV 2 |
#define SIMULATE_FAIL_CORRUPT 3 |
|
static struct simulated_failure_state { |
struct eth_drv_sc *sc; |
unsigned int r_tx_fail; |
unsigned int r_rx_fail; |
unsigned int r_rx_corrupt; |
cyg_tick_count_t droptime; |
cyg_tick_count_t passtime; |
} simulated_failure_states[2] = {{0},{0}}; |
|
static int |
simulate_fail( struct eth_drv_sc *sc, int which ) |
{ |
struct simulated_failure_state *s; |
|
for ( s = &simulated_failure_states[0]; s < &simulated_failure_states[2]; |
s++ ) { |
if ( 0 == s->sc ) { |
s->sc = sc; |
s->r_tx_fail = (unsigned int)sc; |
s->r_rx_fail = (unsigned int)sc ^ 0x01234567; |
s->r_rx_corrupt = (unsigned int)sc ^ 0xdeadbeef; |
s->droptime = 0; |
s->passtime = 0; |
} |
if ( sc == s->sc ) |
break; |
} |
if ( &simulated_failure_states[2] == s ) { |
CYG_FAIL( "No free slot in simulated_failure_states[]" ); |
return 1; // always fail |
} |
|
#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATE_LINE_CUT |
// Regardless of the question, we say "yes" during the period of |
// unpluggedness... |
{ |
cyg_tick_count_t now = cyg_current_time(); |
if ( now > s->droptime && 0 == s->passtime ) { // [initial condition] |
s->droptime = 0; // go into a passing phase |
(void)randomize( &s->r_tx_fail ); |
(void)randomize( &s->r_rx_fail ); |
(void)randomize( &s->r_rx_corrupt ); |
s->passtime = s->r_tx_fail + s->r_rx_fail + s->r_rx_corrupt; |
s->passtime &= 0x3fff; // 16k cS is up to 160S, about 2.5 minutes |
s->passtime += now; |
} |
else if ( now > s->passtime && 0 == s->droptime ) { |
s->passtime = 0; // go into a dropping phase |
(void)randomize( &s->r_tx_fail ); |
(void)randomize( &s->r_rx_fail ); |
(void)randomize( &s->r_rx_corrupt ); |
s->droptime = s->r_tx_fail + s->r_rx_fail + s->r_rx_corrupt; |
s->droptime &= 0x0fff; // 4k cS is up to 40S, about 1/2 a minute |
s->droptime += now; |
} |
|
if ( now < s->droptime ) |
return 1; // Say "no" |
} |
#endif |
|
switch ( which ) { |
#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATE_DROP_TX |
case SIMULATE_FAIL_SEND: { |
unsigned int z = randomize( &s->r_tx_fail ); |
return z < CYGPKG_IO_ETH_DRIVERS_SIMULATE_DROP_TX; |
} |
#endif |
#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATE_DROP_RX |
case SIMULATE_FAIL_RECV: { |
unsigned int z = randomize( &s->r_rx_fail ); |
return z < CYGPKG_IO_ETH_DRIVERS_SIMULATE_DROP_RX; |
} |
#endif |
#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATE_CORRUPT_RX |
case SIMULATE_FAIL_CORRUPT: { |
unsigned int z = randomize( &s->r_rx_corrupt ); |
return z < CYGPKG_IO_ETH_DRIVERS_SIMULATE_CORRUPT_RX; |
} |
#endif |
default: |
// do nothing - for when options above are not enabled. |
} |
return 0; |
} |
|
#define noLOG_CORRUPTION 32 // so you can tell this is really being random |
#ifdef LOG_CORRUPTION |
static struct { |
int len; |
int thislen; |
int off; |
unsigned char xor; |
unsigned char idx; |
} corruption_log[LOG_CORRUPTION]; |
|
static int corruption_index = 0; |
#endif |
|
static void |
simulate_fail_corrupt_sglist( struct eth_drv_sg *sg_list, int sg_len ) |
{ |
unsigned int z, len, i, off; |
HAL_CLOCK_READ( &z ); |
z += simulated_failure_states[0].r_rx_corrupt; |
z += simulated_failure_states[1].r_rx_corrupt; |
|
CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow in corrupt" ); |
|
for ( i = 0, len = 0; i < sg_len && sg_list[i].buf && sg_list[i].len; i++ ) |
len =+ sg_list[i].len; |
|
CYG_ASSERT( 1500 >= len, "sg...len > ether MTU" ); |
if ( 14 >= len ) // normal ether header |
return; |
|
off = z & 2047; // next (2^N-1) > MTU |
while ( off > len ) |
off -= len; |
|
for ( i = 0; i < sg_len && sg_list[i].buf && sg_list[i].len; i++ ) { |
if ( off < sg_list[i].len ) { // corrupt this one |
unsigned char *p = (unsigned char *)sg_list[i].buf; |
p[off] ^= (0xff & (z >> 11)); |
#ifdef LOG_CORRUPTION |
corruption_log[corruption_index].len = len; |
corruption_log[corruption_index].thislen = sg_list[i].len; |
corruption_log[corruption_index].off = off; |
corruption_log[corruption_index].xor = (0xff & (z >> 11)); |
corruption_log[corruption_index].idx = i; |
corruption_index++; |
corruption_index &= (LOG_CORRUPTION-1); |
#endif |
return; |
} |
off -= sg_list[i].len; |
} |
CYG_FAIL( "Didn't corrupt anything" ); |
} |
|
#endif // CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES |
// ------------------------------------------------------------------------ |
|
#ifdef CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT |
|
#include <cyg/hal/hal_if.h> |
|
// Use with care! Local variable defined! |
#define START_CONSOLE() \ |
{ /* NEW BLOCK */ \ |
int _cur_console = \ |
CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); \ |
{ \ |
int i; \ |
if ( CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET, \ |
"info_console_force", &i, \ |
CYGNUM_FLASH_CFG_OP_CONFIG_BOOL ) ) { \ |
if ( i ) { \ |
if ( CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET, \ |
"info_console_number", &i, \ |
CYGNUM_FLASH_CFG_OP_CONFIG_INT ) ) { \ |
/* Then i is the console to force it to: */ \ |
CYGACC_CALL_IF_SET_CONSOLE_COMM( i ); \ |
} \ |
} \ |
} \ |
} |
|
#define END_CONSOLE() \ |
CYGACC_CALL_IF_SET_CONSOLE_COMM(_cur_console); \ |
} /* END BLOCK */ |
|
#else |
#define START_CONSOLE() |
#define END_CONSOLE() |
#endif |
// ------------------------------------------------------------------------ |
|
#ifdef CYGPKG_NET_FREEBSD_STACK |
extern char *_ioctl_name(u_long cmd); |
typedef void void_fun(void *); |
#endif |
|
static int eth_drv_ioctl(struct ifnet *, u_long, caddr_t); |
static void eth_drv_send(struct ifnet *); |
static void eth_drv_start(struct eth_drv_sc *sc); |
|
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG |
int cyg_io_eth_net_debug = CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY; |
#endif |
|
// Interfaces exported to drivers |
|
static void eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr); |
static void eth_drv_recv(struct eth_drv_sc *sc, int total_len); |
static void eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRESS key, int status); |
|
struct eth_drv_funs eth_drv_funs = {eth_drv_init, eth_drv_recv, eth_drv_tx_done}; |
|
// |
// This function is called during system initialization to register a |
// network interface with the system. |
// |
static void |
eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr) |
{ |
struct ifnet *ifp = &sc->sc_arpcom.ac_if; |
#ifdef CYGPKG_NET_FREEBSD_STACK |
int unit; |
char *np, *xp; |
#endif |
|
// Set up hardware address |
if (NULL != enaddr) |
bcopy(enaddr, &sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN); |
|
// Initialize ifnet structure |
ifp->if_softc = sc; |
ifp->if_start = eth_drv_send; |
ifp->if_ioctl = eth_drv_ioctl; |
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; |
#ifdef IFF_NOTRAILERS |
ifp->if_flags |= IFF_NOTRAILERS; |
#endif |
#ifdef CYGPKG_NET_FREEBSD_STACK |
ifp->if_name = xp = ifp->if_xname; |
np = (char *)sc->dev_name; |
unit = 0; |
while (*np && !((*np >= '0') && (*np <= '9'))) *xp++ = *np++; |
if (*np) { |
*xp = '\0'; |
while (*np) { |
unit = (unit * 10) + (*np++ - '0'); |
} |
ifp->if_unit = unit; |
} |
ifp->if_init = (void_fun *)eth_drv_start; |
ifp->if_output = ether_output; |
#else |
bcopy((void *)sc->dev_name, ifp->if_xname, IFNAMSIZ); |
#endif |
sc->state = 0; |
|
// Attach the interface |
#ifdef CYGPKG_NET_FREEBSD_STACK |
ether_ifattach(ifp, 0); |
#else |
if_attach(ifp); |
ether_ifattach(ifp); |
#endif |
|
#ifdef CYGSEM_HAL_VIRTUAL_VECTOR_DIAG |
// Set up interfaces so debug environment can share this device |
{ |
void *dbg = CYGACC_CALL_IF_DBG_DATA(); |
if (!dbg) { |
CYGACC_CALL_IF_DBG_DATA_SET((void *)sc); |
} |
} |
#endif |
} |
|
// |
// This [internal] function will be called to stop activity on an interface. |
// |
static void |
eth_drv_stop(struct eth_drv_sc *sc) |
{ |
(sc->funs->stop)(sc); |
sc->state &= ~ETH_DRV_STATE_ACTIVE; |
} |
|
// |
// This [internal] function will be called to start activity on an interface. |
// |
static void |
eth_drv_start(struct eth_drv_sc *sc) |
{ |
struct ifnet *ifp = &sc->sc_arpcom.ac_if; |
|
// Perform any hardware initialization |
(sc->funs->start)(sc, (unsigned char *)&sc->sc_arpcom.ac_enaddr, 0); |
#ifdef CYGPKG_NET_FREEBSD_STACK |
// resend multicast addresses if present |
if(ifp->if_multiaddrs.lh_first && ifp->if_ioctl) { |
int s = splimp(); |
ifp->if_ioctl(ifp, SIOCADDMULTI, 0); |
splx(s); |
} |
#endif |
// Set 'running' flag, and clear output active flag. |
ifp->if_flags |= IFF_RUNNING; |
ifp->if_flags &= ~IFF_OACTIVE; |
sc->state |= ETH_DRV_STATE_ACTIVE; |
eth_drv_send(ifp); // Try and start up transmit |
} |
|
// |
// This function supports "I/O control" operations on an interface. |
// |
static int |
eth_drv_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) |
{ |
struct eth_drv_sc *sc = ifp->if_softc; |
#ifndef CYGPKG_NET_FREEBSD_STACK |
struct ifaddr *ifa = (struct ifaddr *) data; |
#endif |
struct ifreq *ifr = (struct ifreq *)data; |
int s, error = 0; |
|
// DEBUG |
#ifdef CYGPKG_NET_FREEBSD_STACK |
log(LOG_IOCTL, "%s: cmd: %s, data:\n", __FUNCTION__, _ioctl_name(cmd)); |
log_dump(LOG_IOCTL, data, 32); |
#endif |
// DEBUG |
|
s = splnet(); |
|
#ifdef CYGPKG_NET_FREEBSD_STACK |
if ((error = ether_ioctl(ifp, cmd, data)) > 0) { |
#else |
if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) { |
#endif |
splx(s); |
return error; |
} |
|
switch (cmd) { |
|
case SIOCSIFADDR: |
#ifndef CYGPKG_NET_FREEBSD_STACK // Now in if_ethersubr.c |
ifp->if_flags |= IFF_UP; |
|
switch (ifa->ifa_addr->sa_family) { |
#ifdef INET |
case AF_INET: |
eth_drv_start(sc); |
arp_ifinit(&sc->sc_arpcom, ifa); |
break; |
#endif |
default: |
eth_drv_start(sc); |
break; |
} |
#endif // CYGPKG_NET_FREEBSD_STACK |
break; |
|
case SIOCGIFHWADDR: |
// Get hardware (MAC) address |
ifr->ifr_hwaddr.sa_family = AF_INET; |
bcopy(&sc->sc_arpcom.ac_enaddr, &ifr->ifr_hwaddr.sa_data, ETHER_ADDR_LEN); |
break; |
|
case SIOCSIFHWADDR: |
// Set hardware (MAC) address |
bcopy(&ifr->ifr_hwaddr.sa_data, &sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN); |
if ((sc->funs->control)(sc, ETH_DRV_SET_MAC_ADDRESS, |
&sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN)) { |
error = EINVAL; |
} |
break; |
|
#ifdef SIOCGIFSTATS |
case SIOCGIFSTATS: |
#ifdef SIOCGIFSTATSUD |
case SIOCGIFSTATSUD: |
#endif |
// Get interface statistics: |
if ((sc->funs->control)(sc, (cmd == SIOCGIFSTATS) |
? ETH_DRV_GET_IF_STATS |
: ETH_DRV_GET_IF_STATS_UD, |
data, 0 ) ) { |
error = EINVAL; |
} |
break; |
#endif // SIOCGIFSTATS |
|
case SIOCSIFFLAGS: |
if ((ifp->if_flags & IFF_UP) == 0 && |
(ifp->if_flags & IFF_RUNNING) != 0) { |
/* |
* If interface is marked down and it is running, then |
* stop it. |
*/ |
eth_drv_stop(sc); |
ifp->if_flags &= ~IFF_RUNNING; |
} else |
if ((ifp->if_flags & IFF_UP) != 0 && |
(ifp->if_flags & IFF_RUNNING) == 0) { |
/* |
* If interface is marked up and it is stopped, then |
* start it. |
*/ |
eth_drv_start(sc); |
} else { |
/* |
* Reset the interface to pick up changes in any other |
* flags that affect hardware registers. |
*/ |
eth_drv_stop(sc); |
eth_drv_start(sc); |
} |
break; |
|
#ifdef CYGPKG_NET_FREEBSD_STACK |
case SIOCADDMULTI: |
case SIOCDELMULTI: |
{ |
struct ifmultiaddr *ifma; |
struct eth_drv_mc_list mc_list; |
int mode = (ifp->if_flags & IFF_ALLMULTI) ? ETH_DRV_SET_MC_ALL : |
ETH_DRV_SET_MC_LIST; |
|
#ifdef DEBUG |
log(LOG_ADDR, "%s Multi\n",(cmd == SIOCADDMULTI) ? "Add" : "Del"); |
#endif |
mc_list.len = 0; |
LIST_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) { |
if (ifma->ifma_addr->sa_family != AF_LINK) { |
continue; |
} |
#ifdef DEBUG |
log_dump(LOG_ADDR, LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 6); |
#endif |
if ((LLADDR((struct sockaddr_dl *)ifma->ifma_addr)[0] & 0x01) == 0) { |
#ifdef DEBUG |
log(LOG_ADDR, "** Not a multicast address - ignored\n"); |
#endif |
continue; |
} |
if (mc_list.len < ETH_DRV_MAX_MC) { |
bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), |
mc_list.addrs[mc_list.len], ETHER_ADDR_LEN); |
mc_list.len++; |
} else { |
mode = ETH_DRV_SET_MC_ALL; |
} |
} |
// Note: drivers may behave like IFF_ALLMULTI if the list is |
// more than their hardware can handle, e.g. some can only handle 1. |
if ((sc->funs->control)(sc, mode, &mc_list, sizeof(mc_list))) { |
diag_printf( "[%s] Warning: Driver can't set multi-cast mode\n", |
__FUNCTION__ ); |
error = EINVAL; |
} |
break; |
} |
#endif |
|
default: |
error = EINVAL; |
break; |
} |
|
splx(s); |
return (error); |
} |
|
// |
// Control whether any special locking needs to take place if we intend to |
// cooperate with a ROM monitor (e.g. RedBoot) using this hardware. |
// |
#if defined(CYGSEM_HAL_USE_ROM_MONITOR) && \ |
defined(CYGSEM_HAL_VIRTUAL_VECTOR_DIAG) && \ |
!defined(CYGSEM_HAL_VIRTUAL_VECTOR_CLAIM_COMMS) |
|
// Indicate that special locking precautions are warranted. |
#define _LOCK_WITH_ROM_MONITOR |
|
// This defines the [well known] channel that RedBoot will use when it is |
// using the network hardware for the debug channel. |
#define RedBoot_TCP_CHANNEL CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS |
|
// Define this if you ever need to call 'diag_printf()' from interrupt level |
// code (ISR) and the debug channel might be using the network hardware. If |
// this is not the case, then disabling interrupts here is over-kill. |
//#define _LOCK_USING_INTERRUPTS |
#endif |
|
// |
// This routine is called to start transmitting if there is data |
// available. |
// |
static void |
eth_drv_send(struct ifnet *ifp) |
{ |
struct eth_drv_sc *sc = ifp->if_softc; |
#if MAX_ETH_DRV_SG > 64 |
static // Avoid large stack requirements |
#endif |
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; |
int sg_len; |
struct mbuf *m0, *m; |
int len, total_len; |
unsigned char *data; |
#ifdef _LOCK_WITH_ROM_MONITOR |
#ifdef _LOCK_USING_INTERRUPTS |
cyg_uint32 ints; |
#endif |
bool need_lock = false; |
int debug_chan; |
#endif // _LOCK_WITH_ROM_MONITOR |
|
// This is now only called from network threads, so no guarding is |
// required; locking is in place via the splfoo() mechanism already. |
|
if ((ifp->if_flags & IFF_RUNNING) != IFF_RUNNING) { |
return; |
} |
|
while ((sc->funs->can_send)(sc) > 0) { |
IF_DEQUEUE(&ifp->if_snd, m0); |
if (m0 == 0) { |
break; |
} |
|
#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES |
if ( simulate_fail( sc, SIMULATE_FAIL_SEND ) ) { |
// must free the mbufs |
m_freem(m0); |
continue; // next packet to send |
} |
#endif |
|
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG |
if (cyg_io_eth_net_debug) { |
START_CONSOLE(); |
diag_printf("Sending %d bytes\n", m0->m_pkthdr.len); |
END_CONSOLE(); |
} |
#endif |
|
/* We need to use m->m_pkthdr.len, so require the header */ |
if ((m0->m_flags & M_PKTHDR) == 0) |
panic("eth_drv_send: no header mbuf"); |
|
#if NBPFILTER > 0 |
/* Tap off here if there is a BPF listener. */ |
if (ifp->if_bpf) |
bpf_mtap(ifp->if_bpf, m0); |
#endif |
|
// Extract data pointers (don't actually move data here) |
sg_len = 0; total_len = 0; |
for (m = m0; m ; m = m->m_next) { |
data = mtod(m, u_char *); |
len = m->m_len; |
total_len += len; |
sg_list[sg_len].buf = (CYG_ADDRESS)data; |
sg_list[sg_len].len = len; |
if ( len ) |
sg_len++; |
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG |
if (cyg_io_eth_net_debug) { |
START_CONSOLE(); |
diag_printf("xmit %d bytes at %x sg[%d]\n", len, data, sg_len); |
if ( cyg_io_eth_net_debug > 1) |
diag_dump_buf(data, len); |
END_CONSOLE(); |
} |
#endif |
if ( MAX_ETH_DRV_SG < sg_len ) { |
#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS |
int needed = 0; |
struct mbuf *m1; |
for (m1 = m0; m1 ; m1 = m1->m_next) needed++; |
START_CONSOLE(); |
diag_printf("too many mbufs to tx, %d > %d, need %d\n", |
sg_len, MAX_ETH_DRV_SG, needed ); |
END_CONSOLE(); |
#endif |
sg_len = 0; |
break; // drop it on the floor |
} |
} |
|
#ifdef _LOCK_WITH_ROM_MONITOR |
// Firm lock on this portion of the driver. Since we are about to |
// start messing with the actual hardware, it is imperative that the |
// current thread not loose control of the CPU at this time. Otherwise, |
// the hardware could be left in an unusable state. This caution is |
// only warranted if there is a possibility of some other thread trying |
// to use the hardware simultaneously. The network stack would prevent |
// this implicitly since all accesses are controlled by the "splX()" |
// locks, but if there is a ROM monitor, such as RedBoot, also using |
// the hardware, all bets are off. |
|
// Note: these operations can be avoided if it were well known that |
// RedBoot was not using the network hardware for diagnostic I/O. This |
// can be inferred by checking which I/O channel RedBoot is currently |
// hooked to. |
debug_chan = CYGACC_CALL_IF_SET_DEBUG_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT); |
if (debug_chan == RedBoot_TCP_CHANNEL) { |
need_lock = true; |
#ifdef _LOCK_USING_INTERRUPTS |
HAL_DISABLE_INTERRUPTS(ints); |
#endif |
cyg_drv_dsr_lock(); |
} |
#endif // _LOCK_WITH_ROM_MONITOR |
|
// Tell hardware to send this packet |
if ( sg_len ) |
(sc->funs->send)(sc, sg_list, sg_len, total_len, (unsigned long)m0); |
|
#ifdef _LOCK_WITH_ROM_MONITOR |
// Unlock the driver & hardware. It can once again be safely shared. |
if (need_lock) { |
cyg_drv_dsr_unlock(); |
#ifdef _LOCK_USING_INTERRUPTS |
HAL_RESTORE_INTERRUPTS(ints); |
#endif |
} |
#endif // _LOCK_WITH_ROM_MONITOR |
#undef _LOCK_WITH_ROM_MONITOR |
} |
} |
|
// |
// This function is called from the hardware driver when an output operation |
// has completed - i.e. the packet has been sent. |
// |
static struct mbuf *mbuf_key; |
|
static void |
eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRESS key, int status) |
{ |
struct ifnet *ifp = &sc->sc_arpcom.ac_if; |
struct mbuf *m0 = (struct mbuf *)key; |
CYGARC_HAL_SAVE_GP(); |
|
// Check for errors here (via 'status') |
ifp->if_opackets++; |
// Done with packet |
|
// Guard against a NULL return - can be caused by race conditions in |
// the driver, this is the neatest fixup: |
if (m0) { |
mbuf_key = m0; |
m_freem(m0); |
} |
// Start another if possible |
eth_drv_send(ifp); |
CYGARC_HAL_RESTORE_GP(); |
} |
|
// |
// This function is called from a hardware driver to indicate that an input |
// packet has arrived. The routine will set up appropriate network resources |
// (mbuf's) to hold the data and call back into the driver to retrieve the data. |
// |
static void |
eth_drv_recv(struct eth_drv_sc *sc, int total_len) |
{ |
struct ifnet *ifp = &sc->sc_arpcom.ac_if; |
struct ether_header _eh, *eh=&_eh; |
struct mbuf *top, **mp, *m; |
int mlen; |
unsigned char *data; |
#if MAX_ETH_DRV_SG > 64 |
static // Avoid large stack requirements |
#endif |
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; |
int sg_len; |
|
if ((ifp->if_flags & IFF_RUNNING) != IFF_RUNNING) { |
return; // Interface not up, ignore this request |
} |
|
CYG_ASSERT( 0 != total_len, "total_len is zero!" ); |
CYG_ASSERT( 0 <= total_len, "total_len is negative!" ); |
CYG_ASSERT( sizeof( struct ether_header ) <= total_len, |
"No ether header here!" ); |
|
if ( total_len < sizeof( struct ether_header ) ) |
// Our arithmetic below would go wrong |
return; |
|
CYGARC_HAL_SAVE_GP(); // This is down here to make matching restore neat |
|
/* Pull packet off interface. */ |
MGETHDR(m, M_DONTWAIT, MT_DATA); |
if (m == 0) { |
#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS |
START_CONSOLE(); |
diag_printf("warning: eth_recv out of MBUFs\n"); |
END_CONSOLE(); |
#endif |
} |
|
// Set up buffers |
// Unload ethernet header separately so IP/UDP/TCP headers are aligned |
sg_list[0].buf = (CYG_ADDRESS)eh; |
sg_list[0].len = sizeof(*eh); |
sg_len = 1; |
|
// Compute total length (minus ethernet header) |
total_len -= sizeof(*eh); |
|
top = 0; |
mlen = MHLEN; |
mp = ⊤ |
|
if (m) { |
m->m_pkthdr.rcvif = ifp; |
m->m_pkthdr.len = total_len; |
} else { |
sg_list[sg_len].buf = (CYG_ADDRESS)0; |
sg_list[sg_len].len = total_len; |
sg_len++; |
total_len = 0; |
} |
|
while (total_len > 0) { |
if (top) { |
MGET(m, M_DONTWAIT, MT_DATA); |
if (m == 0) { |
m_freem(top); |
#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS |
START_CONSOLE(); |
diag_printf("out of MBUFs [2]"); |
END_CONSOLE(); |
#endif |
sg_list[sg_len].buf = (CYG_ADDRESS)0; |
sg_list[sg_len].len = total_len; |
sg_len++; |
top = 0; |
break; |
} |
mlen = MLEN; |
} |
if (total_len >= MINCLSIZE) { |
MCLGET(m, M_DONTWAIT); |
if ((m->m_flags & M_EXT) == 0) { |
m_freem(top); |
m_free(m); |
#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS |
START_CONSOLE(); |
diag_printf("warning: eth_recv out of MBUFs\n"); |
END_CONSOLE(); |
#endif |
sg_list[sg_len].buf = (CYG_ADDRESS)0; |
sg_list[sg_len].len = total_len; |
sg_len++; |
top = 0; |
break; |
} |
mlen = MCLBYTES; |
} |
m->m_len = mlen = min(total_len, mlen); |
total_len -= mlen; |
data = mtod(m, caddr_t); |
sg_list[sg_len].buf = (CYG_ADDRESS)data; |
sg_list[sg_len].len = mlen; |
sg_len++; |
*mp = m; |
mp = &m->m_next; |
} // endwhile |
|
// Ask hardware to unload buffers |
(sc->funs->recv)(sc, sg_list, sg_len); |
|
#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES |
if ( simulate_fail( sc, SIMULATE_FAIL_RECV ) ) { |
// toss the packet - note that some hardware gets |
// fussy if the packet isn't "unloaded", thus we |
// have to wait until now to throw it away |
if (top) { |
m_free(top); |
} |
ifp->if_ierrors++; |
return; |
} |
|
if ( simulate_fail( sc, SIMULATE_FAIL_CORRUPT ) ) { |
// Corrupt the data |
simulate_fail_corrupt_sglist( sg_list, sg_len ); |
} |
#endif |
|
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG |
if (cyg_io_eth_net_debug) { |
int i; |
START_CONSOLE(); |
for (i = 0; i < sg_len; i++) { |
if (sg_list[i].buf) { |
diag_printf("rx %d bytes at %x sg[%d]\n", sg_list[i].len, sg_list[i].buf, i); |
if ( cyg_io_eth_net_debug > 1 ) |
diag_dump_buf((void *)sg_list[i].buf, sg_list[i].len); |
} |
} |
END_CONSOLE(); |
} |
#endif |
m = top; |
if (m == 0) { |
ifp->if_ierrors++; |
} |
else { |
ifp->if_ipackets++; |
|
#if NBPFILTER > 0 |
#error FIXME - Need mbuf with ethernet header attached |
/* |
* Check if there's a BPF listener on this interface. |
* If so, hand off the raw packet to bpf. |
*/ |
if (ifp->if_bpf) |
bpf_mtap(ifp->if_bpf, m); |
#endif |
|
// Push data into protocol stacks |
ether_input(ifp, eh, m); |
} |
CYGARC_HAL_RESTORE_GP(); |
} |
|
|
// ------------------------------------------------------------------------ |
// DSR to schedule network delivery thread |
|
extern void ecos_synch_eth_drv_dsr(void); // from ecos/timeout.c in net stack |
|
void |
eth_drv_dsr(cyg_vector_t vector, |
cyg_ucount32 count, |
cyg_addrword_t data) |
{ |
struct eth_drv_sc *sc = (struct eth_drv_sc *)data; |
|
#ifdef CYGDBG_USE_ASSERTS |
// then check that this really is a "sc" |
{ |
cyg_netdevtab_entry_t *t; |
for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) |
if ( ((struct eth_drv_sc *)t->device_instance) == sc ) |
break; // found it |
CYG_ASSERT( t != &__NETDEVTAB_END__, "eth_drv_dsr: Failed to find sc in NETDEVTAB" ); |
} |
#endif // Checking code |
|
sc->state |= ETH_DRV_NEEDS_DELIVERY; |
|
ecos_synch_eth_drv_dsr(); // [request] run delivery function for this dev |
} |
|
// This is called from the delivery thread, to do just that: |
void eth_drv_run_deliveries( void ) |
{ |
cyg_netdevtab_entry_t *t; |
for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) { |
struct eth_drv_sc *sc = (struct eth_drv_sc *)t->device_instance; |
if ( ETH_DRV_NEEDS_DELIVERY & sc->state ) { |
#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) |
cyg_bool was_ctrlc_int; |
#endif |
sc->state &=~ETH_DRV_NEEDS_DELIVERY; |
#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) |
was_ctrlc_int = HAL_CTRLC_CHECK((*sc->funs->int_vector)(sc), (int)sc); |
if (!was_ctrlc_int) // Fall through and run normal code |
#endif |
(*sc->funs->deliver)(sc); |
} |
} |
} |
|
// This is called from the delivery thread, to unstick devices if there is |
// no network activity. |
#ifdef CYGPKG_NET_FAST_THREAD_TICKLE_DEVS |
void eth_drv_tickle_devices( void ) |
{ |
cyg_netdevtab_entry_t *t; |
for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) { |
struct eth_drv_sc *sc = (struct eth_drv_sc *)t->device_instance; |
if ( ETH_DRV_STATE_ACTIVE & sc->state ) { |
struct ifnet *ifp = &sc->sc_arpcom.ac_if; |
// Try to dequeue a packet for this interface, if we can. This |
// will call can_send() for active interfaces. It is calls to |
// this function from tx_done() which normally provide |
// continuous transmissions; otherwise we do not get control. |
// This call fixes that. |
eth_drv_send(ifp); |
} |
} |
} |
#endif // CYGPKG_NET_FAST_THREAD_TICKLE_DEVS |
|
// ------------------------------------------------------------------------ |
|
#ifdef CYGPKG_IO_PCMCIA |
// Lookup a 'netdev' entry, assuming that it is an ethernet device. |
cyg_netdevtab_entry_t * |
eth_drv_netdev(char *name) |
{ |
cyg_netdevtab_entry_t *t; |
struct eth_drv_sc *sc; |
for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) { |
sc = (struct eth_drv_sc *)t->device_instance; |
if (strcmp(sc->dev_name, name) == 0) { |
return t; |
} |
} |
return (cyg_netdevtab_entry_t *)NULL; |
} |
#endif // CYGPKG_IO_PCMCIA |
|
// EOF src/net/eth_drv.c |
/stand_alone/eth_drv.c
0,0 → 1,556
//========================================================================== |
// |
// src/stand_alone/eth_drv.c |
// |
// Stand-alone hardware independent networking support for RedBoot |
// |
//========================================================================== |
//####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): gthomas |
// Contributors: gthomas |
// Date: 2000-07-14 |
// Purpose: |
// Description: |
// |
// This code is part of RedBoot (tm). |
// |
//####DESCRIPTIONEND#### |
// |
//========================================================================== |
|
#include <pkgconf/system.h> |
#include <pkgconf/io_eth_drivers.h> |
|
#include <cyg/infra/cyg_type.h> |
#include <cyg/hal/hal_arch.h> |
#include <cyg/infra/diag.h> |
#include <cyg/hal/drv_api.h> |
#include <cyg/hal/hal_if.h> |
#include <cyg/io/eth/eth_drv.h> |
#include <cyg/io/eth/netdev.h> |
#include <string.h> |
|
// High-level ethernet driver |
|
|
// Interfaces exported to drivers |
|
static void eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr); |
static void eth_drv_recv(struct eth_drv_sc *sc, int total_len); |
static void eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status); |
|
struct eth_drv_funs eth_drv_funs = {eth_drv_init, eth_drv_recv, eth_drv_tx_done}; |
|
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG |
int cyg_io_eth_net_debug = CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY; |
// Usually just the header is enough, the body slows things too much. |
#define DIAG_DUMP_BUF_HDR( a, b ) if (0 < cyg_io_eth_net_debug) diag_dump_buf( (a), (b) ) |
#define DIAG_DUMP_BUF_BDY( a, b ) if (1 < cyg_io_eth_net_debug) diag_dump_buf( (a), (b) ) |
#else |
#define DIAG_DUMP_BUF_HDR( a, b ) |
#define DIAG_DUMP_BUF_BDY( a, b ) |
#endif |
|
unsigned char __local_enet_addr[ETHER_ADDR_LEN+2]; |
struct eth_drv_sc *__local_enet_sc; |
|
#ifdef CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS |
// |
// Horrible hack: In order to allow the stand-alone networking code to work |
// alongside eCos (or any other stack), separate IP addresses must be used. |
// When a packet arrives at the interface, we check to see which IP address |
// it corresponds to and only pass it "up" if it's not for the stand-alone |
// layer. |
// |
// tres degolas :-( |
// |
extern char __local_ip_addr[4]; |
#endif // PASS_PACKETS |
|
#ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT |
// |
// Another horrible hack: In order to do the above passing on of |
// application packets safely - and passing on completion events for |
// pending transmissions (which is not conditional) - we must lock the |
// application scheduler before calling into it. There are several reasons |
// for this: a) We are likely running on a RedBoot special debug stack and |
// so the application's stack checking fires; b) we could even get |
// descheduled if the arrival of a packet causes a higher priority thread |
// to awaken! |
|
#include <cyg/hal/dbg-threads-api.h> |
|
// Use with care! Local variable defined! |
# define LOCK_APPLICATION_SCHEDULER() \ |
{ /* NEW BLOCK */ \ |
threadref currthread; \ |
int threadok; \ |
threadok = dbg_currthread( &currthread ); \ |
if ( threadok ) { \ |
threadok = dbg_scheduler( &currthread, 1, 1 ); /* lock */ \ |
} |
|
# define UNLOCK_APPLICATION_SCHEDULER() \ |
if ( threadok ) { \ |
dbg_scheduler( &currthread, 0, 1 ); /* unlock */ \ |
} \ |
} /* END BLOCK */ |
|
#else |
# define LOCK_APPLICATION_SCHEDULER() CYG_EMPTY_STATEMENT |
# define UNLOCK_APPLICATION_SCHEDULER() CYG_EMPTY_STATEMENT |
#endif // GDB_THREAD_SUPPORT |
|
// |
// Buffer 'get' support. The problem is that this function only gets |
// called when some data is required, but packets may arrive on the device |
// at any time. More particularly, on some devices when data arrive, all |
// of that data needs to be consumed immediately or be lost. This process |
// is driven by interrupts, which in the stand-along case are simulated by |
// calling the "poll" interface. |
// |
// Thus there will be a pool of buffers, some free and some full, to try |
// and manage this. |
// |
|
#define MAX_ETH_MSG 1540 |
#define NUM_ETH_MSG CYGNUM_IO_ETH_DRIVERS_NUM_PKT |
|
struct eth_msg { |
struct eth_msg *next, *prev; |
int len; // Actual number of bytes in message |
unsigned char data[MAX_ETH_MSG]; |
}; |
|
struct eth_msg_hdr { |
struct eth_msg *first, *last; |
}; |
|
static struct eth_msg_hdr eth_msg_free, eth_msg_full; |
static struct eth_msg eth_msgs[NUM_ETH_MSG]; |
|
// Prototypes for functions used in this module |
static void eth_drv_start(struct eth_drv_sc *sc); |
|
// These functions are defined in RedBoot and control access to |
// the "default" console. |
extern int start_console(void); |
extern void end_console(int); |
|
// Simple queue management functions |
|
static void |
eth_drv_msg_put(struct eth_msg_hdr *hdr, struct eth_msg *msg) |
{ |
if (hdr->first != (struct eth_msg *)hdr) { |
// Something already in queue |
hdr->last->next = msg; |
msg->prev = hdr->last; |
msg->next = (struct eth_msg *)hdr; |
hdr->last = msg; |
} else { |
hdr->first = hdr->last = msg; |
msg->next = msg->prev = (struct eth_msg *)hdr; |
} |
} |
|
static struct eth_msg * |
eth_drv_msg_get(struct eth_msg_hdr *hdr) |
{ |
struct eth_msg *msg; |
if (hdr->first != (struct eth_msg *)hdr) { |
msg = hdr->first; |
hdr->first = msg->next; |
msg->next->prev = (struct eth_msg *)hdr; |
} else { |
msg = (struct eth_msg *)NULL; |
} |
return msg; |
} |
|
void |
eth_drv_buffers_init(void) |
{ |
int i; |
struct eth_msg *msg = eth_msgs; |
|
eth_msg_full.first = eth_msg_full.last = (struct eth_msg *)ð_msg_full; |
eth_msg_free.first = eth_msg_free.last = (struct eth_msg *)ð_msg_free; |
for (i = 0; i < NUM_ETH_MSG; i++, msg++) { |
eth_drv_msg_put(ð_msg_free, msg); |
} |
} |
|
// |
// This function is called during system initialization to register a |
// network interface with the system. |
// |
static void |
eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr) |
{ |
// enaddr == 0 -> hardware init was incomplete (no ESA) |
if (enaddr != 0) { |
// Set up hardware address |
memcpy(&sc->sc_arpcom.esa, enaddr, ETHER_ADDR_LEN); |
memcpy(__local_enet_addr, enaddr, ETHER_ADDR_LEN); |
__local_enet_sc = sc; |
eth_drv_start(sc); |
} |
} |
|
#if 0 // Not currently used. Left in case it's needed in the future |
// |
// This [internal] function will be called to stop activity on an interface. |
// |
static void |
eth_drv_stop(struct eth_drv_sc *sc) |
{ |
(sc->funs->stop)(sc); |
} |
#endif |
|
// |
// This [internal] function will be called to start activity on an interface. |
// |
static void |
eth_drv_start(struct eth_drv_sc *sc) |
{ |
// Perform any hardware initialization |
(sc->funs->start)(sc, (unsigned char *)&sc->sc_arpcom.esa, 0); |
} |
|
// |
// Send a packet of data to the hardware |
// |
static int packet_sent; |
|
void |
eth_drv_write(char *eth_hdr, char *buf, int len) |
{ |
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; |
struct eth_drv_sc *sc = __local_enet_sc; |
int sg_len = 2; |
void *dbg = CYGACC_CALL_IF_DBG_DATA(); |
int old_state; |
int wait_time = 5; // Timeout before giving up |
void *eth_drv_old = 0; |
|
if (dbg) { |
sc = (struct eth_drv_sc *)dbg; // Use control from installed driver |
eth_drv_old = sc->funs->eth_drv_old; |
if (eth_drv_old == 0) { |
sc->funs->eth_drv_old = sc->funs->eth_drv; |
sc->funs->eth_drv = ð_drv_funs; // Substitute stand-alone driver |
old_state = sc->state; |
if (!old_state & ETH_DRV_STATE_ACTIVE) { |
// This interface not fully initialized, do it now |
(sc->funs->start)(sc, (unsigned char *)&__local_enet_addr, 0); |
sc->state |= ETH_DRV_STATE_ACTIVE; |
} |
} |
} |
|
while (!(sc->funs->can_send)(sc)) { |
// Give driver a chance to service hardware |
(sc->funs->poll)(sc); |
CYGACC_CALL_IF_DELAY_US(2*100000); |
if (--wait_time <= 0) |
goto reset_and_out; // Give up on sending packet |
} |
|
sg_list[0].buf = (CYG_ADDRESS)eth_hdr; |
sg_list[0].len = 14; // FIXME |
sg_list[1].buf = (CYG_ADDRESS)buf; |
sg_list[1].len = len; |
packet_sent = 0; |
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG |
if (cyg_io_eth_net_debug) { |
int old_console; |
old_console = start_console(); |
diag_printf("Ethernet send:\n"); |
DIAG_DUMP_BUF_HDR(eth_hdr, 14); |
DIAG_DUMP_BUF_BDY(buf, len); |
end_console(old_console); |
} |
#endif |
|
(sc->funs->send)(sc, sg_list, sg_len, len+14, (CYG_ADDRWORD)&packet_sent); |
|
wait_time = 500; |
while (1) { |
(sc->funs->poll)(sc); |
|
if(packet_sent) |
break; |
|
CYGACC_CALL_IF_DELAY_US(2*1000); |
if (--wait_time <= 0) |
goto reset_and_out; // Give up on sending packet |
} |
reset_and_out: |
if (dbg) { |
// if (!old_state & ETH_DRV_STATE_ACTIVE) { |
// // This interface was not fully initialized, shut it back down |
// (sc->funs->stop)(sc); |
// } |
if (eth_drv_old == 0) { |
sc->funs->eth_drv = sc->funs->eth_drv_old; |
sc->funs->eth_drv_old = (struct eth_drv_funs *)0; |
} |
} |
} |
|
// |
// This function is called from the hardware driver when an output operation |
// has completed - i.e. the packet has been sent. |
// |
static void |
eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status) |
{ |
CYGARC_HAL_SAVE_GP(); |
if ((int *)key == &packet_sent) { |
*(int *)key = 1; |
} else { |
// It's possible that this acknowledgement is for a different |
// [logical] driver. Try and pass it on. |
#if defined(CYGDBG_IO_ETH_DRIVERS_DEBUG) && \ |
(CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY >=2 ) |
// Note: not normally enabled - too verbose |
if (cyg_io_eth_net_debug > 1) { |
int old_console; |
old_console = start_console(); |
diag_printf("tx_done for other key: %x\n", key); |
end_console(old_console); |
} |
#endif |
LOCK_APPLICATION_SCHEDULER(); |
if (sc->funs->eth_drv_old) { |
(sc->funs->eth_drv_old->tx_done)(sc, key, status); |
} else { |
(sc->funs->eth_drv->tx_done)(sc, key, status); |
} |
UNLOCK_APPLICATION_SCHEDULER(); |
} |
CYGARC_HAL_RESTORE_GP(); |
} |
|
// |
// Receive one packet of data from the hardware, if available |
// |
int |
eth_drv_read(char *eth_hdr, char *buf, int len) |
{ |
struct eth_drv_sc *sc = __local_enet_sc; |
struct eth_msg *msg; |
int res; |
void *dbg = CYGACC_CALL_IF_DBG_DATA(); |
int old_state; |
void *eth_drv_old = 0; |
|
if (dbg) { |
sc = (struct eth_drv_sc *)dbg; // Use control from installed driver |
eth_drv_old = sc->funs->eth_drv_old; |
if (eth_drv_old == 0) { |
sc->funs->eth_drv_old = sc->funs->eth_drv; |
sc->funs->eth_drv = ð_drv_funs; // Substitute stand-alone driver |
old_state = sc->state; |
if (!old_state & ETH_DRV_STATE_ACTIVE) { |
// This interface not fully initialized, do it now |
(sc->funs->start)(sc, (unsigned char *)&__local_enet_addr, 0); |
sc->state |= ETH_DRV_STATE_ACTIVE; |
} |
} |
} |
(sc->funs->poll)(sc); // Give the driver a chance to fetch packets |
msg = eth_drv_msg_get(ð_msg_full); |
if (msg && len >= msg->len - 14) { |
memcpy(eth_hdr, msg->data, 14); |
memcpy(buf, &msg->data[14], msg->len-14); |
res = msg->len; |
} else { |
res = 0; |
} |
if (msg) { |
eth_drv_msg_put(ð_msg_free, msg); |
} |
|
if (dbg) { |
if (eth_drv_old == 0) { |
sc->funs->eth_drv = sc->funs->eth_drv_old; |
sc->funs->eth_drv_old = (struct eth_drv_funs *)0; |
} |
// if (!old_state & ETH_DRV_STATE_ACTIVE) { |
// // This interface was not fully initialized, shut it back down |
// (sc->funs->stop)(sc); |
// } |
} |
return res; |
} |
|
#ifdef CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS |
// |
// This function is called to copy a message up to the next level. |
// It is only used when this driver has usurped the processing of |
// network functions. |
// |
static unsigned char *eth_drv_copy_recv_buf; |
static void |
eth_drv_copy_recv(struct eth_drv_sc *sc, |
struct eth_drv_sg *sg_list, |
int sg_len) |
{ |
int i; |
unsigned char *ppp; |
CYGARC_HAL_SAVE_GP(); |
ppp = eth_drv_copy_recv_buf; // Be safe against being called again by accident |
for (i = 0; i < sg_len; i++) { |
if ( sg_list[i].buf ) // Be safe against discarding calls |
memcpy((unsigned char *)sg_list[i].buf, |
ppp, sg_list[i].len); |
ppp += sg_list[i].len; |
} |
CYGARC_HAL_RESTORE_GP(); |
} |
#endif |
|
// |
// This function is called from a hardware driver to indicate that an input |
// packet has arrived. The routine will set up appropriate network resources |
// to hold the data and call back into the driver to retrieve the data. |
// |
static void |
eth_drv_recv(struct eth_drv_sc *sc, int total_len) |
{ |
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; |
int sg_len = 0; |
struct eth_msg *msg; |
unsigned char *buf; |
CYGARC_HAL_SAVE_GP(); |
|
if ((total_len > MAX_ETH_MSG) || (total_len < 0)) { |
#ifdef CYGSEM_IO_ETH_DRIVERS_WARN |
int old_console; |
old_console = start_console(); |
diag_printf("%s: packet of %d bytes truncated\n", __FUNCTION__, total_len); |
end_console(old_console); |
#endif |
total_len = MAX_ETH_MSG; |
} |
msg = eth_drv_msg_get(ð_msg_free); |
if (msg) { |
buf = msg->data; |
} else { |
#ifdef CYGSEM_IO_ETH_DRIVERS_WARN |
int old_console; |
old_console = start_console(); |
diag_printf("%s: packet of %d bytes dropped\n", __FUNCTION__, total_len); |
end_console(old_console); |
#endif |
buf = (unsigned char *)0; // Drivers know this means "the bit bucket" |
} |
sg_list[0].buf = (CYG_ADDRESS)buf; |
sg_list[0].len = total_len; |
sg_len = 1; |
|
(sc->funs->recv)(sc, sg_list, sg_len); |
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG |
if (cyg_io_eth_net_debug) { |
int old_console; |
old_console = start_console(); |
diag_printf("Ethernet recv:\n"); |
if ( buf ) { |
DIAG_DUMP_BUF_HDR(buf, 14); |
DIAG_DUMP_BUF_BDY(buf+14, total_len-14); |
} |
else |
diag_printf(" ...NULL buffer.\n"); |
end_console(old_console); |
} |
#endif |
#ifdef CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS |
if ((unsigned char *)0 != buf && // Only pass on a packet we actually got! |
sc->funs->eth_drv_old != (struct eth_drv_funs *)0) { |
void (*hold_recv)(struct eth_drv_sc *sc, |
struct eth_drv_sg *sg_list, |
int sg_len); |
// See if this packet was for us. If not, pass it upwards |
// This is a major layering violation!! |
if (memcmp(&__local_ip_addr, &buf[14+16], 4)) { |
hold_recv = sc->funs->recv; |
sc->funs->recv = eth_drv_copy_recv; |
eth_drv_copy_recv_buf = buf; |
// This calls into the 'other' driver, giving it a chance to |
// do something with this data (since it wasn't for us) |
LOCK_APPLICATION_SCHEDULER(); |
(sc->funs->eth_drv_old->recv)(sc, total_len); |
UNLOCK_APPLICATION_SCHEDULER(); |
sc->funs->recv = hold_recv; |
} |
} |
#endif |
if (msg) { |
msg->len = total_len; |
eth_drv_msg_put(ð_msg_full, msg); |
#ifdef CYGSEM_IO_ETH_DRIVERS_WARN |
// there was an else with a dump_buf() here but it's |
// meaningless; sg_list[0].buf is NULL! |
#endif |
} |
CYGARC_HAL_RESTORE_GP(); |
} |
|
// |
// Determine the interrupt vector used by an interface |
// |
int |
eth_drv_int_vector(void) |
{ |
struct eth_drv_sc *sc = __local_enet_sc; |
return sc->funs->int_vector(sc); |
} |
|
|
void eth_drv_dsr(cyg_vector_t vector, |
cyg_ucount32 count, |
cyg_addrword_t data) |
{ |
diag_printf("eth_drv_dsr should not be called: vector %d, data %x\n", |
vector, data ); |
} |
|
|
// EOF src/stand_alone/eth_drv.c |
/lwip/eth_drv.c
0,0 → 1,226
//========================================================================== |
// |
// src/lwip/eth_drv.c |
// |
// Hardware independent ethernet driver for lwIP |
// |
//========================================================================== |
//####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): Jani Monoses <jani@iv.ro> |
// Contributors: |
// Date: 2002-04-05 |
// Purpose: Hardware independent ethernet driver |
// Description: Based on the standalone driver for RedBoot. |
// |
// TODO: |
// support more than 1 lowlevel device |
// play nice with RedBoot too |
// |
//####DESCRIPTIONEND#### |
// |
//========================================================================== |
|
#include <pkgconf/system.h> |
#include <pkgconf/io_eth_drivers.h> |
|
#include <cyg/infra/cyg_type.h> |
#include <cyg/hal/hal_arch.h> |
#include <cyg/infra/diag.h> |
#include <cyg/hal/drv_api.h> |
#include <cyg/hal/hal_if.h> |
#include <cyg/io/eth/eth_drv.h> |
#include <cyg/io/eth/netdev.h> |
#include <string.h> |
|
#include <cyg/hal/hal_tables.h> |
#include <cyg/kernel/kapi.h> |
|
// Define table boundaries |
CYG_HAL_TABLE_BEGIN( __NETDEVTAB__, netdev ); |
CYG_HAL_TABLE_END( __NETDEVTAB_END__, netdev ); |
|
// Interfaces exported to drivers |
|
static void eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr); |
static void eth_drv_recv(struct eth_drv_sc *sc, int total_len); |
static void eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status); |
|
struct eth_drv_funs eth_drv_funs = {eth_drv_init, eth_drv_recv, eth_drv_tx_done}; |
|
struct eth_drv_sc *__local_enet_sc; |
|
//this is where lwIP keeps hw address |
unsigned char *lwip_hw_addr; |
|
cyg_sem_t delivery; |
|
//lwIP callback to pass received data to |
typedef void (*lwip_input_t)(char *,int); |
static lwip_input_t lwip_input; |
void input_thread(void * arg) |
{ |
struct eth_drv_sc * sc; |
//sc = (struct eth_drv_sc *)arg; |
sc = __local_enet_sc; |
for(;;) { |
cyg_semaphore_wait(&delivery); |
(sc->funs->deliver)(sc); |
} |
|
} |
|
void |
eth_drv_dsr(cyg_vector_t vector, |
cyg_ucount32 count, |
cyg_addrword_t data) |
{ |
// struct eth_drv_sc *sc = (struct eth_drv_sc *)data; |
// sc->state |= ETH_DRV_NEEDS_DELIVERY; |
cyg_semaphore_post(&delivery); |
} |
|
|
|
//Called from lwIP init code to init the hw devices |
//and pass the lwip input callback address |
void init_hw_drivers(unsigned char *hw_addr,lwip_input_t input) |
{ |
cyg_netdevtab_entry_t *t; |
|
lwip_hw_addr = hw_addr; |
lwip_input = input; |
|
// Initialize all network devices |
for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) { |
if (t->init(t)) { |
t->status = CYG_NETDEVTAB_STATUS_AVAIL; |
} else { |
// What to do if device init fails? |
t->status = 0; // Device not [currently] available |
} |
} |
} |
|
// |
// This function is called during system initialization to register a |
// network interface with the system. |
// |
static void |
eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr) |
{ |
// enaddr == 0 -> hardware init was incomplete (no ESA) |
if (enaddr != 0) { |
// Set up hardware address |
memcpy(&sc->sc_arpcom.esa, enaddr, ETHER_ADDR_LEN); |
memcpy(lwip_hw_addr, enaddr, ETHER_ADDR_LEN); |
__local_enet_sc = sc; |
// Perform any hardware initialization |
(sc->funs->start)(sc, (unsigned char *)&sc->sc_arpcom.esa, 0); |
} |
cyg_semaphore_init(&delivery,0); |
} |
|
// |
// Send a packet of data to the hardware |
// |
cyg_sem_t packet_sent; |
|
void |
eth_drv_write(char *eth_hdr, char *buf, int len) |
{ |
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; |
struct eth_drv_sc *sc = __local_enet_sc; |
int sg_len = 1; |
|
while (!(sc->funs->can_send)(sc)) { |
cyg_thread_delay(1); |
} |
|
sg_list[0].buf = (CYG_ADDRESS)buf; |
sg_list[0].len = len; |
// cyg_semaphore_init(&packet_sent,0); |
(sc->funs->send)(sc, sg_list, sg_len, len, (CYG_ADDRWORD)&packet_sent); |
|
// cyg_semaphore_wait(&packet_sent); |
} |
|
// |
// This function is called from the hardware driver when an output operation |
// has completed - i.e. the packet has been sent. |
// |
static void |
eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status) |
{ |
#if 0 |
CYGARC_HAL_SAVE_GP(); |
if (key == (CYG_ADDRWORD)&packet_sent) { |
// cyg_semaphore_post((cyg_sem_t *)&packet_sent); |
} |
CYGARC_HAL_RESTORE_GP(); |
#endif |
} |
|
|
#define MAX_ETH_MSG 1540 |
// |
// This function is called from a hardware driver to indicate that an input |
// packet has arrived. The routine will set up appropriate network resources |
// to hold the data and call back into the driver to retrieve the data. |
// |
static void |
eth_drv_recv(struct eth_drv_sc *sc, int total_len) |
{ |
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; |
int sg_len = 0; |
unsigned char buf[MAX_ETH_MSG]; |
CYGARC_HAL_SAVE_GP(); |
|
if ((total_len > MAX_ETH_MSG) || (total_len < 0)) { |
total_len = MAX_ETH_MSG; |
} |
|
sg_list[0].buf = (CYG_ADDRESS)buf; |
sg_list[0].len = total_len; |
sg_len = 1; |
|
(sc->funs->recv)(sc, sg_list, sg_len); |
(lwip_input)((char*)sg_list[0].buf,total_len); |
CYGARC_HAL_RESTORE_GP(); |
} |
|
|
// EOF src/lwip/eth_drv.c |
/lwip/README
0,0 → 1,10
An EPK of lwip is available from http://humans.iv.ro/jani which has the most |
up-to-date package (at least until it all gets integrated). |
|
It has just been tested on another ARM similar to the EB40 with CS89000 |
and it works there too (that board has 128K of RAM). |
|
Alternatively, lw.diff is the diff against the lwip-0.5.3 tree. It contains |
eCos support + an eCos project sample based on unixsim. Look at |
lwip-0.5.3/proj/ecos to see how to use it. Modify the Makefile to suit |
your needs and to reflect your eCos project dir. |