1 |
27 |
unneback |
//==========================================================================
|
2 |
|
|
//
|
3 |
|
|
// src/stand_alone/eth_drv.c
|
4 |
|
|
//
|
5 |
|
|
// Stand-alone hardware independent networking support for RedBoot
|
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): gthomas
|
44 |
|
|
// Contributors: gthomas
|
45 |
|
|
// Date: 2000-07-14
|
46 |
|
|
// Purpose:
|
47 |
|
|
// Description:
|
48 |
|
|
//
|
49 |
|
|
// This code is part of RedBoot (tm).
|
50 |
|
|
//
|
51 |
|
|
//####DESCRIPTIONEND####
|
52 |
|
|
//
|
53 |
|
|
//==========================================================================
|
54 |
|
|
|
55 |
|
|
#include <pkgconf/system.h>
|
56 |
|
|
#include <pkgconf/io_eth_drivers.h>
|
57 |
|
|
|
58 |
|
|
#include <cyg/infra/cyg_type.h>
|
59 |
|
|
#include <cyg/hal/hal_arch.h>
|
60 |
|
|
#include <cyg/infra/diag.h>
|
61 |
|
|
#include <cyg/hal/drv_api.h>
|
62 |
|
|
#include <cyg/hal/hal_if.h>
|
63 |
|
|
#include <cyg/io/eth/eth_drv.h>
|
64 |
|
|
#include <cyg/io/eth/netdev.h>
|
65 |
|
|
#include <string.h>
|
66 |
|
|
|
67 |
|
|
// High-level ethernet driver
|
68 |
|
|
|
69 |
|
|
|
70 |
|
|
// Interfaces exported to drivers
|
71 |
|
|
|
72 |
|
|
static void eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr);
|
73 |
|
|
static void eth_drv_recv(struct eth_drv_sc *sc, int total_len);
|
74 |
|
|
static void eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status);
|
75 |
|
|
|
76 |
|
|
struct eth_drv_funs eth_drv_funs = {eth_drv_init, eth_drv_recv, eth_drv_tx_done};
|
77 |
|
|
|
78 |
|
|
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
|
79 |
|
|
int cyg_io_eth_net_debug = CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY;
|
80 |
|
|
// Usually just the header is enough, the body slows things too much.
|
81 |
|
|
#define DIAG_DUMP_BUF_HDR( a, b ) if (0 < cyg_io_eth_net_debug) diag_dump_buf( (a), (b) )
|
82 |
|
|
#define DIAG_DUMP_BUF_BDY( a, b ) if (1 < cyg_io_eth_net_debug) diag_dump_buf( (a), (b) )
|
83 |
|
|
#else
|
84 |
|
|
#define DIAG_DUMP_BUF_HDR( a, b )
|
85 |
|
|
#define DIAG_DUMP_BUF_BDY( a, b )
|
86 |
|
|
#endif
|
87 |
|
|
|
88 |
|
|
unsigned char __local_enet_addr[ETHER_ADDR_LEN+2];
|
89 |
|
|
struct eth_drv_sc *__local_enet_sc;
|
90 |
|
|
|
91 |
|
|
#ifdef CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS
|
92 |
|
|
//
|
93 |
|
|
// Horrible hack: In order to allow the stand-alone networking code to work
|
94 |
|
|
// alongside eCos (or any other stack), separate IP addresses must be used.
|
95 |
|
|
// When a packet arrives at the interface, we check to see which IP address
|
96 |
|
|
// it corresponds to and only pass it "up" if it's not for the stand-alone
|
97 |
|
|
// layer.
|
98 |
|
|
//
|
99 |
|
|
// tres degolas :-(
|
100 |
|
|
//
|
101 |
|
|
extern char __local_ip_addr[4];
|
102 |
|
|
#endif // PASS_PACKETS
|
103 |
|
|
|
104 |
|
|
#ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT
|
105 |
|
|
//
|
106 |
|
|
// Another horrible hack: In order to do the above passing on of
|
107 |
|
|
// application packets safely - and passing on completion events for
|
108 |
|
|
// pending transmissions (which is not conditional) - we must lock the
|
109 |
|
|
// application scheduler before calling into it. There are several reasons
|
110 |
|
|
// for this: a) We are likely running on a RedBoot special debug stack and
|
111 |
|
|
// so the application's stack checking fires; b) we could even get
|
112 |
|
|
// descheduled if the arrival of a packet causes a higher priority thread
|
113 |
|
|
// to awaken!
|
114 |
|
|
|
115 |
|
|
#include <cyg/hal/dbg-threads-api.h>
|
116 |
|
|
|
117 |
|
|
// Use with care! Local variable defined!
|
118 |
|
|
# define LOCK_APPLICATION_SCHEDULER() \
|
119 |
|
|
{ /* NEW BLOCK */ \
|
120 |
|
|
threadref currthread; \
|
121 |
|
|
int threadok; \
|
122 |
|
|
threadok = dbg_currthread( &currthread ); \
|
123 |
|
|
if ( threadok ) { \
|
124 |
|
|
threadok = dbg_scheduler( &currthread, 1, 1 ); /* lock */ \
|
125 |
|
|
}
|
126 |
|
|
|
127 |
|
|
# define UNLOCK_APPLICATION_SCHEDULER() \
|
128 |
|
|
if ( threadok ) { \
|
129 |
|
|
dbg_scheduler( &currthread, 0, 1 ); /* unlock */ \
|
130 |
|
|
} \
|
131 |
|
|
} /* END BLOCK */
|
132 |
|
|
|
133 |
|
|
#else
|
134 |
|
|
# define LOCK_APPLICATION_SCHEDULER() CYG_EMPTY_STATEMENT
|
135 |
|
|
# define UNLOCK_APPLICATION_SCHEDULER() CYG_EMPTY_STATEMENT
|
136 |
|
|
#endif // GDB_THREAD_SUPPORT
|
137 |
|
|
|
138 |
|
|
//
|
139 |
|
|
// Buffer 'get' support. The problem is that this function only gets
|
140 |
|
|
// called when some data is required, but packets may arrive on the device
|
141 |
|
|
// at any time. More particularly, on some devices when data arrive, all
|
142 |
|
|
// of that data needs to be consumed immediately or be lost. This process
|
143 |
|
|
// is driven by interrupts, which in the stand-along case are simulated by
|
144 |
|
|
// calling the "poll" interface.
|
145 |
|
|
//
|
146 |
|
|
// Thus there will be a pool of buffers, some free and some full, to try
|
147 |
|
|
// and manage this.
|
148 |
|
|
//
|
149 |
|
|
|
150 |
|
|
#define MAX_ETH_MSG 1540
|
151 |
|
|
#define NUM_ETH_MSG CYGNUM_IO_ETH_DRIVERS_NUM_PKT
|
152 |
|
|
|
153 |
|
|
struct eth_msg {
|
154 |
|
|
struct eth_msg *next, *prev;
|
155 |
|
|
int len; // Actual number of bytes in message
|
156 |
|
|
unsigned char data[MAX_ETH_MSG];
|
157 |
|
|
};
|
158 |
|
|
|
159 |
|
|
struct eth_msg_hdr {
|
160 |
|
|
struct eth_msg *first, *last;
|
161 |
|
|
};
|
162 |
|
|
|
163 |
|
|
static struct eth_msg_hdr eth_msg_free, eth_msg_full;
|
164 |
|
|
static struct eth_msg eth_msgs[NUM_ETH_MSG];
|
165 |
|
|
|
166 |
|
|
// Prototypes for functions used in this module
|
167 |
|
|
static void eth_drv_start(struct eth_drv_sc *sc);
|
168 |
|
|
|
169 |
|
|
// These functions are defined in RedBoot and control access to
|
170 |
|
|
// the "default" console.
|
171 |
|
|
extern int start_console(void);
|
172 |
|
|
extern void end_console(int);
|
173 |
|
|
|
174 |
|
|
// Simple queue management functions
|
175 |
|
|
|
176 |
|
|
static void
|
177 |
|
|
eth_drv_msg_put(struct eth_msg_hdr *hdr, struct eth_msg *msg)
|
178 |
|
|
{
|
179 |
|
|
if (hdr->first != (struct eth_msg *)hdr) {
|
180 |
|
|
// Something already in queue
|
181 |
|
|
hdr->last->next = msg;
|
182 |
|
|
msg->prev = hdr->last;
|
183 |
|
|
msg->next = (struct eth_msg *)hdr;
|
184 |
|
|
hdr->last = msg;
|
185 |
|
|
} else {
|
186 |
|
|
hdr->first = hdr->last = msg;
|
187 |
|
|
msg->next = msg->prev = (struct eth_msg *)hdr;
|
188 |
|
|
}
|
189 |
|
|
}
|
190 |
|
|
|
191 |
|
|
static struct eth_msg *
|
192 |
|
|
eth_drv_msg_get(struct eth_msg_hdr *hdr)
|
193 |
|
|
{
|
194 |
|
|
struct eth_msg *msg;
|
195 |
|
|
if (hdr->first != (struct eth_msg *)hdr) {
|
196 |
|
|
msg = hdr->first;
|
197 |
|
|
hdr->first = msg->next;
|
198 |
|
|
msg->next->prev = (struct eth_msg *)hdr;
|
199 |
|
|
} else {
|
200 |
|
|
msg = (struct eth_msg *)NULL;
|
201 |
|
|
}
|
202 |
|
|
return msg;
|
203 |
|
|
}
|
204 |
|
|
|
205 |
|
|
void
|
206 |
|
|
eth_drv_buffers_init(void)
|
207 |
|
|
{
|
208 |
|
|
int i;
|
209 |
|
|
struct eth_msg *msg = eth_msgs;
|
210 |
|
|
|
211 |
|
|
eth_msg_full.first = eth_msg_full.last = (struct eth_msg *)ð_msg_full;
|
212 |
|
|
eth_msg_free.first = eth_msg_free.last = (struct eth_msg *)ð_msg_free;
|
213 |
|
|
for (i = 0; i < NUM_ETH_MSG; i++, msg++) {
|
214 |
|
|
eth_drv_msg_put(ð_msg_free, msg);
|
215 |
|
|
}
|
216 |
|
|
}
|
217 |
|
|
|
218 |
|
|
//
|
219 |
|
|
// This function is called during system initialization to register a
|
220 |
|
|
// network interface with the system.
|
221 |
|
|
//
|
222 |
|
|
static void
|
223 |
|
|
eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr)
|
224 |
|
|
{
|
225 |
|
|
// enaddr == 0 -> hardware init was incomplete (no ESA)
|
226 |
|
|
if (enaddr != 0) {
|
227 |
|
|
// Set up hardware address
|
228 |
|
|
memcpy(&sc->sc_arpcom.esa, enaddr, ETHER_ADDR_LEN);
|
229 |
|
|
memcpy(__local_enet_addr, enaddr, ETHER_ADDR_LEN);
|
230 |
|
|
__local_enet_sc = sc;
|
231 |
|
|
eth_drv_start(sc);
|
232 |
|
|
}
|
233 |
|
|
}
|
234 |
|
|
|
235 |
|
|
#if 0 // Not currently used. Left in case it's needed in the future
|
236 |
|
|
//
|
237 |
|
|
// This [internal] function will be called to stop activity on an interface.
|
238 |
|
|
//
|
239 |
|
|
static void
|
240 |
|
|
eth_drv_stop(struct eth_drv_sc *sc)
|
241 |
|
|
{
|
242 |
|
|
(sc->funs->stop)(sc);
|
243 |
|
|
}
|
244 |
|
|
#endif
|
245 |
|
|
|
246 |
|
|
//
|
247 |
|
|
// This [internal] function will be called to start activity on an interface.
|
248 |
|
|
//
|
249 |
|
|
static void
|
250 |
|
|
eth_drv_start(struct eth_drv_sc *sc)
|
251 |
|
|
{
|
252 |
|
|
// Perform any hardware initialization
|
253 |
|
|
(sc->funs->start)(sc, (unsigned char *)&sc->sc_arpcom.esa, 0);
|
254 |
|
|
}
|
255 |
|
|
|
256 |
|
|
//
|
257 |
|
|
// Send a packet of data to the hardware
|
258 |
|
|
//
|
259 |
|
|
static int packet_sent;
|
260 |
|
|
|
261 |
|
|
void
|
262 |
|
|
eth_drv_write(char *eth_hdr, char *buf, int len)
|
263 |
|
|
{
|
264 |
|
|
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
|
265 |
|
|
struct eth_drv_sc *sc = __local_enet_sc;
|
266 |
|
|
int sg_len = 2;
|
267 |
|
|
void *dbg = CYGACC_CALL_IF_DBG_DATA();
|
268 |
|
|
int old_state;
|
269 |
|
|
int wait_time = 5; // Timeout before giving up
|
270 |
|
|
void *eth_drv_old = 0;
|
271 |
|
|
|
272 |
|
|
if (dbg) {
|
273 |
|
|
sc = (struct eth_drv_sc *)dbg; // Use control from installed driver
|
274 |
|
|
eth_drv_old = sc->funs->eth_drv_old;
|
275 |
|
|
if (eth_drv_old == 0) {
|
276 |
|
|
sc->funs->eth_drv_old = sc->funs->eth_drv;
|
277 |
|
|
sc->funs->eth_drv = ð_drv_funs; // Substitute stand-alone driver
|
278 |
|
|
old_state = sc->state;
|
279 |
|
|
if (!old_state & ETH_DRV_STATE_ACTIVE) {
|
280 |
|
|
// This interface not fully initialized, do it now
|
281 |
|
|
(sc->funs->start)(sc, (unsigned char *)&__local_enet_addr, 0);
|
282 |
|
|
sc->state |= ETH_DRV_STATE_ACTIVE;
|
283 |
|
|
}
|
284 |
|
|
}
|
285 |
|
|
}
|
286 |
|
|
|
287 |
|
|
while (!(sc->funs->can_send)(sc)) {
|
288 |
|
|
// Give driver a chance to service hardware
|
289 |
|
|
(sc->funs->poll)(sc);
|
290 |
|
|
CYGACC_CALL_IF_DELAY_US(2*100000);
|
291 |
|
|
if (--wait_time <= 0)
|
292 |
|
|
goto reset_and_out; // Give up on sending packet
|
293 |
|
|
}
|
294 |
|
|
|
295 |
|
|
sg_list[0].buf = (CYG_ADDRESS)eth_hdr;
|
296 |
|
|
sg_list[0].len = 14; // FIXME
|
297 |
|
|
sg_list[1].buf = (CYG_ADDRESS)buf;
|
298 |
|
|
sg_list[1].len = len;
|
299 |
|
|
packet_sent = 0;
|
300 |
|
|
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
|
301 |
|
|
if (cyg_io_eth_net_debug) {
|
302 |
|
|
int old_console;
|
303 |
|
|
old_console = start_console();
|
304 |
|
|
diag_printf("Ethernet send:\n");
|
305 |
|
|
DIAG_DUMP_BUF_HDR(eth_hdr, 14);
|
306 |
|
|
DIAG_DUMP_BUF_BDY(buf, len);
|
307 |
|
|
end_console(old_console);
|
308 |
|
|
}
|
309 |
|
|
#endif
|
310 |
|
|
|
311 |
|
|
(sc->funs->send)(sc, sg_list, sg_len, len+14, (CYG_ADDRWORD)&packet_sent);
|
312 |
|
|
|
313 |
|
|
wait_time = 500;
|
314 |
|
|
while (1) {
|
315 |
|
|
(sc->funs->poll)(sc);
|
316 |
|
|
|
317 |
|
|
if(packet_sent)
|
318 |
|
|
break;
|
319 |
|
|
|
320 |
|
|
CYGACC_CALL_IF_DELAY_US(2*1000);
|
321 |
|
|
if (--wait_time <= 0)
|
322 |
|
|
goto reset_and_out; // Give up on sending packet
|
323 |
|
|
}
|
324 |
|
|
reset_and_out:
|
325 |
|
|
if (dbg) {
|
326 |
|
|
// if (!old_state & ETH_DRV_STATE_ACTIVE) {
|
327 |
|
|
// // This interface was not fully initialized, shut it back down
|
328 |
|
|
// (sc->funs->stop)(sc);
|
329 |
|
|
// }
|
330 |
|
|
if (eth_drv_old == 0) {
|
331 |
|
|
sc->funs->eth_drv = sc->funs->eth_drv_old;
|
332 |
|
|
sc->funs->eth_drv_old = (struct eth_drv_funs *)0;
|
333 |
|
|
}
|
334 |
|
|
}
|
335 |
|
|
}
|
336 |
|
|
|
337 |
|
|
//
|
338 |
|
|
// This function is called from the hardware driver when an output operation
|
339 |
|
|
// has completed - i.e. the packet has been sent.
|
340 |
|
|
//
|
341 |
|
|
static void
|
342 |
|
|
eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status)
|
343 |
|
|
{
|
344 |
|
|
CYGARC_HAL_SAVE_GP();
|
345 |
|
|
if ((int *)key == &packet_sent) {
|
346 |
|
|
*(int *)key = 1;
|
347 |
|
|
} else {
|
348 |
|
|
// It's possible that this acknowledgement is for a different
|
349 |
|
|
// [logical] driver. Try and pass it on.
|
350 |
|
|
#if defined(CYGDBG_IO_ETH_DRIVERS_DEBUG) && \
|
351 |
|
|
(CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY >=2 )
|
352 |
|
|
// Note: not normally enabled - too verbose
|
353 |
|
|
if (cyg_io_eth_net_debug > 1) {
|
354 |
|
|
int old_console;
|
355 |
|
|
old_console = start_console();
|
356 |
|
|
diag_printf("tx_done for other key: %x\n", key);
|
357 |
|
|
end_console(old_console);
|
358 |
|
|
}
|
359 |
|
|
#endif
|
360 |
|
|
LOCK_APPLICATION_SCHEDULER();
|
361 |
|
|
if (sc->funs->eth_drv_old) {
|
362 |
|
|
(sc->funs->eth_drv_old->tx_done)(sc, key, status);
|
363 |
|
|
} else {
|
364 |
|
|
(sc->funs->eth_drv->tx_done)(sc, key, status);
|
365 |
|
|
}
|
366 |
|
|
UNLOCK_APPLICATION_SCHEDULER();
|
367 |
|
|
}
|
368 |
|
|
CYGARC_HAL_RESTORE_GP();
|
369 |
|
|
}
|
370 |
|
|
|
371 |
|
|
//
|
372 |
|
|
// Receive one packet of data from the hardware, if available
|
373 |
|
|
//
|
374 |
|
|
int
|
375 |
|
|
eth_drv_read(char *eth_hdr, char *buf, int len)
|
376 |
|
|
{
|
377 |
|
|
struct eth_drv_sc *sc = __local_enet_sc;
|
378 |
|
|
struct eth_msg *msg;
|
379 |
|
|
int res;
|
380 |
|
|
void *dbg = CYGACC_CALL_IF_DBG_DATA();
|
381 |
|
|
int old_state;
|
382 |
|
|
void *eth_drv_old = 0;
|
383 |
|
|
|
384 |
|
|
if (dbg) {
|
385 |
|
|
sc = (struct eth_drv_sc *)dbg; // Use control from installed driver
|
386 |
|
|
eth_drv_old = sc->funs->eth_drv_old;
|
387 |
|
|
if (eth_drv_old == 0) {
|
388 |
|
|
sc->funs->eth_drv_old = sc->funs->eth_drv;
|
389 |
|
|
sc->funs->eth_drv = ð_drv_funs; // Substitute stand-alone driver
|
390 |
|
|
old_state = sc->state;
|
391 |
|
|
if (!old_state & ETH_DRV_STATE_ACTIVE) {
|
392 |
|
|
// This interface not fully initialized, do it now
|
393 |
|
|
(sc->funs->start)(sc, (unsigned char *)&__local_enet_addr, 0);
|
394 |
|
|
sc->state |= ETH_DRV_STATE_ACTIVE;
|
395 |
|
|
}
|
396 |
|
|
}
|
397 |
|
|
}
|
398 |
|
|
(sc->funs->poll)(sc); // Give the driver a chance to fetch packets
|
399 |
|
|
msg = eth_drv_msg_get(ð_msg_full);
|
400 |
|
|
if (msg && len >= msg->len - 14) {
|
401 |
|
|
memcpy(eth_hdr, msg->data, 14);
|
402 |
|
|
memcpy(buf, &msg->data[14], msg->len-14);
|
403 |
|
|
res = msg->len;
|
404 |
|
|
} else {
|
405 |
|
|
res = 0;
|
406 |
|
|
}
|
407 |
|
|
if (msg) {
|
408 |
|
|
eth_drv_msg_put(ð_msg_free, msg);
|
409 |
|
|
}
|
410 |
|
|
|
411 |
|
|
if (dbg) {
|
412 |
|
|
if (eth_drv_old == 0) {
|
413 |
|
|
sc->funs->eth_drv = sc->funs->eth_drv_old;
|
414 |
|
|
sc->funs->eth_drv_old = (struct eth_drv_funs *)0;
|
415 |
|
|
}
|
416 |
|
|
// if (!old_state & ETH_DRV_STATE_ACTIVE) {
|
417 |
|
|
// // This interface was not fully initialized, shut it back down
|
418 |
|
|
// (sc->funs->stop)(sc);
|
419 |
|
|
// }
|
420 |
|
|
}
|
421 |
|
|
return res;
|
422 |
|
|
}
|
423 |
|
|
|
424 |
|
|
#ifdef CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS
|
425 |
|
|
//
|
426 |
|
|
// This function is called to copy a message up to the next level.
|
427 |
|
|
// It is only used when this driver has usurped the processing of
|
428 |
|
|
// network functions.
|
429 |
|
|
//
|
430 |
|
|
static unsigned char *eth_drv_copy_recv_buf;
|
431 |
|
|
static void
|
432 |
|
|
eth_drv_copy_recv(struct eth_drv_sc *sc,
|
433 |
|
|
struct eth_drv_sg *sg_list,
|
434 |
|
|
int sg_len)
|
435 |
|
|
{
|
436 |
|
|
int i;
|
437 |
|
|
unsigned char *ppp;
|
438 |
|
|
CYGARC_HAL_SAVE_GP();
|
439 |
|
|
ppp = eth_drv_copy_recv_buf; // Be safe against being called again by accident
|
440 |
|
|
for (i = 0; i < sg_len; i++) {
|
441 |
|
|
if ( sg_list[i].buf ) // Be safe against discarding calls
|
442 |
|
|
memcpy((unsigned char *)sg_list[i].buf,
|
443 |
|
|
ppp, sg_list[i].len);
|
444 |
|
|
ppp += sg_list[i].len;
|
445 |
|
|
}
|
446 |
|
|
CYGARC_HAL_RESTORE_GP();
|
447 |
|
|
}
|
448 |
|
|
#endif
|
449 |
|
|
|
450 |
|
|
//
|
451 |
|
|
// This function is called from a hardware driver to indicate that an input
|
452 |
|
|
// packet has arrived. The routine will set up appropriate network resources
|
453 |
|
|
// to hold the data and call back into the driver to retrieve the data.
|
454 |
|
|
//
|
455 |
|
|
static void
|
456 |
|
|
eth_drv_recv(struct eth_drv_sc *sc, int total_len)
|
457 |
|
|
{
|
458 |
|
|
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
|
459 |
|
|
int sg_len = 0;
|
460 |
|
|
struct eth_msg *msg;
|
461 |
|
|
unsigned char *buf;
|
462 |
|
|
CYGARC_HAL_SAVE_GP();
|
463 |
|
|
|
464 |
|
|
if ((total_len > MAX_ETH_MSG) || (total_len < 0)) {
|
465 |
|
|
#ifdef CYGSEM_IO_ETH_DRIVERS_WARN
|
466 |
|
|
int old_console;
|
467 |
|
|
old_console = start_console();
|
468 |
|
|
diag_printf("%s: packet of %d bytes truncated\n", __FUNCTION__, total_len);
|
469 |
|
|
end_console(old_console);
|
470 |
|
|
#endif
|
471 |
|
|
total_len = MAX_ETH_MSG;
|
472 |
|
|
}
|
473 |
|
|
msg = eth_drv_msg_get(ð_msg_free);
|
474 |
|
|
if (msg) {
|
475 |
|
|
buf = msg->data;
|
476 |
|
|
} else {
|
477 |
|
|
#ifdef CYGSEM_IO_ETH_DRIVERS_WARN
|
478 |
|
|
int old_console;
|
479 |
|
|
old_console = start_console();
|
480 |
|
|
diag_printf("%s: packet of %d bytes dropped\n", __FUNCTION__, total_len);
|
481 |
|
|
end_console(old_console);
|
482 |
|
|
#endif
|
483 |
|
|
buf = (unsigned char *)0; // Drivers know this means "the bit bucket"
|
484 |
|
|
}
|
485 |
|
|
sg_list[0].buf = (CYG_ADDRESS)buf;
|
486 |
|
|
sg_list[0].len = total_len;
|
487 |
|
|
sg_len = 1;
|
488 |
|
|
|
489 |
|
|
(sc->funs->recv)(sc, sg_list, sg_len);
|
490 |
|
|
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
|
491 |
|
|
if (cyg_io_eth_net_debug) {
|
492 |
|
|
int old_console;
|
493 |
|
|
old_console = start_console();
|
494 |
|
|
diag_printf("Ethernet recv:\n");
|
495 |
|
|
if ( buf ) {
|
496 |
|
|
DIAG_DUMP_BUF_HDR(buf, 14);
|
497 |
|
|
DIAG_DUMP_BUF_BDY(buf+14, total_len-14);
|
498 |
|
|
}
|
499 |
|
|
else
|
500 |
|
|
diag_printf(" ...NULL buffer.\n");
|
501 |
|
|
end_console(old_console);
|
502 |
|
|
}
|
503 |
|
|
#endif
|
504 |
|
|
#ifdef CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS
|
505 |
|
|
if ((unsigned char *)0 != buf && // Only pass on a packet we actually got!
|
506 |
|
|
sc->funs->eth_drv_old != (struct eth_drv_funs *)0) {
|
507 |
|
|
void (*hold_recv)(struct eth_drv_sc *sc,
|
508 |
|
|
struct eth_drv_sg *sg_list,
|
509 |
|
|
int sg_len);
|
510 |
|
|
// See if this packet was for us. If not, pass it upwards
|
511 |
|
|
// This is a major layering violation!!
|
512 |
|
|
if (memcmp(&__local_ip_addr, &buf[14+16], 4)) {
|
513 |
|
|
hold_recv = sc->funs->recv;
|
514 |
|
|
sc->funs->recv = eth_drv_copy_recv;
|
515 |
|
|
eth_drv_copy_recv_buf = buf;
|
516 |
|
|
// This calls into the 'other' driver, giving it a chance to
|
517 |
|
|
// do something with this data (since it wasn't for us)
|
518 |
|
|
LOCK_APPLICATION_SCHEDULER();
|
519 |
|
|
(sc->funs->eth_drv_old->recv)(sc, total_len);
|
520 |
|
|
UNLOCK_APPLICATION_SCHEDULER();
|
521 |
|
|
sc->funs->recv = hold_recv;
|
522 |
|
|
}
|
523 |
|
|
}
|
524 |
|
|
#endif
|
525 |
|
|
if (msg) {
|
526 |
|
|
msg->len = total_len;
|
527 |
|
|
eth_drv_msg_put(ð_msg_full, msg);
|
528 |
|
|
#ifdef CYGSEM_IO_ETH_DRIVERS_WARN
|
529 |
|
|
// there was an else with a dump_buf() here but it's
|
530 |
|
|
// meaningless; sg_list[0].buf is NULL!
|
531 |
|
|
#endif
|
532 |
|
|
}
|
533 |
|
|
CYGARC_HAL_RESTORE_GP();
|
534 |
|
|
}
|
535 |
|
|
|
536 |
|
|
//
|
537 |
|
|
// Determine the interrupt vector used by an interface
|
538 |
|
|
//
|
539 |
|
|
int
|
540 |
|
|
eth_drv_int_vector(void)
|
541 |
|
|
{
|
542 |
|
|
struct eth_drv_sc *sc = __local_enet_sc;
|
543 |
|
|
return sc->funs->int_vector(sc);
|
544 |
|
|
}
|
545 |
|
|
|
546 |
|
|
|
547 |
|
|
void eth_drv_dsr(cyg_vector_t vector,
|
548 |
|
|
cyg_ucount32 count,
|
549 |
|
|
cyg_addrword_t data)
|
550 |
|
|
{
|
551 |
|
|
diag_printf("eth_drv_dsr should not be called: vector %d, data %x\n",
|
552 |
|
|
vector, data );
|
553 |
|
|
}
|
554 |
|
|
|
555 |
|
|
|
556 |
|
|
// EOF src/stand_alone/eth_drv.c
|