📄 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 boolscatter(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 boolgather(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 boolusbs_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 voidusbs_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 + -