📄 usbsethdrv.c
字号:
//==========================================================================
//
// 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -