source: SVN/cambria/redboot/packages/io/eth/current/src/net/eth_drv.c @ 1

Last change on this file since 1 was 1, checked in by Tim Harvey, 2 years ago

restored latest version of files from server backup

Signed-off-by: Tim Harvey <tharvey@…>

File size: 32.9 KB
Line 
1//==========================================================================
2//
3//      src/net/eth_drv.c
4//
5//      Hardware independent ethernet driver
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// Copyright (C) 2002, 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-01-10
47// Purpose:      Hardware independent ethernet driver
48// Description: 
49//             
50//
51//####DESCRIPTIONEND####
52//
53//==========================================================================
54
55// High-level ethernet driver
56
57#include <sys/param.h>
58#include <sys/errno.h>
59#include <sys/ioctl.h>
60#include <sys/mbuf.h>
61#include <sys/socket.h>
62
63#include <net/if.h>
64#include <net/if_dl.h>
65#include <net/if_types.h>
66#include <net/netisr.h>
67
68#ifdef INET
69#include <netinet/in.h>
70#include <netinet/in_systm.h>
71#include <netinet/in_var.h>
72#include <netinet/ip.h>
73#include <netinet/if_ether.h>
74#endif
75
76#ifndef NBPFILTER
77#define NBPFILTER 0
78#endif
79
80#if NBPFILTER > 0
81#include <net/bpf.h>
82#include <net/bpfdesc.h>
83#endif
84
85#include <cyg/infra/cyg_ass.h>
86#include <cyg/hal/drv_api.h>
87#include <pkgconf/hal.h>
88#include <cyg/hal/hal_if.h>
89#include <pkgconf/io_eth_drivers.h> // module configury; SIMULATED_FAILURES
90#include <pkgconf/net.h>            // CYGPKG_NET_FAST_THREAD_TICKLE_DEVS?
91
92#include <cyg/io/eth/eth_drv.h>
93#include <cyg/io/eth/netdev.h>
94
95#ifndef min
96#define min( _x_, _y_ ) ((_x_) < (_y_) ? (_x_) : (_y_))
97#endif
98
99// ------------------------------------------------------------------------
100#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES
101
102#define noLOG_RANDOM 32 // so you can tell this is really being random
103#ifdef LOG_RANDOM
104static struct {
105    unsigned int *which;
106    unsigned int random;
107    unsigned int r100;
108} random_log[LOG_RANDOM];
109
110static int random_index = 0;
111#endif
112
113static unsigned int
114randomize( unsigned int *p )
115{
116    unsigned int r100;
117    HAL_CLOCK_READ( &r100 );
118    r100 ^= *p;
119    *p = (r100 * 1103515245) + 12345;
120    r100 &= 127;
121    if ( r100 >= 100 ) // spread the overflow around evenly
122        r100 = 4 * (r100 - 100);
123    if ( r100 >= 100 ) // and again - (125,126,127=>100,104,108)
124        r100 = 12 * (r100 - 100); // =>(0,48,96)
125#ifdef LOG_RANDOM
126    random_log[random_index].which  = p;
127    random_log[random_index].random = *p;
128    random_log[random_index].r100   = r100;
129    random_index++;
130    random_index &= (LOG_RANDOM-1);
131#endif
132    return r100;
133}
134
135#define SIMULATE_FAIL_SEND     1
136#define SIMULATE_FAIL_RECV     2
137#define SIMULATE_FAIL_CORRUPT  3
138
139static struct simulated_failure_state {
140    struct eth_drv_sc *sc;
141    unsigned int r_tx_fail;
142    unsigned int r_rx_fail;
143    unsigned int r_rx_corrupt;
144    cyg_tick_count_t droptime;
145    cyg_tick_count_t passtime;
146} simulated_failure_states[2] = {{0},{0}};
147
148static int
149simulate_fail( struct eth_drv_sc *sc, int which )
150{
151    struct simulated_failure_state *s; 
152   
153    for ( s = &simulated_failure_states[0]; s < &simulated_failure_states[2];
154          s++ ) {
155        if ( 0 == s->sc ) {
156            s->sc = sc;
157            s->r_tx_fail    = (unsigned int)sc;
158            s->r_rx_fail    = (unsigned int)sc ^ 0x01234567;
159            s->r_rx_corrupt = (unsigned int)sc ^ 0xdeadbeef;
160            s->droptime = 0;
161            s->passtime = 0;
162        }
163        if ( sc == s->sc )
164            break;
165    }
166    if ( &simulated_failure_states[2] == s ) {
167        CYG_FAIL( "No free slot in simulated_failure_states[]" );
168        return 1; // always fail
169    }
170
171#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATE_LINE_CUT
172    // Regardless of the question, we say "yes" during the period of
173    // unpluggedness...
174    {
175        cyg_tick_count_t now = cyg_current_time();
176        if ( now > s->droptime && 0 == s->passtime ) { // [initial condition]
177            s->droptime = 0; // go into a passing phase
178            (void)randomize( &s->r_tx_fail );
179            (void)randomize( &s->r_rx_fail );
180            (void)randomize( &s->r_rx_corrupt );
181            s->passtime = s->r_tx_fail + s->r_rx_fail + s->r_rx_corrupt;
182            s->passtime &= 0x3fff; // 16k cS is up to 160S, about 2.5 minutes
183            s->passtime += now;
184        }
185        else if ( now > s->passtime && 0 == s->droptime ) {
186            s->passtime = 0; // go into a dropping phase
187            (void)randomize( &s->r_tx_fail );
188            (void)randomize( &s->r_rx_fail );
189            (void)randomize( &s->r_rx_corrupt );
190            s->droptime = s->r_tx_fail + s->r_rx_fail + s->r_rx_corrupt;
191            s->droptime &= 0x0fff; // 4k cS is up to 40S, about 1/2 a minute
192            s->droptime += now;
193        }
194
195        if ( now < s->droptime )
196            return 1; // Say "no"
197    }
198#endif
199
200    switch ( which ) {
201#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATE_DROP_TX
202    case SIMULATE_FAIL_SEND: {
203        unsigned int z = randomize( &s->r_tx_fail );
204        return z < CYGPKG_IO_ETH_DRIVERS_SIMULATE_DROP_TX;
205    }
206#endif
207#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATE_DROP_RX
208    case SIMULATE_FAIL_RECV: {
209        unsigned int z = randomize( &s->r_rx_fail );
210        return z < CYGPKG_IO_ETH_DRIVERS_SIMULATE_DROP_RX;
211    }
212#endif
213#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATE_CORRUPT_RX
214    case SIMULATE_FAIL_CORRUPT: {
215        unsigned int z = randomize( &s->r_rx_corrupt );
216        return z < CYGPKG_IO_ETH_DRIVERS_SIMULATE_CORRUPT_RX;
217    }
218#endif
219    default:
220        // do nothing - for when options above are not enabled.
221    }
222    return 0;
223}
224
225#define noLOG_CORRUPTION 32 // so you can tell this is really being random
226#ifdef LOG_CORRUPTION
227static struct {
228    int len;
229    int thislen;
230    int off;
231    unsigned char xor;
232    unsigned char idx;
233} corruption_log[LOG_CORRUPTION];
234
235static int corruption_index = 0;
236#endif
237
238static void
239simulate_fail_corrupt_sglist( struct eth_drv_sg *sg_list, int sg_len )
240{
241    unsigned int z, len, i, off;
242    HAL_CLOCK_READ( &z );
243    z += simulated_failure_states[0].r_rx_corrupt;
244    z += simulated_failure_states[1].r_rx_corrupt;
245
246    CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow in corrupt" );
247
248    for ( i = 0, len = 0; i < sg_len && sg_list[i].buf && sg_list[i].len; i++ )
249        len =+ sg_list[i].len;
250
251    CYG_ASSERT( 1500 >= len, "sg...len > ether MTU" );
252    if ( 14 >= len ) // normal ether header
253        return;
254
255    off = z & 2047; // next (2^N-1) > MTU
256    while ( off > len )
257        off -= len;
258
259    for ( i = 0; i < sg_len && sg_list[i].buf && sg_list[i].len; i++ ) {
260        if ( off < sg_list[i].len ) { // corrupt this one
261            unsigned char *p = (unsigned char *)sg_list[i].buf;
262            p[off] ^= (0xff & (z >> 11));
263#ifdef LOG_CORRUPTION
264            corruption_log[corruption_index].len = len;
265            corruption_log[corruption_index].thislen = sg_list[i].len;
266            corruption_log[corruption_index].off = off;
267            corruption_log[corruption_index].xor = (0xff & (z >> 11));
268            corruption_log[corruption_index].idx = i;
269            corruption_index++;
270            corruption_index &= (LOG_CORRUPTION-1);
271#endif
272            return;
273        }
274        off -= sg_list[i].len;
275    }   
276    CYG_FAIL( "Didn't corrupt anything" );
277}
278
279#endif // CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES
280// ------------------------------------------------------------------------
281
282#ifdef CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT
283
284#include <cyg/hal/hal_if.h>
285
286// Use with care!  Local variable defined!
287#define START_CONSOLE()                                                                 \
288{   /* NEW BLOCK */                                                                     \
289    int _cur_console =                                                                  \
290        CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);      \
291    {                                                                                   \
292        int i;                                                                          \
293        if ( CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET,                 \
294                                          "info_console_force", &i,                     \
295                                          CYGNUM_FLASH_CFG_TYPE_CONFIG_BOOL ) ) {       \
296            if ( i ) {                                                                  \
297                if ( CYGACC_CALL_IF_FLASH_CFG_OP( CYGNUM_CALL_IF_FLASH_CFG_GET,         \
298                                                  "info_console_number", &i,            \
299                                                  CYGNUM_FLASH_CFG_TYPE_CONFIG_INT ) ){ \
300                    /* Then i is the console to force it to: */                         \
301                    CYGACC_CALL_IF_SET_CONSOLE_COMM( i );                               \
302                }                                                                       \
303            }                                                                           \
304        }                                                                               \
305    }
306
307#define END_CONSOLE()                                   \
308    CYGACC_CALL_IF_SET_CONSOLE_COMM(_cur_console);      \
309}   /* END BLOCK */
310
311#else
312#define START_CONSOLE()
313#define END_CONSOLE()
314#endif
315// ------------------------------------------------------------------------
316
317#ifdef CYGPKG_NET_FREEBSD_STACK
318extern char *_ioctl_name(u_long cmd);
319typedef void void_fun(void *);
320#endif
321
322static int  eth_drv_ioctl(struct ifnet *, u_long, caddr_t);
323static void eth_drv_send(struct ifnet *);
324static void eth_drv_start(struct eth_drv_sc *sc);
325
326#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
327int cyg_io_eth_net_debug = CYGDBG_IO_ETH_DRIVERS_DEBUG_VERBOSITY;
328#endif
329
330// Interfaces exported to drivers
331
332static void eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr);
333static void eth_drv_recv(struct eth_drv_sc *sc, int total_len);
334static void eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRESS key, int status);
335
336struct eth_drv_funs eth_drv_funs = {eth_drv_init, eth_drv_recv, eth_drv_tx_done};
337
338//
339// This function is called during system initialization to register a
340// network interface with the system.
341//
342static void
343eth_drv_init(struct eth_drv_sc *sc, unsigned char *enaddr)
344{
345    struct ifnet *ifp = &sc->sc_arpcom.ac_if;
346#ifdef CYGPKG_NET_FREEBSD_STACK
347    int unit;
348    char *np, *xp;
349#endif
350
351    // Set up hardware address
352    if (NULL != enaddr)
353        bcopy(enaddr, &sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
354
355    // Initialize ifnet structure
356    ifp->if_softc = sc;
357    ifp->if_start = eth_drv_send;
358    ifp->if_ioctl = eth_drv_ioctl;
359    ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
360#ifdef IFF_NOTRAILERS
361    ifp->if_flags |= IFF_NOTRAILERS;
362#endif
363#ifdef CYGPKG_NET_FREEBSD_STACK
364    ifp->if_name = xp = ifp->if_xname;
365    np = (char *)sc->dev_name;
366    unit = 0;
367    while (*np && !((*np >= '0') && (*np <= '9'))) *xp++ = *np++;
368    if (*np) {
369        *xp = '\0';
370        while (*np) {
371            unit = (unit * 10) + (*np++ - '0');
372        }
373        ifp->if_unit = unit;
374    }
375    ifp->if_init = (void_fun *)eth_drv_start;
376    ifp->if_output = ether_output;
377#else
378    bcopy((void *)sc->dev_name, ifp->if_xname, IFNAMSIZ);
379#endif
380    sc->state = 0;
381
382    // Attach the interface
383#ifdef CYGPKG_NET_FREEBSD_STACK
384    ether_ifattach(ifp, 0);
385#else
386    if_attach(ifp);
387    ether_ifattach(ifp);
388#endif
389
390#ifdef CYGSEM_HAL_VIRTUAL_VECTOR_DIAG
391// Set up interfaces so debug environment can share this device
392    {
393        void *dbg = CYGACC_CALL_IF_DBG_DATA();
394        if (!dbg) {
395            CYGACC_CALL_IF_DBG_DATA_SET((void *)sc);
396        }
397    }
398#endif
399}
400
401//
402// This [internal] function will be called to stop activity on an interface.
403//
404static void
405eth_drv_stop(struct eth_drv_sc *sc)
406{
407    (sc->funs->stop)(sc);
408    sc->state &= ~ETH_DRV_STATE_ACTIVE;
409}
410
411//
412// This [internal] function will be called to start activity on an interface.
413//
414static void
415eth_drv_start(struct eth_drv_sc *sc)
416{
417    struct ifnet *ifp = &sc->sc_arpcom.ac_if;
418
419    // Perform any hardware initialization
420    (sc->funs->start)(sc, (unsigned char *)&sc->sc_arpcom.ac_enaddr, 0);
421#ifdef CYGPKG_NET_FREEBSD_STACK
422    // resend multicast addresses if present
423    if(ifp->if_multiaddrs.lh_first && ifp->if_ioctl) {
424        int s = splimp();
425        ifp->if_ioctl(ifp, SIOCADDMULTI, 0);
426        splx(s);
427    }
428#endif
429    // Set 'running' flag, and clear output active flag.
430    ifp->if_flags |= IFF_RUNNING;
431    ifp->if_flags &= ~IFF_OACTIVE;
432    sc->state |= ETH_DRV_STATE_ACTIVE;
433    eth_drv_send(ifp);  // Try and start up transmit
434}
435
436//
437// This function supports "I/O control" operations on an interface.
438//
439static int 
440eth_drv_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
441{
442    struct eth_drv_sc *sc = ifp->if_softc;
443#ifndef CYGPKG_NET_FREEBSD_STACK
444    struct ifaddr *ifa = (struct ifaddr *) data;
445#endif
446    struct ifreq *ifr = (struct ifreq *)data;
447    int     s, error = 0;
448
449// DEBUG
450#ifdef CYGPKG_NET_FREEBSD_STACK
451    log(LOG_IOCTL, "%s: cmd: %s, data:\n", __FUNCTION__, _ioctl_name(cmd));
452    log_dump(LOG_IOCTL, data, 32);
453#endif
454// DEBUG
455
456    s = splnet();
457
458#ifdef CYGPKG_NET_FREEBSD_STACK
459    if ((error = ether_ioctl(ifp, cmd, data)) > 0) {
460#else
461    if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) {
462#endif
463        splx(s);
464        return error;
465    }
466
467    switch (cmd) {
468
469    case SIOCSIFADDR:
470#ifndef CYGPKG_NET_FREEBSD_STACK // Now in if_ethersubr.c
471        ifp->if_flags |= IFF_UP;
472
473        switch (ifa->ifa_addr->sa_family) {
474#ifdef INET
475        case AF_INET:
476            eth_drv_start(sc);
477            arp_ifinit(&sc->sc_arpcom, ifa);
478            break;
479#endif
480        default:
481            eth_drv_start(sc);
482            break;
483        }
484#endif // CYGPKG_NET_FREEBSD_STACK
485        break;
486
487    case SIOCGIFHWADDR:
488        // Get hardware (MAC) address
489        ifr->ifr_hwaddr.sa_family = AF_INET;
490        bcopy(&sc->sc_arpcom.ac_enaddr, &ifr->ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
491        break;
492
493    case SIOCSIFHWADDR:
494        // Set hardware (MAC) address
495        bcopy(&ifr->ifr_hwaddr.sa_data, &sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
496        if ((sc->funs->control)(sc, ETH_DRV_SET_MAC_ADDRESS,
497                                &sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN)) {
498            error = EINVAL;
499        }
500        break;
501
502#ifdef SIOCGIFSTATS
503    case SIOCGIFSTATS:
504#ifdef SIOCGIFSTATSUD
505    case SIOCGIFSTATSUD:
506#endif
507        // Get interface statistics:
508        if ((sc->funs->control)(sc, (cmd == SIOCGIFSTATS)
509                                ? ETH_DRV_GET_IF_STATS
510                                : ETH_DRV_GET_IF_STATS_UD,
511                                data, 0 ) ) {
512            error = EINVAL;
513        }
514        break;
515#endif // SIOCGIFSTATS
516
517    case SIOCSIFFLAGS:
518        if ((ifp->if_flags & IFF_UP) == 0 &&
519            (ifp->if_flags & IFF_RUNNING) != 0) {
520            /*
521             * If interface is marked down and it is running, then
522             * stop it.
523             */
524            eth_drv_stop(sc);
525            ifp->if_flags &= ~IFF_RUNNING;
526        } else
527            if ((ifp->if_flags & IFF_UP) != 0 &&
528                (ifp->if_flags & IFF_RUNNING) == 0) {
529                /*
530                 * If interface is marked up and it is stopped, then
531                 * start it.
532                 */
533                eth_drv_start(sc);
534            } else {
535                /*
536                 * Reset the interface to pick up changes in any other
537                 * flags that affect hardware registers.
538                 */
539                eth_drv_stop(sc);
540                eth_drv_start(sc);
541            }
542        break;
543
544#ifdef CYGPKG_NET_FREEBSD_STACK
545    case SIOCADDMULTI:
546    case SIOCDELMULTI:
547    {
548        struct ifmultiaddr *ifma;
549        struct eth_drv_mc_list mc_list;
550        int mode = (ifp->if_flags & IFF_ALLMULTI) ? ETH_DRV_SET_MC_ALL :
551                                                    ETH_DRV_SET_MC_LIST;
552
553#ifdef DEBUG
554        log(LOG_ADDR, "%s Multi\n",(cmd == SIOCADDMULTI) ? "Add" : "Del");
555#endif
556        mc_list.len = 0;
557        LIST_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) {
558            if (ifma->ifma_addr->sa_family != AF_LINK) {
559              continue;
560            }
561#ifdef DEBUG
562            log_dump(LOG_ADDR, LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 6);
563#endif
564            if ((LLADDR((struct sockaddr_dl *)ifma->ifma_addr)[0] & 0x01) == 0) {
565#ifdef DEBUG
566                log(LOG_ADDR, "** Not a multicast address - ignored\n");
567#endif
568                continue;
569            }
570            if (mc_list.len < ETH_DRV_MAX_MC) {
571                bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr),
572                      mc_list.addrs[mc_list.len], ETHER_ADDR_LEN);
573                mc_list.len++;
574            } else {
575                mode = ETH_DRV_SET_MC_ALL;
576            }
577        }
578        // Note: drivers may behave like IFF_ALLMULTI if the list is
579        // more than their hardware can handle, e.g. some can only handle 1.
580        if ((sc->funs->control)(sc, mode, &mc_list, sizeof(mc_list))) {
581            diag_printf( "[%s] Warning: Driver can't set multi-cast mode\n",
582                         __FUNCTION__ );
583            error = EINVAL;
584        }
585        break;
586    }
587#endif
588
589    default:
590        error = EINVAL;
591        break;
592    }
593
594    splx(s);
595    return (error);
596}
597
598//
599// Control whether any special locking needs to take place if we intend to
600// cooperate with a ROM monitor (e.g. RedBoot) using this hardware. 
601//
602#if defined(CYGSEM_HAL_USE_ROM_MONITOR) && \
603    defined(CYGSEM_HAL_VIRTUAL_VECTOR_DIAG) && \
604   !defined(CYGSEM_HAL_VIRTUAL_VECTOR_CLAIM_COMMS)
605
606// Indicate that special locking precautions are warranted.
607#define _LOCK_WITH_ROM_MONITOR
608
609// This defines the [well known] channel that RedBoot will use when it is
610// using the network hardware for the debug channel.
611#define RedBoot_TCP_CHANNEL CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS
612
613// Define this if you ever need to call 'diag_printf()' from interrupt level
614// code (ISR) and the debug channel might be using the network hardware. If
615// this is not the case, then disabling interrupts here is over-kill.
616//#define _LOCK_USING_INTERRUPTS
617#endif
618
619//
620// This routine is called to start transmitting if there is data
621// available.
622//
623static void 
624eth_drv_send(struct ifnet *ifp)
625{
626    struct eth_drv_sc *sc = ifp->if_softc;
627#if MAX_ETH_DRV_SG > 64
628    static  // Avoid large stack requirements
629#endif
630    struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
631    int sg_len;
632    struct mbuf *m0, *m;
633    int len, total_len;
634    unsigned char *data;
635#ifdef _LOCK_WITH_ROM_MONITOR
636#ifdef _LOCK_USING_INTERRUPTS
637    cyg_uint32 ints;
638#endif
639    bool need_lock = false;
640    int debug_chan;
641#endif // _LOCK_WITH_ROM_MONITOR
642
643    // This is now only called from network threads, so no guarding is
644    // required; locking is in place via the splfoo() mechanism already.
645
646    if ((ifp->if_flags & IFF_RUNNING) != IFF_RUNNING) {
647         return;
648    }
649
650    // If nothing on the queue, no need to bother hardware
651    if (IF_IS_EMPTY(&ifp->if_snd)) {
652        return;
653    }
654
655    while ((sc->funs->can_send)(sc) > 0) {
656        IF_DEQUEUE(&ifp->if_snd, m0);
657        if (m0 == 0) {
658            break;
659        }
660
661#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES
662        if ( simulate_fail( sc, SIMULATE_FAIL_SEND ) ) {
663            // must free the mbufs
664            m_freem(m0);
665            continue; // next packet to send
666        }
667#endif
668
669#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
670        if (cyg_io_eth_net_debug) {
671            START_CONSOLE();
672            diag_printf("Sending %d bytes\n", m0->m_pkthdr.len);
673            END_CONSOLE();
674        }
675#endif
676
677        /* We need to use m->m_pkthdr.len, so require the header */
678        if ((m0->m_flags & M_PKTHDR) == 0)
679            panic("eth_drv_send: no header mbuf");
680
681#if NBPFILTER > 0
682        /* Tap off here if there is a BPF listener. */
683        if (ifp->if_bpf)
684            bpf_mtap(ifp->if_bpf, m0);
685#endif
686
687        // Extract data pointers (don't actually move data here)
688        sg_len = 0;  total_len = 0;
689        for (m = m0; m ; m = m->m_next) {
690            data = mtod(m, u_char *);
691            len = m->m_len;
692            total_len += len;
693            sg_list[sg_len].buf = (CYG_ADDRESS)data;
694            sg_list[sg_len].len = len;
695            if ( len )
696                sg_len++;
697#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
698            if (cyg_io_eth_net_debug) {
699                START_CONSOLE();
700                diag_printf("xmit %d bytes at %x sg[%d]\n", len, data, sg_len);
701                if ( cyg_io_eth_net_debug > 1)
702                    diag_dump_buf(data, len);
703                END_CONSOLE();
704            }
705#endif
706            if ( MAX_ETH_DRV_SG < sg_len ) {
707#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS
708                int needed = 0;
709                struct mbuf *m1;
710                for (m1 = m0; m1 ; m1 = m1->m_next) needed++;
711                START_CONSOLE();
712                diag_printf("too many mbufs to tx, %d > %d, need %d\n", 
713                            sg_len, MAX_ETH_DRV_SG, needed );
714                END_CONSOLE();
715#endif
716                sg_len = 0;
717                break; // drop it on the floor
718            }
719        }
720
721#ifdef _LOCK_WITH_ROM_MONITOR
722        // Firm lock on this portion of the driver.  Since we are about to
723        // start messing with the actual hardware, it is imperative that the
724        // current thread not loose control of the CPU at this time.  Otherwise,
725        // the hardware could be left in an unusable state.  This caution is
726        // only warranted if there is a possibility of some other thread trying
727        // to use the hardware simultaneously.  The network stack would prevent
728        // this implicitly since all accesses are controlled by the "splX()"
729        // locks, but if there is a ROM monitor, such as RedBoot, also using
730        // the hardware, all bets are off.
731
732        // Note: these operations can be avoided if it were well known that
733        // RedBoot was not using the network hardware for diagnostic I/O.  This
734        // can be inferred by checking which I/O channel RedBoot is currently
735        // hooked to.
736        debug_chan = CYGACC_CALL_IF_SET_DEBUG_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
737        if (debug_chan == RedBoot_TCP_CHANNEL) {
738            need_lock = true;
739#ifdef _LOCK_USING_INTERRUPTS
740            HAL_DISABLE_INTERRUPTS(ints);
741#endif
742            cyg_drv_dsr_lock();
743        }
744#endif // _LOCK_WITH_ROM_MONITOR
745
746        // Tell hardware to send this packet
747        if ( sg_len )
748            (sc->funs->send)(sc, sg_list, sg_len, total_len, (unsigned long)m0);
749
750#ifdef _LOCK_WITH_ROM_MONITOR
751        // Unlock the driver & hardware.  It can once again be safely shared.
752        if (need_lock) {
753            cyg_drv_dsr_unlock();
754#ifdef _LOCK_USING_INTERRUPTS
755            HAL_RESTORE_INTERRUPTS(ints);
756#endif
757        }
758#endif // _LOCK_WITH_ROM_MONITOR
759#undef _LOCK_WITH_ROM_MONITOR
760    }
761}
762
763//
764// This function is called from the hardware driver when an output operation
765// has completed - i.e. the packet has been sent.
766//
767static struct mbuf *mbuf_key;
768
769static void
770eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRESS key, int status)
771{
772    struct ifnet *ifp = &sc->sc_arpcom.ac_if;
773    struct mbuf *m0 = (struct mbuf *)key;
774    CYGARC_HAL_SAVE_GP();
775
776    // Check for errors here (via 'status')
777    ifp->if_opackets++;
778    // Done with packet
779
780    // Guard against a NULL return - can be caused by race conditions in
781    // the driver, this is the neatest fixup:
782    if (m0) { 
783        mbuf_key = m0;
784        m_freem(m0);
785    }
786    // Start another if possible
787    eth_drv_send(ifp);
788    CYGARC_HAL_RESTORE_GP();
789}
790
791//
792// This function is called from a hardware driver to indicate that an input
793// packet has arrived.  The routine will set up appropriate network resources
794// (mbuf's) to hold the data and call back into the driver to retrieve the data.
795//
796static void
797eth_drv_recv(struct eth_drv_sc *sc, int total_len)
798{
799    struct ifnet *ifp = &sc->sc_arpcom.ac_if;
800    struct ether_header _eh, *eh=&_eh;
801    struct mbuf *top, **mp, *m;
802    int mlen;
803    unsigned char *data;
804#if MAX_ETH_DRV_SG > 64
805    static  // Avoid large stack requirements
806#endif
807    struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
808    int sg_len;
809
810    if ((ifp->if_flags & IFF_RUNNING) != IFF_RUNNING) {
811        return;  // Interface not up, ignore this request
812    }
813
814    CYG_ASSERT( 0 != total_len, "total_len is zero!" );
815    CYG_ASSERT( 0 <= total_len, "total_len is negative!" );
816    CYG_ASSERT( sizeof( struct ether_header ) <= total_len,
817                "No ether header here!" );
818
819    if ( total_len < sizeof( struct ether_header ) )
820        // Our arithmetic below would go wrong
821        return;
822
823    CYGARC_HAL_SAVE_GP();  // This is down here to make matching restore neat
824
825    /* Pull packet off interface. */
826    MGETHDR(m, M_DONTWAIT, MT_DATA);
827    if (m == 0) {
828#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS
829        START_CONSOLE();
830        diag_printf("warning: eth_recv out of MBUFs\n");
831#ifdef CYGDBG_NET_SHOW_MBUFS       
832        cyg_net_show_mbufs();
833#endif
834        END_CONSOLE();
835#endif
836    }
837
838    // Set up buffers
839    // Unload ethernet header separately so IP/UDP/TCP headers are aligned
840    sg_list[0].buf = (CYG_ADDRESS)eh;
841    sg_list[0].len = sizeof(*eh);
842    sg_len = 1;
843
844    // Compute total length (minus ethernet header)
845    total_len -= sizeof(*eh);
846
847    top = 0;
848    mlen = MHLEN;
849    mp = &top;
850
851    if (m) {
852        m->m_pkthdr.rcvif = ifp;
853        m->m_pkthdr.len = total_len;
854    } else {
855        sg_list[sg_len].buf = (CYG_ADDRESS)0;
856        sg_list[sg_len].len = total_len;
857        sg_len++;
858        total_len = 0;
859    }
860
861    while (total_len > 0) {
862        if (top) {
863            MGET(m, M_DONTWAIT, MT_DATA);
864            if (m == 0) {
865                m_freem(top);
866#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS
867                START_CONSOLE();
868                diag_printf("out of MBUFs [2]");
869#ifdef CYGDBG_NET_SHOW_MBUFS               
870                cyg_net_show_mbufs();
871#endif
872                END_CONSOLE();
873#endif
874                sg_list[sg_len].buf = (CYG_ADDRESS)0;
875                sg_list[sg_len].len = total_len;
876                sg_len++;
877                top = 0;
878                break;
879            }
880            mlen = MLEN;
881        }
882        if (total_len >= MINCLSIZE) {
883            MCLGET(m, M_DONTWAIT);
884            if ((m->m_flags & M_EXT) == 0) {
885                m_freem(top);
886                m_free(m);
887#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS
888                START_CONSOLE();
889                diag_printf("warning: eth_recv out of MBUFs\n");
890#ifdef CYGDBG_NET_SHOW_MBUFS               
891                cyg_net_show_mbufs();
892#endif
893                END_CONSOLE();
894#endif
895                sg_list[sg_len].buf = (CYG_ADDRESS)0;
896                sg_list[sg_len].len = total_len;
897                sg_len++;
898                top = 0;
899                break;
900            }
901            mlen = MCLBYTES;
902        }
903        m->m_len = mlen = min(total_len, mlen);
904        total_len -= mlen;
905        data = mtod(m, caddr_t);
906        sg_list[sg_len].buf = (CYG_ADDRESS)data;
907        sg_list[sg_len].len = mlen;
908        sg_len++;
909        *mp = m;
910        mp = &m->m_next;
911    } // endwhile
912
913    // Ask hardware to unload buffers
914    (sc->funs->recv)(sc, sg_list, sg_len);
915
916#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES
917    if ( simulate_fail( sc, SIMULATE_FAIL_RECV ) ) {
918        // toss the packet - note that some hardware gets
919        // fussy if the packet isn't "unloaded", thus we
920        // have to wait until now to throw it away
921        if (top) {
922            m_free(top);
923        }
924        ifp->if_ierrors++;
925        return;
926    }
927
928    if ( simulate_fail( sc, SIMULATE_FAIL_CORRUPT ) ) {
929        // Corrupt the data
930        simulate_fail_corrupt_sglist( sg_list, sg_len );
931    }
932#endif
933
934#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
935    if (cyg_io_eth_net_debug) {
936        int i;
937        START_CONSOLE();
938        for (i = 0;  i < sg_len;  i++) {
939            if (sg_list[i].buf) {
940                diag_printf("rx %d bytes at %x sg[%d]\n", sg_list[i].len, sg_list[i].buf, i);
941                if ( cyg_io_eth_net_debug > 1 )
942                    diag_dump_buf((void *)sg_list[i].buf, sg_list[i].len);
943            }
944        }
945        END_CONSOLE();
946    }
947#endif
948    m = top;
949    if (m == 0) {
950        ifp->if_ierrors++;
951    }
952    else {
953        ifp->if_ipackets++;
954
955#if NBPFILTER > 0
956#error FIXME - Need mbuf with ethernet header attached
957        /*
958         * Check if there's a BPF listener on this interface.
959         * If so, hand off the raw packet to bpf.
960         */
961        if (ifp->if_bpf)
962            bpf_mtap(ifp->if_bpf, m);
963#endif
964
965        // Push data into protocol stacks
966        ether_input(ifp, eh, m);
967    }
968    CYGARC_HAL_RESTORE_GP();
969}
970
971
972// ------------------------------------------------------------------------
973// DSR to schedule network delivery thread
974
975extern void ecos_synch_eth_drv_dsr(void); // from ecos/timeout.c in net stack
976
977void
978eth_drv_dsr(cyg_vector_t vector,
979            cyg_ucount32 count,
980            cyg_addrword_t data)
981{
982    struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
983
984#ifdef CYGDBG_USE_ASSERTS
985    // then check that this really is a "sc"
986    {
987        cyg_netdevtab_entry_t *t;
988        for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++)
989            if ( ((struct eth_drv_sc *)t->device_instance) == sc )
990                break; // found it
991        CYG_ASSERT( t != &__NETDEVTAB_END__, "eth_drv_dsr: Failed to find sc in NETDEVTAB" );
992    }
993#endif // Checking code
994
995    sc->state |= ETH_DRV_NEEDS_DELIVERY;
996
997    ecos_synch_eth_drv_dsr(); // [request] run delivery function for this dev
998}
999
1000// This is called from the delivery thread, to do just that:
1001void eth_drv_run_deliveries( void )
1002{
1003    cyg_netdevtab_entry_t *t;
1004    for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) {
1005        struct eth_drv_sc *sc = (struct eth_drv_sc *)t->device_instance;
1006        if ( ETH_DRV_NEEDS_DELIVERY & sc->state ) {
1007#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT)
1008            cyg_bool was_ctrlc_int;
1009#endif
1010            sc->state &=~ETH_DRV_NEEDS_DELIVERY;
1011#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT)
1012            was_ctrlc_int = HAL_CTRLC_CHECK((*sc->funs->int_vector)(sc), (int)sc);
1013            if (!was_ctrlc_int) // Fall through and run normal code
1014#endif
1015            (*sc->funs->deliver)(sc);
1016        }
1017    }
1018}
1019
1020// This is called from the delivery thread, to unstick devices if there is
1021// no network activity.
1022#ifdef CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
1023void eth_drv_tickle_devices( void )
1024{
1025    cyg_netdevtab_entry_t *t;
1026    for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) {
1027        struct eth_drv_sc *sc = (struct eth_drv_sc *)t->device_instance;
1028        if ( ETH_DRV_STATE_ACTIVE & sc->state ) {
1029            struct ifnet *ifp = &sc->sc_arpcom.ac_if;
1030            // Try to dequeue a packet for this interface, if we can.  This
1031            // will call can_send() for active interfaces.  It is calls to
1032            // this function from tx_done() which normally provide
1033            // continuous transmissions; otherwise we do not get control.
1034            // This call fixes that.
1035            if (!IF_IS_EMPTY(&ifp->if_snd)) {
1036                eth_drv_send(ifp);
1037            }
1038        }
1039    }
1040}
1041#endif // CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
1042
1043// ------------------------------------------------------------------------
1044
1045#ifdef CYGPKG_IO_PCMCIA
1046// Lookup a 'netdev' entry, assuming that it is an ethernet device.
1047cyg_netdevtab_entry_t * 
1048eth_drv_netdev(char *name)
1049{
1050    cyg_netdevtab_entry_t *t;
1051    struct eth_drv_sc *sc;
1052    for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) {
1053        sc = (struct eth_drv_sc *)t->device_instance;
1054        if (strcmp(sc->dev_name, name) == 0) {
1055            return t;
1056        }
1057    }
1058    return (cyg_netdevtab_entry_t *)NULL;
1059}
1060#endif // CYGPKG_IO_PCMCIA
1061
1062// EOF src/net/eth_drv.c
Note: See TracBrowser for help on using the repository browser.