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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [packages/] [io/] [eth/] [v2_0/] [src/] [stand_alone/] [eth_drv.c] - Blame information for rev 654

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

Line No. Rev Author Line
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 *)&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
        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 = &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 *)&__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 = &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 *)&__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(&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

powered by: WebSVN 2.1.0

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