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

Subversion Repositories openrisc_me

Compare Revisions

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

Rev 27 → Rev 174

/usbsethdrv.c
0,0 → 1,616
//==========================================================================
//
// usbethdrv.c
//
// Network device driver for USB-ethernet devices.
//
//==========================================================================
//####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): bartv
// Contributors: bartv
// Date: 2000-10-04
//
//####DESCRIPTIONEND####
//==========================================================================
 
#include <cyg/infra/cyg_type.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/drv_api.h>
 
#define __ECOS 1
#include <cyg/io/eth/netdev.h>
#include <cyg/io/eth/eth_drv.h>
#include <cyg/io/eth/eth_drv_stats.h>
 
#include <pkgconf/io_usb_slave_eth.h>
#include <cyg/io/usb/usbs_eth.h>
 
// ----------------------------------------------------------------------------
// The network driver data structure.
ETH_DRV_SC(usbs_eth_sc0,
(void*) &usbs_eth0,
CYGDAT_USBS_ETHDRV_NAME,
usbs_ethdrv_start,
usbs_ethdrv_stop,
usbs_ethdrv_ioctl,
usbs_ethdrv_can_send,
usbs_ethdrv_send,
usbs_ethdrv_recv,
usbs_ethdrv_deliver,
usbs_ethdrv_poll,
usbs_ethdrv_intvector);
 
NETDEVTAB_ENTRY(usbs_eth_netdev0,
"usbs_eth0",
usbs_ethdrv_init,
&usbs_eth_sc0);
 
// ----------------------------------------------------------------------------
// Statics gathering. The following macro can be used to increment a
// statistic without having to use a #ifdef for the statistics
// configuration option everywhere.
#ifdef CYGFUN_USBS_ETHDRV_STATISTICS
# define INCR_STAT(a) \
CYG_MACRO_START \
(a) += 1; \
CYG_MACRO_END
#else
# define INCR_STAT(a) CYG_EMPTY_STATEMENT
#endif
 
// Various constants related to SNMP statistics. It is not clear
// what these are all for.
#ifdef CYGFUN_USBS_ETHDRV_STATISTICS
# define CYGDAT_USBS_ETHDRV_DESCRIPTION "eCos USB ethernet device"
#endif
// ----------------------------------------------------------------------------
// Utility functions.
//
// The TCP/IP stack works in terms of scatter/gather buffers. USB tends to
// involve DMA operations so it is more convenient to work in terms of
// 1514 byte flat buffers. Actually, the first two bytes of the buffer
// are used to hold the ethernet frame size to work around restrictions
// with certain hardware implementations of USB that may be unable to
// transfer certain packet sizes.
 
static bool
scatter(unsigned char* buf, struct eth_drv_sg* sg, int sg_len)
{
unsigned int size;
size = buf[0] | (buf[1] << 8);
buf++; buf++;
 
CYG_ASSERT((size >= CYGNUM_USBS_ETH_MIN_FRAME_SIZE) && (size <= CYGNUM_USBS_ETH_MAX_FRAME_SIZE),\
"ethernet frame size limits must be observed");
while ((size > 0) && (sg_len > 0)) {
if (size > sg->len) {
memcpy((void*) sg->buf, buf, sg->len);
buf += sg->len;
size -= sg->len;
sg++;
sg_len--;
} else {
memcpy((void*) sg->buf, buf, size);
size = 0;
}
}
 
return 0 == size;
}
 
static bool
gather(unsigned char* buf, unsigned int size, struct eth_drv_sg* sg, int sg_len)
{
unsigned int left = size;
unsigned char* base = buf;
 
buf++; buf++;
while ((left > 0) && (sg_len > 0)) {
if (left > sg->len) {
memcpy(buf, (void*) sg->buf, sg->len);
buf += sg->len;
left -= sg->len;
sg++;
sg_len--;
} else {
memcpy(buf, (void*) sg->buf, left);
left = 0;
}
}
size = size - left;
base[0] = size & 0x00FF;
base[1] = (size >> 8) & 0x00FF;
 
return 0 == left;
}
 
// ----------------------------------------------------------------------------
// usbs_ethdrv_init()
//
// This function is called during system initialization to decide
// whether or not this particular network device is usable. For
// USB-ethernet this is problematical, the device is only really
// usable once both sides have come up. The typical sequence
// of events is something like:
//
// 1) the eCos peripheral is powered up. Static constructors are
// run resulting in basic initialization.
//
// 2) the eCos TCP/IP stack initialization happens. Roughly in
// parallel the eCos USB slave side is initialized as well,
// i.e. enumeration data is supplied to control endpoints,
// endpoints are associated with application classes, and so
// on. The relative order of TCP/IP and USB initialization is
// not particularly important.
//
// It is the TCP/IP stack's initialization code that will
// invoke usbs_eth_init().
//
// 3) host-side USB detects that the eCos peripheral has been
// connected or powered up. It goes through the enumeration
// process and will end up loading a host-side network driver.
// This connects to the eCos-side USB ethernet code to
// e.g. obtain the MAC address.
//
// 4) when the host-side is ready, the eCos side can be brought up.
// The required call is (sc->funs->eth_drv->init)(sc, enaddr)
//
// In practice it is easier for now to invoke the init() function
// immediately. There are not going to be any incoming packets
// until the host is ready, and can_send() can just return false
// for the time being.
//
// Invoked in: thread context only
// ----------------------------------------------------------------------------
 
static bool
usbs_ethdrv_init(struct cyg_netdevtab_entry* ndp)
{
struct eth_drv_sc* sc = (struct eth_drv_sc*)(ndp->device_instance);
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
 
(*sc->funs->eth_drv->init)(sc, eth->ecos_MAC);
return true;
}
 
// ----------------------------------------------------------------------------
// The receive process that is used to transfer a received ethernet
// packet into the stack. The calling sequence is somewhat convoluted.
// It started off as:
//
// 1) Ethernet hw ISR invoked by hardware, schedules its own
// hw_dsr(), and blocks further interrupts in the ethernet chip
// 2) hw_dsr() calls generic eth_drv_dsr() from io/eth common package
// 3) eth_drv_dsr() interacts with the TCP/IP stack and allocates mbufs
// (typically, the TCP/IP stack might not be in use)
// 4) eth_drv_dsr() calls usbs_eth_recv() to transfer the data to mbufs
// 5) eth_drv_dsr() returns to hw_dsr() which reenables interrupts
// 6) hw_dsr() completes and everything can proceed.
//
// The problem with this is that the whole ethernet packet gets copied
// inside a DSR, affecting dispatch latency (but not interrupt latency).
// This is bad. Hence there is an alternative route involving a separate
// thread in the TCP/IP stack.
//
// 1) Ethernet hw ISR runs as before, scheduling hw_dsr()
// 2) hw_dsr() calls up into eth_drv_dsr()
// 3) eth_drv_dsr() wakes up a thread inside the TCP/IP stack
// 4) eth_drv_dsr() returns to hw_dsr(), which performs no further
// processing. Ethernet chip interrupts remain disabled.
// 5) The TCP/IP thread ends up calling hw_deliver(). This should take
// care of any pending activity. For every buffered packet there should
// be a call to a generic recv() function which then goes back into
// the driver-specific recv() function.
//
// The advantage is that ethernet packet copying now happens at thread
// level rather than DSR level so thread priorities can be used to
// schedule things.
//
// USB-ethernet does not interact directly with any hardware, instead
// it just passes information to lower levels of USB code. The reception
// process is started by usbs_ethdrv_start() when the TCP/IP stack brings
// up the interface.
//
// When the USB transfer has completed a callback will be invoked, at
// DSR level. Assuming the transfer went ok, the callback will invoke
// eth_drv_dsr() to inform the higher level code.
//
// The deliver function can check the state of the buffer
// and go through the sc->funs->eth_drv->recv()/recv() sequence
// to transfer the data into the stack.
//
// usbs_ethdrv_recv() does a scatter from the internal buffer into the
// mbuf, thus freeing up the buffer. This allows it to start another
// receive,
//
// Synchronisation involves the scheduler lock because the recv
// callback is invoked inside a DSR.
 
static void usbs_ethdrv_halted_callback(void*, int);
 
static void
usbs_ethdrv_recv_callback(usbs_eth* eth, void* callback_data, int size)
{
cyg_bool resubmit = true;
struct eth_drv_sc* sc = (struct eth_drv_sc*) callback_data;
CYG_ASSERT( eth == (usbs_eth*)(sc->driver_private), "USB and TCP/IP worlds need to be consistent");
 
INCR_STAT(eth->interrupts);
if (!eth->ecos_up) {
// This message should just be discarded since the eCos TCP/IP
// stack is not expecting anything from this interface.
// Reception will resume when the interface comes back up.
eth->rx_active = false;
resubmit = false;
} else if (size < 0) {
// An error has occurred. The likely possibilities are:
// -EPIPE: connection to the host has been broken
// -EAGAIN: the endpoint is haltedn
// -EMSGSIZE: bogus message from host
// -EIO: other
 
if (-EAGAIN == size) {
// EAGAIN should be handled by waiting for the endpoint to be reset.
resubmit = false;
usbs_start_rx_endpoint_wait(eth->rx_endpoint, &usbs_ethdrv_halted_callback, (void*) sc);
} else if (-EMSGSIZE == size) {
// Do nothing for now
} else {
// EPIPE should be resubmitted, the usbseth.c will use the
// pending rx support. EIO could mean anything.
}
} else if (0 == size) {
// The endpoint is no longer halted. Just do the resubmit at
// the end.
} else {
// A packet has been received. Now do a size sanity check
// based on the first two bytes.
int real_size = eth->rx_bufptr[0] + (eth->rx_bufptr[1] << 8);
if (real_size < CYGNUM_USBS_ETH_MIN_FRAME_SIZE) {
INCR_STAT(eth->rx_short_frames);
} else if (real_size > CYGNUM_USBS_ETH_MAX_FRAME_SIZE) {
INCR_STAT(eth->rx_too_long_frames);
} else {
// The packet appears to be valid. Inform higher level
// code and mark the buffer as in use.
resubmit = false;
eth->rx_buffer_full = true;
eth->rx_active = false;
eth_drv_dsr(0, 0, (cyg_addrword_t) sc);
}
}
if (resubmit) {
eth->rx_active = true;
usbs_eth_start_rx(eth, eth->rx_bufptr, &usbs_ethdrv_recv_callback, callback_data);
}
}
 
// Another callback, used to wait while an endpoint is halted.
static void
usbs_ethdrv_halted_callback(void* callback_data, int size)
{
struct eth_drv_sc* sc = (struct eth_drv_sc*) callback_data;
usbs_ethdrv_recv_callback((usbs_eth*) sc->driver_private, callback_data, 0);
}
 
// Start a receive operation. It is not possible to abort an existing
// rx operation, so a valid sequence of events is: start, rx ongoing,
// stop, restart. The rx_active field is used to keep track of whether
// or not there is still a receive in progress. The receive callback
// will just discard incoming data if the eCos stack is not currently
// running.
static void
usbs_ethdrv_start_recv(struct eth_drv_sc* sc, usbs_eth* eth)
{
cyg_drv_dsr_lock();
if (!eth->rx_active) {
eth->rx_active = true;
usbs_eth_start_rx(eth, eth->rx_bufptr, &usbs_ethdrv_recv_callback, (void*) sc);
}
cyg_drv_dsr_unlock();
}
 
// This is invoked from the delivery thread when a valid buffer
// has been received. The buffer should be scattered into the
// supplied list, then another receive should be started.
 
static void
usbs_ethdrv_recv(struct eth_drv_sc* sc,
struct eth_drv_sg* sg_list, int sg_len)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
 
CYG_ASSERT( eth->rx_buffer_full, "This function should only be called when there is a buffer available");
(void) scatter(eth->rx_bufptr, sg_list, sg_len);
eth->rx_buffer_full = false;
eth->rx_active = true;
usbs_eth_start_rx(eth, eth->rx_bufptr, &usbs_ethdrv_recv_callback, (void*) sc);
}
 
// ----------------------------------------------------------------------------
// Now for the transmit process.
//
// When an application thread writes down a socket the data gets moved
// into mbufs, and then passed to the appropriate device driver - which
// may or may not be able to process it immediately. There is also a
// timeout thread within the TCP/IP to handle retransmits etc.
//
// The stack will start by calling usbs_ethdrv_can_send() to determine
// whether or not the driver can accept the packet. For the purposes
// of the USB-ethernet driver this is true provided both host
// and target are up and there is a spare buffer available.
//
// If the usbs_eth_can_send() returns true then there will be a call
// to usbs_ethdrv_send(). This gathers the data into a single
// buffer. If there is no transmit in progress yet then one is started.
//
// At some point the packet will have been transmitted and a callback
// gets invoked. This needs to call eth_drv_dsr(), waking up the
// delivery thread. The deliver() function can then check which
// transmissions have completed and inform the higher level code
// via sc->funs->eth_drv->tx_done(). The buffer can be re-used at
// that point.
 
static void
usbs_ethdrv_send_callback(usbs_eth* eth, void* callback_data, int size)
{
struct eth_drv_sc* sc = (struct eth_drv_sc*) callback_data;
CYG_ASSERT( eth == (usbs_eth*)(sc->driver_private), "USB and TCP/IP worlds need to be consistent");
 
INCR_STAT(eth->interrupts);
 
// There are a variety of possible error codes. -EAGAIN indicates
// that the endpoint is stalled. -EPIPE indicates that the
// connection to the host has been lost. These are not really
// particularly interesting. Whatever happens the buffer
// must be cleared and higher-level code informed so that
// the mbufs can be released.
if (size > 0) {
INCR_STAT(eth->tx_count);
}
eth->tx_done = true;
eth_drv_dsr(0, 0, (cyg_addrword_t) sc);
}
 
// Is it possible to send an ethernet frame? This requires
// an empty buffer, i.e. there should be no existing
// transmit in progress. It also requires that the host
// is connected and that the endpoint is not currently halted.
static int
usbs_ethdrv_can_send(struct eth_drv_sc* sc)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
return eth->host_up && !eth->tx_buffer_full && !eth->tx_endpoint->halted;
}
 
// Actually start a packet transmission. This means collecting
// all the data into a single buffer and then invoking the
// lower-level code. The latter may discard the packet immediately
// if the MAC is not appropriate: it would be more efficient to
// catch that here, especially for large packets, but the check
// has to happen inside the lower-level code anyway in case
// that is being invoked directly rather than via the driver.
//
// There is a possible recursion problem,
// send->start_tx->tx_done->can_send->send, which is guarded
// against using the tx_in_send flag.
 
static void
usbs_ethdrv_send(struct eth_drv_sc* sc,
struct eth_drv_sg* sgl_list, int sg_len, int total_len,
unsigned long key)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
CYG_ASSERT( 0 == eth->tx_in_send, "send() should not be invoked recursively");
CYG_ASSERT( total_len <= CYGNUM_USBS_ETH_MAX_FRAME_SIZE, "ethernet maximum frame size should be observed");
CYG_ASSERT( CYGNUM_USBS_ETH_MIN_FRAME_SIZE <= total_len, "ethernet minimum frame size should be observed");
 
eth->tx_in_send = true;
CYG_ASSERT( !eth->tx_buffer_full, "the transmit buffer should be empty");
gather(eth->tx_buffer, CYGNUM_USBS_ETH_MAX_FRAME_SIZE, sgl_list, sg_len);
eth->tx_buffer_full = true;
eth->tx_done = false;
eth->tx_key = key;
usbs_eth_start_tx(eth, eth->tx_buffer, &usbs_ethdrv_send_callback, (void*) sc);
eth->tx_in_send = false;
}
 
// ----------------------------------------------------------------------------
// Deliver needs to take into account both receive and transmit buffers.
 
static void
usbs_ethdrv_deliver(struct eth_drv_sc* sc)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
 
if (eth->rx_buffer_full) {
int size = eth->rx_bufptr[0] + (eth->rx_bufptr[1] << 8);
(*sc->funs->eth_drv->recv)(sc, size);
}
if (eth->tx_done) {
unsigned long key = eth->tx_key;
eth->tx_buffer_full = false;
eth->tx_done = false;
(*sc->funs->eth_drv->tx_done)(sc, key, 1);
}
}
 
// ----------------------------------------------------------------------------
// usbs_ethdrv_start()
//
// This gets called by the TCP/IP stack later on during
// initialization, when the stack is ready to send and receive
// packets. It may get called multiple times while the stack
// is running, with different flags values.
//
// As far as transmits are concerned, nothing needs to be done. If no
// transmit is in progress then everything is fine anyway. If a
// transmit is already in progress then it must be allowed to complete
// via the usual route. Receives should however be restarted, the
// start function has appropriate safeguards.
//
// Invoked in: thread context only
// ----------------------------------------------------------------------------
 
static void
usbs_ethdrv_start(struct eth_drv_sc* sc, unsigned char* enaddr, int flags)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
if (!eth->ecos_up) {
eth->ecos_up = true;
usbs_ethdrv_start_recv(sc, eth);
}
}
 
// ----------------------------------------------------------------------------
// usbs_ethdrv_stop()
//
// Similarly this gets called by the TCP/IP stack to bring the network
// interface down. Nothing should happen for any packets currently
// being transmitted or received, that would cause confusion everywhere.
// The receive callback checks the ecos_up flag and does the right
// thing. The TCP/IP stack should not call can_send() after taking
// the interface down so no new transmits will be initiated.
//
// Invoked in: thread context only
// ----------------------------------------------------------------------------
 
static void
usbs_ethdrv_stop(struct eth_drv_sc* sc)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
eth->ecos_up = false;
}
 
// ----------------------------------------------------------------------------
// usbs_eth_ioctl()
//
// The operations to worry about here are:
//
// SET_MAC_ADDRESS,via the SIOCSIFHWADDR ioctl
//
// GET_IF_STATS and GET_IF_STATS_UD, to report gathered statistics.
//
// Invoked in: thread context only
// ----------------------------------------------------------------------------
 
static int
usbs_ethdrv_ioctl(struct eth_drv_sc* sc, unsigned long key, void* data, int data_length)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
int result = EINVAL;
switch(key) {
case ETH_DRV_SET_MAC_ADDRESS:
{
if (6 == data_length) {
memcpy(eth->ecos_MAC, data, 6);
result = 0;
}
}
break;
#if defined(CYGFUN_USBS_ETHDRV_STATISTICS) && defined(ETH_DRV_GET_IF_STATS_UD)
case ETH_DRV_GET_IF_STATS_UD:
case ETH_DRV_GET_IF_STATS:
{
static unsigned char my_chipset[] = { 0, 0 };
struct ether_drv_stats *p = (struct ether_drv_stats*) data;
int i;
strcpy(p->description, CYGDAT_USBS_ETHDRV_DESCRIPTION);
for ( i = 0; i < SNMP_CHIPSET_LEN; i++ ) {
if ( 0 == (p->snmp_chipset[i] = my_chipset[i]) ) {
break;
}
}
p->duplex = 3; // 3 == duplex
p->operational = (eth->host_up && eth->ecos_up) ? 3 : 2; // 3 == up, 2 == down
p->speed = 10 * 1000000;
p->supports_dot3 = 1;
p->rx_too_long_frames = eth->rx_too_long_frames;
p->rx_short_frames = eth->rx_short_frames;
p->interrupts = eth->interrupts;
p->rx_count = eth->rx_count;
p->tx_count = eth->tx_count;
p->tx_queue_len = 1;
}
break;
#endif
default:
break;
}
 
return result;
}
 
// ----------------------------------------------------------------------------
// usbs_ethdrv_poll()
//
// On real ethernet hardware this is used by RedBoot once the
// application has started running, so that the network device can be
// used for debugging purposes as well as for the application's own
// needs. The lower-level USB device may supply a poll function as well.
// ----------------------------------------------------------------------------
static void
usbs_ethdrv_poll(struct eth_drv_sc* sc)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
(*eth->control_endpoint->poll_fn)(eth->control_endpoint);
}
 
// ----------------------------------------------------------------------------
// usbs_ethdrv_intvector()
//
// See usbs_eth_poll().
// ----------------------------------------------------------------------------
 
static int
usbs_ethdrv_intvector(struct eth_drv_sc* sc)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
return eth->control_endpoint->interrupt_vector;
}
 
 
/usbseth.c
0,0 → 1,321
//==========================================================================
//
// usbseth.c
//
// Support for USB-ethernet devices, slave-side.
//
//==========================================================================
//####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): bartv
// Contributors: bartv
// Date: 2000-10-04
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/cyg_trac.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/drv_api.h>
 
#include <pkgconf/io_usb_slave_eth.h>
 
#define __ECOS 1
#include <cyg/io/usb/usbs_eth.h>
 
#ifdef CYGPKG_USBS_ETHDRV
#include <cyg/io/eth/netdev.h>
#include <cyg/io/eth/eth_drv.h>
#endif
 
// ----------------------------------------------------------------------------
// Static data.
//
// usbs_eth0 contains the per-device data, both the low-level data
// such as which endpoints to use and the network-driver data such as
// SNMP statistics. If this package is loaded then the assumption
// is that there should be at least one USB-ethernet device. Additional
// ones can be instantiated in application code if necessary. A call
// to usbs_eth_init() is required for initialization.
usbs_eth usbs_eth0;
 
// ----------------------------------------------------------------------------
// Initialization. This should be called explicitly by application code
// at an appropriate point in the system startup.
void
usbs_eth_init(usbs_eth* eth, usbs_control_endpoint* ctrl, usbs_rx_endpoint* rx, usbs_tx_endpoint* tx, unsigned char* mac)
{
eth->control_endpoint = ctrl;
eth->rx_endpoint = rx;
eth->tx_endpoint = tx;
eth->host_up = false;
eth->host_promiscuous = false;
memcpy(eth->host_MAC, mac, 6);
eth->rx_pending_buf = (unsigned char*) 0;
// Install default handlers for some messages. Higher level code
// may override this.
ctrl->state_change_fn = &usbs_eth_state_change_handler;
ctrl->state_change_data = (void*) eth;
ctrl->class_control_fn = &usbs_eth_class_control_handler;
ctrl->class_control_data = (void*) eth;
#ifdef CYGPKG_USBS_ETHDRV
eth->ecos_up = false;
eth->rx_active = false;
# ifdef CYGFUN_USBS_ETHDRV_STATISTICS
eth->interrupts = 0;
eth->tx_count = 0;
eth->rx_count = 0;
# endif
# ifndef HAL_DCACHE_LINE_SIZE
eth->rx_bufptr = eth->rx_buffer;
# else
# endif
eth->rx_bufptr = (unsigned char*) ((((cyg_uint32)eth->rx_buffer) + HAL_DCACHE_LINE_SIZE - 1)
& ~(HAL_DCACHE_LINE_SIZE - 1));
eth->rx_buffer_full = false;
eth->tx_in_send = false;
eth->tx_buffer_full = false;
eth->tx_done = false;
#endif
}
 
 
// ----------------------------------------------------------------------------
// Generic transmit and receive operations. These can be called
// explicitly by application code, or implicitly via the eCos ethernet
// device driver code in usbsethdrv.c. These two modes of operation
// should not be mixed since the routines do not perform any
// synchronization themselves, instead they rely on higher level code.
 
// Packet transmission. The exported function is usbs_eth_start_tx(),
// which can be invoked from thread context or DSR context. The
// supplied buffer must already be in a form that can be transmitted
// directly out of the USB endpoint with no further processing
// (although it is necessary to extract the size information from the
// buffer).
//
// When the underlying USB transfer has completed the USB code will invoke
// usbs_eth_tx_callback(), usually in DSR context although possibly in
// thread context depending on the specific USB implementation. The
// underlying USB driver may have had to do some padding so the amount
// transferred may be slightly greater than requested.
 
static void
usbs_eth_tx_callback(void* usbs_callback_arg, int size)
{
usbs_eth* eth = (usbs_eth*) usbs_callback_arg;
CYG_ASSERT( (size < 0) || (size >= CYGNUM_USBS_ETH_MINTU), "returned size must be valid.");
(*eth->tx_callback_fn)(eth, eth->tx_callback_arg, size);
}
 
void
usbs_eth_start_tx(usbs_eth* eth, unsigned char* buf, void (*callback_fn)(usbs_eth*, void*, int), void* callback_arg)
{
int size;
cyg_bool address_ok = false;
static const unsigned char broadcast_mac[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
size = buf[0] + (buf[1] << 8);
CYG_ASSERT( (size < 0) || ((size >= CYGNUM_USBS_ETH_MIN_FRAME_SIZE) && (size <= CYGNUM_USBS_ETH_MAX_FRAME_SIZE)), \
"ethernet frame size constraints must be observed");
 
if ((0 == memcmp(buf + 2, eth->host_MAC, 6)) ||
(0 == memcmp(buf + 2, broadcast_mac, 6))) {
address_ok = true;
}
 
// The following checks involve data that can change as a result
// of control operations, so it is necessary to synchronize with
// those. The control operations will typically run at DSR level
// so a DSR lock has to be used.
cyg_drv_dsr_lock();
if (eth->host_up && (address_ok || eth->host_promiscuous)) {
eth->tx_callback_fn = callback_fn;
eth->tx_callback_arg = callback_arg;
eth->tx_endpoint->buffer = buf;
eth->tx_endpoint->buffer_size = size + 2;
eth->tx_endpoint->complete_fn = &usbs_eth_tx_callback;
eth->tx_endpoint->complete_data = (void*) eth;
(*(eth->tx_endpoint->start_tx_fn))(eth->tx_endpoint);
} else {
// Packets not intended for the host can be discarded quietly.
// A broken connection needs to be reported.
(*callback_fn)(eth, callback_arg, eth->host_up ? size : -EPIPE);
}
cyg_drv_dsr_unlock();
}
 
// Packet reception. This simply involves starting a transfer for
// up to the maximum ethernet frame size. The lower-level USB code
// will detect the end of the transfer. The exported function is
// usbs_eth_start_rx().
static void
usbs_eth_rx_callback(void* usbs_callback_arg, int size)
{
usbs_eth* eth = (usbs_eth*) usbs_callback_arg;
 
CYG_ASSERT( (size <= 0) || ((size >= CYGNUM_USBS_ETH_MINTU) && (size <= CYGNUM_USBS_ETH_MAXTU)), \
"ethernet frame size constraints must be observed");
(*eth->rx_callback_fn)(eth, eth->rx_callback_arg, size);
}
 
void
usbs_eth_start_rx(usbs_eth* eth, unsigned char* buf, void (*callback_fn)(usbs_eth*, void*, int), void* callback_arg)
{
eth->rx_callback_fn = callback_fn;
eth->rx_callback_arg = callback_arg;
 
cyg_drv_dsr_lock();
if (eth->host_up) {
eth->rx_endpoint->buffer = buf;
eth->rx_endpoint->buffer_size = CYGNUM_USBS_ETH_RXSIZE;
eth->rx_endpoint->complete_fn = &usbs_eth_rx_callback;
eth->rx_endpoint->complete_data = (void*) eth;
(*(eth->rx_endpoint->start_rx_fn))(eth->rx_endpoint);
} else {
CYG_ASSERT( (void*) 0 == eth->rx_pending_buf, "No RX operation should be in progress");
eth->rx_pending_buf = buf;
}
cyg_drv_dsr_unlock();
}
// ----------------------------------------------------------------------------
// Control operations. The host may send two types of application-specific
// control messages, one to get the MAC address and one to enable/disable
// promiscuous mode on the host side. This callback will typically be invoked
// in DSR context.
 
// These constants need to be shared somehow with the driver in ../host/,
// but if some variant of that driver becomes part of the Linux kernel
// then its sources must be self-contained with no dependencies on
// eCos sources or headers. Hence a duplicate definition for now.
#define USBS_ETH_CONTROL_GET_MAC_ADDRESS 0x01
#define USBS_ETH_CONTROL_SET_PROMISCUOUS_MODE 0x02
 
usbs_control_return
usbs_eth_class_control_handler(usbs_control_endpoint* endpoint, void* callback_data)
{
usbs_control_return result = USBS_CONTROL_RETURN_STALL;
usbs_eth* eth = (usbs_eth*) callback_data;
usb_devreq* devreq = (usb_devreq*) endpoint->control_buffer;
int size = (devreq->length_hi << 8) + devreq->length_lo;
 
CYG_ASSERT(endpoint == eth->control_endpoint, "USB ethernet control messages correctly routed");
 
if (USBS_ETH_CONTROL_GET_MAC_ADDRESS == devreq->request) {
// This should be an IN operation for at least six bytes.
if ((size >= 6) &&
(USB_DEVREQ_DIRECTION_IN == (devreq->type & USB_DEVREQ_DIRECTION_MASK))) {
 
endpoint->buffer = eth->host_MAC;
endpoint->buffer_size = 6;
result = USBS_CONTROL_RETURN_HANDLED;
}
// Otherwise drop through with a return value of STALL
} else if (USBS_ETH_CONTROL_SET_PROMISCUOUS_MODE == devreq->request) {
// The length should be 0, no more data is expected by either side.
if (0 == size) {
// The new promiscuity mode is encoded in value_lo;
eth->host_promiscuous = devreq->value_lo;
result = USBS_CONTROL_RETURN_HANDLED;
}
}
 
return result;
}
 
// State changes. As far as the ethernet code is concerned, if there
// is a change to CONFIGURED state then the device has come up,
// otherwise if there is a change from CONFIGURED state it has gone
// down. All other state changes are irrelevant.
void
usbs_eth_state_change_handler(usbs_control_endpoint* endpoint, void* callback_data, usbs_state_change change, int old_state)
{
usbs_eth* eth = (usbs_eth*) callback_data;
CYG_ASSERT(endpoint == eth->control_endpoint, "USB ethernet state changes correctly routed");
 
if (USBS_STATE_CHANGE_CONFIGURED == change) {
if (USBS_STATE_CONFIGURED != old_state) {
usbs_eth_enable(eth);
}
} else if ((USBS_STATE_CHANGE_RESUMED == change) && (USBS_STATE_CONFIGURED == (USBS_STATE_MASK & old_state))) {
usbs_eth_enable(eth);
} else if (eth->host_up) {
usbs_eth_disable(eth);
}
}
 
// Disabling the ethernet device means clearing the host_up flag.
// This will block future transmits and receives but not any
// that are currently underway.
void
usbs_eth_disable(usbs_eth* eth)
{
eth->host_up = false;
}
 
// Enabling the ethernet device means setting the host_up flag and
// possibly activating a pending rx operation.
void
usbs_eth_enable(usbs_eth* eth)
{
if (!eth->host_up) {
eth->host_up = true;
eth->host_promiscuous = false;
if ((void*) 0 != eth->rx_pending_buf) {
eth->rx_endpoint->buffer = eth->rx_pending_buf;
eth->rx_endpoint->buffer_size = CYGNUM_USBS_ETH_RXSIZE;
eth->rx_endpoint->complete_fn = &usbs_eth_rx_callback;
eth->rx_endpoint->complete_data = (void*) eth;
eth->rx_pending_buf = (void*) 0;
(*(eth->rx_endpoint->start_rx_fn))(eth->rx_endpoint);
}
}
}
 

powered by: WebSVN 2.1.0

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