1 //==========================================================================
3 // src/stand_alone/eth_drv.c
5 // Stand-alone hardware independent networking support for RedBoot
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
12 // Copyright (C) 2003 Gary Thomas
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
45 // Contributors: gthomas
50 // This code is part of RedBoot (tm).
52 //####DESCRIPTIONEND####
54 //==========================================================================
56 #include <pkgconf/system.h>
57 #include <pkgconf/io_eth_drivers.h>
59 #include <cyg/infra/cyg_type.h>
60 #include <cyg/hal/hal_arch.h>
61 #include <cyg/infra/diag.h>
62 #include <cyg/hal/drv_api.h>
63 #include <cyg/hal/hal_if.h>
64 #include <cyg/io/eth/eth_drv.h>
65 #include <cyg/io/eth/netdev.h>
68 // High-level ethernet driver
71 // Interfaces exported to drivers
73 static void eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr);
74 static void eth_drv_recv(struct eth_drv_sc *sc, int total_len);
75 static void eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status);
77 struct eth_drv_funs eth_drv_funs = {eth_drv_init, eth_drv_recv, eth_drv_tx_done};
79 #ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
80 int cyg_io_eth_net_debug = CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY;
81 // Usually just the header is enough, the body slows things too much.
82 #define DIAG_DUMP_BUF_HDR( a, b ) if (0 < cyg_io_eth_net_debug) diag_dump_buf( (a), (b) )
83 #define DIAG_DUMP_BUF_BDY( a, b ) if (1 < cyg_io_eth_net_debug) diag_dump_buf( (a), (b) )
85 #define DIAG_DUMP_BUF_HDR( a, b )
86 #define DIAG_DUMP_BUF_BDY( a, b )
89 struct eth_drv_sc *__local_enet_sc = NULL;
91 #ifdef CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS
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
101 extern char __local_ip_addr[4];
102 #endif // PASS_PACKETS
104 #ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT
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
115 #include <cyg/hal/dbg-threads-api.h>
117 // Use with care! Local variable defined!
118 # define LOCK_APPLICATION_SCHEDULER() \
120 threadref currthread; \
122 threadok = dbg_currthread( &currthread ); \
124 threadok = dbg_scheduler( &currthread, 1, 1 ); /* lock */ \
127 # define UNLOCK_APPLICATION_SCHEDULER() \
129 dbg_scheduler( &currthread, 0, 1 ); /* unlock */ \
134 # define LOCK_APPLICATION_SCHEDULER() CYG_EMPTY_STATEMENT
135 # define UNLOCK_APPLICATION_SCHEDULER() CYG_EMPTY_STATEMENT
136 #endif // GDB_THREAD_SUPPORT
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.
146 // Thus there will be a pool of buffers, some free and some full, to try
150 #define MAX_ETH_MSG 1540
151 #define NUM_ETH_MSG CYGNUM_IO_ETH_DRIVERS_NUM_PKT
154 struct eth_msg *next, *prev;
155 int len; // Actual number of bytes in message
156 unsigned char data[MAX_ETH_MSG];
160 struct eth_msg *first, *last;
163 static struct eth_msg_hdr eth_msg_free, eth_msg_full;
164 static struct eth_msg eth_msgs[NUM_ETH_MSG];
166 // Prototypes for functions used in this module
167 static void eth_drv_start(struct eth_drv_sc *sc);
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);
174 // Simple queue management functions
177 eth_drv_msg_put(struct eth_msg_hdr *hdr, struct eth_msg *msg)
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;
186 hdr->first = hdr->last = msg;
187 msg->next = msg->prev = (struct eth_msg *)hdr;
191 static struct eth_msg *
192 eth_drv_msg_get(struct eth_msg_hdr *hdr)
195 if (hdr->first != (struct eth_msg *)hdr) {
197 hdr->first = msg->next;
198 msg->next->prev = (struct eth_msg *)hdr;
200 msg = (struct eth_msg *)NULL;
206 eth_drv_buffers_init(void)
209 struct eth_msg *msg = eth_msgs;
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);
219 // This function is called during system initialization to register a
220 // network interface with the system.
223 eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr)
225 // enaddr == 0 -> hardware init was incomplete (no ESA)
227 // Set up hardware address
228 memcpy(&sc->sc_arpcom.esa, enaddr, ETHER_ADDR_LEN);
229 __local_enet_sc = sc;
235 // This [internal] function will be called to stop activity on an interface.
240 struct eth_drv_sc *sc = __local_enet_sc;
243 (sc->funs->stop)(sc);
247 // This [internal] function will be called to start activity on an interface.
250 eth_drv_start(struct eth_drv_sc *sc)
252 // Perform any hardware initialization
253 (sc->funs->start)(sc, (unsigned char *)&sc->sc_arpcom.esa, 0);
257 // Send a packet of data to the hardware
259 static int packet_sent;
262 eth_drv_write(char *eth_hdr, char *buf, int len)
264 struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
265 struct eth_drv_sc *sc = __local_enet_sc;
267 void *dbg = CYGACC_CALL_IF_DBG_DATA();
269 int wait_time = 5; // Timeout before giving up
270 void *eth_drv_old = 0;
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 *)sc->sc_arpcom.esa, 0);
282 sc->state |= ETH_DRV_STATE_ACTIVE;
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
295 sg_list[0].buf = (CYG_ADDRESS)eth_hdr;
296 sg_list[0].len = (6+6+2); // FIXME
297 sg_list[1].buf = (CYG_ADDRESS)buf;
298 sg_list[1].len = len;
300 #ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
301 if (cyg_io_eth_net_debug) {
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);
311 (sc->funs->send)(sc, sg_list, sg_len, len+14, (CYG_ADDRWORD)&packet_sent);
315 (sc->funs->poll)(sc);
320 CYGACC_CALL_IF_DELAY_US(2*10);
321 if (--wait_time <= 0)
322 goto reset_and_out; // Give up on sending packet
326 // if (!(old_state & ETH_DRV_STATE_ACTIVE)) {
327 // // This interface was not fully initialized, shut it back down
328 // (sc->funs->stop)(sc);
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;
338 // This function is called from the hardware driver when an output operation
339 // has completed - i.e. the packet has been sent.
342 eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRWORD key, int status)
344 CYGARC_HAL_SAVE_GP();
345 if ((int *)key == &packet_sent) {
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) {
355 old_console = start_console();
356 diag_printf("tx_done for other key: %x\n", key);
357 end_console(old_console);
360 LOCK_APPLICATION_SCHEDULER();
361 if (sc->funs->eth_drv_old) {
362 (sc->funs->eth_drv_old->tx_done)(sc, key, status);
364 (sc->funs->eth_drv->tx_done)(sc, key, status);
366 UNLOCK_APPLICATION_SCHEDULER();
368 CYGARC_HAL_RESTORE_GP();
372 // Receive one packet of data from the hardware, if available
375 eth_drv_read(char *eth_hdr, char *buf, int len)
377 struct eth_drv_sc *sc = __local_enet_sc;
380 void *dbg = CYGACC_CALL_IF_DBG_DATA();
382 void *eth_drv_old = 0;
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 *)sc->sc_arpcom.esa, 0);
394 sc->state |= ETH_DRV_STATE_ACTIVE;
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);
408 eth_drv_msg_put(ð_msg_free, msg);
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;
416 // if (!old_state & ETH_DRV_STATE_ACTIVE) {
417 // // This interface was not fully initialized, shut it back down
418 // (sc->funs->stop)(sc);
424 #ifdef CYGSEM_IO_ETH_DRIVERS_PASS_PACKETS
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.
430 static unsigned char *eth_drv_copy_recv_buf;
432 eth_drv_copy_recv(struct eth_drv_sc *sc,
433 struct eth_drv_sg *sg_list,
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;
446 CYGARC_HAL_RESTORE_GP();
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.
456 eth_drv_recv(struct eth_drv_sc *sc, int total_len)
458 struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
462 CYGARC_HAL_SAVE_GP();
464 if ((total_len > MAX_ETH_MSG) || (total_len < 0)) {
465 #ifdef CYGSEM_IO_ETH_DRIVERS_WARN
467 old_console = start_console();
468 diag_printf("%s: packet of %d bytes truncated\n", __FUNCTION__, total_len);
469 end_console(old_console);
471 total_len = MAX_ETH_MSG;
473 msg = eth_drv_msg_get(ð_msg_free);
477 #ifdef CYGSEM_IO_ETH_DRIVERS_WARN
479 old_console = start_console();
480 diag_printf("%s: packet of %d bytes dropped\n", __FUNCTION__, total_len);
481 end_console(old_console);
483 buf = (unsigned char *)0; // Drivers know this means "the bit bucket"
485 sg_list[0].buf = (CYG_ADDRESS)buf;
486 sg_list[0].len = total_len;
489 (sc->funs->recv)(sc, sg_list, sg_len);
490 #ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
491 if (cyg_io_eth_net_debug) {
493 old_console = start_console();
494 diag_printf("Ethernet recv:\n");
496 DIAG_DUMP_BUF_HDR(buf, 14);
497 DIAG_DUMP_BUF_BDY(buf+14, total_len-14);
500 diag_printf(" ...NULL buffer.\n");
501 end_console(old_console);
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,
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;
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!
533 CYGARC_HAL_RESTORE_GP();
537 // Determine the interrupt vector used by an interface
540 eth_drv_int_vector(void)
542 struct eth_drv_sc *sc = __local_enet_sc;
543 return sc->funs->int_vector(sc);
547 void eth_drv_dsr(cyg_vector_t vector,
551 diag_printf("eth_drv_dsr should not be called: vector %d, data %x\n",
556 // EOF src/stand_alone/eth_drv.c