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, \ |
ð_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 — 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 |
“Network Delivery Thread” |
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 “magic” |
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 <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(<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, |
&<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 — 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->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->funs->eth_drv->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 “start.” |
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 “control” |
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 “UD” 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 |
“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), 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 “disposition” code |
(done, send pending, etc). At this point, you should move the data to some |
“safe” 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 “Network Delivery Thread” in |
order to let the device driver do the time-consuming work associated with |
receiving a packet — 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 “something requires attention.” |
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 “intelligence.” |
</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 “CTRL-C” 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 “up” 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> “started”) |
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 “foreground” 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 “daemon”, |
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 “ping” 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 |
“send that.” It also remembers the key that is associated with |
this tx request. |
</para></listitem> |
<listitem><para> |
These calls return … time passes … |
</para></listitem> |
<listitem><para> |
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. |
</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 |
“Network Delivery Thread” 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 |
“yes, we can send” |
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 “Network Delivery Thread” |
which then sleeps, awaiting the next asynchronous event. |
</para></listitem> |
<listitem><para> |
All done … |
</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 |
“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. |
</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 |
“Network Delivery Thread” 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 |
“Network Delivery Thread” |
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 = ⊤ |
|
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 *)ð_msg_full; |
eth_msg_free.first = eth_msg_free.last = (struct eth_msg *)ð_msg_free; |
for (i = 0; i < NUM_ETH_MSG; i++, msg++) { |
eth_drv_msg_put(ð_msg_free, msg); |
} |
} |
|
// |
// This function is called during system initialization to register a |
// network interface with the system. |
// |
static void |
eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr) |
{ |
// enaddr == 0 -> hardware init was incomplete (no ESA) |
if (enaddr != 0) { |
// Set up hardware address |
memcpy(&sc->sc_arpcom.esa, enaddr, ETHER_ADDR_LEN); |
memcpy(__local_enet_addr, enaddr, ETHER_ADDR_LEN); |
__local_enet_sc = sc; |
eth_drv_start(sc); |
} |
} |
|
#if 0 // Not currently used. Left in case it's needed in the future |
// |
// This [internal] function will be called to stop activity on an interface. |
// |
static void |
eth_drv_stop(struct eth_drv_sc *sc) |
{ |
(sc->funs->stop)(sc); |
} |
#endif |
|
// |
// This [internal] function will be called to start activity on an interface. |
// |
static void |
eth_drv_start(struct eth_drv_sc *sc) |
{ |
// Perform any hardware initialization |
(sc->funs->start)(sc, (unsigned char *)&sc->sc_arpcom.esa, 0); |
} |
|
// |
// Send a packet of data to the hardware |
// |
static int packet_sent; |
|
void |
eth_drv_write(char *eth_hdr, char *buf, int len) |
{ |
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; |
struct eth_drv_sc *sc = __local_enet_sc; |
int sg_len = 2; |
void *dbg = CYGACC_CALL_IF_DBG_DATA(); |
int old_state; |
int wait_time = 5; // Timeout before giving up |
void *eth_drv_old = 0; |
|
if (dbg) { |
sc = (struct eth_drv_sc *)dbg; // Use control from installed driver |
eth_drv_old = sc->funs->eth_drv_old; |
if (eth_drv_old == 0) { |
sc->funs->eth_drv_old = sc->funs->eth_drv; |
sc->funs->eth_drv = ð_drv_funs; // Substitute stand-alone driver |
old_state = sc->state; |
if (!old_state & ETH_DRV_STATE_ACTIVE) { |
// This interface not fully initialized, do it now |
(sc->funs->start)(sc, (unsigned char *)&__local_enet_addr, 0); |
sc->state |= ETH_DRV_STATE_ACTIVE; |
} |
} |
} |
|
while (!(sc->funs->can_send)(sc)) { |
// Give driver a chance to service hardware |
(sc->funs->poll)(sc); |
CYGACC_CALL_IF_DELAY_US(2*100000); |
if (--wait_time <= 0) |
goto reset_and_out; // Give up on sending packet |
} |
|
sg_list[0].buf = (CYG_ADDRESS)eth_hdr; |
sg_list[0].len = 14; // FIXME |
sg_list[1].buf = (CYG_ADDRESS)buf; |
sg_list[1].len = len; |
packet_sent = 0; |
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG |
if (cyg_io_eth_net_debug) { |
int old_console; |
old_console = start_console(); |
diag_printf("Ethernet send:\n"); |
DIAG_DUMP_BUF_HDR(eth_hdr, 14); |
DIAG_DUMP_BUF_BDY(buf, len); |
end_console(old_console); |
} |
#endif |
|
(sc->funs->send)(sc, sg_list, sg_len, len+14, (CYG_ADDRWORD)&packet_sent); |
|
wait_time = 500; |
while (1) { |
(sc->funs->poll)(sc); |
|
if(packet_sent) |
break; |
|
CYGACC_CALL_IF_DELAY_US(2*1000); |
if (--wait_time <= 0) |
goto reset_and_out; // Give up on sending packet |
} |
reset_and_out: |
if (dbg) { |
// if (!old_state & ETH_DRV_STATE_ACTIVE) { |
// // This interface was not fully initialized, shut it back down |
// (sc->funs->stop)(sc); |
// } |
if (eth_drv_old == 0) { |
sc->funs->eth_drv = sc->funs->eth_drv_old; |
sc->funs->eth_drv_old = (struct eth_drv_funs *)0; |
} |
} |
} |
|
// |
// This function is called from the hardware driver when an output operation |
// has completed - i.e. the packet has been sent. |
// |
static void |
eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status) |
{ |
CYGARC_HAL_SAVE_GP(); |
if ((int *)key == &packet_sent) { |
*(int *)key = 1; |
} else { |
// It's possible that this acknowledgement is for a different |
// [logical] driver. Try and pass it on. |
#if defined(CYGDBG_IO_ETH_DRIVERS_DEBUG) && \ |
(CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY >=2 ) |
// Note: not normally enabled - too verbose |
if (cyg_io_eth_net_debug > 1) { |
int old_console; |
old_console = start_console(); |
diag_printf("tx_done for other key: %x\n", key); |
end_console(old_console); |
} |
#endif |
LOCK_APPLICATION_SCHEDULER(); |
if (sc->funs->eth_drv_old) { |
(sc->funs->eth_drv_old->tx_done)(sc, key, status); |
} else { |
(sc->funs->eth_drv->tx_done)(sc, key, status); |
} |
UNLOCK_APPLICATION_SCHEDULER(); |
} |
CYGARC_HAL_RESTORE_GP(); |
} |
|
// |
// Receive one packet of data from the hardware, if available |
// |
int |
eth_drv_read(char *eth_hdr, char *buf, int len) |
{ |
struct eth_drv_sc *sc = __local_enet_sc; |
struct eth_msg *msg; |
int res; |
void *dbg = CYGACC_CALL_IF_DBG_DATA(); |
int old_state; |
void *eth_drv_old = 0; |
|
if (dbg) { |
sc = (struct eth_drv_sc *)dbg; // Use control from installed driver |
eth_drv_old = sc->funs->eth_drv_old; |
if (eth_drv_old == 0) { |
sc->funs->eth_drv_old = sc->funs->eth_drv; |
sc->funs->eth_drv = ð_drv_funs; // Substitute stand-alone driver |
old_state = sc->state; |
if (!old_state & ETH_DRV_STATE_ACTIVE) { |
// This interface not fully initialized, do it now |
(sc->funs->start)(sc, (unsigned char *)&__local_enet_addr, 0); |
sc->state |= ETH_DRV_STATE_ACTIVE; |
} |
} |
} |
(sc->funs->poll)(sc); // Give the driver a chance to fetch packets |
msg = eth_drv_msg_get(ð_msg_full); |
if (msg && len >= msg->len - 14) { |
memcpy(eth_hdr, msg->data, 14); |
memcpy(buf, &msg->data[14], msg->len-14); |
res = msg->len; |
} else { |
res = 0; |
} |
if (msg) { |
eth_drv_msg_put(ð_msg_free, msg); |
} |
|
if (dbg) { |
if (eth_drv_old == 0) { |
sc->funs->eth_drv = sc->funs->eth_drv_old; |
sc->funs->eth_drv_old = (struct eth_drv_funs *)0; |
} |
// if (!old_state & ETH_DRV_STATE_ACTIVE) { |
// // This interface was not fully initialized, shut it back down |
// (sc->funs->stop)(sc); |
// } |
} |
return res; |
} |
|
#ifdef CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS |
// |
// This function is called to copy a message up to the next level. |
// It is only used when this driver has usurped the processing of |
// network functions. |
// |
static unsigned char *eth_drv_copy_recv_buf; |
static void |
eth_drv_copy_recv(struct eth_drv_sc *sc, |
struct eth_drv_sg *sg_list, |
int sg_len) |
{ |
int i; |
unsigned char *ppp; |
CYGARC_HAL_SAVE_GP(); |
ppp = eth_drv_copy_recv_buf; // Be safe against being called again by accident |
for (i = 0; i < sg_len; i++) { |
if ( sg_list[i].buf ) // Be safe against discarding calls |
memcpy((unsigned char *)sg_list[i].buf, |
ppp, sg_list[i].len); |
ppp += sg_list[i].len; |
} |
CYGARC_HAL_RESTORE_GP(); |
} |
#endif |
|
// |
// This function is called from a hardware driver to indicate that an input |
// packet has arrived. The routine will set up appropriate network resources |
// to hold the data and call back into the driver to retrieve the data. |
// |
static void |
eth_drv_recv(struct eth_drv_sc *sc, int total_len) |
{ |
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG]; |
int sg_len = 0; |
struct eth_msg *msg; |
unsigned char *buf; |
CYGARC_HAL_SAVE_GP(); |
|
if ((total_len > MAX_ETH_MSG) || (total_len < 0)) { |
#ifdef CYGSEM_IO_ETH_DRIVERS_WARN |
int old_console; |
old_console = start_console(); |
diag_printf("%s: packet of %d bytes truncated\n", __FUNCTION__, total_len); |
end_console(old_console); |
#endif |
total_len = MAX_ETH_MSG; |
} |
msg = eth_drv_msg_get(ð_msg_free); |
if (msg) { |
buf = msg->data; |
} else { |
#ifdef CYGSEM_IO_ETH_DRIVERS_WARN |
int old_console; |
old_console = start_console(); |
diag_printf("%s: packet of %d bytes dropped\n", __FUNCTION__, total_len); |
end_console(old_console); |
#endif |
buf = (unsigned char *)0; // Drivers know this means "the bit bucket" |
} |
sg_list[0].buf = (CYG_ADDRESS)buf; |
sg_list[0].len = total_len; |
sg_len = 1; |
|
(sc->funs->recv)(sc, sg_list, sg_len); |
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG |
if (cyg_io_eth_net_debug) { |
int old_console; |
old_console = start_console(); |
diag_printf("Ethernet recv:\n"); |
if ( buf ) { |
DIAG_DUMP_BUF_HDR(buf, 14); |
DIAG_DUMP_BUF_BDY(buf+14, total_len-14); |
} |
else |
diag_printf(" ...NULL buffer.\n"); |
end_console(old_console); |
} |
#endif |
#ifdef CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS |
if ((unsigned char *)0 != buf && // Only pass on a packet we actually got! |
sc->funs->eth_drv_old != (struct eth_drv_funs *)0) { |
void (*hold_recv)(struct eth_drv_sc *sc, |
struct eth_drv_sg *sg_list, |
int sg_len); |
// See if this packet was for us. If not, pass it upwards |
// This is a major layering violation!! |
if (memcmp(&__local_ip_addr, &buf[14+16], 4)) { |
hold_recv = sc->funs->recv; |
sc->funs->recv = eth_drv_copy_recv; |
eth_drv_copy_recv_buf = buf; |
// This calls into the 'other' driver, giving it a chance to |
// do something with this data (since it wasn't for us) |
LOCK_APPLICATION_SCHEDULER(); |
(sc->funs->eth_drv_old->recv)(sc, total_len); |
UNLOCK_APPLICATION_SCHEDULER(); |
sc->funs->recv = hold_recv; |
} |
} |
#endif |
if (msg) { |
msg->len = total_len; |
eth_drv_msg_put(ð_msg_full, msg); |
#ifdef CYGSEM_IO_ETH_DRIVERS_WARN |
// there was an else with a dump_buf() here but it's |
// meaningless; sg_list[0].buf is NULL! |
#endif |
} |
CYGARC_HAL_RESTORE_GP(); |
} |
|
// |
// Determine the interrupt vector used by an interface |
// |
int |
eth_drv_int_vector(void) |
{ |
struct eth_drv_sc *sc = __local_enet_sc; |
return sc->funs->int_vector(sc); |
} |
|
|
void eth_drv_dsr(cyg_vector_t vector, |
cyg_ucount32 count, |
cyg_addrword_t data) |
{ |
diag_printf("eth_drv_dsr should not be called: vector %d, data %x\n", |
vector, data ); |
} |
|
|
// EOF src/stand_alone/eth_drv.c |
/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. |