]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/io/eth/v2_0/src/stand_alone/eth_drv.c
Initial revision
[karo-tx-redboot.git] / packages / io / eth / v2_0 / src / stand_alone / eth_drv.c
1 //==========================================================================
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, 2003 Red Hat, Inc.
12 // Copyright (C) 2003 Gary Thomas
13 //
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.
17 //
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
21 // for more details.
22 //
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.
26 //
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.
33 //
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.
36 //
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####
43 //
44 // Author(s):    gthomas
45 // Contributors: gthomas
46 // Date:         2000-07-14
47 // Purpose:      
48 // Description:  
49 //              
50 // This code is part of RedBoot (tm).
51 //
52 //####DESCRIPTIONEND####
53 //
54 //==========================================================================
55
56 #include <pkgconf/system.h>
57 #include <pkgconf/io_eth_drivers.h>
58
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>
66 #include <string.h>
67
68 // High-level ethernet driver
69
70
71 // Interfaces exported to drivers
72
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);
76
77 struct eth_drv_funs eth_drv_funs = {eth_drv_init, eth_drv_recv, eth_drv_tx_done};
78
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) )
84 #else
85 #define DIAG_DUMP_BUF_HDR( a, b )
86 #define DIAG_DUMP_BUF_BDY( a, b )
87 #endif
88
89 struct eth_drv_sc *__local_enet_sc = NULL;
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 *)&eth_msg_full;
212     eth_msg_free.first = eth_msg_free.last = (struct eth_msg *)&eth_msg_free;
213     for (i = 0;  i < NUM_ETH_MSG;  i++, msg++) {
214         eth_drv_msg_put(&eth_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         __local_enet_sc = sc;
230         eth_drv_start(sc);
231     }
232 }
233
234 //
235 // This [internal] function will be called to stop activity on an interface.
236 //
237 void
238 eth_drv_stop(void)
239 {
240     struct eth_drv_sc *sc = __local_enet_sc;
241
242     if (sc != NULL)
243         (sc->funs->stop)(sc);
244 }
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 = &eth_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;
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 = (6+6+2);  // 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 = 50000;
314     while (1) {
315         (sc->funs->poll)(sc);
316
317         if(packet_sent)
318             break;
319         
320         CYGACC_CALL_IF_DELAY_US(2*10);
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 = &eth_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;
395             }
396         }
397     }
398     (sc->funs->poll)(sc);  // Give the driver a chance to fetch packets
399     msg = eth_drv_msg_get(&eth_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(&eth_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(&eth_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(&eth_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