1 //==========================================================================
5 // Support for USB-ethernet devices, slave-side.
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //==========================================================================
41 //#####DESCRIPTIONBEGIN####
44 // Contributors: bartv
47 //####DESCRIPTIONEND####
49 //==========================================================================
51 #include <cyg/infra/cyg_type.h>
52 #include <cyg/infra/cyg_ass.h>
53 #include <cyg/infra/cyg_trac.h>
54 #include <cyg/infra/diag.h>
55 #include <cyg/hal/hal_arch.h>
56 #include <cyg/infra/diag.h>
57 #include <cyg/hal/drv_api.h>
59 #include <pkgconf/io_usb_slave_eth.h>
62 #include <cyg/io/usb/usbs_eth.h>
64 #ifdef CYGPKG_USBS_ETHDRV
65 #include <cyg/io/eth/netdev.h>
66 #include <cyg/io/eth/eth_drv.h>
69 // ----------------------------------------------------------------------------
72 // usbs_eth0 contains the per-device data, both the low-level data
73 // such as which endpoints to use and the network-driver data such as
74 // SNMP statistics. If this package is loaded then the assumption
75 // is that there should be at least one USB-ethernet device. Additional
76 // ones can be instantiated in application code if necessary. A call
77 // to usbs_eth_init() is required for initialization.
80 // ----------------------------------------------------------------------------
81 // Initialization. This should be called explicitly by application code
82 // at an appropriate point in the system startup.
84 usbs_eth_init(usbs_eth* eth, usbs_control_endpoint* ctrl, usbs_rx_endpoint* rx, usbs_tx_endpoint* tx, unsigned char* mac)
86 eth->control_endpoint = ctrl;
87 eth->rx_endpoint = rx;
88 eth->tx_endpoint = tx;
90 eth->host_promiscuous = false;
91 memcpy(eth->host_MAC, mac, 6);
92 eth->rx_pending_buf = (unsigned char*) 0;
94 // Install default handlers for some messages. Higher level code
96 ctrl->state_change_fn = &usbs_eth_state_change_handler;
97 ctrl->state_change_data = (void*) eth;
98 ctrl->class_control_fn = &usbs_eth_class_control_handler;
99 ctrl->class_control_data = (void*) eth;
101 #ifdef CYGPKG_USBS_ETHDRV
102 eth->ecos_up = false;
103 eth->rx_active = false;
104 # ifdef CYGFUN_USBS_ETHDRV_STATISTICS
109 # ifndef HAL_DCACHE_LINE_SIZE
110 eth->rx_bufptr = eth->rx_buffer;
113 eth->rx_bufptr = (unsigned char*) ((((cyg_uint32)eth->rx_buffer) + HAL_DCACHE_LINE_SIZE - 1)
114 & ~(HAL_DCACHE_LINE_SIZE - 1));
115 eth->rx_buffer_full = false;
116 eth->tx_in_send = false;
117 eth->tx_buffer_full = false;
118 eth->tx_done = false;
123 // ----------------------------------------------------------------------------
124 // Generic transmit and receive operations. These can be called
125 // explicitly by application code, or implicitly via the eCos ethernet
126 // device driver code in usbsethdrv.c. These two modes of operation
127 // should not be mixed since the routines do not perform any
128 // synchronization themselves, instead they rely on higher level code.
130 // Packet transmission. The exported function is usbs_eth_start_tx(),
131 // which can be invoked from thread context or DSR context. The
132 // supplied buffer must already be in a form that can be transmitted
133 // directly out of the USB endpoint with no further processing
134 // (although it is necessary to extract the size information from the
137 // When the underlying USB transfer has completed the USB code will invoke
138 // usbs_eth_tx_callback(), usually in DSR context although possibly in
139 // thread context depending on the specific USB implementation. The
140 // underlying USB driver may have had to do some padding so the amount
141 // transferred may be slightly greater than requested.
144 usbs_eth_tx_callback(void* usbs_callback_arg, int size)
146 usbs_eth* eth = (usbs_eth*) usbs_callback_arg;
147 CYG_ASSERT( (size < 0) || (size >= CYGNUM_USBS_ETH_MINTU), "returned size must be valid.");
148 (*eth->tx_callback_fn)(eth, eth->tx_callback_arg, size);
152 usbs_eth_start_tx(usbs_eth* eth, unsigned char* buf, void (*callback_fn)(usbs_eth*, void*, int), void* callback_arg)
155 cyg_bool address_ok = false;
156 static const unsigned char broadcast_mac[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
158 size = buf[0] + (buf[1] << 8);
159 CYG_ASSERT( (size < 0) || ((size >= CYGNUM_USBS_ETH_MIN_FRAME_SIZE) && (size <= CYGNUM_USBS_ETH_MAX_FRAME_SIZE)), \
160 "ethernet frame size constraints must be observed");
162 if ((0 == memcmp(buf + 2, eth->host_MAC, 6)) ||
163 (0 == memcmp(buf + 2, broadcast_mac, 6))) {
167 // The following checks involve data that can change as a result
168 // of control operations, so it is necessary to synchronize with
169 // those. The control operations will typically run at DSR level
170 // so a DSR lock has to be used.
173 if (eth->host_up && (address_ok || eth->host_promiscuous)) {
175 eth->tx_callback_fn = callback_fn;
176 eth->tx_callback_arg = callback_arg;
177 eth->tx_endpoint->buffer = buf;
178 eth->tx_endpoint->buffer_size = size + 2;
179 eth->tx_endpoint->complete_fn = &usbs_eth_tx_callback;
180 eth->tx_endpoint->complete_data = (void*) eth;
181 (*(eth->tx_endpoint->start_tx_fn))(eth->tx_endpoint);
184 // Packets not intended for the host can be discarded quietly.
185 // A broken connection needs to be reported.
186 (*callback_fn)(eth, callback_arg, eth->host_up ? size : -EPIPE);
188 cyg_drv_dsr_unlock();
191 // Packet reception. This simply involves starting a transfer for
192 // up to the maximum ethernet frame size. The lower-level USB code
193 // will detect the end of the transfer. The exported function is
194 // usbs_eth_start_rx().
196 usbs_eth_rx_callback(void* usbs_callback_arg, int size)
198 usbs_eth* eth = (usbs_eth*) usbs_callback_arg;
200 CYG_ASSERT( (size <= 0) || ((size >= CYGNUM_USBS_ETH_MINTU) && (size <= CYGNUM_USBS_ETH_MAXTU)), \
201 "ethernet frame size constraints must be observed");
203 (*eth->rx_callback_fn)(eth, eth->rx_callback_arg, size);
207 usbs_eth_start_rx(usbs_eth* eth, unsigned char* buf, void (*callback_fn)(usbs_eth*, void*, int), void* callback_arg)
209 eth->rx_callback_fn = callback_fn;
210 eth->rx_callback_arg = callback_arg;
214 eth->rx_endpoint->buffer = buf;
215 eth->rx_endpoint->buffer_size = CYGNUM_USBS_ETH_RXSIZE;
216 eth->rx_endpoint->complete_fn = &usbs_eth_rx_callback;
217 eth->rx_endpoint->complete_data = (void*) eth;
218 (*(eth->rx_endpoint->start_rx_fn))(eth->rx_endpoint);
220 CYG_ASSERT( (void*) 0 == eth->rx_pending_buf, "No RX operation should be in progress");
221 eth->rx_pending_buf = buf;
223 cyg_drv_dsr_unlock();
226 // ----------------------------------------------------------------------------
227 // Control operations. The host may send two types of application-specific
228 // control messages, one to get the MAC address and one to enable/disable
229 // promiscuous mode on the host side. This callback will typically be invoked
232 // These constants need to be shared somehow with the driver in ../host/,
233 // but if some variant of that driver becomes part of the Linux kernel
234 // then its sources must be self-contained with no dependencies on
235 // eCos sources or headers. Hence a duplicate definition for now.
236 #define USBS_ETH_CONTROL_GET_MAC_ADDRESS 0x01
237 #define USBS_ETH_CONTROL_SET_PROMISCUOUS_MODE 0x02
240 usbs_eth_class_control_handler(usbs_control_endpoint* endpoint, void* callback_data)
242 usbs_control_return result = USBS_CONTROL_RETURN_STALL;
244 usbs_eth* eth = (usbs_eth*) callback_data;
245 usb_devreq* devreq = (usb_devreq*) endpoint->control_buffer;
246 int size = (devreq->length_hi << 8) + devreq->length_lo;
248 CYG_ASSERT(endpoint == eth->control_endpoint, "USB ethernet control messages correctly routed");
250 if (USBS_ETH_CONTROL_GET_MAC_ADDRESS == devreq->request) {
251 // This should be an IN operation for at least six bytes.
253 (USB_DEVREQ_DIRECTION_IN == (devreq->type & USB_DEVREQ_DIRECTION_MASK))) {
255 endpoint->buffer = eth->host_MAC;
256 endpoint->buffer_size = 6;
257 result = USBS_CONTROL_RETURN_HANDLED;
259 // Otherwise drop through with a return value of STALL
261 } else if (USBS_ETH_CONTROL_SET_PROMISCUOUS_MODE == devreq->request) {
262 // The length should be 0, no more data is expected by either side.
264 // The new promiscuity mode is encoded in value_lo;
265 eth->host_promiscuous = devreq->value_lo;
266 result = USBS_CONTROL_RETURN_HANDLED;
273 // State changes. As far as the ethernet code is concerned, if there
274 // is a change to CONFIGURED state then the device has come up,
275 // otherwise if there is a change from CONFIGURED state it has gone
276 // down. All other state changes are irrelevant.
278 usbs_eth_state_change_handler(usbs_control_endpoint* endpoint, void* callback_data, usbs_state_change change, int old_state)
280 usbs_eth* eth = (usbs_eth*) callback_data;
281 CYG_ASSERT(endpoint == eth->control_endpoint, "USB ethernet state changes correctly routed");
283 if (USBS_STATE_CHANGE_CONFIGURED == change) {
284 if (USBS_STATE_CONFIGURED != old_state) {
285 usbs_eth_enable(eth);
287 } else if ((USBS_STATE_CHANGE_RESUMED == change) && (USBS_STATE_CONFIGURED == (USBS_STATE_MASK & old_state))) {
288 usbs_eth_enable(eth);
289 } else if (eth->host_up) {
290 usbs_eth_disable(eth);
294 // Disabling the ethernet device means clearing the host_up flag.
295 // This will block future transmits and receives but not any
296 // that are currently underway.
298 usbs_eth_disable(usbs_eth* eth)
300 eth->host_up = false;
303 // Enabling the ethernet device means setting the host_up flag and
304 // possibly activating a pending rx operation.
306 usbs_eth_enable(usbs_eth* eth)
310 eth->host_promiscuous = false;
311 if ((void*) 0 != eth->rx_pending_buf) {
312 eth->rx_endpoint->buffer = eth->rx_pending_buf;
313 eth->rx_endpoint->buffer_size = CYGNUM_USBS_ETH_RXSIZE;
314 eth->rx_endpoint->complete_fn = &usbs_eth_rx_callback;
315 eth->rx_endpoint->complete_data = (void*) eth;
316 eth->rx_pending_buf = (void*) 0;
317 (*(eth->rx_endpoint->start_rx_fn))(eth->rx_endpoint);