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