OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /openrisc/trunk/rtos/ecos-2.0/packages/io/eth
    from Rev 27 to Rev 174
    Reverse comparison

Rev 27 → Rev 174

/v2_0/cdl/eth_drivers.cdl
0,0 → 1,247
# ====================================================================
#
# eth_drivers.cdl
#
# Ethernet drivers - platform independent support
#
# ====================================================================
#####ECOSGPLCOPYRIGHTBEGIN####
## -------------------------------------------
## This file is part of eCos, the Embedded Configurable Operating System.
## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
##
## eCos is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free
## Software Foundation; either version 2 or (at your option) any later version.
##
## eCos is distributed in the hope that it will be useful, but WITHOUT ANY
## WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
## for more details.
##
## You should have received a copy of the GNU General Public License along
## with eCos; if not, write to the Free Software Foundation, Inc.,
## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
##
## As a special exception, if other files instantiate templates or use macros
## or inline functions from this file, or you compile this file and link it
## with other works to produce a work based on this file, this file does not
## by itself cause the resulting work to be covered by the GNU General Public
## License. However the source code for this file must still be made available
## in accordance with section (3) of the GNU General Public License.
##
## This exception does not invalidate any other reasons why a work based on
## this file might be covered by the GNU General Public License.
##
## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
## at http://sources.redhat.com/ecos/ecos-license/
## -------------------------------------------
#####ECOSGPLCOPYRIGHTEND####
# ====================================================================
######DESCRIPTIONBEGIN####
#
# Author(s): gthomas
# Original data: gthomas
# Contributors:
# Date: 2000-01-25
#
#####DESCRIPTIONEND####
#
# ====================================================================
 
cdl_package CYGPKG_IO_ETH_DRIVERS {
display "Common ethernet support"
include_dir cyg/io/eth
parent CYGPKG_IO
description "Platform independent ethernet drivers"
doc ref/io-eth-drv-generic.html
 
implements CYGPKG_NET_DRIVER_FRAMEWORK
 
cdl_interface CYGINT_IO_ETH_MULTICAST {
display "Driver supports multicast addressing"
description "
This interface defines whether or not a driver can handle
requests for multicast addressing."
}
 
cdl_component CYGDBG_IO_ETH_DRIVERS_DEBUG {
display "Support printing driver debug information"
flavor bool
default_value 1
description "
Selecting this option will include code to allow the driver to
print lots of information on diagnostic output such as full
packet dumps."
 
cdl_option CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY {
display "Driver debug output verbosity"
flavor data
default_value 0
description "
The value of this option indicates the default verbosity
level of debugging output. 0 means no debugging output
is made by default. Higher values indicate higher verbosity.
The verbosity level may also be changed at run time by
changing the variable cyg_io_eth_net_debug."
}
}
 
cdl_option CYGNUM_IO_ETH_DRIVERS_SG_LIST_SIZE {
display "Size of scatter-gather I/O lists"
flavor data
default_value 32
description "
A scatter-gather list is used to pass requests to/from
the physical device driver. This list can typically be
small, as the data is normally already packed into reasonable
chunks."
}
 
cdl_component CYGPKG_IO_ETH_DRIVERS_NET {
display "Support for standard eCos TCP/IP stack."
flavor bool
active_if CYGPKG_NET
requires CYGINT_ISO_STRING_STRFUNCS
implements CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
default_value 1
compile net/eth_drv.c
 
cdl_component CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS {
display "Warn when there are no more mbufs"
flavor bool
default_value 1
description "
Warnings about running out of mbufs are printed to the
diagnostic output channel via diag_printf() if this option
is enabled. Mbufs are the network stack's basic dynamic
memory objects that hold all packets in transit; running
out is bad for performance but not fatal, not a crash.
You might want to turn off the warnings to preserve realtime
properties of the system even in extremis."
}
 
cdl_component CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES {
display "Simulate network failures for testing"
flavor bool
default_value 0
description "
This package contains a suite of simulated failure modes
for the ethernet device layer, including dropping and/or
corrupting received packets, dropping packets queued for
transmission, and simulating a complete network break.
It requires the kernel as a source of time information."
 
cdl_option CYGPKG_IO_ETH_DRIVERS_SIMULATE_DROP_RX {
display "Drop incoming packets (percentage)"
flavor booldata
legal_values 10 50 80
default_value 10
}
 
cdl_option CYGPKG_IO_ETH_DRIVERS_SIMULATE_CORRUPT_RX {
display "Corrupt incoming packets (percentage)"
flavor booldata
legal_values 10 50 80
default_value 10
}
 
cdl_option CYGPKG_IO_ETH_DRIVERS_SIMULATE_DROP_TX {
display "Drop outgoing packets (percentage)"
flavor booldata
legal_values 10 50 80
default_value 10
}
 
cdl_option CYGPKG_IO_ETH_DRIVERS_SIMULATE_LINE_CUT {
display "Simulate a line cut from time to time"
flavor bool
default_value 0
description "
This option causes the system to drop all packets for a
short random period (10s of seconds), and then act
normally for up to 4 times that long. This simulates your
sysadmin fiddling with plugs in the network switch
cupboard."
}
}
}
 
cdl_component CYGPKG_IO_ETH_DRIVERS_STAND_ALONE {
display "Support for stand-alone network stack."
flavor bool
active_if !CYGPKG_NET
requires CYGINT_ISO_STRING_MEMFUNCS
default_value 1
compile stand_alone/eth_drv.c
 
cdl_option CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS {
display "Pass packets to an alternate stack"
flavor bool
default_value { 0 != CYGPKG_REDBOOT_NETWORKING }
description "
Define this to allow packets seen by this layer to be
passed on to the previous logical layer, i.e. when
stand-alone processing replaces system (eCos) processing."
}
 
cdl_option CYGNUM_IO_ETH_DRIVERS_NUM_PKT {
display "Number of \[network\] buffers"
flavor data
default_value 4
legal_values 2 to 32
description "
This option is used to allocate space to buffer incoming network
packets. These buffers are used to hold data until they can be
logically processed by higher layers."
}
 
cdl_option CYGSEM_IO_ETH_DRIVERS_WARN {
display "Show driver warnings"
active_if CYGPKG_REDBOOT
flavor bool
default_value 0
description "
Selecting this option will allows the stand-alone ethernet driver
to display warnings on the system console when incoming network
packets are being discarded due to lack of buffer space."
}
}
 
cdl_component CYGPKG_IO_ETH_DRIVERS_LWIP {
display "Support for lwIP network stack."
flavor bool
requires !CYGPKG_NET
active_if CYGPKG_NET_LWIP
default_value 1
implements CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
compile lwip/eth_drv.c
}
 
cdl_interface CYGINT_IO_ETH_INT_SUPPORT_REQUIRED {
display "Interrupt support required"
flavor booldata
description "This interface is used to indicate to the low
level device drivers that interrupt driven operation
is required by higher layers."
}
 
cdl_component CYGPKG_IO_ETH_DRIVERS_OPTIONS {
display "Common ethernet support build options"
flavor none
no_define
 
cdl_option CYGPKG_IO_ETH_DRIVERS_CFLAGS_ADD {
display "Additional compiler flags"
flavor data
no_define
default_value { "-D_KERNEL -D__ECOS" }
description "
This option modifies the set of compiler flags for
building the common ethernet support package. These flags are used in addition
to the set of global flags."
}
}
}
 
# EOF eth_drivers.cdl
/v2_0/include/eth_drv_stats.h
0,0 → 1,122
#ifndef CYGONCE_IO_ETH_ETH_DRV_STATS_H
#define CYGONCE_IO_ETH_ETH_DRV_STATS_H
//==========================================================================
//
// include/cyg/io/eth/eth_drv_stats.h
//
// High level networking driver interfaces - statistics gathering
//
//==========================================================================
//####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): hmt
// Contributors: hmt
// Date: 2000-08-23
// Purpose:
// Description: High level networking driver interfaces - stats gathering
//
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <pkgconf/system.h>
 
#ifdef CYGPKG_NET
#include <sys/param.h>
#include <sys/socket.h>
 
#include <net/if.h>
 
// This information is oriented towards SNMP's needs.
 
#define DESC_LEN (48)
#define SNMP_CHIPSET_LEN (20)
 
struct ether_drv_stats {
struct ifreq ifreq; // tell ioctl() which interface.
 
char description[ DESC_LEN ]; // Textual description of hardware
unsigned char snmp_chipset[ SNMP_CHIPSET_LEN ];
// SNMP ID of chipset
unsigned char duplex; // 1 = UNKNOWN, 2 = SIMPLEX, 3 = DUPLEX
unsigned char operational; // 1 = UNKNOWN, 2 = DOWN, 3 = UP
// These are general status information:
unsigned int speed; // 10,000,000 or 100,000,000
// to infinity and beyond?
 
// These are typically kept by device hardware - and there may be some
// cost for getting up to date values:
 
unsigned int supports_dot3; /* Boolean value if the device supports dot3*/
unsigned int tx_good;
unsigned int tx_max_collisions;
unsigned int tx_late_collisions;
unsigned int tx_underrun;
unsigned int tx_carrier_loss;
unsigned int tx_deferred;
unsigned int tx_sqetesterrors;
unsigned int tx_single_collisions;
unsigned int tx_mult_collisions;
unsigned int tx_total_collisions;
unsigned int rx_good;
unsigned int rx_crc_errors;
unsigned int rx_align_errors;
unsigned int rx_resource_errors;
unsigned int rx_overrun_errors;
unsigned int rx_collisions;
unsigned int rx_short_frames;
unsigned int rx_too_long_frames;
unsigned int rx_symbol_errors;
 
// These are typically kept by driver software:
unsigned int interrupts;
unsigned int rx_count;
unsigned int rx_deliver;
unsigned int rx_resource;
unsigned int rx_restart;
unsigned int tx_queue_len;
unsigned int tx_count;
unsigned int tx_complete;
unsigned int tx_dropped;
 
// Add any others here...
 
};
#endif // CYGPKG_NET
#endif // CYGONCE_IO_ETH_ETH_DRV_STATS_H
 
// EOF include/cyg/io/eth/eth_drv_stats.h
/v2_0/include/eth_drv.h
0,0 → 1,230
//==========================================================================
//
// include/cyg/io/eth/eth_drv.h
//
// High level networking driver interfaces
//
//==========================================================================
//####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-01-10
// Purpose:
// Description: High level networking driver interfaces
//
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
// Ethernet driver structure
 
#ifndef _ETH_DRV_H_
#define _ETH_DRV_H_
 
#include <pkgconf/system.h>
#include <pkgconf/io_eth_drivers.h>
 
#ifdef CYGPKG_NET
#include <sys/param.h>
#include <sys/socket.h>
 
#include <net/if.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
 
#else // !CYGPKG_NET
#include <cyg/hal/drv_api.h>
#endif
 
struct eth_drv_sg {
CYG_ADDRESS buf;
CYG_ADDRWORD len;
};
 
#define MAX_ETH_DRV_SG CYGNUM_IO_ETH_DRIVERS_SG_LIST_SIZE
 
struct eth_drv_sc;
 
struct eth_drv_funs {
// Logical driver - initialization
void (*init)(struct eth_drv_sc *sc,
unsigned char *enaddr);
// Logical driver - incoming packet notifier
void (*recv)(struct eth_drv_sc *sc,
int total_len);
// Logical driver - outgoing packet notifier
void (*tx_done)(struct eth_drv_sc *sc,
CYG_ADDRESS key,
int status);
};
 
struct eth_hwr_funs {
// Initialize hardware (including startup)
void (*start)(struct eth_drv_sc *sc,
unsigned char *enaddr,
int flags);
// Shut down hardware
void (*stop)(struct eth_drv_sc *sc);
// Device control (ioctl pass-thru)
int (*control)(struct eth_drv_sc *sc,
unsigned long key,
void *data,
int data_length);
// Query - can a packet be sent?
int (*can_send)(struct eth_drv_sc *sc);
// Send a packet of data
void (*send)(struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list,
int sg_len,
int total_len,
unsigned long key);
// Receive [unload] a packet of data
void (*recv)(struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list,
int sg_len);
// Deliver data to/from device from/to stack memory space
// (moves lots of memcpy()s out of DSRs into thread)
void (*deliver)(struct eth_drv_sc *sc);
// Poll for interrupts/device service
void (*poll)(struct eth_drv_sc *sc);
// Get interrupt information from hardware driver
int (*int_vector)(struct eth_drv_sc *sc);
// Logical driver interface
struct eth_drv_funs *eth_drv, *eth_drv_old;
};
 
#ifndef CYGPKG_NET
struct arpcom {
unsigned char esa[6];
};
#endif
 
struct eth_drv_sc {
struct eth_hwr_funs *funs;
void *driver_private;
const char *dev_name;
int state;
struct arpcom sc_arpcom; /* ethernet common */
};
 
#define ETH_DRV_SC(sc,priv,name,start,stop,control,can_send,send,recv,deliver,poll,int_vector) \
static void start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags); \
static void stop(struct eth_drv_sc *sc); \
static int control(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length); \
static int can_send(struct eth_drv_sc *sc); \
static void send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total, unsigned long key); \
static void recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len); \
static void deliver(struct eth_drv_sc *sc); \
static void poll(struct eth_drv_sc *sc); \
static int int_vector(struct eth_drv_sc *sc); \
static struct eth_hwr_funs sc##_funs = { \
start, \
stop, \
control, \
can_send, \
send, \
recv, \
deliver, \
poll, \
int_vector, \
&eth_drv_funs, \
(struct eth_drv_funs *)0 }; \
struct eth_drv_sc sc = {&sc##_funs, priv, name};
 
#define ETH_DRV_STATE_ACTIVE 0x0001
#define ETH_DRV_NEEDS_DELIVERY 0x0002
#define ETH_DRV_STATE_DEBUG 0x1000
 
// Register this as your DSR within your driver: it will cause your deliver
// routine to be called from the network thread. The "data" parameter
// *must* be your own "struct eth_drv_sc *sc" pointer.
extern void eth_drv_dsr(cyg_vector_t vector,
cyg_ucount32 count,
cyg_addrword_t data);
 
extern struct eth_drv_funs eth_drv_funs;
 
#ifdef CYGPKG_IO_PCMCIA
#ifdef CYGPKG_NET
#include <cyg/io/eth/netdev.h>
cyg_netdevtab_entry_t *eth_drv_netdev(char *name);
#endif
#endif // CYGPKG_IO_PCMCIA
 
// Control 'key's
#define ETH_DRV_SET_MAC_ADDRESS 0x0100
 
#ifdef CYGPKG_NET
#define ETH_DRV_GET_IF_STATS_UD 0x0101
#define ETH_DRV_GET_IF_STATS 0x0102
#include <cyg/io/eth/eth_drv_stats.h> // The struct * for these ops.
#endif
 
#ifndef ETHER_ADDR_LEN
#define ETHER_ADDR_LEN 6
#endif
 
#define ETH_DRV_SET_MC_LIST 0x0110 // Set multicast list
#define ETH_DRV_SET_MC_ALL 0x0111 // Set multicast all mode
#define ETH_DRV_MAX_MC 8
struct eth_drv_mc_list {
int len;
unsigned char addrs[ETH_DRV_MAX_MC][ETHER_ADDR_LEN];
};
 
#ifndef CYGPKG_NET
extern void eth_drv_buffers_init(void);
extern int eth_drv_read(char *eth_hdr, char *buf, int len);
extern void eth_drv_write(char *eth_hdr, char *buf, int len);
extern int eth_drv_int_vector(void);
extern unsigned char __local_enet_addr[];
extern struct eth_drv_sc *__local_enet_sc;
#endif
 
#endif // _ETH_DRV_H_
/v2_0/include/netdev.h
0,0 → 1,79
#ifndef _NETDEV_H_
#define _NETDEV_H_
//==========================================================================
//
// include/netdev.h
//
// Network device description
//
//==========================================================================
//####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-01-10
// Purpose:
// Description:
//
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <cyg/hal/hal_tables.h>
 
// Network device support
 
typedef struct cyg_netdevtab_entry {
const char *name;
bool (*init)(struct cyg_netdevtab_entry *tab);
void *device_instance; // Local data, instance specific
unsigned long status;
} CYG_HAL_TABLE_TYPE cyg_netdevtab_entry_t;
 
#define CYG_NETDEVTAB_STATUS_AVAIL 0x0001
 
extern cyg_netdevtab_entry_t __NETDEVTAB__[], __NETDEVTAB_END__;
 
#define NETDEVTAB_ENTRY(_l,_name,_init,_instance) \
static bool _init(struct cyg_netdevtab_entry *tab); \
cyg_netdevtab_entry_t _l CYG_HAL_TABLE_ENTRY(netdev) = { \
_name, \
_init, \
_instance \
};
 
#endif // _NETDEV_H_
/v2_0/doc/driver_doc
0,0 → 1,373
This file provides a simple description of how to write a low-level,
hardware dependent ethernet driver.
 
The basic idea is that there is a high-level driver (which is only
code/functions) that is part of the stack. There will be one or more
low-level driver tied to the actual network hardware. Each of these
drivers contains one or more driver instances. The principal idea is
that the low-level drivers know nothing of the details of the stack that
will be using them. Thus, the same driver can be used by the eCos
supported TCP/IP stack, or any other, with no changes.
 
A driver instance is contained within a "struct eth_drv_sc".
 
struct eth_hwr_funs {
// Initialize hardware (including startup)
void (*start)(struct eth_drv_sc *sc,
unsigned char *enaddr);
// Shut down hardware
void (*stop)(struct eth_drv_sc *sc);
// Control interface
int (*control)(struct eth_drv_sc *sc,
unsigned long cmd,
void *data,
int len);
// Query interface - can a packet be sent?
int (*can_send)(struct eth_drv_sc *sc);
// Send a packet of data
void (*send)(struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list,
int sg_len,
int total_len,
unsigned long key);
// Receive [unload] a packet of data
void (*recv)(struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list,
int sg_len);
// Deliver data to/from device from/to stack memory space
// (moves lots of memcpy()s out of DSRs into thread)
void (*deliver)(struct eth_drv_sc *sc);
// Poll for interrupts/device service
void (*poll)(struct eth_drv_sc *sc);
// Get interrupt information from hardware driver
int (*int_vector)(struct eth_drv_sc *sc);
// Logical driver interface
struct eth_drv_funs *eth_drv, *eth_drv_old;
};
 
struct eth_drv_sc {
struct eth_hwr_funs *funs;
void *driver_private;
const char *dev_name;
struct arpcom sc_arpcom; /* ethernet common */
};
 
You create one of these instances using the "ETH_DRV_SC()" macro which
sets up the structure, including the prototypes for the functions, etc.
By doing things this way, if the internal design of the ethernet drivers
changes (e.g. we need to add a new low-level implementation function),
existing drivers will no longer compile until updated. This is much
better than to have all of the definitions in the low-level drivers
themselves and have them be [quietly] broken if the interfaces change.
 
The "magic" which gets the drivers started [and indeed, linked] is
similar to what is used for the I/O subsystem. [Note: I may try and
make it part of the I/O subsystem later.] This is done using the
"NETDEVTAB_ENTRY()" macro, which defines an initialization function
and the basic data structures for the low-level driver.
 
typedef struct cyg_netdevtab_entry {
const char *name;
bool (*init)(struct cyg_netdevtab_entry *tab);
void *device_instance;
unsigned long status;
} cyg_netdevtab_entry_t;
 
The "device_instance" entry here would point to the "struct
eth_drv_sc" entry previously defined. This allows the network driver
setup to work with any class of driver, not just ethernet drivers. In
the future, there will surely be serial PPP drivers, etc. These will
use the "NETDEVTAB" setup to create the basic driver, but they will
most likely be built on top of other high-level device driver layers.
 
So, the bottom line is that a hardware driver will have a template
(boilerplate) which looks like this:
 
#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/io/eth/netdev.h>
#include <cyg/io/eth/eth_drv.h>
 
ETH_DRV_SC(DRV_sc,
0, // No driver specific data needed
"eth0", // Name for this interface
HRDWR_start,
HRDWR_stop,
HRDWR_control,
HRDWR_can_send
HRDWR_send,
HRDWR_recv);
 
NETDEVTAB_ENTRY(DRV_netdev,
"DRV",
DRV_HRDWR_init,
&DRV_sc);
 
This, along with the referenced functions, completely define the driver.
Extensibility note: if one needed the same low-level driver to handle
multiple similar hardware interfaces, you would need multiple invocations
of the "ETH_DRV_SC()/NETDEVTAB_ENTRY()" macros. You would add a pointer
to some instance specific data, e.g. containing base addresses, interrupt
numbers, etc, where the "0, // No driver specific data" is currently.
 
Now a quick waltz through the functions. This discussion will use the
generic names from above.
 
static bool DRV_HDWR_init(struct cyg_netdevtab_entry *tab)
==========================================================
 
This function is called as part of system initialization. Its primary
function is to decide if the hardware [as indicated via
tab->device_instance] is working and if the interface needs to be made
available in the system. If this is the case, this function needs to
finish with a call to the ethernet driver function:
 
eth_drv_init((struct eth_drv_sc *)tab->device_instance,
unsigned char *enaddr);
 
where 'enaddr' is a pointer to the ethernet station address for this
unit. Note: the ethernet station address is supposed to be a
world-unique, 48 bit address for this particular ethernet interface.
Typically it is provided by the board/hardware manufacturer in ROM.
 
In many packages it is possible for the ESA to be set from RedBoot,
(perhaps from 'fconfig' data), hard-coded from CDL, or from an EPROM.
A driver should choose a run-time specified ESA (e.g. from RedBoot)
preferentially, otherwise (in order) it should use a CDL specified
ESA if one has been set, otherwise an EPROM set ESA, or otherwise
fail. See the cl/cs8900a eth driver for an example.
 
static void
HRDWR_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
====================================================================
 
This function is called, perhaps much later than system initialization
time, when the system (an application) is ready for the interface to
become active. The purpose of this function is to set up the hardware
interface to start accepting packets from the network and be able to
send packets out. Note: this function will be called whenever the
up/down state of the logical interface changes, e.g. when the IP address
changes. This may occur more than one time, so this function needs to
be prepared for that case.
 
FUTURE: the "flags" field (currently unused) may be used to tell the
function how to start up, e.g. whether interrupts will be used,
selection of "promiscuous" mode etc.
 
static void HRDWR_stop(struct eth_drv_sc *sc)
=============================================
 
This function is the inverse of "start". It should shut down the
hardware and keep it from interacting with the physical network.
 
static int
HRDWR_control(struct eth_drv_sc *sc, unsigned long key, void *data, int len)
============================================================================
 
This function is used to perform low-level "control" operations on the
interface. These operations would be initiated via 'ioctl()' in the BSD
stack, and would be anything that would require the hardware setup to
possibly change (i.e. cannot be performed totally by the
platform-independent layers).
 
Current operations:
 
ETH_DRV_SET_MAC_ADDRESS:
This function sets the ethernet station address (ESA or MAC) for the
device. Normally this address is kept in non-volatile memory and is
unique in the world. This function must at least set the interface to
use the new address. It may also update the NVM as appropriate.
 
This function should return zero if the specified operation was
completed successfully. It should return non-zero if the operation
could not be performed, for any reason.
 
static int HRDWR_can_send(struct eth_drv_sc *sc)
================================================
 
This function is called to determine if it is possible to start the
transmission of a packet on the interface. Some interfaces will allow
multiple packets to be "queued" and this function allows for the highest
possible utilization of that mode.
 
Return the number of packets which could be accepted at this time, zero
implies that the interface is saturated/busy.
 
static void
HRDWR_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
=========================================================================
 
This function is used to send a packet of data to the network. It is
the responsibility of this function to somehow hand the data over to the
hardware interface. This will most likely require copying, but just the
address/length values could be used by smart hardware.
 
NOTE: All data in/out of the driver is specified via a "scatter-gather"
list. This is just an array of address/length pairs which describe
sections of data to move (in the order given by the array).
 
Once the data has been successfully sent by the interface (or if an
error occurs), the driver should call 'eth_drv_tx_done()' using the
specified 'key'. Only then will the upper layers release the resources
for that packet and start another transmission.
 
FUTURE: This function may be extended so that the data need not be
copied by having the function return a "disposition" code (done, send
pending, etc). At this point, you should move the data to some "safe"
location before returning.
 
static void
HRDWR_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
=========================================================================
 
This function is actually a call back, only invoked after the
upper-level function
eth_drv_recv(struct eth_drv_sc *sc, int total_len)
has been called. This upper level function is called by the hardware
driver when it knows that a packet of data is available on the
interface. The 'eth_drv_recv()' function then arranges network buffers
and structures for the data and then calls "HRDWR_recv()" to actually
move the data from the interface.
 
static void
HRDWR_deliver(struct eth_drv_sc *sc)
=========================================================================
 
This function is actually a call back, and notifies the driver that delivery
is happening. This allows it to actually do the copy of packet data to/from
the hardware from/to the packet buffer. And once that's done, then do things
like unmask its interrupts, and free any relevant resources so it can process
further packets.
 
In general it will be called from the user thread responsible for delivering
network packets.
 
static void
HRDWR_poll(struct eth_drv_sc *sc)
=========================================================================
 
This function is used when in a non-interrupt driven system, e.g. when
interrupts are completely disabled. This allows the driver time to check
whether anything needs doing either for transmission, or to check if
anything has been received, or if any other processing needs doing..
 
static int
HRDWR_int_vector(struct eth_drv_sc *sc)
=========================================================================
 
This function returns the interrupt vector number used for RX interrupts.
This is so the common GDB stubs infrastructure can detect when to check
for incoming ctrl-c's when doing debugging over ethernet.
 
Upper layer functions - called by drivers
=========================================
 
These functions are defined by the upper layers (machine independent) of
the networking driver support. They are present to hide the interfaces
to the actual networking stack so that the hardware drivers may possibly
be used by any network stack implementation.
 
These functions require a pointer to a "struct eth_drv_sc" table which
describes the interface at a logical level. It is assumed that the
driver [lowest level hardware support] will keep track of this pointer
so it may be passed "up" as appropriate.
 
struct eth_drv_sc {
struct eth_drv_funs *funs; // Pointer to hardware functions (see above)
void *driver_private; // Device specific data
const char *dev_name;
struct arpcom sc_arpcom; // ethernet common
};
 
This structure is created, one per logical interface, via ETH_DRV_SC macro.
 
void eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr)
===============================================================
 
This function establishes the device at initialization time. The
hardware should be totally intialized (not "started") when this function
is called.
 
void eth_drv_tx_done(struct eth_drv_sc *sc, unsigned long key, int status)
==========================================================================
 
This function is called when a packet completes transmission on the
interface. The 'key' value must be one of the keys provided to
"HRDWR_send()" above. The value 'status' should be non-zero (currently
undefined) to indicate that an error occurred during the transmission.
 
void eth_drv_recv(struct eth_drv_sc *sc, int len)
=================================================
 
This function is called to indicate that a packet of length 'len' has
arrived at the interface. The callback "HRDWR_recv()" function
described above will be used to actually unload the data from the
interface into buffers used by the machine independent layers.
 
 
 
Calling graph for Transmit and Receive
--------------------------------------
 
It may be worth clarifying further the flow of control in the transmit
and receive cases, where the hardware driver does use interrupts and so
DSRs to tell the "foreground" when something asynchronous has occurred.
 
Transmit:
Foreground task calls into network stack to send a packet (or the
stack decides to send a packet in response to incoming traffic).
The driver calls the HRDWR_can_send() function in the hardware driver.
HRDWR_can_send() returns the number of available "slots" in which it
can store a pending transmit packet.
If it cannot send at this time, the packet is queued outside the
hardware driver for later; in this case, the hardware is already busy
transmitting, so expect an interrupt as described below for completion
of the packet currently outgoing.
If it can send right now, HRDWR_send() is called.
HRDWR_send() copies the data into special hardware buffers, or
instructs the hardware to "send that".
It also remembers the key that is associated with this tx request.
these calls return.
...
 
Asynchronously, the hardware makes an interrupt to say "transmit is
done"; the ISR quietens the interrupt source in the hardware and
requests that the associated DSR be run.
The DSR realizes that a transmit request has completed, and calls
eth_drv_tx_done() with the same key that it remembered for this tx.
eth_drv_tx_done() uses the key to find the resources associated with
this transmit request; thus the stack knows that the transmit has
completed and its resources can be freed.
eth_drv_tx_done() also enquires whether HRDWR_can_send() now says
"yes, we can send" and if so, dequeues a further transmit request
which may have been queued as described above. If so:
HRDWR_send() copies the data into the hardware buffers, or
instructs the hardware to "send that" and remembers the new key.
these calls return to the DSR and thus to the foreground.
...
 
 
Receive:
...
 
Asynchronously, the hardware makes an interrupt to say "there is ready
data in a receive buffer"; the ISR quietens the interrupt source in
the hardware and requests that the associated DSR be run.
The DSR realizes that there is data ready and calls eth_drv_recv()
with the length of the data that is available.
eth_drv_recv() prepares a set of scatter-gather buffers that can
accommodate that data.
It then calls back into the hardware driver routine HRDWR_recv().
HRDWR_recv() must copy the data from the hardware's buffers into
the scatter-gather buffers provided, and return.
eth_drv_recv() sends the new packet up the network stack and returns.
Back in the DSR now, the driver cleans the receive buffer and returns
it to the hardware's control, available to receive another packet from
the network.
The DSR returns to the foreground.
...
 
 
/v2_0/doc/ethdrv.sgml
0,0 → 1,769
<!-- {{{ Banner -->
 
<!-- =============================================================== -->
<!-- -->
<!-- ethdrv.sgml -->
<!-- -->
<!-- eCos generic ethernet driver documentation -->
<!-- -->
<!-- =============================================================== -->
<!-- ####COPYRIGHTBEGIN#### -->
<!-- -->
<!-- =============================================================== -->
<!-- Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. -->
<!-- This material may be distributed only subject to the terms -->
<!-- and conditions set forth in the Open Publication License, v1.0 -->
<!-- or later (the latest version is presently available at -->
<!-- http://www.opencontent.org/openpub/) -->
<!-- Distribution of the work or derivative of the work in any -->
<!-- standard (paper) book form is prohibited unless prior -->
<!-- permission obtained from the copyright holder -->
<!-- =============================================================== -->
<!-- -->
<!-- ####COPYRIGHTEND#### -->
<!-- =============================================================== -->
<!-- #####DESCRIPTIONBEGIN#### -->
<!-- -->
<!-- ####DESCRIPTIONEND#### -->
<!-- =============================================================== -->
 
<!-- }}} -->
 
<part id="io-eth-drv-generic">
<title>Ethernet Device Drivers</title>
<chapter id="io-eth-drv-generic1">
<title>Generic Ethernet Device Driver</title>
<sect1 id="io-eth-drv-api">
<title>Generic Ethernet API</title>
<para>
This file provides a simple description of how to write a low-level,
hardware dependent ethernet driver.
</para>
<para>
There is a high-level driver (which is only code &mdash; with no state of
its own) that is part of the stack. There will be one or more low-level
drivers tied to the actual network hardware. Each of these drivers
contains one or more driver instances. The intent is that the
low-level drivers know nothing of the details of the stack that will be
using them. Thus, the same driver can be used by the
<productname>eCos</productname>
supported
<acronym>TCP/IP</acronym>
stack,
<productname>RedBoot</productname>,
or any other, with no changes.
</para>
<para>
A driver instance is contained within a
<type>struct eth_drv_sc</type>:
<programlisting>
struct eth_hwr_funs {
// Initialize hardware (including startup)
void (*start)(struct eth_drv_sc *sc,
unsigned char *enaddr,
int flags);
// Shut down hardware
void (*stop)(struct eth_drv_sc *sc);
// Device control (ioctl pass-thru)
int (*control)(struct eth_drv_sc *sc,
unsigned long key,
void *data,
int data_length);
// Query - can a packet be sent?
int (*can_send)(struct eth_drv_sc *sc);
// Send a packet of data
void (*send)(struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list,
int sg_len,
int total_len,
unsigned long key);
// Receive [unload] a packet of data
void (*recv)(struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list,
int sg_len);
// Deliver data to/from device from/to stack memory space
// (moves lots of memcpy()s out of DSRs into thread)
void (*deliver)(struct eth_drv_sc *sc);
// Poll for interrupts/device service
void (*poll)(struct eth_drv_sc *sc);
// Get interrupt information from hardware driver
int (*int_vector)(struct eth_drv_sc *sc);
// Logical driver interface
struct eth_drv_funs *eth_drv, *eth_drv_old;
};
 
struct eth_drv_sc {
struct eth_hwr_funs *funs;
void *driver_private;
const char *dev_name;
int state;
struct arpcom sc_arpcom; /* ethernet common */
};
</programlisting>
</para><note><para>
If you have two instances of the same hardware, you only need one
<type>struct eth_hwr_funs</type> shared between them.
</para></note><para>
There is another structure which is used to communicate with the rest of
the stack:
<programlisting>
struct eth_drv_funs {
// Logical driver - initialization
void (*init)(struct eth_drv_sc *sc,
unsigned char *enaddr);
// Logical driver - incoming packet notifier
void (*recv)(struct eth_drv_sc *sc,
int total_len);
// Logical driver - outgoing packet notifier
void (*tx_done)(struct eth_drv_sc *sc,
CYG_ADDRESS key,
int status);
};
</programlisting>
Your driver does <emphasis>not</emphasis> create an instance of this
structure. It is provided for driver code to use in the
<type>eth_drv</type> member of the function record.
Its usage is described below in <xref linkend=io-eth-drv-upper-api>
</para><para>
One more function completes the API with which your driver communicates
with the rest of the stack:
<programlisting>
extern void eth_drv_dsr(cyg_vector_t vector,
cyg_ucount32 count,
cyg_addrword_t data);
</programlisting>
</para><para>
This function is designed so that it can be registered as the DSR for your
interrupt handler. It will awaken the
&ldquo;Network Delivery Thread&rdquo;
to call your deliver routine. See <xref linkend=io-eth-drv-api-deliver>.
</para><para>
You create an instance of <type>struct eth_drv_sc</type>
using the
<function>ETH_DRV_SC()</function>
macro which
sets up the structure, including the prototypes for the functions, etc.
By doing things this way, if the internal design of the ethernet drivers
changes (e.g. we need to add a new low-level implementation function),
existing drivers will no longer compile until updated. This is much
better than to have all of the definitions in the low-level drivers
themselves and have them be (quietly) broken if the interfaces change.
</para><para>
The &ldquo;magic&rdquo;
which gets the drivers started (and indeed, linked) is
similar to what is used for the I/O subsystem.
This is done using the
<function>NETDEVTAB_ENTRY()</function>
macro, which defines an initialization function
and the basic data structures for the low-level driver.
</para>
<para><programlisting>
typedef struct cyg_netdevtab_entry {
const char *name;
bool (*init)(struct cyg_netdevtab_entry *tab);
void *device_instance;
unsigned long status;
} cyg_netdevtab_entry_t;
</programlisting>
The <varname>device_instance</varname>
entry here would point to the <type>struct eth_drv_sc</type>
entry previously defined. This allows the network driver
setup to work with any class of driver, not just ethernet drivers. In
the future, there will surely be serial <acronym>PPP</acronym>
drivers, etc. These will
use the <function>NETDEVTAB_ENTRY()</function>
setup to create the basic driver, but they will
most likely be built on top of other high-level device driver layers.
</para><para>
To instantiate itself, and connect it to the system,
a hardware driver will have a template
(boilerplate) which looks something like this:
<programlisting>
#include &lt;cyg/infra/cyg_type.h&gt;
#include &lt;cyg/hal/hal_arch.h&gt;
#include &lt;cyg/infra/diag.h&gt;
#include &lt;cyg/hal/drv_api.h&gt;
#include &lt;cyg/io/eth/netdev.h&gt;
#include &lt;cyg/io/eth/eth_drv.h&gt;
 
ETH_DRV_SC(<replaceable>DRV</replaceable>_sc,
0, // No driver specific data needed
"eth0", // Name for this interface
<replaceable>HRDWR</replaceable>_start,
<replaceable>HRDWR</replaceable>_stop,
<replaceable>HRDWR</replaceable>_control,
<replaceable>HRDWR</replaceable>_can_send
<replaceable>HRDWR</replaceable>_send,
<replaceable>HRDWR</replaceable>_recv,
<replaceable>HRDWR</replaceable>_deliver,
<replaceable>HRDWR</replaceable>_poll,
<replaceable>HRDWR</replaceable>_int_vector
);
 
NETDEVTAB_ENTRY(<replaceable>DRV</replaceable>_netdev,
"<replaceable>DRV</replaceable>",
<replaceable>DRV_HRDWR</replaceable>_init,
&amp;<replaceable>DRV</replaceable>_sc);
</programlisting>
</para><para>
This, along with the referenced functions, completely define the driver.
</para><note><para>
If one needed the same low-level driver to handle
multiple similar hardware interfaces, you would need multiple invocations
of the
<function>ETH_DRV_SC()</function>/<function>NETDEVTAB_ENTRY()</function>
macros. You would add a pointer
to some instance specific data, e.g. containing base addresses, interrupt
numbers, etc, where the
<programlisting>
0, // No driver specific data
</programlisting>
is currently.
</para></note>
</sect1>
<sect1 id="io-eth-drv-api-funcs">
<title>Review of the functions</title>
<para>
Now a brief review of the functions. This discussion will use generic
names for the functions &mdash; your driver should use hardware-specific
names to maintain uniqueness against any other drivers.
</para>
<sect2 id="io-eth-drv-api-init">
<title>Init function</title>
<para>
<programlisting>
static bool <replaceable>DRV_HDWR</replaceable>_init(struct cyg_netdevtab_entry *tab)
</programlisting>
This function is called as part of system initialization. Its primary
function is to decide if the hardware (as indicated via
<type>tab-&gt;device_instance</type>)
is working and if the interface needs to be made
available in the system. If this is the case, this function needs to
finish with a call to the ethernet driver function:
<programlisting>
struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
<replaceable>....initialization code....</replaceable>
// Initialize upper level driver
(sc-&gt;funs-&gt;eth_drv-&gt;init)( sc, unsigned char *enaddr );
</programlisting>
where <parameter>enaddr</parameter>
is a pointer to the ethernet station address for this unit, to inform
the stack of this device's readiness and availability.
</para>
<note><para>The ethernet station address
(<acronym>ESA</acronym>)
is supposed to be a
world-unique, 48 bit address for this particular ethernet interface.
Typically it is provided by the board/hardware manufacturer in ROM.
</para>
<para>
In many packages it is possible for the
<acronym>ESA</acronym>
to be set from RedBoot,
(perhaps from 'fconfig' data), hard-coded from
<acronym>CDL</acronym>, or from an <acronym>EPROM</acronym>.
A driver should choose a run-time specified
<acronym>ESA</acronym>
(e.g. from RedBoot)
preferentially, otherwise (in order) it should use a <acronym>CDL</acronym> specified
<acronym>ESA</acronym>
if one has been set, otherwise an <acronym>EPROM</acronym> set
<acronym>ESA</acronym>, or otherwise
fail. See the <filename>cl/cs8900a</filename>
ethernet driver for an example.
</para></note>
</sect2>
<sect2 id="io-eth-drv-api-start">
<title>Start function</title>
<para>
<programlisting>
static void
<replaceable>HRDWR</replaceable>_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
</programlisting>
This function is called, perhaps much later than system initialization
time, when the system (an application) is ready for the interface to
become active. The purpose of this function is to set up the hardware
interface to start accepting packets from the network and be able to
send packets out. The receiver hardware should not be enabled prior to
this call.
</para><note><para>This function will be called whenever the
up/down state of the logical interface changes, e.g. when the IP address
changes, or when promiscuous mode is selected by means of an
<function>ioctl()</function> call in the application.
This may occur more than once, so this function needs to
be prepared for that case.
</para></note><note><para>
In future, the <parameter>flags</parameter>
field (currently unused) may be used to tell the
function how to start up, e.g. whether interrupts will be used,
alternate means of selecting promiscuous mode etc.
</para></note>
</sect2>
<sect2 id="io-eth-drv-api-stop">
<title>Stop function</title>
<para>
<programlisting>
static void <replaceable>HRDWR</replaceable>_stop(struct eth_drv_sc *sc)
</programlisting>
This function is the inverse of &ldquo;start.&rdquo;
It should shut down the hardware, disable the receiver, and keep it from
interacting with the physical network.
</para>
</sect2>
<sect2 id="io-eth-drv-api-control">
<title>Control function</title>
<para>
<programlisting>
static int
<replaceable>HRDWR</replaceable>_control(
struct eth_drv_sc *sc, unsigned long key,
void *data, int len)
</programlisting>
This function is used to perform low-level &ldquo;control&rdquo;
operations on the
interface. These operations would typically be initiated via
<function>ioctl()</function> calls in the BSD
stack, and would be anything that might require the hardware setup to
change (i.e. cannot be performed totally by the
platform-independent layers).
</para><para>
The <parameter>key</parameter> parameter selects the operation, and the
<parameter>data</parameter> and <parameter>len</parameter> params point describe,
as required, some data for the operation in question.
</para>
<variablelist><title>Available Operations:</title>
<varlistentry><term>ETH_DRV_SET_MAC_ADDRESS</term>
<listitem><para>
This operation sets the ethernet station address (ESA or MAC) for the
device. Normally this address is kept in non-volatile memory and is
unique in the world. This function must at least set the interface to
use the new address. It may also update the NVM as appropriate.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>ETH_DRV_GET_IF_STATS_UD</term>
<term>ETH_DRV_GET_IF_STATS</term>
<listitem><para>
These acquire a set of statistical counters from the interface, and write
the information into the memory pointed to by <parameter>data</parameter>.
The &ldquo;UD&rdquo; variant explicitly instructs the driver to acquire
up-to-date values. This is a separate option because doing so may take
some time, depending on the hardware.
</para><para>
The definition of the data structure is in
<filename>cyg/io/eth/eth_drv_stats.h</filename>.
</para><para>
This call is typically made by SNMP, see <xref linkend=net-snmp-ecos-port>.
</para>
</listitem>
</varlistentry>
<varlistentry><term>ETH_DRV_SET_MC_LIST</term>
<listitem><para>
This entry instructs the device to set up multicast packet filtering
to receive only packets addressed to the multicast ESAs in the list pointed
to by <parameter>data</parameter>.
</para><para>
The format of the data is a 32-bit count of the ESAs in the list, followed
by packed bytes which are the ESAs themselves, thus:
<programlisting>
#define ETH_DRV_MAX_MC 8
struct eth_drv_mc_list {
int len;
unsigned char addrs[ETH_DRV_MAX_MC][ETHER_ADDR_LEN];
};
</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry><term>ETH_DRV_SET_MC_ALL</term>
<listitem><para>
This entry instructs the device to receive all multicast packets, and
delete any explicit filtering which had been set up.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
This function should return zero if the specified operation was
completed successfully. It should return non-zero if the operation
could not be performed, for any reason.
</para>
</sect2>
<sect2 id="io-eth-drv-api-can-send">
<title>Can-send function</title>
<para>
<programlisting>
static int <replaceable>HRDWR</replaceable>_can_send(struct eth_drv_sc *sc)
</programlisting>
This function is called to determine if it is possible to start the
transmission of a packet on the interface. Some interfaces will allow
multiple packets to be "queued" and this function allows for the highest
possible utilization of that mode.
</para><para>
Return the number of packets which could be accepted at this time, zero
implies that the interface is saturated/busy.
</para>
</sect2>
<sect2 id="io-eth-drv-api-send">
<title>Send function</title>
<para>
<programlisting>
struct eth_drv_sg {
CYG_ADDRESS buf;
CYG_ADDRWORD len;
};
 
static void
<replaceable>HRDWR</replaceable>_send(
struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
</programlisting>
This function is used to send a packet of data to the network. It is
the responsibility of this function to somehow hand the data over to the
hardware interface. This will most likely require copying, but just the
address/length values could be used by smart hardware.
</para><note><para>
All data in/out of the driver is specified via a
&ldquo;scatter-gather&rdquo;
list. This is just an array of address/length pairs which describe
sections of data to move (in the order given by the array), as in the
<type>struct eth_drv_sg</type> defined above and pointed to by
<parameter>sg_list</parameter>.
</para></note><para>
Once the data has been successfully sent by the interface (or if an
error occurs), the driver should call
<function>(sc->funs->eth_drv->tx_done)()</function>
(see <xref linkend=io-eth-drv-tx-done>)
using the specified <parameter>key</parameter>.
Only then will the upper layers release the resources
for that packet and start another transmission.
</para><note><para>
In future, this function may be extended so that the data need not be
copied by having the function return a &ldquo;disposition&rdquo; code
(done, send pending, etc). At this point, you should move the data to some
&ldquo;safe&rdquo; location before returning.
</para></note>
</sect2>
<sect2 id="io-eth-drv-api-deliver">
<title>Deliver function</title>
<para>
<programlisting>
static void
<replaceable>HRDWR</replaceable>_deliver(struct eth_drv_sc *sc)
</programlisting>
This function is called from the &ldquo;Network Delivery Thread&rdquo; in
order to let the device driver do the time-consuming work associated with
receiving a packet &mdash; usually copying the entire packet from the
hardware or a special memory location into the network stack's memory.
</para><para>
After handling any outstanding incoming packets or pending transmission
status, it can unmask the device's interrupts, and free any relevant
resources so it can process further packets.
</para><para>
It will be called when the interrupt handler for the network device
has called
<programlisting>
eth_drv_dsr( vector, count, (cyg_addrword_t)sc );
</programlisting>
to alert the system that &ldquo;something requires attention.&rdquo;
This <function>eth_drv_dsr()</function> call must occur from within the
interrupt handler's DSR (not the ISR) or actually <emphasis>be</emphasis>
the DSR, whenever it is determined that
the device needs attention from the foreground. The third parameter
(<parameter>data</parameter> in the prototype of
<function>eth_drv_dsr()</function> <emphasis>must</emphasis>
be a valid <type>struct eth_drv_sc</type> pointer <varname>sc</varname>.
</para><para>
The reason for this slightly convoluted train of events is to keep the DSR
(and ISR) execution time as short as possible, so that other activities of
higher priority than network servicing are not denied the CPU by network
traffic.
</para><para>
To deliver a newly-received packet into the network stack, the deliver
routine must call
<programlisting>
(sc->funs->eth_drv->recv)(sc, len);
</programlisting>
which will in turn call the receive function, which we talk about next.
See also <xref linkend=io-eth-drv-upper-recv> below.
</para>
</sect2>
<sect2 id="io-eth-drv-api-recv">
<title>Receive function</title>
<para>
<programlisting>
static void
<replaceable>HRDWR</replaceable>_recv(
struct eth_drv_sc *sc,
struct eth_drv_sg *sg_list, int sg_len)
</programlisting>
This function is a call back, only invoked after the
upper-level function
<programlisting>
(sc->funs->eth_drv->recv)(struct eth_drv_sc *sc, int total_len)
</programlisting>
has been called itself from your deliver function when it knows that a
packet of data is available on the
interface. The <function>(sc->funs->eth_drv->recv)()</function>
function then arranges network buffers
and structures for the data and then calls
<function><replaceable>HRDWR</replaceable>_recv()</function> to actually
move the data from the interface.
</para><para>
A scatter-gather list (<type>struct eth_drv_sg</type>) is used once more,
just like in the send case.
</para>
</sect2>
<sect2 id="io-eth-drv-api-poll">
<title>Poll function</title>
<para>
<programlisting>
static void
<replaceable>HRDWR</replaceable>_poll(struct eth_drv_sc *sc)
</programlisting>
This function is used when in a non-interrupt driven system, e.g. when
interrupts are completely disabled. This allows the driver time to check
whether anything needs doing either for transmission, or to check if
anything has been received, or if any other processing needs doing.
</para><para>
It is perfectly correct and acceptable for the poll function to look like
this:
<programlisting>
static void
<replaceable>HRDWR</replaceable>_poll(struct eth_drv_sc *sc)
{
<replaceable>my_interrupt_ISR</replaceable>(sc);
<replaceable>HRDWR</replaceable>_deliver(struct eth_drv_sc *sc);
}
</programlisting>
provided that both the ISR and the deliver functions are idempotent and
harmless if called when there is no attention needed by the hardware. Some
devices might not need a call to the ISR here if the deliver function
contains all the &ldquo;intelligence.&rdquo;
</para>
</sect2>
<sect2 id="io-eth-drv-api-int-vector">
<title>Interrupt-vector function</title>
<para>
<programlisting>
static int
<replaceable>HRDWR</replaceable>_int_vector(struct eth_drv_sc *sc)
</programlisting>
This function returns the interrupt vector number used for receive
interrupts.
This is so that the common GDB stubs can detect when to check
for incoming &ldquo;CTRL-C&rdquo; packets (used to asynchronously
halt the application) when debugging over ethernet.
The GDB stubs need to know which interrupt the ethernet device uses
so that they can mask or unmask that interrupt as required.
</para>
</sect2>
</sect1>
<sect1 id=io-eth-drv-upper-api>
<title>Upper Layer Functions</title>
<para>
Upper layer functions are called by drivers to deliver received packets
or transmission completion status back up into the network stack.
</para><para>
These functions are defined by the hardware independent upper layers of
the networking driver support. They are present to hide the interfaces
to the actual networking stack so that the hardware drivers may
be used by different network stack implementations without change.
</para><para>
These functions require a pointer to a <type>struct eth_drv_sc</type>
which describes the interface at a logical level. It is assumed that the
low level hardware driver will keep track of this pointer so
it may be passed &ldquo;up&rdquo; as appropriate.
</para>
<sect2 id="io-eth-drv-upper-init">
<title>Callback Init function</title>
<para>
<programlisting>
void (sc->funs->eth_drv->init)(
struct eth_drv_sc *sc, unsigned char *enaddr)
</programlisting>
This function establishes the device at initialization time.
It should be called once per device instance only, from the
initialization function, if all is well
(see <xref linkend=io-eth-drv-api-init>).
The hardware should be totally initialized
(<emphasis>not</emphasis> &ldquo;started&rdquo;)
when this function is called.
</para>
</sect2>
<sect2 id="io-eth-drv-tx-done">
<title>Callback Tx-Done function</title>
<para>
<programlisting>
void (sc->funs->eth_drv->tx_done)(
struct eth_drv_sc *sc,
unsigned long key, int status)
</programlisting>
This function is called when a packet completes transmission on the
interface. The <parameter>key</parameter>
value must be one of the keys provided to
<function><replaceable>HRDWR</replaceable>_send()</function>
above. The value <parameter>status</parameter> should be non-zero
(details currently undefined) to indicate that an error occurred during the
transmission, and zero if all was well.
</para><para>
It should be called from the deliver function
(see <xref linkend=io-eth-drv-api-deliver>)
or poll function
(see <xref linkend=io-eth-drv-api-poll>).
</para>
</sect2>
<sect2 id="io-eth-drv-upper-recv">
<title>Callback Receive function</title>
<para>
<programlisting>
void (sc->funs->eth_drv->recv)(struct eth_drv_sc *sc, int len)
</programlisting>
This function is called to indicate that a packet of length
<parameter>len</parameter> has
arrived at the interface.
The callback
<function><replaceable>HRDWR</replaceable>_recv()</function> function
described above will be used to actually unload the data from the
interface into buffers used by the device independent layers.
</para><para>
It should be called from the deliver function
(see <xref linkend=io-eth-drv-api-deliver>)
or poll function
(see <xref linkend=io-eth-drv-api-poll>).
</para>
</sect2>
</sect1>
<sect1 id=io-eth-call-graph>
<title>Calling graph for Transmission and Reception</title>
<para>
It may be worth clarifying further the flow of control in the transmit and
receive cases, where the hardware driver does use interrupts and so DSRs to
tell the &ldquo;foreground&rdquo; when something asynchronous has occurred.
</para>
<sect2 id=io-eth-call-graph-tx>
<title>Transmission</title>
<orderedlist>
<listitem><para>
Some foreground task such as the application, SNMP &ldquo;daemon&rdquo;,
DHCP management thread or whatever, calls into network stack to send a
packet, or the stack decides to send a packet in response to incoming
traffic such as a &ldquo;ping&rdquo; or <acronym>ARP</acronym> request.
</para></listitem>
<listitem><para>
The driver calls the
<function><replaceable>HRDWR</replaceable>_can_send()</function>
function in the hardware driver.
</para></listitem>
<listitem><para>
<function><replaceable>HRDWR</replaceable>_can_send()</function>
returns the number of available "slots" in which it
can store a pending transmit packet.
If it cannot send at this time, the packet is queued outside the
hardware driver for later; in this case, the hardware is already busy
transmitting, so expect an interrupt as described below for completion
of the packet currently outgoing.
</para></listitem>
<listitem><para>
If it can send right now, <replaceable>HRDWR</replaceable>_send() is called.
<function><replaceable>HRDWR</replaceable>_send()</function> copies the
data into special hardware buffers, or instructs the hardware to
&ldquo;send that.&rdquo; It also remembers the key that is associated with
this tx request.
</para></listitem>
<listitem><para>
These calls return &hellip; time passes &hellip;
</para></listitem>
<listitem><para>
Asynchronously, the hardware makes an interrupt to say
&ldquo;transmit is done.&rdquo;
The ISR quietens the interrupt source in the hardware and
requests that the associated DSR be run.
</para></listitem>
<listitem><para>
The DSR calls (or <emphasis>is</emphasis>) the
<function>eth_drv_dsr()</function> function in the generic driver.
</para></listitem>
<listitem><para>
<function>eth_drv_dsr()</function> in the generic driver awakens the
&ldquo;Network Delivery Thread&rdquo; which calls the deliver function
<replaceable>HRDWR</replaceable>_deliver() in the driver.
</para></listitem>
<listitem><para>
The deliver function realizes that a transmit request has completed,
and calls the callback tx-done function
<function>(sc->funs->eth_drv->tx_done)()</function>
with the same key that it remembered for this tx.
</para></listitem>
<listitem><para>
The callback tx-done function
uses the key to find the resources associated with
this transmit request; thus the stack knows that the transmit has
completed and its resources can be freed.
</para></listitem>
<listitem><para>
The callback tx-done function
also enquires whether <replaceable>HRDWR</replaceable>_can_send() now says
&ldquo;yes, we can send&rdquo;
and if so, dequeues a further transmit request
which may have been queued as described above. If so, then
<replaceable>HRDWR</replaceable>_send() copies the data into the hardware buffers, or
instructs the hardware to "send that" and remembers the new key, as above.
These calls then all return to the &ldquo;Network Delivery Thread&rdquo;
which then sleeps, awaiting the next asynchronous event.
</para></listitem>
<listitem><para>
All done &hellip;
</para></listitem>
</orderedlist>
</sect2>
<sect2 id=io-eth-call-graph-rx>
<title>Receive</title>
<orderedlist>
<listitem><para>
Asynchronously, the hardware makes an interrupt to say
&ldquo;there is ready data in a receive buffer.&rdquo;
The ISR quietens the interrupt source in the hardware and
requests that the associated DSR be run.
</para></listitem>
<listitem><para>
The DSR calls (or <emphasis>is</emphasis>) the
<function>eth_drv_dsr()</function> function in the generic driver.
</para></listitem>
<listitem><para>
<function>eth_drv_dsr()</function> in the generic driver awakens the
&ldquo;Network Delivery Thread&rdquo; which calls the deliver function
<replaceable>HRDWR</replaceable>_deliver() in the driver.
</para></listitem>
<listitem><para>
The deliver function realizes that there is data ready and calls
the callback receive function
<function>(sc->funs->eth_drv->recv)()</function>
to tell it how many bytes to prepare for.
</para></listitem>
<listitem><para>
The callback receive function allocates memory within the stack
(eg. <type>MBUFs</type> in BSD/Unix style stacks) and prepares
a set of scatter-gather buffers that can
accommodate the packet.
</para></listitem>
<listitem><para>
It then calls back into the hardware driver routine
<replaceable>HRDWR</replaceable>_recv().
<replaceable>HRDWR</replaceable>_recv() must copy the data from the
hardware's buffers into the scatter-gather buffers provided, and return.
</para></listitem>
<listitem><para>
The network stack now has the data in-hand, and does with it what it will.
This might include recursive calls to transmit a response packet.
When this all is done, these calls return, and the
&ldquo;Network Delivery Thread&rdquo;
sleeps once more, awaiting the next asynchronous event.
</para></listitem>
</orderedlist>
</sect2>
</sect1>
</chapter>
</part>
/v2_0/ChangeLog
0,0 → 1,609
2003-02-24 Jonathan Larmour <jifl@eCosCentric.com>
 
* cdl/eth_drivers.cdl: Add doc link.
 
2003-02-07 Jonathan Larmour <jifl@eCosCentric.com>
 
* src/net/eth_drv.c: If driver can't set multi-cast, indicate it's
only a warning.
 
2002-08-14 Gary Thomas <gthomas@ecoscentric.com>
 
* cdl/eth_drivers.cdl: CYGNUM_IO_ETH_DRIVERS_SG_LIST_SIZE
needs to be global (used by all drivers, not just NET).
 
2002-08-13 Gary Thomas <gthomas@ecoscentric.com>
 
* src/net/eth_drv.c (eth_drv_send): Print a better message
if the scatter-gather list overflows.
 
* include/eth_drv.h (MAX_ETH_DRV_SG):
* cdl/eth_drivers.cdl: Add control over size of scatter-gather
data lists used to pass requests to physical layer. Previous
value was sometimes too small.
 
2002-07-31 Gary Thomas <gary@chez-thomas.org>
 
* src/net/eth_drv.c (eth_drv_start): Fix compile error when
built with old stack (no multicast) - introduced below.
 
2002-07-26 Gary Thomas <gary@chez-thomas.org>
2002-07-26 Ken Cox <jkc@redhat.com>
 
* src/net/eth_drv.c (eth_drv_start): Force multicast address
setup any time chip is reset/reconfigured.
 
2002-05-30 Jonathan Larmour <jlarmour@redhat.com>
 
* cdl/eth_drivers.cdl: Provide CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
interface to indicate if interrupt support is required.
 
2002-05-28 Jonathan Larmour <jlarmour@redhat.com>
 
* src/lwip/lw.diff: Remove. Obsolete.
 
2002-05-13 Jesper Skov <jskov@redhat.com>
 
* cdl/eth_drivers.cdl: Moved the package's header files to
cyg/io/eth to clean up the include root directory.
 
2002-04-30 Jonathan Larmour <jlarmour@redhat.com>
 
* src/lwip/eth_drv.c: Update from Jani Monoses.
 
* cdl/eth_drivers.cdl: Move CYGINT_ISO_STRING_STRFUNCS requirement
into CYGPKG_IO_ETH_DRIVERS_NET where it belongs.
Move CYGINT_ISO_STRING_MEMFUNCS requirement into
CYGPKG_IO_ETH_DRIVERS_STAND_ALONE where it belongs.
 
2002-04-10 Gary Thomas <gthomas@redhat.com>
 
* src/net/eth_drv.c: Only pass mutlicast addresses down to drivers.
 
2002-04-05 Jani Monoses <jani@iv.ro>
 
* src/lwip/eth_drv.c: New file. Add eth driver support for lwip.
* src/lwip/lw.diff: New file. diff against lwip sources.
* src/lwip/README: New file.
* cdl/eth_drivers.cdl: Add lwip driver support.
 
2002-03-09 Gary Thomas <gthomas@redhat.com>
 
* src/net/eth_drv.c: Select 'multicast_all' if too many addresses
are required. Also, ignore any non-multicast addresses.
 
2002-02-22 Hugo Tyson <hmt@redhat.com>
 
* doc/ethdrv.sgml: New file. SGML-ized the existing driver-doc
file and brought the information up to date with deliver functions
and all that.
 
2002-02-18 Gary Thomas <gthomas@redhat.com>
 
* cdl/eth_drivers.cdl:
Add new interface CYGINT_IO_ETH_MULTICAST which is used to
insure that drivers support multicast addresses if IPv6 is used.
 
2002-02-15 Gary Thomas <gthomas@redhat.com>
 
* src/net/eth_drv.c:
* include/eth_drv.h: Add framework for multicast address support.
 
2002-01-30 Hugo Tyson <hmt@redhat.com>
 
* cdl/eth_drivers.cdl (CYGPKG_IO_ETH_DRIVERS_WARN_FORCE_CONSOLE):
CDL options removed; control is now from RedBoot.
 
* src/net/eth_drv.c (START_CONSOLE): Changed to use RedBoot
fconfig items for deciding what console to use for special debug
messages such as "out of MBUFs" and the like.
 
* src/stand_alone/eth_drv.c (LOCK_APPLICATION_SCHEDULER): New
macro to do what it says, before passing packets to the app.
Used in two places: when passing a received packet, and when
passing a tx completion status back.
 
2002-01-28 Gary Thomas <gthomas@redhat.com>
 
* src/net/eth_drv.c (eth_drv_init): Changes for supporting new
FreeBSD based stack (minor API changes internal to that stack).
 
2001-12-12 Hugo Tyson <hmt@redhat.com>
 
* src/net/eth_drv.c (START_CONSOLE,END_CONSOLE): New macros akin
to those within RedBoot's internal net_io.c, for directing warning
messages to a serial line if required. All printf's wrapped in
them for safety.
 
* cdl/eth_drivers.cdl (CYGPKG_IO_ETH_DRIVERS_WARN_FORCE_CONSOLE):
and ..._NUMBER, new options to direct application warning output
somewhere useful (less harmful) for when you are debugging over
the net. Trying to print within a call which occurred from within
RedBoot itself is such a recursive situation as to be fatal.
Defaults are disabled, you must choose to set these up if
you want to debug your app safely over the network.
 
2001-12-12 Hugo Tyson <hmt@redhat.com>
 
* src/stand_alone/eth_drv.c (DIAG_DUMP_BUF_HDR,DIAG_DUMP_BUF_BDY):
Separate macros for debug dumps of ethernet header and body.
(eth_drv_write): Use them.
(eth_drv_copy_recv): Do not corrupt the static data pointer, so
that if we are erroneously called twice, at least what we pass is
a packet, rather than random memory; do not escape to SEGV. Guard
against NULL sg_list[].buf pointers, in case caller out of MBUFs.
(eth_drv_recv): Only dump packet if we actually have a buffer.
Only forward the packet if actually have a buffer.
 
2001-12-04 Richard Sandiford <rsandifo@redhat.com>
 
* src/stand_alone/eth_drv.c (eth_drv_read): Check that the return
buffer is big enough to hold the packet.
 
2001-11-28 Jonathan Larmour <jlarmour@redhat.com>
 
* doc/driver_doc: Mention preferences on how the ESA should be set.
 
2001-10-30 Jonathan Larmour <jlarmour@redhat.com>
 
* doc/driver_doc: Add description of poll, deliver and int_vector
driver functions.
 
2001-10-29 Hugo Tyson <hmt@redhat.com>
 
* src/net/eth_drv.c (eth_drv_tickle_devices): [CASE 106613] Rather
than calling can_send() in the device driver directly, call our
own eth_drv_send() function which will also dequeue a waiting
packet if we find we indeed can_send(). This helps to recover
from queue full situations within the if_ethersubr layer above.
This change is belt & braces with a similar call in the code which
drops a packet if the queue is full, in the main network code in
net/tcpip/.../src/sys/net/if_ethersubr.c (ether_output); this
change will recover the situation shortly even if the application
gives up trying to send because of ENOBUFS.
 
2001-10-18 Jonathan Larmour <jlarmour@redhat.com>
 
* cdl/eth_drivers.cdl: Rename CYGSEM_IO_ETH_DRIVERS_DEBUG to
CYGDBG_IO_ETH_DRIVERS_DEBUG and make it a common option.
Add separate CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY config.
 
* include/eth_drv.h: Don't claim BSD if it isn't.
* include/eth_drv_stats.h: Ditto.
* include/netdev.h: Ditto.
* src/net/eth_drv.c: Ditto.
Also use above CDL options to conditionalize debugging (and the
verbosity level).
* src/stand_alone/eth_drv.c: Use above renamed CDL options.
 
2001-10-11 Gary Thomas <gthomas@redhat.com>
 
* src/stand_alone/eth_drv.c (eth_drv_recv):
Defensive programming - insure valid packet size.
 
2001-10-05 Jonathan Larmour <jlarmour@redhat.com>
 
* src/net/eth_drv.c: Add default implementation of min().
 
2001-09-26 Jesper Skov <jskov@redhat.com>
 
* cdl/eth_drivers.cdl: Require the string functions.
 
* src/stand_alone/eth_drv.c: Fix warnings.
 
2001-09-25 Hugo Tyson <hmt@redhat.com>
 
* src/net/eth_drv.c (eth_drv_dsr): Assert that the "sc" given to
eth_drv_dsr() really is a sc from the valid list. This can save a
few hours debug time of a typo.
 
2001-09-13 Hugo Tyson <hmt@redhat.com>
 
* src/net/eth_drv.c (eth_drv_tickle_devices): New function to call
can_send() on all devices to allow them to unstick from a lost
interrupt or other wedge - it's up to the device to detect this,
of course, can_send() does not explicitly do this, it's just used
as an opportunity harmlessly to get control into the device so it
can examine its world.
 
2001-09-05 Gary Thomas <gthomas@redhat.com>
 
* src/stand_alone/eth_drv.c (eth_drv_init): Don't initialize
structures if ESA is zero. This is used by low level drivers
to indicate that a device is present, but can't currently be
used. The eCos driver handles this case as well.
 
2001-08-31 Hugo Tyson <hmt@redhat.com>
2001-08-31 Andrew Lunn <Andrew.Lunn@ascom.ch>
 
* src/net/eth_drv.c (eth_drv_tx_done): Guard against a NULL key
return - which can be caused by race conditions in the driver,
this is the neatest fixup. It's good defensive programming
anyway and ASCOM's tests indicate a benefit for CASE 106059.
 
2001-08-22 Gary Thomas <gthomas@redhat.com>
 
* src/stand_alone/eth_drv.c:
printf() is no longer a part of RedBoot. Thus all programs
must use diag_printf() and related functions instead.
 
* cdl/eth_drivers.cdl: Only enable warning/debug modes if RedBoot
is present as the debug environment (since there are implicit
dependencies on RedBoot functions).
 
2001-08-20 Jonathan Larmour <jlarmour@redhat.com>
 
* src/net/eth_drv.c (eth_drv_send): Move endif location to fix build
error.
 
2001-08-17 Jonathan Larmour <jlarmour@redhat.com>
 
* cdl/eth_drivers.cdl (CYGSEM_IO_ETH_DRIVERS_DEBUG): Now booldata.
* src/stand_along/eth_drv.c (eth_drv_tx_done): Use different
value of CYGSEM_IO_ETH_DRIVERS_DEBUG for verbosity.
 
2001-08-14 Gary Thomas <gthomas@redhat.com>
 
* src/stand_alone/eth_drv.c (eth_drv_write):
(eth_drv_tx_done):
(eth_drv_read): Better handling of stacking (layering) of drivers.
RedBoot (stand alone code) is designed to call into the eCos
stack and these changes make sure that this is done properly
nested/stacked. These changes also affect the behaviour positively
for CR 902745-CR.
 
* src/net/eth_drv.c (eth_drv_send): Add locking of driver while
actual hardware routines are involved. Since the same driver
can be shared by both eCos and RedBoot, it is imperative that
additional locking (in the form of locking the scheduler) be
employed during this window to make sure that the hardware is
handled in complete, consistent steps. This helps with known
bug CR 902745-CR.
 
2001-08-09 Hugo Tyson <hmt@redhat.com>
 
* src/net/eth_drv.c (eth_drv_recv): Add a pair of
CYGARC_HAL_SAVE_GP()/CYGARC_HAL_RESTORE_GP() on entry and exit;
this function is intercalled between RedBoot and the application.
A minor re-org at the ending to accommodate these macros' nesting.
(eth_drv_tx_done): CYGARC_HAL_SAVE_GP()/CYGARC_HAL_RESTORE_GP().
 
* src/stand_alone/eth_drv.c (eth_drv_tx_done): Add a pair of
CYGARC_HAL_SAVE_GP()/CYGARC_HAL_RESTORE_GP() on entry and exit;
this function is intercalled between RedBoot and the application.
Also make printing message about "tx_done for other key" also
conditional on net_debug variable - it's commonplace.
(eth_drv_recv): CYGARC_HAL_SAVE_GP()/CYGARC_HAL_RESTORE_GP().
(eth_drv_copy_recv): CYGARC_HAL_SAVE_GP()/CYGARC_HAL_RESTORE_GP().
 
2001-07-03 Gary Thomas <gthomas@redhat.com>
 
* src/net/eth_drv.c (eth_drv_recv): Better cleanup when running
out of mbufs - could have lost mbufs before.
 
2001-06-21 David Woodhouse <dwmw2@redhat.com>
 
* src/stand_alone/eth_drv.c: Timeout when waiting for Ethernet
driver to finish sending packet.
* src/stand_alone/eth_drv.c: Don't delay if it's ready immediately.
 
2001-06-11 Gary Thomas <gthomas@redhat.com>
 
* cdl/eth_drivers.cdl: Make debug default for RedBoot.
 
2001-05-22 Jonathan Larmour <jlarmour@redhat.com>
 
* cdl/eth_drivers.cdl: New option: CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS
which replaces the static define in...
* src/stand_alone_eth_drv.c: Rename ETH_DRV_PASS_PACKETS to
new CDL option above.
Also, ensure references to start_console/end_console are conditional
on CYGSEM_IO_ETH_DRIVERS_DEBUG.
 
2001-05-22 Hugo Tyson <hmt@redhat.com>
2001-05-22 Sanjay Bisen <Sanjay.Bisen@ascom.ch>
 
* src/net/eth_drv.c (eth_drv_recv): Patch from Sanjay at Ascom; it
leaked one mbuf if you run out of clusters. Fix is simply to
free m in addition to top.
 
* src/net/eth_drv.c (eth_drv_run_deliveries): Fixed a warning with
an int cast in HAL_CTRLC_CHECK().
 
2001-03-21 Gary Thomas <gthomas@redhat.com>
 
* cdl/eth_drivers.cdl: Fewer buffers needed in stand-alone mode.
 
2001-03-12 Hugo Tyson <hmt@redhat.com>
 
* cdl/eth_drivers.cdl: Permit defines for
CYGPKG_IO_ETH_DRIVERS_STAND_ALONE and CYGPKG_IO_ETH_DRIVERS_NET.
The generic i82559 driver needs to know, unfortunately; it calls
its own DSR directly to unblock after a lost interrupt. And you
mustn't call the DSR under RedBoot == ....STAND_ALONE.
 
2001-01-15 Jesper Skov <jskov@redhat.com>
 
* src/net/eth_drv.c (eth_drv_init): Do not use enaddr if NULL.
 
2001-01-07 Gary Thomas <gthomas@redhat.com>
 
* cdl/eth_drivers.cdl: Add interface 'CYGPKG_NET_DRIVER_FRAMEWORK'
to describe interdependencies between network stack and driver
framework packages.
 
2001-01-04 Gary Thomas <gthomas@redhat.com>
 
* src/net/eth_drv.c (eth_drv_tx_done): Update count of Tx packets.
 
2000-12-11 Gary Thomas <gthomas@redhat.com>
 
* src/net/eth_drv.c (eth_drv_run_deliveries): Support ^C when
using network based debug channel.
 
2000-12-02 Gary Thomas <gthomas@redhat.com>
 
* src/stand_alone/eth_drv.c (eth_drv_write): Debug: dump packet
before calling hardware routine for improved usability.
 
2000-10-30 Gary Thomas <gthomas@redhat.com>
 
* include/eth_drv.h: Fix compile error in stand-alone mode.
 
2000-10-17 Hugo Tyson <hmt@redhat.com>
2000-10-10 Andrew Lunn <Andrew.Lunn@ascom.ch>
* cdl/eth_drivers.cdl: Add configury to disable the warnings about
out of mbufs for receives.
 
* src/net/eth_drv.c: The changes required for the above.
 
2000-09-28 Hugo Tyson <hmt@redhat.com>
 
* src/net/eth_drv.c (eth_drv_run_deliveries): Remove race
condition; a chance to deliver could be delayed until "next time"
if the DSR snuck in just right.
 
2000-09-14 Hugo Tyson <hmt@redhat.com>
 
* cdl/eth_drivers.cdl: Add configury to control new features. All
is controlled globally by CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES
which is by default off, natch.
 
* src/net/eth_drv.c (simulate_fail): Implement simulated failures
in ethernet packet delivery - independent tx and rx dropping and
rx packet corruption. Also a "line break" drop-all feature.
 
2000-09-13 Gary Thomas <gthomas@redhat.com>
 
* src/stand_alone/eth_drv.c: Take out noisy dump when out of buffers.
 
2000-09-11 Hugo Tyson <hmt@cygnus.co.uk>
 
* src/stand_alone/eth_drv.c (eth_drv_write): If we timeout waiting
for the write to complete, do reset the vectors to point to the
application, rather just a bald "return".
 
2000-09-08 Hugo Tyson <hmt@cygnus.co.uk>
 
* include/netdev.h (NETDEVTAB_ENTRY): Work around feature of new
version of CYG_HAL_TABLE_ENTRY() whereby no spaces are allowed.
 
2000-09-07 Jonathan Larmour <jlarmour@redhat.com>
 
* include/netdev.h (cyg_netdevtab_entry_t): Correct syntax for
CYG_HAL_TABLE_TYPE
 
2000-09-04 Jonathan Larmour <jlarmour@redhat.com>
 
* include/netdev.h (cyg_netdevtab_entry_t): Apply CYG_HAL_TABLE_TYPE
 
2000-09-01 Hugo Tyson <hmt@cygnus.co.uk>
 
* src/stand_alone/eth_drv.c (eth_drv_dsr): New function, never
called but maybe referenced in stand_alone context, which lets
redboot work in the new world.
 
2000-09-01 Hugo Tyson <hmt@cygnus.co.uk>
 
* OVERVIEW: This is part of the change to the network stack to
greatly reduce latencies both of (other) DSRs and of thread
scheduling. All the work that the network stack *and* individual
ether drivers used to do in DSRs (including alarm callbacks and
data copies to/from the device memory) is moved into a "fast
network thread" instead. It calls a device's "deliver" function
to do the work that was previously in the DSR. This is a separate
thread so that it can be set higher priority than application
threads in order to minimize packet loss (depending on the
driver), if required (the application threads presumed to be
higher priority in turn than the network thread). A crucial
consequence of this is that we are no longer locking against DSRs,
so a plain mutex can be used rather than the global scheduler
lock, thus simplifying all the splfoo/splx() style functions.
 
These changes WILL BREAK individual device drivers until they are
updated AND the standalone logical ether driver in this component,
until it is updated also.
 
* include/eth_drv.h (ETH_DRV_SC): Add "deliver" entry to struct
eth_hwr_funs interface record; declare available DSR and flag for
"needs delivery" in SC status field.
 
* src/net/eth_drv.c (eth_drv_run_deliveries): New function,
performs callbacks to deliver funcs for all devs that want it.
(eth_drv_dsr): New function, sets flag in sc and calls up to net
stack to schedule the fast network thread.
(eth_drv_send): No need to lock scheduler here.
 
2000-08-29 Gary Thomas <gthomas@redhat.com>
 
* src/stand_alone/eth_drv.c: Use null buffer, (char *)0,
instead of wasting memory - low level drivers must be designed
to handle this case.
 
2000-08-28 Gary Thomas <gthomas@redhat.com>
 
* src/stand_alone/eth_drv.c: Use new configuration parameters.
 
* cdl/eth_drivers.cdl: Add new configury to control number of
internal buffers used by this layer. Also exposed some of the
debug flags here.
 
2000-08-25 Hugo Tyson <hmt@cygnus.co.uk>
 
* include/eth_drv_stats.h (ether_drv_stats): A little further
diddling; have a bool to say whether the dot3 info is filled in.
 
2000-08-24 Hugo Tyson <hmt@cygnus.co.uk>
 
* src/net/eth_drv.c (eth_drv_ioctl): Implement ioctl() calls.
sockio.h numbers SIOCGIFSTATS and SIOCGIFSTATSUD map to
ETH_DRV_GET_IF_STATS and ETH_DRV_GET_IF_STATS_UD respectively.
 
* include/eth_drv.h (ETH_DRV_GET_IF_STATS_UD): Add new device
interface ioctl() numbers, to get the struct below filled in.
Also pull in the definition include file (below).
 
* include/eth_drv_stats.h (ether_drv_stats): New file: Define
common structure for ether devices to return stats info to higher
up, via an ioctl() call. SNMP uses this.
 
2000-08-23 Gary Thomas <gthomas@redhat.com>
 
* src/stand_alone/eth_drv.c:
* include/eth_drv.h: Low level drivers now need to export a
function which returns the interrupt vector used by that interface.
This allows outside code to determine this in a portable fashion.
 
2000-08-16 Gary Thomas <gthomas@redhat.com>
 
* src/stand_alone/eth_drv.c:
* include/eth_drv.h: Clean up warnings (better protyping).
 
* src/stand_alone/eth_drv.c (eth_drv_write): Abandon sending
packet if device goes not ready for a long time (in some cases,
this is the only indication that the link is down).
 
2000-08-07 Gary Thomas <gthomas@redhat.com>
 
* src/net/eth_drv.c (eth_drv_recv): Add safety in case this gets
called [from stand-alone code] while interface is not up.
* src/stand_alone/eth_drv.c:
* include/eth_drv.h: Rework to more fully support mixed stand-alone
and system (eCos) stacks.
 
2000-08-03 Gary Thomas <gthomas@redhat.com>
 
* cdl/eth_drivers.cdl: Reparent within I/O, not NET. This allows
for stand-alone use as well as support for other stack implementations.
 
2000-07-28 Hugo Tyson <hmt@cygnus.co.uk>
 
* src/eth_drv.c (eth_drv_recv): Assert that the length we're asked
to deal with is at least an ether header, and also be defensive in
any case; discard small packets. [CASE 104206]
 
2000-07-26 Gary Thomas <gthomas@redhat.com>
 
* src/eth_drv.c:
* include/eth_drv.h: Change interfaces used by hardware layer to
be "soft" (pointers to functions). This will allow those drivers
to be shared by applications and the ROM/debug environment.
 
2000-07-15 Gary Thomas <gthomas@redhat.com>
 
* include/eth_drv.h: Add [initial] extensions to let this
layer work either in an eCos environment or stand-alone.
 
2000-07-11 Gary Thomas <gthomas@redhat.com>
 
* include/eth_drv.h: Add minimal PCMCIA support.
 
* src/eth_drv.c (eth_drv_netdev): New function - used to find the
ethernet device info for PCMCIA devices.
 
2000-06-23 Hugo Tyson <hmt@cygnus.co.uk>
 
* src/eth_drv.c (eth_drv_send): Do not consume an SG entry for
zero length data; tolerate overflow of the SG. Before this, pings
of 6000 bytes crashed the system!
 
You can now set net_debug to 2 to get quieter output; 1 gives the
whole packet dump as before.
 
* include/eth_drv.h (MAX_ETH_DRV_SG): Make this 16 so that an MTU
made of all mbufs will not overflow.
 
2000-03-28 Gary Thomas <gthomas@redhat.com>
 
* src/eth_drv.c (eth_drv_recv): Tolerate running out of MBUFs
instead of "panic"ing.
 
2000-03-08 Gary Thomas <gthomas@redhat.com>
 
* src/eth_drv.c: Add some function [block] comments.
(eth_drv_send): Use eCos scheduler lock instead of interrupt lock.
 
2000-03-08 Hugo Tyson <hmt@cygnus.co.uk>
 
* doc/driver_doc: Add some clarification about what's called when,
proofreading results, shorter lines so I can print it nicely.
 
2000-03-06 Gary Thomas <gthomas@redhat.com>
 
* src/eth_drv.c:
* include/eth_drv.h:
* doc/driver_doc: Remove generic "priv"ate references.
 
2000-02-29 Gary Thomas <gthomas@cygnus.co.uk>
 
* src/eth_drv.c:
* include/eth_drv.h: New expanded API for hardware drivers.
 
2000-02-18 Gary Thomas <gthomas@cygnus.co.uk>
 
* src/eth_drv.c (eth_drv_send): Disable interrupts while initiating
the buffer send - avoid a possible race.
 
2000-02-08 John Dallaway <jld@cygnus.co.uk>
 
* cdl/eth_drivers.cdl:
Reparent under CYGPKG_NET and tidy display strings.
 
//===========================================================================
//####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####
//===========================================================================
 
/v2_0/src/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 = &top;
 
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
/v2_0/src/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 *)&eth_msg_full;
eth_msg_free.first = eth_msg_free.last = (struct eth_msg *)&eth_msg_free;
for (i = 0; i < NUM_ETH_MSG; i++, msg++) {
eth_drv_msg_put(&eth_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 = &eth_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 = &eth_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(&eth_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(&eth_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(&eth_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(&eth_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
/v2_0/src/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
/v2_0/src/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.

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.