OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [io/] [eth/] [current/] [src/] [stand_alone/] [eth_drv.c] - Blame information for rev 825

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.