]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/io/usb/eth/slave/v2_0/src/usbseth.c
Initial revision
[karo-tx-redboot.git] / packages / io / usb / eth / slave / v2_0 / src / usbseth.c
1 //==========================================================================
2 //
3 //      usbseth.c
4 //
5 //      Support for USB-ethernet devices, slave-side.
6 //
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.
12 //
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.
16 //
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
20 // for more details.
21 //
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.
25 //
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.
32 //
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.
35 //
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####
42 //
43 // Author(s):    bartv
44 // Contributors: bartv
45 // Date:         2000-10-04
46 //
47 //####DESCRIPTIONEND####
48 //
49 //==========================================================================
50
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>
58
59 #include <pkgconf/io_usb_slave_eth.h>
60
61 #define __ECOS 1
62 #include <cyg/io/usb/usbs_eth.h>
63
64 #ifdef CYGPKG_USBS_ETHDRV
65 #include <cyg/io/eth/netdev.h>
66 #include <cyg/io/eth/eth_drv.h>
67 #endif
68
69 // ----------------------------------------------------------------------------
70 // Static data.
71 //
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.
78 usbs_eth usbs_eth0;
79
80 // ----------------------------------------------------------------------------
81 // Initialization. This should be called explicitly by application code
82 // at an appropriate point in the system startup.
83 void
84 usbs_eth_init(usbs_eth* eth, usbs_control_endpoint* ctrl, usbs_rx_endpoint* rx, usbs_tx_endpoint* tx, unsigned char* mac)
85 {
86     eth->control_endpoint       = ctrl;
87     eth->rx_endpoint            = rx;
88     eth->tx_endpoint            = tx;
89     eth->host_up                = false;
90     eth->host_promiscuous       = false;
91     memcpy(eth->host_MAC, mac, 6);
92     eth->rx_pending_buf         = (unsigned char*) 0;
93     
94     // Install default handlers for some messages. Higher level code
95     // may override this.
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;
100     
101 #ifdef CYGPKG_USBS_ETHDRV
102     eth->ecos_up                = false;
103     eth->rx_active              = false;
104 # ifdef CYGFUN_USBS_ETHDRV_STATISTICS    
105     eth->interrupts             = 0;
106     eth->tx_count               = 0;
107     eth->rx_count               = 0;
108 # endif
109 # ifndef HAL_DCACHE_LINE_SIZE
110     eth->rx_bufptr              = eth->rx_buffer;
111 # else
112 # endif    
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;
119 #endif
120 }
121
122
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.
129
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
135 // buffer).
136 //
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.
142
143 static void
144 usbs_eth_tx_callback(void* usbs_callback_arg, int size)
145 {
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);
149 }
150
151 void
152 usbs_eth_start_tx(usbs_eth* eth, unsigned char* buf, void (*callback_fn)(usbs_eth*, void*, int), void* callback_arg)
153 {
154     int      size;
155     cyg_bool address_ok = false;
156     static const unsigned char broadcast_mac[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
157     
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");
161
162     if ((0 == memcmp(buf + 2, eth->host_MAC, 6)) ||
163         (0 == memcmp(buf + 2, broadcast_mac, 6))) {
164         address_ok = true;
165     }
166
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. 
171     
172     cyg_drv_dsr_lock();
173     if (eth->host_up && (address_ok || eth->host_promiscuous)) {
174         
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);
182         
183     } else {
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);
187     }
188     cyg_drv_dsr_unlock();
189 }
190
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(). 
195 static void
196 usbs_eth_rx_callback(void* usbs_callback_arg, int size)
197 {
198     usbs_eth*   eth = (usbs_eth*) usbs_callback_arg;
199
200     CYG_ASSERT( (size <= 0) || ((size >= CYGNUM_USBS_ETH_MINTU) && (size <= CYGNUM_USBS_ETH_MAXTU)), \
201                 "ethernet frame size constraints must be observed");
202     
203     (*eth->rx_callback_fn)(eth, eth->rx_callback_arg, size);
204 }
205
206 void
207 usbs_eth_start_rx(usbs_eth* eth, unsigned char* buf, void (*callback_fn)(usbs_eth*, void*, int), void* callback_arg)
208 {
209     eth->rx_callback_fn  = callback_fn;
210     eth->rx_callback_arg = callback_arg;
211
212     cyg_drv_dsr_lock();
213     if (eth->host_up) {
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);
219     } else {
220         CYG_ASSERT( (void*) 0 == eth->rx_pending_buf, "No RX operation should be in progress");
221         eth->rx_pending_buf = buf;
222     }
223     cyg_drv_dsr_unlock();
224 }
225  
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
230 // in DSR context.
231
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
238
239 usbs_control_return
240 usbs_eth_class_control_handler(usbs_control_endpoint* endpoint, void* callback_data)
241 {
242     usbs_control_return result = USBS_CONTROL_RETURN_STALL;
243     
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;
247
248     CYG_ASSERT(endpoint == eth->control_endpoint, "USB ethernet control messages correctly routed");
249
250     if (USBS_ETH_CONTROL_GET_MAC_ADDRESS == devreq->request) {
251         // This should be an IN operation for at least six bytes.
252         if ((size >= 6) &&
253             (USB_DEVREQ_DIRECTION_IN == (devreq->type & USB_DEVREQ_DIRECTION_MASK))) {
254
255             endpoint->buffer      = eth->host_MAC;
256             endpoint->buffer_size = 6;
257             result = USBS_CONTROL_RETURN_HANDLED;
258         }
259         // Otherwise drop through with a return value of STALL
260         
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.
263         if (0 == size) {
264             // The new promiscuity mode is encoded in value_lo;
265             eth->host_promiscuous = devreq->value_lo;
266             result = USBS_CONTROL_RETURN_HANDLED;
267         }
268     } 
269
270     return result;
271 }
272
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.
277 void
278 usbs_eth_state_change_handler(usbs_control_endpoint* endpoint, void* callback_data, usbs_state_change change, int old_state)
279 {
280     usbs_eth* eth       = (usbs_eth*) callback_data;
281     CYG_ASSERT(endpoint == eth->control_endpoint, "USB ethernet state changes correctly routed");
282
283     if (USBS_STATE_CHANGE_CONFIGURED == change) {
284         if (USBS_STATE_CONFIGURED != old_state) {
285             usbs_eth_enable(eth);
286         }
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);
291     }
292 }
293
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.
297 void
298 usbs_eth_disable(usbs_eth* eth)
299 {
300     eth->host_up = false;
301 }
302
303 // Enabling the ethernet device means setting the host_up flag and
304 // possibly activating a pending rx operation.
305 void
306 usbs_eth_enable(usbs_eth* eth)
307 {
308     if (!eth->host_up) {
309         eth->host_up            = true;
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);
318         }
319     }
320 }
321